fix(ivy): prevent ngcc from referencing missing ɵsetClassMetadata (#27055)
When ngtsc compiles @angular/core, it rewrites core imports to the r3_symbols.ts file that exposes all internal symbols under their external name. When creating the FESM bundle, the r3_symbols.ts file causes the external symbol names to be rewritten to their internal name. Under ngcc compilations of FESM bundles, the indirection of r3_symbols.ts is no longer in place such that the external names are retained in the bundle. Previously, the external name `ɵdefineNgModule` was explicitly declared internally to resolve this issue, but the recently added `setClassMetadata` was not declared as such, causing runtime errors. Instead of relying on the r3_symbols.ts file to perform the rewrite of the external modules to their internal variants, the translation is moved into the `ImportManager` during the compilation itself. This avoids the need for providing the external name manually. PR Close #27055
This commit is contained in:
@ -17,7 +17,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, ReflectionHost} from '../../host';
|
||||
|
||||
const TS_DTS_JS_EXTENSION = /(\.d)?\.ts|\.js$/;
|
||||
const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
|
||||
|
||||
/**
|
||||
* Represents a value which cannot be determined statically.
|
||||
|
@ -38,16 +38,16 @@ const BINARY_OPERATORS = new Map<BinaryOperator, ts.BinaryOperator>([
|
||||
[BinaryOperator.Plus, ts.SyntaxKind.PlusToken],
|
||||
]);
|
||||
|
||||
const CORE_SUPPORTED_SYMBOLS = new Set<string>([
|
||||
'defineInjectable',
|
||||
'defineInjector',
|
||||
'ɵdefineNgModule',
|
||||
'inject',
|
||||
'ɵsetClassMetadata',
|
||||
'ɵInjectableDef',
|
||||
'ɵInjectorDef',
|
||||
'ɵNgModuleDefWithMeta',
|
||||
'ɵNgModuleFactory',
|
||||
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 {
|
||||
@ -56,14 +56,28 @@ export class ImportManager {
|
||||
|
||||
constructor(protected isCore: boolean, private prefix = 'i') {}
|
||||
|
||||
generateNamedImport(moduleName: string, symbol: string): string|null {
|
||||
generateNamedImport(moduleName: string, symbol: string):
|
||||
{moduleImport: string | null, symbol: string} {
|
||||
if (!this.moduleToIndex.has(moduleName)) {
|
||||
this.moduleToIndex.set(moduleName, `${this.prefix}${this.nextIndex++}`);
|
||||
}
|
||||
if (this.isCore && moduleName === '@angular/core' && !CORE_SUPPORTED_SYMBOLS.has(symbol)) {
|
||||
throw new Error(`Importing unexpected symbol ${symbol} while compiling core`);
|
||||
|
||||
return {
|
||||
moduleImport: this.moduleToIndex.get(moduleName) !,
|
||||
symbol: this.rewriteSymbol(moduleName, 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 this.moduleToIndex.get(moduleName) !;
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
getAllImports(contextPath: string, rewriteCoreImportsTo: ts.SourceFile|null):
|
||||
@ -216,12 +230,13 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
||||
if (ast.value.moduleName === null || ast.value.name === null) {
|
||||
throw new Error(`Import unknown module or symbol ${ast.value}`);
|
||||
}
|
||||
const importIdentifier = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
|
||||
if (importIdentifier === null) {
|
||||
return ts.createIdentifier(ast.value.name);
|
||||
const {moduleImport, symbol} =
|
||||
this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
|
||||
if (moduleImport === null) {
|
||||
return ts.createIdentifier(symbol);
|
||||
} else {
|
||||
return ts.createPropertyAccess(
|
||||
ts.createIdentifier(importIdentifier), ts.createIdentifier(ast.value.name));
|
||||
ts.createIdentifier(moduleImport), ts.createIdentifier(symbol));
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,8 +397,9 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
|
||||
if (ast.value.moduleName === null || ast.value.name === null) {
|
||||
throw new Error(`Import unknown module or symbol`);
|
||||
}
|
||||
const moduleSymbol = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
|
||||
const base = `${moduleSymbol}.${ast.value.name}`;
|
||||
const {moduleImport, symbol} =
|
||||
this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
|
||||
const base = moduleImport ? `${moduleImport}.${symbol}` : symbol;
|
||||
if (ast.typeParams !== null) {
|
||||
const generics = ast.typeParams.map(type => type.visitType(this, context)).join(', ');
|
||||
return `${base}<${generics}>`;
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
const TS_DTS_EXTENSION = /(\.d)?\.ts$/;
|
||||
const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
|
||||
|
||||
export function relativePathBetween(from: string, to: string): string|null {
|
||||
let relative = path.posix.relative(path.dirname(from), to).replace(TS_DTS_EXTENSION, '');
|
||||
let relative = path.posix.relative(path.dirname(from), to).replace(TS_DTS_JS_EXTENSION, '');
|
||||
|
||||
if (relative === '') {
|
||||
return null;
|
||||
@ -25,4 +25,4 @@ export function relativePathBetween(from: string, to: string): string|null {
|
||||
}
|
||||
|
||||
return relative;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user