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:

committed by
Andrew Kushnir

parent
5a0deb8d69
commit
3cf1b62722
@ -9,6 +9,7 @@ ts_library(
|
||||
deps = [
|
||||
"//packages:types",
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/imports",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
"@ngdeps//typescript",
|
||||
],
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeVisitor, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {relativePathBetween} from '../../util/src/path';
|
||||
import {ImportRewriter, NoopImportRewriter} from '../../imports';
|
||||
|
||||
export class Context {
|
||||
constructor(readonly isStatement: boolean) {}
|
||||
@ -38,60 +38,41 @@ const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([
|
||||
[BinaryOperator.Plus, ts.SyntaxKind.PlusToken],
|
||||
]);
|
||||
|
||||
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'],
|
||||
]);
|
||||
|
||||
|
||||
export class ImportManager {
|
||||
private moduleToIndex = new Map<string, string>();
|
||||
private importedModules = new Set<string>();
|
||||
private nextIndex = 0;
|
||||
|
||||
constructor(protected isCore: boolean, private prefix = 'i') {}
|
||||
constructor(protected rewriter: ImportRewriter = new NoopImportRewriter(), private prefix = 'i') {
|
||||
}
|
||||
|
||||
generateNamedImport(moduleName: string, symbol: string):
|
||||
generateNamedImport(moduleName: string, originalSymbol: string):
|
||||
{moduleImport: string | null, symbol: string} {
|
||||
// First, rewrite the symbol name.
|
||||
const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName);
|
||||
|
||||
// Ask the rewriter if this symbol should be imported at all. If not, it can be referenced
|
||||
// directly (moduleImport: null).
|
||||
if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) {
|
||||
// The symbol should be referenced directly.
|
||||
return {moduleImport: null, symbol};
|
||||
}
|
||||
|
||||
// If not, this symbol will be imported. Allocate a prefix for the imported module if needed.
|
||||
if (!this.moduleToIndex.has(moduleName)) {
|
||||
this.moduleToIndex.set(moduleName, `${this.prefix}${this.nextIndex++}`);
|
||||
}
|
||||
const moduleImport = this.moduleToIndex.get(moduleName) !;
|
||||
|
||||
return {
|
||||
moduleImport: this.moduleToIndex.get(moduleName) !,
|
||||
symbol: this.rewriteSymbol(moduleName, symbol)
|
||||
};
|
||||
return {moduleImport, symbol};
|
||||
}
|
||||
|
||||
protected rewriteSymbol(moduleName: string, symbol: string): string {
|
||||
if (this.isCore && moduleName === '@angular/core') {
|
||||
if (!CORE_SUPPORTED_SYMBOLS.has(symbol)) {
|
||||
throw new Error(`Importing unexpected symbol ${symbol} while compiling core`);
|
||||
}
|
||||
|
||||
symbol = CORE_SUPPORTED_SYMBOLS.get(symbol) !;
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
getAllImports(contextPath: string, rewriteCoreImportsTo: ts.SourceFile|null):
|
||||
{name: string, as: string}[] {
|
||||
getAllImports(contextPath: string): {name: string, as: string}[] {
|
||||
return Array.from(this.moduleToIndex.keys()).map(name => {
|
||||
const as: string|null = this.moduleToIndex.get(name) !;
|
||||
if (rewriteCoreImportsTo !== null && name === '@angular/core') {
|
||||
const relative = relativePathBetween(contextPath, rewriteCoreImportsTo.fileName);
|
||||
if (relative === null) {
|
||||
throw new Error(
|
||||
`Failed to rewrite import inside core: ${contextPath} -> ${rewriteCoreImportsTo.fileName}`);
|
||||
}
|
||||
name = relative;
|
||||
}
|
||||
const as = this.moduleToIndex.get(name) !;
|
||||
name = this.rewriter.rewriteSpecifier(name, contextPath);
|
||||
return {name, as};
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user