refactor(ivy): extract import rewriting into a separate interface (#27998)

Currently the ImportManager class handles various rewriting actions of
imports when compiling @angular/core. This is required as code compiled
within @angular/core cannot import from '@angular/core'. To work around
this, imports are rewritten to get core symbols from a particular file,
r3_symbols.ts.

In this refactoring, this rewriting logic is moved out of the ImportManager
and put behind an interface, ImportRewriter. There are three implementers
of the interface:

* NoopImportRewriter, used for compiling all non-core packages.
* R3SymbolsImportRewriter, used when ngtsc compiles @angular/core.
* NgccFlatImportRewriter, used when ngcc compiles @angular/core (special
  logic is needed because ngcc has to rewrite imports in flat bundles
  differently than in non-flat bundles).

This is a precursor to using this rewriting logic in other contexts besides
the ImportManager.

PR Close #27998
This commit is contained in:
Alex Rickabaugh
2019-01-08 11:49:58 -08:00
committed by Andrew Kushnir
parent 5a0deb8d69
commit 3cf1b62722
15 changed files with 222 additions and 95 deletions

View File

@ -1,22 +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 {ImportManager} from '../../../ngtsc/translator';
export class NgccImportManager extends ImportManager {
constructor(private isFlat: boolean, isCore: boolean, prefix?: string) { super(isCore, prefix); }
generateNamedImport(moduleName: string, symbol: string):
{moduleImport: string | null, symbol: string} {
if (this.isFlat && this.isCore && moduleName === '@angular/core') {
return {moduleImport: null, symbol: this.rewriteSymbol(moduleName, symbol)};
}
return super.generateNamedImport(moduleName, symbol);
}
}

View File

@ -0,0 +1,34 @@
/**
* @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 {ImportRewriter, validateAndRewriteCoreSymbol} from '../../../ngtsc/imports';
export class NgccFlatImportRewriter implements ImportRewriter {
shouldImportSymbol(symbol: string, specifier: string): boolean {
if (specifier === '@angular/core') {
// Don't use imports for @angular/core symbols in a flat bundle, as they'll be visible
// directly.
return false;
} else {
return true;
}
}
rewriteSymbol(symbol: string, specifier: string): string {
if (specifier === '@angular/core') {
return validateAndRewriteCoreSymbol(symbol);
} else {
return symbol;
}
}
rewriteSpecifier(originalModulePath: string, inContextOfFile: string): string {
return originalModulePath;
}
}

View File

@ -13,9 +13,10 @@ import {basename, dirname, relative, resolve} from 'canonical-path';
import {SourceMapConsumer, SourceMapGenerator, RawSourceMap} from 'source-map';
import * as ts from 'typescript';
import {NoopImportRewriter, ImportRewriter, R3SymbolsImportRewriter} from '@angular/compiler-cli/src/ngtsc/imports';
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
import {translateStatement, translateType, ImportManager} from '../../../ngtsc/translator';
import {NgccImportManager} from './ngcc_import_manager';
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
@ -135,7 +136,8 @@ export abstract class Renderer {
}
if (compiledFile) {
const importManager = new NgccImportManager(this.bundle.isFlat, this.isCore, IMPORT_PREFIX);
const importManager = new ImportManager(
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlat), IMPORT_PREFIX);
// TODO: remove constructor param metadata and property decorators (we need info from the
// handlers to do this)
@ -152,9 +154,7 @@ export abstract class Renderer {
renderConstantPool(compiledFile.sourceFile, compiledFile.constantPool, importManager),
compiledFile.sourceFile);
this.addImports(
outputText, importManager.getAllImports(
compiledFile.sourceFile.fileName, this.bundle.src.r3SymbolsFile));
this.addImports(outputText, importManager.getAllImports(compiledFile.sourceFile.fileName));
}
// Add exports to the entry-point file
@ -169,7 +169,8 @@ export abstract class Renderer {
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
const input = this.extractSourceMap(dtsFile);
const outputText = new MagicString(input.source);
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
const importManager = new ImportManager(
this.getImportRewriter(this.bundle.dts !.r3SymbolsFile, false), IMPORT_PREFIX);
renderInfo.classInfo.forEach(dtsClass => {
const endOfClass = dtsClass.dtsDeclaration.getEnd();
@ -181,8 +182,7 @@ export abstract class Renderer {
});
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
this.addImports(
outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile));
this.addImports(outputText, importManager.getAllImports(dtsFile.fileName));
this.addExports(outputText, dtsFile.fileName, renderInfo.privateExports);
@ -199,7 +199,7 @@ export abstract class Renderer {
*/
protected addModuleWithProvidersParams(
outputText: MagicString, moduleWithProviders: ModuleWithProvidersInfo[],
importManager: NgccImportManager): void {
importManager: ImportManager): void {
moduleWithProviders.forEach(info => {
const ngModuleName = (info.ngModule.node as ts.ClassDeclaration).name !.text;
const declarationFile = info.declaration.getSourceFile().fileName;
@ -417,6 +417,16 @@ export abstract class Renderer {
return (
id && id.name === 'ModuleWithProviders' && (this.isCore || id.from === '@angular/core'));
}
private getImportRewriter(r3SymbolsFile: ts.SourceFile|null, isFlat: boolean): ImportRewriter {
if (this.isCore && isFlat) {
return new NgccFlatImportRewriter();
} else if (this.isCore) {
return new R3SymbolsImportRewriter(r3SymbolsFile !.fileName);
} else {
return new NoopImportRewriter();
}
}
}
/**
@ -451,7 +461,7 @@ export function mergeSourceMaps(
* Render the constant pool as source code for the given class.
*/
export function renderConstantPool(
sourceFile: ts.SourceFile, constantPool: ConstantPool, imports: NgccImportManager): string {
sourceFile: ts.SourceFile, constantPool: ConstantPool, imports: ImportManager): string {
const printer = ts.createPrinter();
return constantPool.statements.map(stmt => translateStatement(stmt, imports))
.map(stmt => printer.printNode(ts.EmitHint.Unspecified, stmt, sourceFile))
@ -467,7 +477,7 @@ export function renderConstantPool(
* @param imports An object that tracks the imports that are needed by the rendered definitions.
*/
export function renderDefinitions(
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: NgccImportManager): string {
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager): string {
const printer = ts.createPrinter();
const name = (compiledClass.declaration as ts.NamedDeclaration).name !;
const definitions =