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:

committed by
Miško Hevery

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().`);
|
||||
|
Reference in New Issue
Block a user