feat(ivy): Add AOT handling for bare classes with Input and Output decorators (#25367)
PR Close #25367
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
export {ResourceLoader} from './src/api';
|
||||
export {BaseDefDecoratorHandler} from './src/base_def';
|
||||
export {ComponentDecoratorHandler} from './src/component';
|
||||
export {DirectiveDecoratorHandler} from './src/directive';
|
||||
export {InjectableDecoratorHandler} from './src/injectable';
|
||||
|
120
packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts
Normal file
120
packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @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 {R3BaseRefMetaData, compileBaseDefFromMetadata} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassMember, Decorator, ReflectionHost} from '../../host';
|
||||
import {staticallyResolve} from '../../metadata';
|
||||
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
||||
import {isAngularCore} from './util';
|
||||
|
||||
function containsNgTopLevelDecorator(decorators: Decorator[] | null): boolean {
|
||||
if (!decorators) {
|
||||
return false;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => (decorator.name === 'Component' || decorator.name === 'Directive' ||
|
||||
decorator.name === 'NgModule') &&
|
||||
isAngularCore(decorator)) !== undefined;
|
||||
}
|
||||
|
||||
export class BaseDefDecoratorHandler implements
|
||||
DecoratorHandler<R3BaseRefMetaData, R3BaseRefDecoratorDetection> {
|
||||
constructor(private checker: ts.TypeChecker, private reflector: ReflectionHost, ) {}
|
||||
|
||||
detect(node: ts.ClassDeclaration, decorators: Decorator[]|null): R3BaseRefDecoratorDetection
|
||||
|undefined {
|
||||
if (containsNgTopLevelDecorator(decorators)) {
|
||||
// If the class is already decorated by @Component or @Directive let that
|
||||
// DecoratorHandler handle this. BaseDef is unnecessary.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let result: R3BaseRefDecoratorDetection|undefined = undefined;
|
||||
|
||||
this.reflector.getMembersOfClass(node).forEach(property => {
|
||||
const {decorators} = property;
|
||||
if (decorators) {
|
||||
for (const decorator of decorators) {
|
||||
const decoratorName = decorator.name;
|
||||
if (decoratorName === 'Input' && isAngularCore(decorator)) {
|
||||
result = result || {};
|
||||
const inputs = result.inputs = result.inputs || [];
|
||||
inputs.push({decorator, property});
|
||||
} else if (decoratorName === 'Output' && isAngularCore(decorator)) {
|
||||
result = result || {};
|
||||
const outputs = result.outputs = result.outputs || [];
|
||||
outputs.push({decorator, property});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
analyze(node: ts.ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
|
||||
AnalysisOutput<R3BaseRefMetaData> {
|
||||
const analysis: R3BaseRefMetaData = {};
|
||||
if (metadata.inputs) {
|
||||
const inputs = analysis.inputs = {} as{[key: string]: string | [string, string]};
|
||||
metadata.inputs.forEach(({decorator, property}) => {
|
||||
const propName = property.name;
|
||||
const args = decorator.args;
|
||||
let value: string|[string, string];
|
||||
if (args && args.length > 0) {
|
||||
const resolvedValue = staticallyResolve(args[0], this.reflector, this.checker);
|
||||
if (typeof resolvedValue !== 'string') {
|
||||
throw new TypeError('Input alias does not resolve to a string value');
|
||||
}
|
||||
value = [resolvedValue, propName];
|
||||
} else {
|
||||
value = propName;
|
||||
}
|
||||
inputs[propName] = value;
|
||||
});
|
||||
}
|
||||
|
||||
if (metadata.outputs) {
|
||||
const outputs = analysis.outputs = {} as{[key: string]: string};
|
||||
metadata.outputs.forEach(({decorator, property}) => {
|
||||
const propName = property.name;
|
||||
const args = decorator.args;
|
||||
let value: string;
|
||||
if (args && args.length > 0) {
|
||||
const resolvedValue = staticallyResolve(args[0], this.reflector, this.checker);
|
||||
if (typeof resolvedValue !== 'string') {
|
||||
throw new TypeError('Output alias does not resolve to a string value');
|
||||
}
|
||||
value = resolvedValue;
|
||||
} else {
|
||||
value = propName;
|
||||
}
|
||||
outputs[propName] = value;
|
||||
});
|
||||
}
|
||||
|
||||
return {analysis};
|
||||
}
|
||||
|
||||
compile(node: ts.Declaration, analysis: R3BaseRefMetaData): CompileResult[]|CompileResult {
|
||||
const {expression, type} = compileBaseDefFromMetadata(analysis);
|
||||
|
||||
return {
|
||||
name: 'ngBaseDef',
|
||||
initializer: expression, type,
|
||||
statements: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface R3BaseRefDecoratorDetection {
|
||||
inputs?: Array<{property: ClassMember, decorator: Decorator}>;
|
||||
outputs?: Array<{property: ClassMember, decorator: Decorator}>;
|
||||
}
|
@ -24,7 +24,7 @@ const EMPTY_MAP = new Map<string, Expression>();
|
||||
/**
|
||||
* `DecoratorHandler` which handles the `@Component` annotation.
|
||||
*/
|
||||
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata> {
|
||||
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata, Decorator> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean,
|
||||
@ -33,7 +33,10 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
|
||||
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Component' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
@ -18,12 +18,15 @@ import {getConstructorDependencies, isAngularCore, unwrapExpression, unwrapForwa
|
||||
|
||||
const EMPTY_OBJECT: {[key: string]: string} = {};
|
||||
|
||||
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata> {
|
||||
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata, Decorator> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Directive' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
@ -19,11 +19,15 @@ import {getConstructorDependencies, isAngularCore} from './util';
|
||||
/**
|
||||
* Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
|
||||
*/
|
||||
export class InjectableDecoratorHandler implements DecoratorHandler<R3InjectableMetadata> {
|
||||
export class InjectableDecoratorHandler implements
|
||||
DecoratorHandler<R3InjectableMetadata, Decorator> {
|
||||
constructor(private reflector: ReflectionHost, private isCore: boolean) {}
|
||||
|
||||
detect(decorator: Decorator[]): Decorator|undefined {
|
||||
return decorator.find(
|
||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Injectable' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,15 @@ export interface NgModuleAnalysis {
|
||||
*
|
||||
* TODO(alxhub): handle injector side of things as well.
|
||||
*/
|
||||
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis> {
|
||||
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis, Decorator> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorators: Decorator[]): Decorator|undefined {
|
||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'NgModule' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
@ -16,13 +16,16 @@ import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
||||
import {SelectorScopeRegistry} from './selector_scope';
|
||||
import {getConstructorDependencies, isAngularCore, unwrapExpression} from './util';
|
||||
|
||||
export class PipeDecoratorHandler implements DecoratorHandler<R3PipeMetadata> {
|
||||
export class PipeDecoratorHandler implements DecoratorHandler<R3PipeMetadata, Decorator> {
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||
|
||||
detect(decorator: Decorator[]): Decorator|undefined {
|
||||
return decorator.find(
|
||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
return decorators.find(
|
||||
decorator => decorator.name === 'Pipe' && (this.isCore || isAngularCore(decorator)));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user