diff --git a/packages/compiler-cli/src/ngcc/src/analyzer.ts b/packages/compiler-cli/src/ngcc/src/analyzer.ts index 30423bda7c..dfc72254b5 100644 --- a/packages/compiler-cli/src/ngcc/src/analyzer.ts +++ b/packages/compiler-cli/src/ngcc/src/analyzer.ts @@ -9,8 +9,7 @@ import {ConstantPool} from '@angular/compiler'; import * as fs from 'fs'; import * as ts from 'typescript'; -import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations'; -import {BaseDefDecoratorHandler} from '../../ngtsc/annotations/src/base_def'; +import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../ngtsc/annotations'; import {Decorator} from '../../ngtsc/host'; import {CompileResult, DecoratorHandler} from '../../ngtsc/transform'; @@ -34,7 +33,7 @@ export interface AnalyzedFile { export interface MatchingHandler { handler: DecoratorHandler; - decorator: Decorator; + match: M; } /** @@ -80,9 +79,10 @@ export class Analyzer { |undefined { const matchingHandlers = this.handlers - .map( - handler => - ({handler, decorator: handler.detect(clazz.declaration, clazz.decorators)})) + .map(handler => ({ + handler, + decorator: handler.detect(clazz.declaration, clazz.decorators), + })) .filter(isMatchingHandler); if (matchingHandlers.length > 1) { @@ -105,5 +105,5 @@ export class Analyzer { function isMatchingHandler(handler: Partial>): handler is MatchingHandler { - return !!handler.decorator; + return !!handler.match; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/index.ts b/packages/compiler-cli/src/ngtsc/annotations/index.ts index 3beccc7aa8..77a4860842 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/index.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/index.ts @@ -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'; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts b/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts index a2a8cf5354..5924bde1e2 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts @@ -32,7 +32,7 @@ export class BaseDefDecoratorHandler implements |undefined { if (containsNgTopLevelDecorator(decorators)) { // If the class is already decorated by @Component or @Directive let that - // decorator handle this. BaseDef is unnecessary. + // DecoratorHandler handle this. BaseDef is unnecessary. return undefined; } @@ -67,10 +67,16 @@ export class BaseDefDecoratorHandler implements metadata.inputs.forEach(({decorator, property}) => { const propName = property.name; const args = decorator.args; - const value: string|[string, string] = args && args.length >= 1 ? - [staticallyResolve(args[0], this.reflector, this.checker) as string, propName] : - propName; - + 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; }); } @@ -80,9 +86,16 @@ export class BaseDefDecoratorHandler implements metadata.outputs.forEach(({decorator, property}) => { const propName = property.name; const args = decorator.args; - const value = args && args.length >= 1 ? - staticallyResolve(args[0], this.reflector, this.checker) as string : - propName; + 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; }); } diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index 4d9486a3f8..6789d0e807 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -22,7 +22,7 @@ import {DtsFileTransformer} from './declaration'; interface EmitFieldOperation { adapter: DecoratorHandler; analysis: AnalysisOutput; - decorator: Decorator; + metadata: M; } /** @@ -104,7 +104,7 @@ export class IvyCompilation { this.analysis.set(node, { adapter, analysis: analysis.analysis, - decorator: metadata, + metadata: metadata, }); } @@ -185,7 +185,7 @@ export class IvyCompilation { return undefined; } - return this.analysis.get(original) !.decorator; + return this.analysis.get(original) !.metadata; } /** diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index cfdd9ec8f0..68444a0c02 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -1979,7 +1979,6 @@ describe('compiler compliance', () => { // ... `; const result = compile(files, angularFiles); - debugger; expectEmit(result.source, expectedOutput, 'Invalid base definition'); }); @@ -2019,7 +2018,7 @@ describe('compiler compliance', () => { } }; const result = compile(files, angularFiles); - expect(result.source.indexOf('ngBaseDef')).toBe(-1); + expect(result.source).not.toContain('ngBaseDef'); }); it('should NOT add ngBaseDef if @Directive is present', () => { @@ -2057,7 +2056,7 @@ describe('compiler compliance', () => { } }; const result = compile(files, angularFiles); - expect(result.source.indexOf('ngBaseDef')).toBe(-1); + expect(result.source).not.toContain('ngBaseDef'); }); }); }); diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index a8701e2ed4..8dabc20746 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -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. * @param key "inputs" or "outputs" @@ -787,7 +792,7 @@ const updateBaseDefFromIOProp = (getProp: (baseDef: {inputs?: any, outputs?: any (target: any, name: string, ...args: any[]) => { const constructor = target.constructor; - if (!constructor.hasOwnProperty('ngBaseDef')) { + if (!constructor.hasOwnProperty(NG_BASE_DEF)) { initializeBaseDef(target); }