Skip to content

Commit

Permalink
feat(compiler): generate proper reexports in .ngfactory.ts files to…
Browse files Browse the repository at this point in the history
… not need transitive deps for compiling `.ngfactory.ts` files. (angular#13524)

Note: This checks the constructors of `@Injectable` classes more strictly.
E.g this will fail now as the constructor argument has no `@Inject` nor is
the type of the argument a DI token.

```
@Injectable()
class MyService {
  constructor(dep: string) {}
}
```

Last part of angular#12787
Closes angular#12787
  • Loading branch information
tbosch authored and hansl committed Dec 27, 2016
1 parent 6976903 commit 9c69703
Show file tree
Hide file tree
Showing 37 changed files with 995 additions and 410 deletions.
85 changes: 21 additions & 64 deletions modules/@angular/compiler/src/aot/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {SchemaMetadata} from '@angular/core';

import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, componentFactoryName, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection';
Expand All @@ -22,13 +22,14 @@ import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';

import {AotCompilerHost} from './compiler_host';
import {GeneratedFile} from './generated_file';
import {StaticSymbol} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries, summaryFileName} from './summary_serializer';
import {serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';

export class AotCompiler {
private _animationCompiler = new AnimationCompiler();
Expand Down Expand Up @@ -65,12 +66,13 @@ export class AotCompiler {
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
injectables: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const generatedFiles: GeneratedFile[] = [];

generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
generatedFiles.push(this._createSummary(
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));

// compile all ng modules
exportedVars.push(
Expand Down Expand Up @@ -109,15 +111,16 @@ export class AotCompiler {
});
if (statements.length > 0) {
const srcModule = this._codegenSourceModule(
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
generatedFiles.unshift(srcModule);
}
return generatedFiles;
}

private _createSummary(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
targetExportedVars: string[]): GeneratedFile {
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
const typeSummaries = [
Expand All @@ -126,8 +129,13 @@ export class AotCompiler {
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
];
const json = serializeSummaries(
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
const {json, exportAs} = serializeSummaries(
this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
exportAs.forEach((entry) => {
targetStatements.push(
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
targetExportedVars.push(entry.exportAs);
});
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
}

Expand All @@ -150,12 +158,6 @@ export class AotCompiler {
}

const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);

appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
});

targetStatements.push(...appCompileResult.statements);
return appCompileResult.ngModuleFactoryVar;
}
Expand All @@ -172,13 +174,12 @@ export class AotCompiler {
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
const hostMeta = createHostComponentMeta(
this._symbolResolver.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const compFactoryVar = _componentFactoryName(compMeta.type);
const compFactoryVar = componentFactoryName(compMeta.type.reference);
targetStatements.push(
o.variable(compFactoryVar)
.set(o.importExpr(
Expand Down Expand Up @@ -219,7 +220,7 @@ export class AotCompiler {
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
}
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
targetStatements.push(...viewResult.statements);
return viewResult.viewClassVar;
}

Expand All @@ -241,27 +242,6 @@ export class AotCompiler {
}
}

function _resolveViewStatements(
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
vfd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.reference = reflector.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
}
});
return compileResult.statements;
}


function _resolveStyleStatements(
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] {
Expand All @@ -272,15 +252,6 @@ function _resolveStyleStatements(
return compileResult.statements;
}

function _ngfactoryModuleUrl(dirUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}

function _componentFactoryName(comp: CompileIdentifierMetadata): string {
return `${identifierName(comp)}NgFactory`;
}

function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
}
Expand All @@ -292,20 +263,6 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
}
}

function _splitTypescriptSuffix(path: string): string[] {
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}

const lastDot = path.lastIndexOf('.');

if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
}

return [path, ''];
}

export interface NgAnalyzedModules {
ngModules: CompileNgModuleMetadata[];
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
Expand Down
15 changes: 11 additions & 4 deletions modules/@angular/compiler/src/aot/compiler_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
import {StaticSymbolCache} from './static_symbol';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {AotSummaryResolver} from './summary_resolver';



/**
* Creates a new AotCompiler based on options and a host.
*/
Expand Down Expand Up @@ -69,13 +70,19 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector);
symbolCache, staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const importResolver = {
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
fileNameToModuleName: (fileName: string, containingFilePath: string) =>
compilerHost.fileNameToModuleName(fileName, containingFilePath)
};
const compiler = new AotCompiler(
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry),
symbolResolver);
return {compiler, reflector: staticReflector};
}
15 changes: 10 additions & 5 deletions modules/@angular/compiler/src/aot/compiler_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ImportResolver} from '../output/path_util';

import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolverHost} from './static_symbol_resolver';
import {AotSummaryResolverHost} from './summary_resolver';
import {AotSummarySerializerHost} from './summary_serializer';

/**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
AotSummaryResolverHost, AotSummarySerializerHost {
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
/**
* Converts a file path to a module name that can be used as an `import.
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
*
* See ImportResolver.
*/
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/;

/**
* Loads a resource (e.g. html / css)
*/
Expand Down
34 changes: 23 additions & 11 deletions modules/@angular/compiler/src/aot/static_reflector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ export class StaticReflector implements ReflectorReader {
annotations = [];
const classMetadata = this.getTypeMetadata(type);
if (classMetadata['extends']) {
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
annotations.push(...parentAnnotations);
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
const parentAnnotations = this.annotations(parentType);
annotations.push(...parentAnnotations);
}
}
if (classMetadata['decorators']) {
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
Expand All @@ -101,10 +104,13 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type);
propMetadata = {};
if (classMetadata['extends']) {
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
Object.keys(parentPropMetadata).forEach((parentProp) => {
propMetadata[parentProp] = parentPropMetadata[parentProp];
});
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
const parentPropMetadata = this.propMetadata(parentType);
Object.keys(parentPropMetadata).forEach((parentProp) => {
propMetadata[parentProp] = parentPropMetadata[parentProp];
});
}
}

const members = classMetadata['members'] || {};
Expand Down Expand Up @@ -156,7 +162,10 @@ export class StaticReflector implements ReflectorReader {
parameters.push(nestedResult);
});
} else if (classMetadata['extends']) {
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
parameters = this.parameters(parentType);
}
}
if (!parameters) {
parameters = [];
Expand All @@ -176,10 +185,13 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type);
methodNames = {};
if (classMetadata['extends']) {
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
Object.keys(parentMethodNames).forEach((parentProp) => {
methodNames[parentProp] = parentMethodNames[parentProp];
});
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
const parentMethodNames = this._methodNames(parentType);
Object.keys(parentMethodNames).forEach((parentProp) => {
methodNames[parentProp] = parentMethodNames[parentProp];
});
}
}

const members = classMetadata['members'] || {};
Expand Down
9 changes: 8 additions & 1 deletion modules/@angular/compiler/src/aot/static_symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
* This token is unique for a filePath and name and can be used as a hash table key.
*/
export class StaticSymbol {
constructor(public filePath: string, public name: string, public members?: string[]) {}
constructor(public filePath: string, public name: string, public members: string[]) {}

assertNoMembers() {
if (this.members.length) {
throw new Error(
`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
}
}
}

/**
Expand Down
Loading

0 comments on commit 9c69703

Please sign in to comment.