feat(ivy): able to compile @angular/core with ngtsc (#24677)
@angular/core is unique in that it defines the Angular decorators (@Component, @Directive, etc). Ordinarily ngtsc looks for imports from @angular/core in order to identify these decorators. Clearly within core itself, this strategy doesn't work. Instead, a special constant ITS_JUST_ANGULAR is declared within a known file in @angular/core. If ngtsc sees this constant it knows core is being compiled and can ignore the imports when evaluating decorators. Additionally, when compiling decorators ngtsc will often write an import to @angular/core for needed symbols. However @angular/core cannot import itself. This change creates a module within core to export all the symbols needed to compile it and adds intelligence within ngtsc to write relative imports to that module, instead of absolute imports to @angular/core. PR Close #24677
This commit is contained in:
parent
c57b491778
commit
104d30507a
|
@ -25,10 +25,11 @@ const EMPTY_MAP = new Map<string, Expression>();
|
|||
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry) {}
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
return decorators.find(decorator => decorator.name === 'Component' && isAngularCore(decorator));
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Component' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3ComponentMetadata> {
|
||||
|
@ -43,7 +44,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
|||
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
|
||||
// on it.
|
||||
const directiveMetadata =
|
||||
extractDirectiveMetadata(node, decorator, this.checker, this.reflector);
|
||||
extractDirectiveMetadata(node, decorator, this.checker, this.reflector, this.isCore);
|
||||
if (directiveMetadata === undefined) {
|
||||
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
|
||||
// case, compilation of the decorator is skipped. Returning an empty object signifies
|
||||
|
|
|
@ -22,14 +22,16 @@ const EMPTY_OBJECT: {[key: string]: string} = {};
|
|||
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry) {}
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
return decorators.find(decorator => decorator.name === 'Directive' && isAngularCore(decorator));
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Directive' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3DirectiveMetadata> {
|
||||
const analysis = extractDirectiveMetadata(node, decorator, this.checker, this.reflector);
|
||||
const analysis =
|
||||
extractDirectiveMetadata(node, decorator, this.checker, this.reflector, this.isCore);
|
||||
|
||||
// If the directive has a selector, it should be registered with the `SelectorScopeRegistry` so
|
||||
// when this directive appears in an `@NgModule` scope, its selector can be determined.
|
||||
|
@ -57,7 +59,7 @@ export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMe
|
|||
*/
|
||||
export function extractDirectiveMetadata(
|
||||
clazz: ts.ClassDeclaration, decorator: Decorator, checker: ts.TypeChecker,
|
||||
reflector: ReflectionHost): R3DirectiveMetadata|undefined {
|
||||
reflector: ReflectionHost, isCore: boolean): R3DirectiveMetadata|undefined {
|
||||
if (decorator.args === null || decorator.args.length !== 1) {
|
||||
throw new Error(`Incorrect number of arguments to @${decorator.name} decorator`);
|
||||
}
|
||||
|
@ -108,7 +110,7 @@ export function extractDirectiveMetadata(
|
|||
|
||||
return {
|
||||
name: clazz.name !.text,
|
||||
deps: getConstructorDependencies(clazz, reflector),
|
||||
deps: getConstructorDependencies(clazz, reflector, isCore),
|
||||
host: {
|
||||
attributes: {},
|
||||
listeners: {},
|
||||
|
|
|
@ -20,15 +20,16 @@ import {getConstructorDependencies, isAngularCore} from './util';
|
|||
* Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
|
||||
*/
|
||||
export class InjectableDecoratorHandler implements DecoratorHandler<R3InjectableMetadata> {
|
||||
constructor(private reflector: ReflectionHost) {}
|
||||
constructor(private reflector: ReflectionHost, private isCore: boolean) {}
|
||||
|
||||
detect(decorator: Decorator[]): Decorator|undefined {
|
||||
return decorator.find(decorator => decorator.name === 'Injectable' && isAngularCore(decorator));
|
||||
return decorator.find(
|
||||
decorator => decorator.name === 'Injectable' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<R3InjectableMetadata> {
|
||||
return {
|
||||
analysis: extractInjectableMetadata(node, decorator, this.reflector),
|
||||
analysis: extractInjectableMetadata(node, decorator, this.reflector, this.isCore),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -48,8 +49,8 @@ export class InjectableDecoratorHandler implements DecoratorHandler<R3Injectable
|
|||
* metadata needed to run `compileIvyInjectable`.
|
||||
*/
|
||||
function extractInjectableMetadata(
|
||||
clazz: ts.ClassDeclaration, decorator: Decorator,
|
||||
reflector: ReflectionHost): R3InjectableMetadata {
|
||||
clazz: ts.ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
|
||||
isCore: boolean): R3InjectableMetadata {
|
||||
if (clazz.name === undefined) {
|
||||
throw new Error(`@Injectables must have names`);
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ function extractInjectableMetadata(
|
|||
name,
|
||||
type,
|
||||
providedIn: new LiteralExpr(null),
|
||||
deps: getConstructorDependencies(clazz, reflector),
|
||||
deps: getConstructorDependencies(clazz, reflector, isCore),
|
||||
};
|
||||
} else if (decorator.args.length === 1) {
|
||||
const metaNode = decorator.args[0];
|
||||
|
@ -102,7 +103,7 @@ function extractInjectableMetadata(
|
|||
}
|
||||
return {name, type, providedIn, useFactory: factory, deps};
|
||||
} else {
|
||||
const deps = getConstructorDependencies(clazz, reflector);
|
||||
const deps = getConstructorDependencies(clazz, reflector, isCore);
|
||||
return {name, type, providedIn, deps};
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -29,10 +29,11 @@ export interface NgModuleAnalysis {
|
|||
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry) {}
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
return decorators.find(decorator => decorator.name === 'NgModule' && isAngularCore(decorator));
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'NgModule' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<NgModuleAnalysis> {
|
||||
|
@ -89,7 +90,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
|
|||
const ngInjectorDef: R3InjectorMetadata = {
|
||||
name: node.name !.text,
|
||||
type: new WrappedNodeExpr(node.name !),
|
||||
deps: getConstructorDependencies(node, this.reflector), providers,
|
||||
deps: getConstructorDependencies(node, this.reflector, this.isCore), providers,
|
||||
imports: new LiteralArrayExpr(
|
||||
[...imports, ...exports].map(imp => referenceToExpression(imp, context))),
|
||||
};
|
||||
|
|
|
@ -13,14 +13,15 @@ import {Decorator, ReflectionHost} from '../../host';
|
|||
import {Reference} from '../../metadata';
|
||||
|
||||
export function getConstructorDependencies(
|
||||
clazz: ts.ClassDeclaration, reflector: ReflectionHost): R3DependencyMetadata[] {
|
||||
clazz: ts.ClassDeclaration, reflector: ReflectionHost,
|
||||
isCore: boolean): R3DependencyMetadata[] {
|
||||
const useType: R3DependencyMetadata[] = [];
|
||||
const ctorParams = reflector.getConstructorParameters(clazz) || [];
|
||||
ctorParams.forEach((param, idx) => {
|
||||
let tokenExpr = param.type;
|
||||
let optional = false, self = false, skipSelf = false, host = false;
|
||||
let resolved = R3ResolvedDependencyType.Token;
|
||||
(param.decorators || []).filter(isAngularCore).forEach(dec => {
|
||||
(param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
|
||||
if (dec.name === 'Inject') {
|
||||
if (dec.args === null || dec.args.length !== 1) {
|
||||
throw new Error(`Unexpected number of arguments to @Inject().`);
|
||||
|
|
|
@ -13,5 +13,6 @@ ts_library(
|
|||
"//packages:types",
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/host",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -91,17 +91,21 @@ export class NgtscProgram implements api.Program {
|
|||
const mergeEmitResultsCallback = opts && opts.mergeEmitResultsCallback || mergeEmitResults;
|
||||
|
||||
const checker = this.tsProgram.getTypeChecker();
|
||||
const isCore = isAngularCorePackage(this.tsProgram);
|
||||
const reflector = new TypeScriptReflectionHost(checker);
|
||||
const scopeRegistry = new SelectorScopeRegistry(checker, reflector);
|
||||
|
||||
// Set up the IvyCompilation, which manages state for the Ivy transformer.
|
||||
const handlers = [
|
||||
new ComponentDecoratorHandler(checker, reflector, scopeRegistry),
|
||||
new DirectiveDecoratorHandler(checker, reflector, scopeRegistry),
|
||||
new InjectableDecoratorHandler(reflector),
|
||||
new NgModuleDecoratorHandler(checker, reflector, scopeRegistry),
|
||||
new ComponentDecoratorHandler(checker, reflector, scopeRegistry, isCore),
|
||||
new DirectiveDecoratorHandler(checker, reflector, scopeRegistry, isCore),
|
||||
new InjectableDecoratorHandler(reflector, isCore),
|
||||
new NgModuleDecoratorHandler(checker, reflector, scopeRegistry, isCore),
|
||||
];
|
||||
const compilation = new IvyCompilation(handlers, checker, reflector);
|
||||
|
||||
const coreImportsFrom = isCore && getR3SymbolsFile(this.tsProgram) || null;
|
||||
|
||||
const compilation = new IvyCompilation(handlers, checker, reflector, coreImportsFrom);
|
||||
|
||||
// Analyze every source file in the program.
|
||||
this.tsProgram.getSourceFiles()
|
||||
|
@ -115,7 +119,7 @@ export class NgtscProgram implements api.Program {
|
|||
sourceFiles: ReadonlyArray<ts.SourceFile>) => {
|
||||
if (fileName.endsWith('.d.ts')) {
|
||||
data = sourceFiles.reduce(
|
||||
(data, sf) => compilation.transformedDtsFor(sf.fileName, data), data);
|
||||
(data, sf) => compilation.transformedDtsFor(sf.fileName, data, fileName), data);
|
||||
}
|
||||
this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
};
|
||||
|
@ -128,7 +132,7 @@ export class NgtscProgram implements api.Program {
|
|||
options: this.options,
|
||||
emitOnlyDtsFiles: false, writeFile,
|
||||
customTransformers: {
|
||||
before: [ivyTransformFactory(compilation)],
|
||||
before: [ivyTransformFactory(compilation, coreImportsFrom)],
|
||||
},
|
||||
});
|
||||
return emitResult;
|
||||
|
@ -152,3 +156,47 @@ function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
|
|||
}
|
||||
return {diagnostics, emitSkipped, emittedFiles};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the 'r3_symbols.ts' file in the given `Program`, or return `null` if it wasn't there.
|
||||
*/
|
||||
function getR3SymbolsFile(program: ts.Program): ts.SourceFile|null {
|
||||
return program.getSourceFiles().find(file => file.fileName.indexOf('r3_symbols.ts') >= 0) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given `Program` is @angular/core.
|
||||
*/
|
||||
function isAngularCorePackage(program: ts.Program): boolean {
|
||||
// Look for its_just_angular.ts somewhere in the program.
|
||||
const r3Symbols = getR3SymbolsFile(program);
|
||||
if (r3Symbols === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for the constant ITS_JUST_ANGULAR in that file.
|
||||
return r3Symbols.statements.some(stmt => {
|
||||
// The statement must be a variable declaration statement.
|
||||
if (!ts.isVariableStatement(stmt)) {
|
||||
return false;
|
||||
}
|
||||
// It must be exported.
|
||||
if (stmt.modifiers === undefined ||
|
||||
!stmt.modifiers.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) {
|
||||
return false;
|
||||
}
|
||||
// It must declare ITS_JUST_ANGULAR.
|
||||
return stmt.declarationList.declarations.some(decl => {
|
||||
// The declaration must match the name.
|
||||
if (!ts.isIdentifier(decl.name) || decl.name.text !== 'ITS_JUST_ANGULAR') {
|
||||
return false;
|
||||
}
|
||||
// It must initialize the variable to true.
|
||||
if (decl.initializer === undefined || decl.initializer.kind !== ts.SyntaxKind.TrueKeyword) {
|
||||
return false;
|
||||
}
|
||||
// This definition matches.
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -46,9 +46,18 @@ export class IvyCompilation {
|
|||
*/
|
||||
private dtsMap = new Map<string, DtsFileTransformer>();
|
||||
|
||||
/**
|
||||
* @param handlers array of `DecoratorHandler`s which will be executed against each class in the
|
||||
* program
|
||||
* @param checker TypeScript `TypeChecker` instance for the program
|
||||
* @param reflector `ReflectionHost` through which all reflection operations will be performed
|
||||
* @param coreImportsFrom a TypeScript `SourceFile` which exports symbols needed for Ivy imports
|
||||
* when compiling @angular/core, or `null` if the current program is not @angular/core. This is
|
||||
* `null` in most cases.
|
||||
*/
|
||||
constructor(
|
||||
private handlers: DecoratorHandler<any>[], private checker: ts.TypeChecker,
|
||||
private reflector: ReflectionHost) {}
|
||||
private reflector: ReflectionHost, private coreImportsFrom: ts.SourceFile|null) {}
|
||||
|
||||
/**
|
||||
* Analyze a source file and produce diagnostics for it (if any).
|
||||
|
@ -147,19 +156,19 @@ export class IvyCompilation {
|
|||
* Process a .d.ts source string and return a transformed version that incorporates the changes
|
||||
* made to the source file.
|
||||
*/
|
||||
transformedDtsFor(tsFileName: string, dtsOriginalSource: string): string {
|
||||
transformedDtsFor(tsFileName: string, dtsOriginalSource: string, dtsPath: string): string {
|
||||
// No need to transform if no changes have been requested to the input file.
|
||||
if (!this.dtsMap.has(tsFileName)) {
|
||||
return dtsOriginalSource;
|
||||
}
|
||||
|
||||
// Return the transformed .d.ts source.
|
||||
return this.dtsMap.get(tsFileName) !.transform(dtsOriginalSource);
|
||||
return this.dtsMap.get(tsFileName) !.transform(dtsOriginalSource, tsFileName);
|
||||
}
|
||||
|
||||
private getDtsTransformer(tsFileName: string): DtsFileTransformer {
|
||||
if (!this.dtsMap.has(tsFileName)) {
|
||||
this.dtsMap.set(tsFileName, new DtsFileTransformer());
|
||||
this.dtsMap.set(tsFileName, new DtsFileTransformer(this.coreImportsFrom));
|
||||
}
|
||||
return this.dtsMap.get(tsFileName) !;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {relativePathBetween} from '../../util/src/path';
|
||||
|
||||
import {CompileResult} from './api';
|
||||
import {ImportManager, translateType} from './translator';
|
||||
|
||||
|
@ -18,7 +20,11 @@ import {ImportManager, translateType} from './translator';
|
|||
*/
|
||||
export class DtsFileTransformer {
|
||||
private ivyFields = new Map<string, CompileResult[]>();
|
||||
private imports = new ImportManager();
|
||||
private imports: ImportManager;
|
||||
|
||||
constructor(private coreImportsFrom: ts.SourceFile|null) {
|
||||
this.imports = new ImportManager(coreImportsFrom !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track that a static field was added to the code for a class.
|
||||
|
@ -28,7 +34,7 @@ export class DtsFileTransformer {
|
|||
/**
|
||||
* Process the .d.ts text for a file and add any declarations which were recorded.
|
||||
*/
|
||||
transform(dts: string): string {
|
||||
transform(dts: string, tsPath: string): string {
|
||||
const dtsFile =
|
||||
ts.createSourceFile('out.d.ts', dts, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
||||
|
||||
|
@ -51,7 +57,7 @@ export class DtsFileTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
const imports = this.imports.getAllImports();
|
||||
const imports = this.imports.getAllImports(tsPath, this.coreImportsFrom);
|
||||
if (imports.length !== 0) {
|
||||
dts = imports.map(i => `import * as ${i.as} from '${i.name}';\n`).join() + dts;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,12 @@ import {CompileResult} from './api';
|
|||
import {IvyCompilation} from './compilation';
|
||||
import {ImportManager, translateExpression, translateStatement} from './translator';
|
||||
|
||||
export function ivyTransformFactory(compilation: IvyCompilation):
|
||||
ts.TransformerFactory<ts.SourceFile> {
|
||||
export function ivyTransformFactory(
|
||||
compilation: IvyCompilation,
|
||||
coreImportsFrom: ts.SourceFile | null): ts.TransformerFactory<ts.SourceFile> {
|
||||
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
|
||||
return (file: ts.SourceFile): ts.SourceFile => {
|
||||
return transformIvySourceFile(compilation, context, file);
|
||||
return transformIvySourceFile(compilation, context, coreImportsFrom, file);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -74,18 +75,19 @@ class IvyVisitor extends Visitor {
|
|||
*/
|
||||
function transformIvySourceFile(
|
||||
compilation: IvyCompilation, context: ts.TransformationContext,
|
||||
file: ts.SourceFile): ts.SourceFile {
|
||||
const importManager = new ImportManager();
|
||||
coreImportsFrom: ts.SourceFile | null, file: ts.SourceFile): ts.SourceFile {
|
||||
const importManager = new ImportManager(coreImportsFrom !== null);
|
||||
|
||||
// Recursively scan through the AST and perform any updates requested by the IvyCompilation.
|
||||
const sf = visit(file, new IvyVisitor(compilation, importManager), context);
|
||||
|
||||
// Generate the import statements to prepend.
|
||||
const imports = importManager.getAllImports().map(
|
||||
i => ts.createImportDeclaration(
|
||||
const imports = importManager.getAllImports(file.fileName, coreImportsFrom).map(i => {
|
||||
return ts.createImportDeclaration(
|
||||
undefined, undefined,
|
||||
ts.createImportClause(undefined, ts.createNamespaceImport(ts.createIdentifier(i.as))),
|
||||
ts.createLiteral(i.name)));
|
||||
ts.createLiteral(i.name));
|
||||
});
|
||||
|
||||
// Prepend imports if needed.
|
||||
if (imports.length > 0) {
|
||||
|
|
|
@ -8,6 +8,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, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
import {relativePathBetween} from '../../util/src/path';
|
||||
|
||||
const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([
|
||||
[BinaryOperator.And, ts.SyntaxKind.AmpersandAmpersandToken],
|
||||
|
@ -28,20 +29,44 @@ const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([
|
|||
[BinaryOperator.Plus, ts.SyntaxKind.PlusToken],
|
||||
]);
|
||||
|
||||
const CORE_SUPPORTED_SYMBOLS = new Set<string>([
|
||||
'defineInjectable',
|
||||
'defineInjector',
|
||||
'ɵdefineNgModule',
|
||||
'inject',
|
||||
'InjectableDef',
|
||||
'InjectorDef',
|
||||
'NgModuleDef',
|
||||
]);
|
||||
|
||||
export class ImportManager {
|
||||
private moduleToIndex = new Map<string, string>();
|
||||
private nextIndex = 0;
|
||||
|
||||
generateNamedImport(moduleName: string): string {
|
||||
constructor(private isCore: boolean) {}
|
||||
|
||||
generateNamedImport(moduleName: string, symbol: string): string {
|
||||
if (!this.moduleToIndex.has(moduleName)) {
|
||||
this.moduleToIndex.set(moduleName, `i${this.nextIndex++}`);
|
||||
}
|
||||
if (this.isCore && moduleName === '@angular/core' && !CORE_SUPPORTED_SYMBOLS.has(symbol)) {
|
||||
throw new Error(`Importing unexpected symbol ${symbol} while compiling core`);
|
||||
}
|
||||
return this.moduleToIndex.get(moduleName) !;
|
||||
}
|
||||
|
||||
getAllImports(): {name: string, as: string}[] {
|
||||
getAllImports(contextPath: string, rewriteCoreImportsTo: ts.SourceFile|null):
|
||||
{name: string, as: string}[] {
|
||||
return Array.from(this.moduleToIndex.keys()).map(name => {
|
||||
const as = this.moduleToIndex.get(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;
|
||||
}
|
||||
return {name, as};
|
||||
});
|
||||
}
|
||||
|
@ -166,7 +191,7 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
|||
throw new Error(`Import unknown module or symbol ${ast.value}`);
|
||||
}
|
||||
return ts.createPropertyAccess(
|
||||
ts.createIdentifier(this.imports.generateNamedImport(ast.value.moduleName)),
|
||||
ts.createIdentifier(this.imports.generateNamedImport(ast.value.moduleName, ast.value.name)),
|
||||
ts.createIdentifier(ast.value.name));
|
||||
}
|
||||
|
||||
|
@ -314,7 +339,8 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
|
|||
if (ast.value.moduleName === null || ast.value.name === null) {
|
||||
throw new Error(`Import unknown module or symbol`);
|
||||
}
|
||||
const base = `${this.imports.generateNamedImport(ast.value.moduleName)}.${ast.value.name}`;
|
||||
const moduleSymbol = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
|
||||
const base = `${moduleSymbol}.${ast.value.name}`;
|
||||
if (ast.typeParams !== null) {
|
||||
const generics = ast.typeParams.map(type => type.visitType(this, context)).join(', ');
|
||||
return `${base}<${generics}>`;
|
||||
|
|
|
@ -9,4 +9,7 @@ ts_library(
|
|||
"src/**/*.ts",
|
||||
]),
|
||||
module_name = "@angular/compiler-cli/src/ngtsc/util",
|
||||
deps = [
|
||||
"//packages:types",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @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 * as path from 'path';
|
||||
|
||||
const TS_DTS_EXTENSION = /(\.d)?\.ts$/;
|
||||
|
||||
export function relativePathBetween(from: string, to: string): string|null {
|
||||
let relative = path.posix.relative(path.dirname(from), to).replace(TS_DTS_EXTENSION, '');
|
||||
|
||||
if (relative === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// path.relative() does not include the leading './'.
|
||||
if (!relative.startsWith('.')) {
|
||||
relative = `./${relative}`;
|
||||
}
|
||||
|
||||
return relative;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file exists to support compilation of @angular/core in Ivy mode.
|
||||
*
|
||||
* When the Angular compiler processes a compilation unit, it normally writes imports to
|
||||
* @angular/core. When compiling the core package itself this strategy isn't usable. Instead, the
|
||||
* compiler writes imports to this file.
|
||||
*
|
||||
* Only a subset of such imports are supported - core is not allowed to declare components or pipes.
|
||||
* A check in ngtsc's translator.ts validates this condition.
|
||||
*
|
||||
* The below symbols are used for @Injectable and @NgModule compilation.
|
||||
*/
|
||||
|
||||
export {InjectableDef, InjectorDef, defineInjectable, defineInjector} from './di/defs';
|
||||
export {inject} from './di/injector';
|
||||
export {NgModuleDef} from './metadata/ng_module';
|
||||
export {defineNgModule as ɵdefineNgModule} from './render3/definition';
|
||||
|
||||
|
||||
/**
|
||||
* The existence of this constant (in this particular file) informs the Angular compiler that the
|
||||
* current program is actually @angular/core, which needs to be compiled specially.
|
||||
*/
|
||||
export const ITS_JUST_ANGULAR = true;
|
Loading…
Reference in New Issue