refactor(ivy): remove the backpatch compiler (#23441)

PR Close #23441
This commit is contained in:
Victor Berchet
2018-04-18 16:23:38 -07:00
parent 6ff164be0e
commit a19e018439
8 changed files with 53 additions and 686 deletions

View File

@ -6,19 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotCompilerHost, AotCompilerOptions, AotSummaryResolver, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompileTypeMetadata, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleResolver, ParseError, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver, templateSourceUrl} from '@angular/compiler';
import {AotCompilerOptions, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleResolver, ParseError, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core';
import * as ts from 'typescript';
import {NgAnalyzedModules} from '../../src/aot/compiler';
import {ConstantPool} from '../../src/constant_pool';
import {ParserError} from '../../src/expression_parser/ast';
import * as html from '../../src/ml_parser/ast';
import {removeWhitespaces} from '../../src/ml_parser/html_whitespaces';
import * as o from '../../src/output/output_ast';
import {ModuleKind, compileModuleBackPatch} from '../../src/render3/r3_back_patch_compiler';
import {compileModuleFactory} from '../../src/render3/r3_module_factory_compiler';
import {compilePipe} from '../../src/render3/r3_pipe_compiler';
import {OutputMode} from '../../src/render3/r3_types';
import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler';
import {HtmlToTemplateTransform} from '../../src/render3/r3_template_transform';
import {compileComponent, compileDirective} from '../../src/render3/r3_view_compiler_local';
import {BindingParser} from '../../src/template_parser/binding_parser';
import {OutputContext, escapeRegExp} from '../../src/util';
import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, expectNoDiagnostics, settings, toMockFileArray} from '../aot/test_util';
@ -252,9 +251,6 @@ function doCompile(
const emitter = new TypeScriptEmitter();
const moduleName = compilerHost.fileNameToModuleName(
fakeOutputContext.genFilePath, fakeOutputContext.genFilePath);
const result = emitter.emitStatementsAndContext(
fakeOutputContext.genFilePath, fakeOutputContext.statements, '', false,
/* referenceFilter */ undefined,
@ -294,81 +290,18 @@ export function compile(
const parsedTemplate = templateParser.parse(
metadata, htmlAst, directives, pipes, module.schemas, fakeUrl, false);
compileComponent(
outputCtx, metadata, pipes, parsedTemplate.template, reflector, hostBindingParser,
OutputMode.PartialClass);
outputCtx, metadata, pipes, parsedTemplate.template, reflector, hostBindingParser);
} else {
compileDirective(
outputCtx, metadata, reflector, hostBindingParser, OutputMode.PartialClass);
outputCtx, metadata, reflector, hostBindingParser);
}
} else if (resolver.isPipe(pipeOrDirective)) {
const metadata = resolver.getPipeMetadata(pipeOrDirective);
if (metadata) {
compilePipe(outputCtx, metadata, reflector, OutputMode.PartialClass);
compilePipe(outputCtx, metadata, reflector);
}
}
}
});
}
const DTS = /\.d\.ts$/;
const EXT = /(\.\w+)+$/;
const NONE_WORD = /\W/g;
const NODE_MODULES = /^.*\/node_modules\//;
function getBackPatchFunctionName(type: CompileTypeMetadata) {
const filePath = (type.reference.filePath as string)
.replace(EXT, '')
.replace(NODE_MODULES, '')
.replace(NONE_WORD, '_');
return `ngBackPatch_${filePath.split('/').filter(s => !!s).join('_')}_${type.reference.name}`;
}
function getBackPatchReference(type: CompileTypeMetadata): o.Expression {
return o.variable(getBackPatchFunctionName(type));
}
export function backPatch(
data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {},
errorCollector: (error: any, fileName?: string) => void = error => { throw error;}) {
return doCompile(
data, angularFiles, options, errorCollector,
(outputCtx: OutputContext, analyzedModules: NgAnalyzedModules,
resolver: CompileMetadataResolver, htmlParser: HtmlParser, templateParser: TemplateParser,
hostBindingParser: BindingParser, reflector: StaticReflector) => {
const parseTemplate =
(compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
directiveIdentifiers: CompileIdentifierMetadata[]) => {
const directives =
directiveIdentifiers.map(dir => resolver.getDirectiveSummary(dir.reference));
const pipes = ngModule.transitiveModule.pipes.map(
pipe => resolver.getPipeSummary(pipe.reference));
return templateParser.parse(
compMeta, compMeta.template !.htmlAst !, directives, pipes, ngModule.schemas,
templateSourceUrl(ngModule.type, compMeta, compMeta.template !), true);
};
for (const module of analyzedModules.ngModules) {
compileModuleBackPatch(
outputCtx, getBackPatchFunctionName(module.type), module,
DTS.test(module.type.reference.filePath) ? ModuleKind.Renderer2 :
ModuleKind.Renderer3,
getBackPatchReference, parseTemplate, reflector, resolver);
}
});
}
export function createFactories(
data: MockDirectory, context: MockData, options: AotCompilerOptions = {},
errorCollector: (error: any, fileName?: string) => void = error => { throw error;}) {
return doCompile(
data, context, options, errorCollector,
(outputCtx: OutputContext, analyzedModules: NgAnalyzedModules,
resolver: CompileMetadataResolver, htmlParser: HtmlParser, templateParser: TemplateParser,
hostBindingParser: BindingParser, reflector: StaticReflector) => {
for (const module of analyzedModules.ngModules) {
compileModuleFactory(outputCtx, module, getBackPatchReference, resolver);
}
});
}

