refactor(ivy): remove ngBaseDef (#33264)

Removes `ngBaseDef` from the compiler and any runtime code that was still referring to it. In the cases where we'd previously generate a base def we now generate a definition for an abstract directive.

PR Close #33264
This commit is contained in:
crisbeto
2019-10-25 19:45:08 +02:00
committed by Andrew Kushnir
parent 3505692f75
commit 14c4b1b205
29 changed files with 310 additions and 660 deletions

View File

@ -9,7 +9,6 @@
/// <reference types="node" />
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';

View File

@ -1,173 +0,0 @@
/**
* @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 {ConstantPool, EMPTY_SOURCE_SPAN, R3BaseRefMetaData, WrappedNodeExpr, compileBaseDefFromMetadata, makeBindingParser} from '@angular/compiler';
import {PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, ClassMember, Decorator, ReflectionHost} from '../../reflection';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
import {extractHostBindings, queriesFromFields} from './directive';
import {isAngularDecorator} from './util';
function containsNgTopLevelDecorator(decorators: Decorator[] | null, isCore: boolean): boolean {
if (!decorators) {
return false;
}
return decorators.some(
decorator => isAngularDecorator(decorator, 'Component', isCore) ||
isAngularDecorator(decorator, 'Directive', isCore) ||
isAngularDecorator(decorator, 'NgModule', isCore));
}
export class BaseDefDecoratorHandler implements
DecoratorHandler<R3BaseRefMetaData, R3BaseRefDecoratorDetection> {
constructor(
private reflector: ReflectionHost, private evaluator: PartialEvaluator,
private isCore: boolean) {}
readonly precedence = HandlerPrecedence.WEAK;
detect(node: ClassDeclaration, decorators: Decorator[]|null):
DetectResult<R3BaseRefDecoratorDetection>|undefined {
if (containsNgTopLevelDecorator(decorators, this.isCore)) {
// 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) {
return;
}
for (const decorator of decorators) {
if (isAngularDecorator(decorator, 'Input', this.isCore)) {
result = result || {};
const inputs = result.inputs = result.inputs || [];
inputs.push({decorator, property});
} else if (isAngularDecorator(decorator, 'Output', this.isCore)) {
result = result || {};
const outputs = result.outputs = result.outputs || [];
outputs.push({decorator, property});
} else if (
isAngularDecorator(decorator, 'ViewChild', this.isCore) ||
isAngularDecorator(decorator, 'ViewChildren', this.isCore)) {
result = result || {};
const viewQueries = result.viewQueries = result.viewQueries || [];
viewQueries.push({member: property, decorators});
} else if (
isAngularDecorator(decorator, 'ContentChild', this.isCore) ||
isAngularDecorator(decorator, 'ContentChildren', this.isCore)) {
result = result || {};
const queries = result.queries = result.queries || [];
queries.push({member: property, decorators});
} else if (
isAngularDecorator(decorator, 'HostBinding', this.isCore) ||
isAngularDecorator(decorator, 'HostListener', this.isCore)) {
result = result || {};
const host = result.host = result.host || [];
host.push(property);
}
}
});
if (result !== undefined) {
return {
metadata: result,
trigger: null,
};
} else {
return undefined;
}
}
analyze(node: ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
AnalysisOutput<R3BaseRefMetaData> {
const analysis: R3BaseRefMetaData = {
name: node.name.text,
type: new WrappedNodeExpr(node.name),
typeSourceSpan: EMPTY_SOURCE_SPAN,
};
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 = this.evaluator.evaluate(args[0]);
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 = this.evaluator.evaluate(args[0]);
if (typeof resolvedValue !== 'string') {
throw new TypeError('Output alias does not resolve to a string value');
}
value = resolvedValue;
} else {
value = propName;
}
outputs[propName] = value;
});
}
if (metadata.viewQueries) {
analysis.viewQueries =
queriesFromFields(metadata.viewQueries, this.reflector, this.evaluator);
}
if (metadata.queries) {
analysis.queries = queriesFromFields(metadata.queries, this.reflector, this.evaluator);
}
if (metadata.host) {
analysis.host = extractHostBindings(
metadata.host, this.evaluator, this.isCore ? undefined : '@angular/core');
}
return {analysis};
}
compile(node: ClassDeclaration, analysis: R3BaseRefMetaData, pool: ConstantPool):
CompileResult[]|CompileResult {
const {expression, type} = compileBaseDefFromMetadata(analysis, pool, makeBindingParser());
return {
name: 'ngBaseDef',
initializer: expression, type,
statements: [],
};
}
}
export interface R3BaseRefDecoratorDetection {
inputs?: {property: ClassMember, decorator: Decorator}[];
outputs?: {property: ClassMember, decorator: Decorator}[];
viewQueries?: {member: ClassMember, decorators: Decorator[]}[];
queries?: {member: ClassMember, decorators: Decorator[]}[];
host?: ClassMember[];
}

View File

@ -19,16 +19,24 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFl
import {compileNgFactoryDefField} from './factory';
import {generateSetClassMetadataCall} from './metadata';
import {findAngularDecorator, getConstructorDependencies, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies} from './util';
import {findAngularDecorator, getConstructorDependencies, isAngularDecorator, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies} from './util';
const EMPTY_OBJECT: {[key: string]: string} = {};
const FIELD_DECORATORS = [
'Input', 'Output', 'ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren', 'HostBinding',
'HostListener'
];
const LIFECYCLE_HOOKS = new Set([
'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
'ngAfterContentInit', 'ngAfterContentChecked'
]);
export interface DirectiveHandlerData {
meta: R3DirectiveMetadata;
metadataStmt: Statement|null;
}
export class DirectiveDecoratorHandler implements
DecoratorHandler<DirectiveHandlerData, Decorator> {
DecoratorHandler<DirectiveHandlerData, Decorator|null> {
constructor(
private reflector: ReflectionHost, private evaluator: PartialEvaluator,
private metaRegistry: MetadataRegistry, private defaultImportRecorder: DefaultImportRecorder,
@ -36,27 +44,41 @@ export class DirectiveDecoratorHandler implements
readonly precedence = HandlerPrecedence.PRIMARY;
detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult<Decorator>|undefined {
if (!decorators) {
detect(node: ClassDeclaration, decorators: Decorator[]|null):
DetectResult<Decorator|null>|undefined {
// Compiling declaration files is invalid.
if (node.getSourceFile().isDeclarationFile) {
return undefined;
}
const decorator = findAngularDecorator(decorators, 'Directive', this.isCore);
if (decorator !== undefined) {
return {
trigger: decorator.node,
metadata: decorator,
};
// If the class is undecorated, check if any of the fields have Angular decorators or lifecycle
// hooks, and if they do, label the class as an abstract directive.
if (!decorators) {
const angularField = this.reflector.getMembersOfClass(node).find(member => {
if (!member.isStatic && member.kind === ClassMemberKind.Method &&
LIFECYCLE_HOOKS.has(member.name)) {
return true;
}
if (member.decorators) {
return member.decorators.some(
decorator => FIELD_DECORATORS.some(
decoratorName => isAngularDecorator(decorator, decoratorName, this.isCore)));
}
return false;
});
return angularField ? {trigger: angularField.node, metadata: null} : undefined;
} else {
return undefined;
const decorator = findAngularDecorator(decorators, 'Directive', this.isCore);
return decorator ? {trigger: decorator.node, metadata: decorator} : undefined;
}
}
analyze(node: ClassDeclaration, decorator: Decorator, flags = HandlerFlags.NONE):
analyze(node: ClassDeclaration, decorator: Decorator|null, flags = HandlerFlags.NONE):
AnalysisOutput<DirectiveHandlerData> {
const directiveResult = extractDirectiveMetadata(
node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore,
flags);
const analysis = directiveResult && directiveResult.metadata;
if (analysis === undefined) {
return {};
}
@ -112,15 +134,14 @@ export class DirectiveDecoratorHandler implements
* the module.
*/
export function extractDirectiveMetadata(
clazz: ClassDeclaration, decorator: Decorator, reflector: ReflectionHost,
clazz: ClassDeclaration, decorator: Decorator | null, reflector: ReflectionHost,
evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
flags: HandlerFlags, defaultSelector: string | null = null): {
decorator: Map<string, ts.Expression>,
metadata: R3DirectiveMetadata,
decoratedElements: ClassMember[],
}|undefined {
let directive: Map<string, ts.Expression>;
if (decorator.args === null || decorator.args.length === 0) {
if (decorator === null || decorator.args === null || decorator.args.length === 0) {
directive = new Map<string, ts.Expression>();
} else if (decorator.args.length !== 1) {
throw new FatalDiagnosticError(
@ -256,7 +277,7 @@ export function extractDirectiveMetadata(
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
typeSourceSpan: EMPTY_SOURCE_SPAN, usesInheritance, exportAs, providers
};
return {decoratedElements, decorator: directive, metadata};
return {decorator: directive, metadata};
}
export function extractQueryMetadata(

View File

@ -14,7 +14,6 @@ import {nocollapseHack} from '../transformers/nocollapse_hack';
import {verifySupportedTypeScriptVersion} from '../typescript_support';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from './annotations';
import {BaseDefDecoratorHandler} from './annotations/src/base_def';
import {CycleAnalyzer, ImportGraph} from './cycles';
import {ErrorCode, ngErrorCode} from './diagnostics';
import {FlatIndexGenerator, ReferenceGraph, checkForPrivateExports, findFlatIndexEntryPoint} from './entry_point';
@ -587,7 +586,6 @@ export class NgtscProgram implements api.Program {
// Set up the IvyCompilation, which manages state for the Ivy transformer.
const handlers = [
new BaseDefDecoratorHandler(this.reflector, evaluator, this.isCore),
new ComponentDecoratorHandler(
this.reflector, evaluator, metaRegistry, this.metaReader !, scopeReader, scopeRegistry,
this.isCore, this.resourceManager, this.rootDirs,

View File

@ -19,7 +19,6 @@
// Pattern matching all Render3 property names.
const R3_DEF_NAME_PATTERN = [
'ngBaseDef',
'ɵcmp',
'ɵdir',
'ɵprov',