feat(compiler): generate proper reexports in .ngfactory.ts files to not need transitive deps for compiling .ngfactory.ts files. (#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 #12787
Closes #12787
This commit is contained in:
Tobias Bosch
2016-12-27 09:36:47 -08:00
committed by Hans
parent 697690349f
commit 9c697030e6
37 changed files with 995 additions and 410 deletions

View File

@ -7,7 +7,8 @@
*/
import {identifierModuleUrl, identifierName} from '../compile_metadata';
import {StaticSymbol} from '../aot/static_symbol';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang';
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
@ -16,17 +17,17 @@ import * as o from './output_ast';
import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importGenerator: ImportResolver) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(moduleUrl);
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`var ${prefix} = req` +
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`);
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
@ -36,20 +37,23 @@ export class JavaScriptEmitter implements OutputEmitter {
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
importsWithPrefixes = new Map<string, string>();
constructor(private _moduleUrl: string) { super(); }
constructor(private _genFilePath: string, private _importResolver: ImportResolver) { super(); }
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
const reference = value.reference;
if (!(reference instanceof StaticSymbol)) {
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
}
return this._importResolver.getImportAs(reference) || reference;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
const name = identifierName(ast.value);
const moduleUrl = identifierModuleUrl(ast.value);
if (isBlank(name)) {
console.error('>>>', ast.value);
throw new Error(`Internal error: unknown identifier ${ast.value}`);
}
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(moduleUrl);
const {name, filePath} = this._resolveStaticSymbol(ast.value);
if (filePath != this._genFilePath) {
let prefix = this.importsWithPrefixes.get(filePath);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(moduleUrl, prefix);
this.importsWithPrefixes.set(filePath, prefix);
}
ctx.print(`${prefix}.`);
}