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

@ -6,5 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
export {AbsoluteReference, ImportMode, NodeReference, Reference, ResolvedReference} from './src/references';
export {ReferenceResolver, TsReferenceResolver} from './src/resolver';

View File

@ -0,0 +1,103 @@
/**
* @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 {relativePathBetween} from '../../util/src/path';
/**
* Rewrites imports of symbols being written into generated code.
*/
export interface ImportRewriter {
/**
* Should the given symbol be imported at all?
*
* If `true`, the symbol should be imported from the given specifier. If `false`, the symbol
* should be referenced directly, without an import.
*/
shouldImportSymbol(symbol: string, specifier: string): boolean;
/**
* Optionally rewrite a reference to an imported symbol, changing either the binding prefix or the
* symbol name itself.
*/
rewriteSymbol(symbol: string, specifier: string): string;
/**
* Optionally rewrite the given module specifier in the context of a given file.
*/
rewriteSpecifier(specifier: string, inContextOfFile: string): string;
}
/**
* `ImportRewriter` that does no rewriting.
*/
export class NoopImportRewriter implements ImportRewriter {
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; }
rewriteSymbol(symbol: string, specifier: string): string { return symbol; }
rewriteSpecifier(specifier: string, inContextOfFile: string): string { return specifier; }
}
/**
* A mapping of supported symbols that can be imported from within @angular/core, and the names by
* which they're exported from r3_symbols.
*/
const CORE_SUPPORTED_SYMBOLS = new Map<string, string>([
['defineInjectable', 'defineInjectable'],
['defineInjector', 'defineInjector'],
['ɵdefineNgModule', 'defineNgModule'],
['inject', 'inject'],
['ɵsetClassMetadata', 'setClassMetadata'],
['ɵInjectableDef', 'InjectableDef'],
['ɵInjectorDef', 'InjectorDef'],
['ɵNgModuleDefWithMeta', 'NgModuleDefWithMeta'],
['ɵNgModuleFactory', 'NgModuleFactory'],
]);
const CORE_MODULE = '@angular/core';
/**
* `ImportRewriter` that rewrites imports from '@angular/core' to be imported from the r3_symbols.ts
* file instead.
*/
export class R3SymbolsImportRewriter implements ImportRewriter {
constructor(private r3SymbolsPath: string) {}
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; }
rewriteSymbol(symbol: string, specifier: string): string {
if (specifier !== CORE_MODULE) {
// This import isn't from core, so ignore it.
return symbol;
}
return validateAndRewriteCoreSymbol(symbol);
}
rewriteSpecifier(specifier: string, inContextOfFile: string): string {
if (specifier !== CORE_MODULE) {
// This module isn't core, so ignore it.
return specifier;
}
const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
if (relativePathToR3Symbols === null) {
throw new Error(
`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`);
}
return relativePathToR3Symbols;
}
}
export function validateAndRewriteCoreSymbol(name: string): string {
if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
}
return CORE_SUPPORTED_SYMBOLS.get(name) !;
}