feat(ivy): Add AOT handling for bare classes with Input and Output decorators (#25367)
PR Close #25367
This commit is contained in:
parent
26066f282e
commit
a0a29fdd27
|
@ -9,7 +9,7 @@ import {ConstantPool} from '@angular/compiler';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations';
|
import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations';
|
||||||
import {Decorator} from '../../ngtsc/host';
|
import {Decorator} from '../../ngtsc/host';
|
||||||
import {CompileResult, DecoratorHandler} from '../../ngtsc/transform';
|
import {CompileResult, DecoratorHandler} from '../../ngtsc/transform';
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ import {ParsedClass} from './parsing/parsed_class';
|
||||||
import {ParsedFile} from './parsing/parsed_file';
|
import {ParsedFile} from './parsing/parsed_file';
|
||||||
import {isDefined} from './utils';
|
import {isDefined} from './utils';
|
||||||
|
|
||||||
export interface AnalyzedClass<T = any> extends ParsedClass {
|
export interface AnalyzedClass<A = any, M = any> extends ParsedClass {
|
||||||
handler: DecoratorHandler<T>;
|
handler: DecoratorHandler<A, M>;
|
||||||
analysis: any;
|
analysis: any;
|
||||||
diagnostics?: ts.Diagnostic[];
|
diagnostics?: ts.Diagnostic[];
|
||||||
compilation: CompileResult[];
|
compilation: CompileResult[];
|
||||||
|
@ -31,9 +31,9 @@ export interface AnalyzedFile {
|
||||||
constantPool: ConstantPool;
|
constantPool: ConstantPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MatchingHandler<T> {
|
export interface MatchingHandler<A, M> {
|
||||||
handler: DecoratorHandler<T>;
|
handler: DecoratorHandler<A, M>;
|
||||||
decorator: Decorator;
|
match: M;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +46,8 @@ export class FileResourceLoader implements ResourceLoader {
|
||||||
export class Analyzer {
|
export class Analyzer {
|
||||||
resourceLoader = new FileResourceLoader();
|
resourceLoader = new FileResourceLoader();
|
||||||
scopeRegistry = new SelectorScopeRegistry(this.typeChecker, this.host);
|
scopeRegistry = new SelectorScopeRegistry(this.typeChecker, this.host);
|
||||||
handlers: DecoratorHandler<any>[] = [
|
handlers: DecoratorHandler<any, any>[] = [
|
||||||
|
new BaseDefDecoratorHandler(this.typeChecker, this.host),
|
||||||
new ComponentDecoratorHandler(
|
new ComponentDecoratorHandler(
|
||||||
this.typeChecker, this.host, this.scopeRegistry, false, this.resourceLoader),
|
this.typeChecker, this.host, this.scopeRegistry, false, this.resourceLoader),
|
||||||
new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
|
new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
|
||||||
|
@ -76,20 +77,23 @@ export class Analyzer {
|
||||||
|
|
||||||
protected analyzeClass(file: ts.SourceFile, pool: ConstantPool, clazz: ParsedClass): AnalyzedClass
|
protected analyzeClass(file: ts.SourceFile, pool: ConstantPool, clazz: ParsedClass): AnalyzedClass
|
||||||
|undefined {
|
|undefined {
|
||||||
const matchingHandlers =
|
const matchingHandlers = this.handlers
|
||||||
this.handlers.map(handler => ({handler, decorator: handler.detect(clazz.decorators)}))
|
.map(handler => ({
|
||||||
.filter(isMatchingHandler);
|
handler,
|
||||||
|
match: handler.detect(clazz.declaration, clazz.decorators),
|
||||||
|
}))
|
||||||
|
.filter(isMatchingHandler);
|
||||||
|
|
||||||
if (matchingHandlers.length > 1) {
|
if (matchingHandlers.length > 1) {
|
||||||
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
|
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchingHandlers.length == 0) {
|
if (matchingHandlers.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {handler, decorator} = matchingHandlers[0];
|
const {handler, match} = matchingHandlers[0];
|
||||||
const {analysis, diagnostics} = handler.analyze(clazz.declaration, decorator);
|
const {analysis, diagnostics} = handler.analyze(clazz.declaration, match);
|
||||||
let compilation = handler.compile(clazz.declaration, analysis, pool);
|
let compilation = handler.compile(clazz.declaration, analysis, pool);
|
||||||
if (!Array.isArray(compilation)) {
|
if (!Array.isArray(compilation)) {
|
||||||
compilation = [compilation];
|
compilation = [compilation];
|
||||||
|
@ -98,6 +102,7 @@ export class Analyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMatchingHandler<T>(handler: Partial<MatchingHandler<T>>): handler is MatchingHandler<T> {
|
function isMatchingHandler<A, M>(handler: Partial<MatchingHandler<A, M>>):
|
||||||
return !!handler.decorator;
|
handler is MatchingHandler<A, M> {
|
||||||
|
return !!handler.match;
|
||||||
}
|
}
|
|
@ -28,14 +28,18 @@ const TEST_PROGRAM = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function createTestHandler() {
|
function createTestHandler() {
|
||||||
const handler = jasmine.createSpyObj<DecoratorHandler<any>>('TestDecoratorHandler', [
|
const handler = jasmine.createSpyObj<DecoratorHandler<any, any>>('TestDecoratorHandler', [
|
||||||
'detect',
|
'detect',
|
||||||
'analyze',
|
'analyze',
|
||||||
'compile',
|
'compile',
|
||||||
]);
|
]);
|
||||||
// Only detect the Component decorator
|
// Only detect the Component decorator
|
||||||
handler.detect.and.callFake(
|
handler.detect.and.callFake((node: ts.Declaration, decorators: Decorator[]) => {
|
||||||
(decorators: Decorator[]) => decorators.find(d => d.name === 'Component'));
|
if (!decorators) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return decorators.find(d => d.name === 'Component');
|
||||||
|
});
|
||||||
// The "test" analysis is just the name of the decorator being analyzed
|
// The "test" analysis is just the name of the decorator being analyzed
|
||||||
handler.analyze.and.callFake(
|
handler.analyze.and.callFake(
|
||||||
((decl: ts.Declaration, dec: Decorator) => ({analysis: dec.name, diagnostics: null})));
|
((decl: ts.Declaration, dec: Decorator) => ({analysis: dec.name, diagnostics: null})));
|
||||||
|
@ -69,7 +73,7 @@ function createParsedFile(program: ts.Program) {
|
||||||
describe('Analyzer', () => {
|
describe('Analyzer', () => {
|
||||||
describe('analyzeFile()', () => {
|
describe('analyzeFile()', () => {
|
||||||
let program: ts.Program;
|
let program: ts.Program;
|
||||||
let testHandler: jasmine.SpyObj<DecoratorHandler<any>>;
|
let testHandler: jasmine.SpyObj<DecoratorHandler<any, any>>;
|
||||||
let result: AnalyzedFile;
|
let result: AnalyzedFile;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -87,9 +91,9 @@ describe('Analyzer', () => {
|
||||||
|
|
||||||
it('should call detect on the decorator handlers with each class from the parsed file', () => {
|
it('should call detect on the decorator handlers with each class from the parsed file', () => {
|
||||||
expect(testHandler.detect).toHaveBeenCalledTimes(2);
|
expect(testHandler.detect).toHaveBeenCalledTimes(2);
|
||||||
expect(testHandler.detect.calls.allArgs()[0][0]).toEqual([jasmine.objectContaining(
|
expect(testHandler.detect.calls.allArgs()[0][1]).toEqual([jasmine.objectContaining(
|
||||||
{name: 'Component'})]);
|
{name: 'Component'})]);
|
||||||
expect(testHandler.detect.calls.allArgs()[1][0]).toEqual([jasmine.objectContaining(
|
expect(testHandler.detect.calls.allArgs()[1][1]).toEqual([jasmine.objectContaining(
|
||||||
{name: 'Injectable'})]);
|
{name: 'Injectable'})]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {ResourceLoader} from './src/api';
|
export {ResourceLoader} from './src/api';
|
||||||
|
export {BaseDefDecoratorHandler} from './src/base_def';
|
||||||
export {ComponentDecoratorHandler} from './src/component';
|
export {ComponentDecoratorHandler} from './src/component';
|
||||||
export {DirectiveDecoratorHandler} from './src/directive';
|
export {DirectiveDecoratorHandler} from './src/directive';
|
||||||
export {InjectableDecoratorHandler} from './src/injectable';
|
export {InjectableDecoratorHandler} from './src/injectable';
|
||||||
|
|
|
@ -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.
|
* `DecoratorHandler` which handles the `@Component` annotation.
|
||||||
*/
|
*/
|
||||||
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata> {
|
export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMetadata, Decorator> {
|
||||||
constructor(
|
constructor(
|
||||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean,
|
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean,
|
||||||
|
@ -33,7 +33,10 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||||
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
|
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(
|
return decorators.find(
|
||||||
decorator => decorator.name === 'Component' && (this.isCore || isAngularCore(decorator)));
|
decorator => decorator.name === 'Component' && (this.isCore || isAngularCore(decorator)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,15 @@ import {getConstructorDependencies, isAngularCore, unwrapExpression, unwrapForwa
|
||||||
|
|
||||||
const EMPTY_OBJECT: {[key: string]: string} = {};
|
const EMPTY_OBJECT: {[key: string]: string} = {};
|
||||||
|
|
||||||
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata> {
|
export class DirectiveDecoratorHandler implements DecoratorHandler<R3DirectiveMetadata, Decorator> {
|
||||||
constructor(
|
constructor(
|
||||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
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(
|
return decorators.find(
|
||||||
decorator => decorator.name === 'Directive' && (this.isCore || isAngularCore(decorator)));
|
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.
|
* 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) {}
|
constructor(private reflector: ReflectionHost, private isCore: boolean) {}
|
||||||
|
|
||||||
detect(decorator: Decorator[]): Decorator|undefined {
|
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||||
return decorator.find(
|
if (!decorators) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return decorators.find(
|
||||||
decorator => decorator.name === 'Injectable' && (this.isCore || isAngularCore(decorator)));
|
decorator => decorator.name === 'Injectable' && (this.isCore || isAngularCore(decorator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,15 @@ export interface NgModuleAnalysis {
|
||||||
*
|
*
|
||||||
* TODO(alxhub): handle injector side of things as well.
|
* TODO(alxhub): handle injector side of things as well.
|
||||||
*/
|
*/
|
||||||
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis> {
|
export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalysis, Decorator> {
|
||||||
constructor(
|
constructor(
|
||||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
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(
|
return decorators.find(
|
||||||
decorator => decorator.name === 'NgModule' && (this.isCore || isAngularCore(decorator)));
|
decorator => decorator.name === 'NgModule' && (this.isCore || isAngularCore(decorator)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,16 @@ import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
|
||||||
import {SelectorScopeRegistry} from './selector_scope';
|
import {SelectorScopeRegistry} from './selector_scope';
|
||||||
import {getConstructorDependencies, isAngularCore, unwrapExpression} from './util';
|
import {getConstructorDependencies, isAngularCore, unwrapExpression} from './util';
|
||||||
|
|
||||||
export class PipeDecoratorHandler implements DecoratorHandler<R3PipeMetadata> {
|
export class PipeDecoratorHandler implements DecoratorHandler<R3PipeMetadata, Decorator> {
|
||||||
constructor(
|
constructor(
|
||||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {}
|
||||||
|
|
||||||
detect(decorator: Decorator[]): Decorator|undefined {
|
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||||
return decorator.find(
|
if (!decorators) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return decorators.find(
|
||||||
decorator => decorator.name === 'Pipe' && (this.isCore || isAngularCore(decorator)));
|
decorator => decorator.name === 'Pipe' && (this.isCore || isAngularCore(decorator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import * as ts from 'typescript';
|
||||||
import * as api from '../transformers/api';
|
import * as api from '../transformers/api';
|
||||||
|
|
||||||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations';
|
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations';
|
||||||
|
import {BaseDefDecoratorHandler} from './annotations/src/base_def';
|
||||||
import {FactoryGenerator, FactoryInfo, GeneratedFactoryHostWrapper, generatedFactoryTransform} from './factories';
|
import {FactoryGenerator, FactoryInfo, GeneratedFactoryHostWrapper, generatedFactoryTransform} from './factories';
|
||||||
import {TypeScriptReflectionHost} from './metadata';
|
import {TypeScriptReflectionHost} from './metadata';
|
||||||
import {FileResourceLoader, HostResourceLoader} from './resource_loader';
|
import {FileResourceLoader, HostResourceLoader} from './resource_loader';
|
||||||
|
@ -169,6 +170,7 @@ export class NgtscProgram implements api.Program {
|
||||||
|
|
||||||
// Set up the IvyCompilation, which manages state for the Ivy transformer.
|
// Set up the IvyCompilation, which manages state for the Ivy transformer.
|
||||||
const handlers = [
|
const handlers = [
|
||||||
|
new BaseDefDecoratorHandler(checker, this.reflector),
|
||||||
new ComponentDecoratorHandler(
|
new ComponentDecoratorHandler(
|
||||||
checker, this.reflector, scopeRegistry, this.isCore, this.resourceLoader),
|
checker, this.reflector, scopeRegistry, this.isCore, this.resourceLoader),
|
||||||
new DirectiveDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore),
|
new DirectiveDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore),
|
||||||
|
|
|
@ -20,12 +20,12 @@ import {Decorator} from '../../host';
|
||||||
* responsible for extracting the information required to perform compilation from the decorators
|
* responsible for extracting the information required to perform compilation from the decorators
|
||||||
* and Typescript source, invoking the decorator compiler, and returning the result.
|
* and Typescript source, invoking the decorator compiler, and returning the result.
|
||||||
*/
|
*/
|
||||||
export interface DecoratorHandler<A> {
|
export interface DecoratorHandler<A, M> {
|
||||||
/**
|
/**
|
||||||
* Scan a set of reflected decorators and determine if this handler is responsible for compilation
|
* Scan a set of reflected decorators and determine if this handler is responsible for compilation
|
||||||
* of one of them.
|
* of one of them.
|
||||||
*/
|
*/
|
||||||
detect(decorator: Decorator[]): Decorator|undefined;
|
detect(node: ts.Declaration, decorators: Decorator[]|null): M|undefined;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,14 +34,14 @@ export interface DecoratorHandler<A> {
|
||||||
* `preAnalyze` is optional and is not guaranteed to be called through all compilation flows. It
|
* `preAnalyze` is optional and is not guaranteed to be called through all compilation flows. It
|
||||||
* will only be called if asynchronicity is supported in the CompilerHost.
|
* will only be called if asynchronicity is supported in the CompilerHost.
|
||||||
*/
|
*/
|
||||||
preanalyze?(node: ts.Declaration, decorator: Decorator): Promise<void>|undefined;
|
preanalyze?(node: ts.Declaration, metadata: M): Promise<void>|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform analysis on the decorator/class combination, producing instructions for compilation
|
* Perform analysis on the decorator/class combination, producing instructions for compilation
|
||||||
* if successful, or an array of diagnostic messages if the analysis fails or the decorator
|
* if successful, or an array of diagnostic messages if the analysis fails or the decorator
|
||||||
* isn't valid.
|
* isn't valid.
|
||||||
*/
|
*/
|
||||||
analyze(node: ts.Declaration, decorator: Decorator): AnalysisOutput<A>;
|
analyze(node: ts.Declaration, metadata: M): AnalysisOutput<A>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a description of the field which should be added to the class, including any
|
* Generate a description of the field which should be added to the class, including any
|
||||||
|
|
|
@ -19,10 +19,10 @@ import {DtsFileTransformer} from './declaration';
|
||||||
* Record of an adapter which decided to emit a static field, and the analysis it performed to
|
* Record of an adapter which decided to emit a static field, and the analysis it performed to
|
||||||
* prepare for that operation.
|
* prepare for that operation.
|
||||||
*/
|
*/
|
||||||
interface EmitFieldOperation<T> {
|
interface EmitFieldOperation<A, M> {
|
||||||
adapter: DecoratorHandler<T>;
|
adapter: DecoratorHandler<A, M>;
|
||||||
analysis: AnalysisOutput<T>;
|
analysis: AnalysisOutput<A>;
|
||||||
decorator: Decorator;
|
metadata: M;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,7 @@ export class IvyCompilation {
|
||||||
* Tracks classes which have been analyzed and found to have an Ivy decorator, and the
|
* Tracks classes which have been analyzed and found to have an Ivy decorator, and the
|
||||||
* information recorded about them for later compilation.
|
* information recorded about them for later compilation.
|
||||||
*/
|
*/
|
||||||
private analysis = new Map<ts.Declaration, EmitFieldOperation<any>>();
|
private analysis = new Map<ts.Declaration, EmitFieldOperation<any, any>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks factory information which needs to be generated.
|
* Tracks factory information which needs to be generated.
|
||||||
|
@ -59,7 +59,7 @@ export class IvyCompilation {
|
||||||
* `null` in most cases.
|
* `null` in most cases.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private handlers: DecoratorHandler<any>[], private checker: ts.TypeChecker,
|
private handlers: DecoratorHandler<any, any>[], private checker: ts.TypeChecker,
|
||||||
private reflector: ReflectionHost, private coreImportsFrom: ts.SourceFile|null,
|
private reflector: ReflectionHost, private coreImportsFrom: ts.SourceFile|null,
|
||||||
private sourceToFactorySymbols: Map<string, Set<string>>|null) {}
|
private sourceToFactorySymbols: Map<string, Set<string>>|null) {}
|
||||||
|
|
||||||
|
@ -78,15 +78,14 @@ export class IvyCompilation {
|
||||||
|
|
||||||
const analyzeClass = (node: ts.Declaration): void => {
|
const analyzeClass = (node: ts.Declaration): void => {
|
||||||
// The first step is to reflect the decorators.
|
// The first step is to reflect the decorators.
|
||||||
const decorators = this.reflector.getDecoratorsOfDeclaration(node);
|
const classDecorators = this.reflector.getDecoratorsOfDeclaration(node);
|
||||||
if (decorators === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Look through the DecoratorHandlers to see if any are relevant.
|
// Look through the DecoratorHandlers to see if any are relevant.
|
||||||
this.handlers.forEach(adapter => {
|
this.handlers.forEach(adapter => {
|
||||||
|
|
||||||
// An adapter is relevant if it matches one of the decorators on the class.
|
// An adapter is relevant if it matches one of the decorators on the class.
|
||||||
const decorator = adapter.detect(decorators);
|
const metadata = adapter.detect(node, classDecorators);
|
||||||
if (decorator === undefined) {
|
if (metadata === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,14 +96,15 @@ export class IvyCompilation {
|
||||||
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
|
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run analysis on the decorator. This will produce either diagnostics, an
|
// Run analysis on the metadata. This will produce either diagnostics, an
|
||||||
// analysis result, or both.
|
// analysis result, or both.
|
||||||
const analysis = adapter.analyze(node, decorator);
|
const analysis = adapter.analyze(node, metadata);
|
||||||
|
|
||||||
if (analysis.analysis !== undefined) {
|
if (analysis.analysis !== undefined) {
|
||||||
this.analysis.set(node, {
|
this.analysis.set(node, {
|
||||||
adapter,
|
adapter,
|
||||||
analysis: analysis.analysis, decorator,
|
analysis: analysis.analysis,
|
||||||
|
metadata: metadata,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ export class IvyCompilation {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (preanalyze && adapter.preanalyze !== undefined) {
|
if (preanalyze && adapter.preanalyze !== undefined) {
|
||||||
const preanalysis = adapter.preanalyze(node, decorator);
|
const preanalysis = adapter.preanalyze(node, metadata);
|
||||||
if (preanalysis !== undefined) {
|
if (preanalysis !== undefined) {
|
||||||
promises.push(preanalysis.then(() => completeAnalysis()));
|
promises.push(preanalysis.then(() => completeAnalysis()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -185,7 +185,7 @@ export class IvyCompilation {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.analysis.get(original) !.decorator;
|
return this.analysis.get(original) !.metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,7 +14,7 @@ import * as ts from 'typescript';
|
||||||
|
|
||||||
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
||||||
import {compareVersions} from '../diagnostics/typescript_version';
|
import {compareVersions} from '../diagnostics/typescript_version';
|
||||||
import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
import {MetadataCollector, ModuleMetadata, createBundleIndexHost} from '../metadata';
|
||||||
import {NgtscProgram} from '../ngtsc/program';
|
import {NgtscProgram} from '../ngtsc/program';
|
||||||
|
|
||||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback, TsMergeEmitResultsCallback} from './api';
|
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, DiagnosticMessageChain, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback, TsMergeEmitResultsCallback} from './api';
|
||||||
|
|
|
@ -1837,4 +1837,226 @@ describe('compiler compliance', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('inherited bare classes', () => {
|
||||||
|
it('should add ngBaseDef if one or more @Input is present', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule, Input} from '@angular/core';
|
||||||
|
export class BaseClass {
|
||||||
|
@Input()
|
||||||
|
input1 = 'test';
|
||||||
|
|
||||||
|
@Input('alias2')
|
||||||
|
input2 = 'whatever';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<div>{{input1}} {{input2}}</div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent extends BaseClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectedOutput = `
|
||||||
|
// ...
|
||||||
|
BaseClass.ngBaseDef = i0.ɵdefineBase({
|
||||||
|
inputs: {
|
||||||
|
input1: "input1",
|
||||||
|
input2: ["alias2", "input2"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// ...
|
||||||
|
`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, expectedOutput, 'Invalid base definition');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add ngBaseDef if one or more @Output is present', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule, Output, EventEmitter} from '@angular/core';
|
||||||
|
export class BaseClass {
|
||||||
|
@Output()
|
||||||
|
output1 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
output2 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
clicked() {
|
||||||
|
this.output1.emit('test');
|
||||||
|
this.output2.emit('test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<button (click)="clicked()">Click Me</button>\`
|
||||||
|
})
|
||||||
|
export class MyComponent extends BaseClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectedOutput = `
|
||||||
|
// ...
|
||||||
|
BaseClass.ngBaseDef = i0.ɵdefineBase({
|
||||||
|
outputs: {
|
||||||
|
output1: "output1",
|
||||||
|
output2: "output2"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// ...
|
||||||
|
`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, expectedOutput, 'Invalid base definition');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add ngBaseDef if a mixture of @Input and @Output props are present', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule, Input, Output, EventEmitter} from '@angular/core';
|
||||||
|
export class BaseClass {
|
||||||
|
@Output()
|
||||||
|
output1 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
output2 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
input1 = 'test';
|
||||||
|
|
||||||
|
@Input('whatever')
|
||||||
|
input2 = 'blah';
|
||||||
|
|
||||||
|
clicked() {
|
||||||
|
this.output1.emit('test');
|
||||||
|
this.output2.emit('test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<button (click)="clicked()">Click Me</button>\`
|
||||||
|
})
|
||||||
|
export class MyComponent extends BaseClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const expectedOutput = `
|
||||||
|
// ...
|
||||||
|
BaseClass.ngBaseDef = i0.ɵdefineBase({
|
||||||
|
inputs: {
|
||||||
|
input1: "input1",
|
||||||
|
input2: ["whatever", "input2"]
|
||||||
|
},
|
||||||
|
outputs: {
|
||||||
|
output1: "output1",
|
||||||
|
output2: "output2"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// ...
|
||||||
|
`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expectEmit(result.source, expectedOutput, 'Invalid base definition');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT add ngBaseDef if @Component is present', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule, Output, EventEmitter} from '@angular/core';
|
||||||
|
@Component({
|
||||||
|
selector: 'whatever',
|
||||||
|
template: '<button (click)="clicked()">Click {{input1}}</button>'
|
||||||
|
})
|
||||||
|
export class BaseClass {
|
||||||
|
@Output()
|
||||||
|
output1 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
input1 = 'whatever';
|
||||||
|
|
||||||
|
clicked() {
|
||||||
|
this.output1.emit('test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<div>What is this developer doing?</div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent extends BaseClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expect(result.source).not.toContain('ngBaseDef');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT add ngBaseDef if @Directive is present', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, Directive, NgModule, Output, EventEmitter} from '@angular/core';
|
||||||
|
@Directive({
|
||||||
|
selector: 'whatever',
|
||||||
|
})
|
||||||
|
export class BaseClass {
|
||||||
|
@Output()
|
||||||
|
output1 = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
input1 = 'whatever';
|
||||||
|
|
||||||
|
clicked() {
|
||||||
|
this.output1.emit('test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: '<button (click)="clicked()">Click {{input1}}</button>'
|
||||||
|
})
|
||||||
|
export class MyComponent extends BaseClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
expect(result.source).not.toContain('ngBaseDef');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -87,5 +87,5 @@ export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata
|
||||||
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
||||||
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
||||||
export {R3Reference} from './render3/util';
|
export {R3Reference} from './render3/util';
|
||||||
export {compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings} from './render3/view/compiler';
|
export {compileBaseDefFromMetadata, R3BaseRefMetaData, compileComponentFromMetadata, compileDirectiveFromMetadata, parseHostBindings} from './render3/view/compiler';
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
|
@ -115,6 +115,13 @@ export class Identifiers {
|
||||||
|
|
||||||
static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE};
|
static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE};
|
||||||
|
|
||||||
|
static defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE};
|
||||||
|
|
||||||
|
static BaseDef: o.ExternalReference = {
|
||||||
|
name: 'ɵBaseDef',
|
||||||
|
moduleName: CORE,
|
||||||
|
};
|
||||||
|
|
||||||
static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};
|
static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};
|
||||||
|
|
||||||
static ComponentDef: o.ExternalReference = {
|
static ComponentDef: o.ExternalReference = {
|
||||||
|
|
|
@ -106,6 +106,42 @@ export function compileDirectiveFromMetadata(
|
||||||
return {expression, type, statements};
|
return {expression, type, statements};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface R3BaseRefMetaData {
|
||||||
|
inputs?: {[key: string]: string | [string, string]};
|
||||||
|
outputs?: {[key: string]: string};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile a base definition for the render3 runtime as defined by {@link R3BaseRefMetadata}
|
||||||
|
* @param meta the metadata used for compilation.
|
||||||
|
*/
|
||||||
|
export function compileBaseDefFromMetadata(meta: R3BaseRefMetaData) {
|
||||||
|
const definitionMap = new DefinitionMap();
|
||||||
|
if (meta.inputs) {
|
||||||
|
const inputs = meta.inputs;
|
||||||
|
const inputsMap = Object.keys(inputs).map(key => {
|
||||||
|
const v = inputs[key];
|
||||||
|
const value = Array.isArray(v) ? o.literalArr(v.map(vx => o.literal(vx))) : o.literal(v);
|
||||||
|
return {key, value, quoted: false};
|
||||||
|
});
|
||||||
|
definitionMap.set('inputs', o.literalMap(inputsMap));
|
||||||
|
}
|
||||||
|
if (meta.outputs) {
|
||||||
|
const outputs = meta.outputs;
|
||||||
|
const outputsMap = Object.keys(outputs).map(key => {
|
||||||
|
const value = o.literal(outputs[key]);
|
||||||
|
return {key, value, quoted: false};
|
||||||
|
});
|
||||||
|
definitionMap.set('outputs', o.literalMap(outputsMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
const expression = o.importExpr(R3.defineBase).callFn([definitionMap.toLiteralMap()]);
|
||||||
|
|
||||||
|
const type = new o.ExpressionType(o.importExpr(R3.BaseDef));
|
||||||
|
|
||||||
|
return {expression, type};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
* Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
export {
|
export {
|
||||||
|
defineBase as ɵdefineBase,
|
||||||
defineComponent as ɵdefineComponent,
|
defineComponent as ɵdefineComponent,
|
||||||
defineDirective as ɵdefineDirective,
|
defineDirective as ɵdefineDirective,
|
||||||
definePipe as ɵdefinePipe,
|
definePipe as ɵdefinePipe,
|
||||||
|
@ -97,6 +98,7 @@ export {
|
||||||
st as ɵst,
|
st as ɵst,
|
||||||
ld as ɵld,
|
ld as ɵld,
|
||||||
Pp as ɵPp,
|
Pp as ɵPp,
|
||||||
|
BaseDef as ɵBaseDef,
|
||||||
ComponentDef as ɵComponentDef,
|
ComponentDef as ɵComponentDef,
|
||||||
ComponentDefInternal as ɵComponentDefInternal,
|
ComponentDefInternal as ɵComponentDefInternal,
|
||||||
DirectiveDef as ɵDirectiveDef,
|
DirectiveDef as ɵDirectiveDef,
|
||||||
|
|
|
@ -779,6 +779,11 @@ const initializeBaseDef = (target: any): void => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to get the minified alias of ngBaseDef
|
||||||
|
*/
|
||||||
|
const NG_BASE_DEF = Object.keys({ngBaseDef: true})[0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the work of creating the `ngBaseDef` property for the @Input and @Output decorators.
|
* Does the work of creating the `ngBaseDef` property for the @Input and @Output decorators.
|
||||||
* @param key "inputs" or "outputs"
|
* @param key "inputs" or "outputs"
|
||||||
|
@ -787,7 +792,7 @@ const updateBaseDefFromIOProp = (getProp: (baseDef: {inputs?: any, outputs?: any
|
||||||
(target: any, name: string, ...args: any[]) => {
|
(target: any, name: string, ...args: any[]) => {
|
||||||
const constructor = target.constructor;
|
const constructor = target.constructor;
|
||||||
|
|
||||||
if (!constructor.hasOwnProperty('ngBaseDef')) {
|
if (!constructor.hasOwnProperty(NG_BASE_DEF)) {
|
||||||
initializeBaseDef(target);
|
initializeBaseDef(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {defineBase, defineComponent, defineDirective, defineNgModule, definePipe
|
||||||
import {InheritDefinitionFeature} from './features/inherit_definition_feature';
|
import {InheritDefinitionFeature} from './features/inherit_definition_feature';
|
||||||
import {NgOnChangesFeature} from './features/ng_onchanges_feature';
|
import {NgOnChangesFeature} from './features/ng_onchanges_feature';
|
||||||
import {PublicFeature} from './features/public_feature';
|
import {PublicFeature} from './features/public_feature';
|
||||||
import {ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition';
|
import {BaseDef, ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition';
|
||||||
|
|
||||||
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref';
|
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref';
|
||||||
export {Render3DebugRendererFactory2} from './debug';
|
export {Render3DebugRendererFactory2} from './debug';
|
||||||
|
@ -152,6 +152,7 @@ export {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
BaseDef,
|
||||||
ComponentDef,
|
ComponentDef,
|
||||||
ComponentDefInternal,
|
ComponentDefInternal,
|
||||||
ComponentTemplate,
|
ComponentTemplate,
|
||||||
|
|
|
@ -18,6 +18,7 @@ import * as sanitization from '../../sanitization/sanitization';
|
||||||
* This should be kept up to date with the public exports of @angular/core.
|
* This should be kept up to date with the public exports of @angular/core.
|
||||||
*/
|
*/
|
||||||
export const angularCoreEnv: {[name: string]: Function} = {
|
export const angularCoreEnv: {[name: string]: Function} = {
|
||||||
|
'ɵdefineBase': r3.defineBase,
|
||||||
'ɵdefineComponent': r3.defineComponent,
|
'ɵdefineComponent': r3.defineComponent,
|
||||||
'ɵdefineDirective': r3.defineDirective,
|
'ɵdefineDirective': r3.defineDirective,
|
||||||
'defineInjectable': defineInjectable,
|
'defineInjectable': defineInjectable,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {Identifiers} from '@angular/compiler/src/render3/r3_identifiers';
|
||||||
import {angularCoreEnv} from '../../src/render3/jit/environment';
|
import {angularCoreEnv} from '../../src/render3/jit/environment';
|
||||||
|
|
||||||
const INTERFACE_EXCEPTIONS = new Set<string>([
|
const INTERFACE_EXCEPTIONS = new Set<string>([
|
||||||
|
'ɵBaseDef',
|
||||||
'ɵComponentDef',
|
'ɵComponentDef',
|
||||||
'ɵDirectiveDef',
|
'ɵDirectiveDef',
|
||||||
'ɵInjectorDef',
|
'ɵInjectorDef',
|
||||||
|
|
Loading…
Reference in New Issue