View File

@ -1,218 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {MockDirectory, emitLibrary, mergeMaps, setup} from '../aot/test_util';
import {backPatch, expectEmit} from './mock_compile';
describe('r3_back_patch_compiler', () => {
const angularFiles = setup({
compileAngular: true,
compileAnimations: false,
compileCommon: true,
});
it('should back-patch a component in a library', () => {
const libraries = {
lib1: {
src: {
'component.ts': `
import {Component} from '@angular/core';
@Component({
selector: 'lib1-cmp',
template: '<h1> Hello, {{name}}!</h1>'
})
export class Lib1Component {
name: string;
}
`,
'directive.ts': `
import {Directive, HostBinding} from '@angular/core';
@Directive({selector: '[lib1-dir]'})
export class Lib1Directive {
@HostBinding('id') dirId = 'some id';
}
`,
'service.ts': `
import {Injectable} from '@angular/core';
@Injectable()
export class Lib1Service {
getSomeInfo() { return 'some info'; }
}
`,
'pipe.ts': `
import {Pipe} from '@angular/core';
@Pipe({name: 'lib1Pipe', pure: true})
export class Lib1Pipe {
transform(v: any) { return v; }
}
`,
'module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Component} from './component';
import {Lib1Directive} from './directive';
import {Lib1Service} from './service';
import {Lib1Pipe} from './pipe';
@NgModule({
exports: [Lib1Component, Lib1Directive, Lib1Pipe],
declarations: [Lib1Component, Lib1Directive, Lib1Pipe],
providers: [Lib1Service]
})
export class Lib1Module {}
`
}
},
lib2: {
src: {
'component.ts': `
import {Component} from '@angular/core';
@Component({
selector: 'lib2-cmp',
template: '<h1> Hello, {{name}}!</h1>'
})
export class Lib2Component {
name: string;
}
`,
'directive.ts': `
import {Directive, HostBinding} from '@angular/core';
@Directive({selector: '[lib2-dir]'})
export class Lib2Directive {
@HostBinding('id') dirId = 'some id';
}
`,
'service.ts': `
import {Injectable} from '@angular/core';
@Injectable()
export class Lib2Service {
getSomeInfo() { return 'some info'; }
}
`,
'pipe.ts': `
import {Pipe} from '@angular/core';
@Pipe({name: 'lib2Pipe', pure: true})
export class Lib2Pipe {
transform(v: any) { return v; }
}
`,
'module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Module} from '../../lib1/src/module';
import {Lib2Component} from './component';
import {Lib2Directive} from './directive';
import {Lib2Service} from './service';
import {Lib2Pipe} from './pipe';
@NgModule({
imports: [Lib1Module],
exports: [Lib2Component, Lib2Directive, Lib2Pipe],
declarations: [Lib2Component, Lib2Directive, Lib2Pipe],
providers: [Lib2Service]
})
export class Lib2Module {}
`
}
},
};
const app = {
app: {
src: {
'app.component.ts': `
import {Component} from '@angular/core';
import {Lib1Service} from '../../lib1/src/service';
import {Lib2Service} from '../../lib2/src/service';
@Component({
selector: 'app-cmp',
template: \`
<lib1-cmp lib2-dir>{{'v' | lib1Pipe | lib2Pipe}}</lib1-cmp>
<lib2-cmp lib1-dir>{{'v' | lib2Pipe | lib2Pipe}}</lib2-cmp>
\`
})
export class AppComponent {
constructor(public lib1s: Lib1Service, public lib2s: Lib2Service) {}
}
`,
'app.module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Module} from '../../lib1/src/module';
import {Lib2Module} from '../../lib2/src/module';
import {AppComponent} from './app.component';
@NgModule({
imports: [Lib1Module, Lib2Module],
declarations: [AppComponent]
})
export class AppModule {
}
`
}
}
};
const lib1_module_back_patch = `
export function ngBackPatch__lib1_src_module_Lib1Module() {
// @__BUILD_OPTIMIZER_COLOCATE__
$lib1_c$.Lib1Component.ngComponentDef = $r3$.ɵdefineComponent(…);
// @__BUILD_OPTIMIZER_COLOCATE__
$lib1_d$.Lib1Directive.ngDirectiveDef = $r3$.ɵdefineDirective(…);
// @__BUILD_OPTIMIZER_COLOCATE__
$lib1_p$.Lib1Pipe.ngPipeDef = $r3$.ɵdefinePipe(…);
}
`;
const lib2_module_back_patch = `
export function ngBackPatch__lib2_src_module_Lib2Module() {
// @__BUILD_OPTIMIZER_REMOVE__
ngBackPatch__lib1_src_module_Lib1Module();
// @__BUILD_OPTIMIZER_COLOCATE__
$lib2_c$.Lib2Component.ngComponentDef = $r3$.ɵdefineComponent(…);
// @__BUILD_OPTIMIZER_COLOCATE__
$lib2_d$.Lib2Directive.ngDirectiveDef = $r3$.ɵdefineDirective(…);
// @__BUILD_OPTIMIZER_COLOCATE__
$lib1_p$.Lib2Pipe.ngPipeDef = $r3$.ɵdefinePipe(…);
}
`;
const app_module_back_patch = `
export function ngBackPatch__app_src_app_AppModule() {
// @__BUILD_OPTIMIZER_REMOVE__
ngBackPatch__lib1_src_module_Lib1Module();
// @__BUILD_OPTIMIZER_REMOVE__
ngBackPatch__lib2_src_module_Lib2Module();
}
`;
const context = mergeMaps(emitLibrary(angularFiles, libraries), angularFiles);
const result = backPatch(app, context);
expectEmit(result.source, lib1_module_back_patch, 'Invalid lib1 back-patch');
expectEmit(result.source, lib2_module_back_patch, 'Invalid lib2 back-patch');
expectEmit(result.source, app_module_back_patch, 'Invalid app module back-patch');
});
});

View File

@ -1,202 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {MockDirectory, emitLibrary, mergeMaps, setup} from '../aot/test_util';
import {createFactories, expectEmit} from './mock_compile';
describe('r3_factory_compiler', () => {
const angularFiles = setup({
compileAngular: true,
compileAnimations: false,
compileCommon: true,
});
it('should generate factories for all modules', () => {
const libraries = {
lib1: {
src: {
'component.ts': `
import {Component} from '@angular/core';
@Component({
selector: 'lib1-cmp',
template: '<h1> Hello, {{name}}!</h1>'
})
export class Lib1Component {
name: string;
}
`,
'directive.ts': `
import {Directive, HostBinding} from '@angular/core';
@Directive({selector: '[lib1-dir]'})
export class Lib1Directive {
@HostBinding('id') dirId = 'some id';
}
`,
'service.ts': `
import {Injectable} from '@angular/core';
@Injectable()
export class Lib1Service {
getSomeInfo() { return 'some info'; }
}
`,
'module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Component} from './component';
import {Lib1Directive} from './directive';
import {Lib1Service} from './service';
@NgModule({
exports: [Lib1Component, Lib1Directive],
declarations: [Lib1Component, Lib1Directive],
providers: [Lib1Service]
})
export class Lib1Module {}
`
}
},
lib2: {
src: {
'component.ts': `
import {Component} from '@angular/core';
@Component({
selector: 'lib2-cmp',
template: '<h1> Hello, {{name}}!</h1>'
})
export class Lib2Component {
name: string;
}
`,
'directive.ts': `
import {Directive, HostBinding} from '@angular/core';
@Directive({selector: '[lib2-dir]'})
export class Lib2Directive {
@HostBinding('id') dirId = 'some id';
}
`,
'service.ts': `
import {Injectable} from '@angular/core';
@Injectable()
export class Lib2Service {
getSomeInfo() { return 'some info'; }
}
`,
'module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Module} from '../../lib1/src/module';
import {Lib2Component} from './component';
import {Lib2Directive} from './directive';
import {Lib2Service} from './service';
@NgModule({
imports: [Lib1Module],
exports: [Lib2Component, Lib2Directive],
declarations: [Lib2Component, Lib2Directive],
providers: [Lib2Service]
})
export class Lib2Module {}
`
},
}
};
const app = {
app: {
src: {
'app.component.ts': `
import {Component} from '@angular/core';
import {Lib1Service} from '../../lib1/src/service';
import {Lib2Service} from '../../lib2/src/service';
@Component({
selector: 'app-cmp',
template: \`
<lib1-cmp lib2-dir></lib1-cmp>
<lib2-cmp lib1-dir></lib2-cmp>
\`
})
export class AppComponent {
constructor(public lib1s: Lib1Service, public lib2s: Lib2Service) {}
}
`,
'app.module.ts': `
import {NgModule} from '@angular/core';
import {Lib1Module} from '../../lib1/src/module';
import {Lib2Module} from '../../lib2/src/module';
import {AppComponent} from './app.component';
@NgModule({
imports: [Lib1Module, Lib2Module],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {
}
`
}
}
};
const lib1_module_factory = `
export const Lib1ModuleNgFactory: $any$ = {
moduleType: $i1$.Lib1Module,
create: function Lib1ModuleNgFactory_Create(parentInjector: $any$) {
if ((this.patchedDeps !== true)) {
this.patchedDeps = true;
ngBackPatch__lib1_src_module_Lib1Module();
}
}
};
`;
const lib2_module_factory = `
export const Lib2ModuleNgFactory: $any$ = {
moduleType: $i2$.Lib2Module,
create: function Lib2ModuleNgFactory_Create(parentInjector: $any$) {
if ((this.patchedDeps !== true)) {
this.patchedDeps = true;
ngBackPatch__lib2_src_module_Lib2Module();
}
}
};
`;
// TODO(chuckj): What should we do with the bootstrap components?
const app_module_factory = `
export const AppModuleNgFactory: $any$ = {
moduleType: AppModule,
create: function AppModuleNgFactory_Create(parentInjector: $any$) {
if ((this.patchedDeps !== true)) {
this.patchedDeps = true;
ngBackPatch__app_src_app_AppModule();
}
}
};
`;
const context = mergeMaps(emitLibrary(angularFiles, libraries), angularFiles);
const result = createFactories(app, context);
expectEmit(result.source, lib1_module_factory, 'Invalid module factory for lib1');
expectEmit(result.source, lib2_module_factory, 'Invalid module factory for lib2');
expectEmit(result.source, app_module_factory, 'Invalid module factory for app');
});
});