fix(ivy): ngcc should process undecorated base classes (#30821)
Currently undecorated classes are intentionally not processed with ngcc. This is causing unexpected behavior because decorator handlers such as `base_def.ts` are specifically interested in class definitions without top-level decorators, so that the base definition can be generated if there are Angular-specific class members. In order to ensure that undecorated base-classes work as expected with Ivy, we need to run the decorator handlers for all top-level class declarations (not only for those with decorators). This is similar to when `ngtsc` runs decorator handlers when analyzing source-files. Resolves FW-1355. Fixes https://github.com/angular/components/issues/16178 PR Close #30821
This commit is contained in:

committed by
Igor Minar

parent
271d2b51a9
commit
2b4d5c7548
@ -14,10 +14,10 @@ import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy,
|
||||
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {AbsoluteFsPath, LogicalFileSystem} from '../../../src/ngtsc/path';
|
||||
import {ClassDeclaration, ClassSymbol, Decorator} from '../../../src/ngtsc/reflection';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
|
||||
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {DecoratedClass} from '../host/decorated_class';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {isDefined} from '../utils';
|
||||
|
||||
@ -26,7 +26,10 @@ export interface AnalyzedFile {
|
||||
analyzedClasses: AnalyzedClass[];
|
||||
}
|
||||
|
||||
export interface AnalyzedClass extends DecoratedClass {
|
||||
export interface AnalyzedClass {
|
||||
name: string;
|
||||
decorators: Decorator[]|null;
|
||||
declaration: ClassDeclaration;
|
||||
diagnostics?: ts.Diagnostic[];
|
||||
matches: {handler: DecoratorHandler<any, any>; analysis: any;}[];
|
||||
}
|
||||
@ -133,19 +136,18 @@ export class DecorationAnalyzer {
|
||||
}
|
||||
|
||||
protected analyzeFile(sourceFile: ts.SourceFile): AnalyzedFile|undefined {
|
||||
const decoratedClasses = this.reflectionHost.findDecoratedClasses(sourceFile);
|
||||
return decoratedClasses.length ? {
|
||||
sourceFile,
|
||||
analyzedClasses: decoratedClasses.map(clazz => this.analyzeClass(clazz)).filter(isDefined)
|
||||
} :
|
||||
undefined;
|
||||
const analyzedClasses = this.reflectionHost.findClassSymbols(sourceFile)
|
||||
.map(symbol => this.analyzeClass(symbol))
|
||||
.filter(isDefined);
|
||||
return analyzedClasses.length ? {sourceFile, analyzedClasses} : undefined;
|
||||
}
|
||||
|
||||
protected analyzeClass(clazz: DecoratedClass): AnalyzedClass|null {
|
||||
protected analyzeClass(symbol: ClassSymbol): AnalyzedClass|null {
|
||||
const declaration = symbol.valueDeclaration;
|
||||
const decorators = this.reflectionHost.getDecoratorsOfSymbol(symbol);
|
||||
const matchingHandlers = this.handlers
|
||||
.map(handler => {
|
||||
const detected =
|
||||
handler.detect(clazz.declaration, clazz.decorators);
|
||||
const detected = handler.detect(declaration, decorators);
|
||||
return {handler, detected};
|
||||
})
|
||||
.filter(isMatchingHandler);
|
||||
@ -183,13 +185,19 @@ export class DecorationAnalyzer {
|
||||
const matches: {handler: DecoratorHandler<any, any>, analysis: any}[] = [];
|
||||
const allDiagnostics: ts.Diagnostic[] = [];
|
||||
for (const {handler, detected} of detections) {
|
||||
const {analysis, diagnostics} = handler.analyze(clazz.declaration, detected.metadata);
|
||||
const {analysis, diagnostics} = handler.analyze(declaration, detected.metadata);
|
||||
if (diagnostics !== undefined) {
|
||||
allDiagnostics.push(...diagnostics);
|
||||
}
|
||||
matches.push({handler, analysis});
|
||||
}
|
||||
return {...clazz, matches, diagnostics: allDiagnostics.length > 0 ? allDiagnostics : undefined};
|
||||
return {
|
||||
name: symbol.name,
|
||||
declaration,
|
||||
decorators,
|
||||
matches,
|
||||
diagnostics: allDiagnostics.length > 0 ? allDiagnostics : undefined
|
||||
};
|
||||
}
|
||||
|
||||
protected compileFile(analyzedFile: AnalyzedFile): CompiledFile {
|
||||
|
@ -1,28 +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 {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
|
||||
|
||||
/**
|
||||
* A simple container that holds the details of a decorated class that has been
|
||||
* found in a `DecoratedFile`.
|
||||
*/
|
||||
export class DecoratedClass {
|
||||
/**
|
||||
* Initialize a `DecoratedClass` that was found in a `DecoratedFile`.
|
||||
* @param name The name of the class that has been found. This is mostly used
|
||||
* for informational purposes.
|
||||
* @param declaration The TypeScript AST node where this class is declared. In ES5 code, where a
|
||||
* class can be represented by both a variable declaration and a function declaration (inside an
|
||||
* IIFE), `declaration` will always refer to the outer variable declaration, which represents the
|
||||
* class to the rest of the program.
|
||||
* @param decorators The collection of decorators that have been found on this class.
|
||||
*/
|
||||
constructor(
|
||||
public name: string, public declaration: ClassDeclaration, public decorators: Decorator[]) {}
|
||||
}
|
@ -13,7 +13,6 @@ import {Logger} from '../logging/logger';
|
||||
import {BundleProgram} from '../packages/bundle_program';
|
||||
import {findAll, getNameText, hasNameIdentifier, isDefined} from '../utils';
|
||||
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ModuleWithProvidersFunction, NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
|
||||
|
||||
export const DECORATORS = 'decorators' as ts.__String;
|
||||
@ -233,6 +232,16 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return superDeclaration;
|
||||
}
|
||||
|
||||
/** Gets all decorators of the given class symbol. */
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
} else {
|
||||
return this.getClassDecoratorsFromHelperCall(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given module for variable declarations in which the initializer
|
||||
* is an identifier marked with the `PRE_R3_MARKER`.
|
||||
@ -306,24 +315,24 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the classes that contain decorations in a given file.
|
||||
* @param sourceFile The source file to search for decorated classes.
|
||||
* @returns An array of decorated classes.
|
||||
* Find all top-level class symbols in the given file.
|
||||
* @param sourceFile The source file to search for classes.
|
||||
* @returns An array of class symbols.
|
||||
*/
|
||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[] {
|
||||
const classes: DecoratedClass[] = [];
|
||||
findClassSymbols(sourceFile: ts.SourceFile): ClassSymbol[] {
|
||||
const classes: ClassSymbol[] = [];
|
||||
this.getModuleStatements(sourceFile).forEach(statement => {
|
||||
if (ts.isVariableStatement(statement)) {
|
||||
statement.declarationList.declarations.forEach(declaration => {
|
||||
const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(declaration));
|
||||
if (decoratedClass) {
|
||||
classes.push(decoratedClass);
|
||||
const classSymbol = this.getClassSymbol(declaration);
|
||||
if (classSymbol) {
|
||||
classes.push(classSymbol);
|
||||
}
|
||||
});
|
||||
} else if (ts.isClassDeclaration(statement)) {
|
||||
const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(statement));
|
||||
if (decoratedClass) {
|
||||
classes.push(decoratedClass);
|
||||
const classSymbol = this.getClassSymbol(statement);
|
||||
if (classSymbol) {
|
||||
classes.push(classSymbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -488,25 +497,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return Array.from(sourceFile.statements);
|
||||
}
|
||||
|
||||
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
|
||||
if (decoratorsProperty) {
|
||||
return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
|
||||
} else {
|
||||
return this.getClassDecoratorsFromHelperCall(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected getDecoratedClassFromSymbol(symbol: ClassSymbol|undefined): DecoratedClass|null {
|
||||
if (symbol) {
|
||||
const decorators = this.getDecoratorsOfSymbol(symbol);
|
||||
if (decorators && decorators.length) {
|
||||
return new DecoratedClass(symbol.name, symbol.valueDeclaration, decorators);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the AST looking for an assignment to the specified symbol.
|
||||
* @param node The current node we are searching.
|
||||
|
@ -211,6 +211,16 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return this.getMembersOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
/** Gets all decorators of the given class symbol. */
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol =
|
||||
this.getInnerFunctionSymbolFromClassDeclaration(symbol.valueDeclaration);
|
||||
if (!innerFunctionSymbol) return null;
|
||||
|
||||
return super.getDecoratorsOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
@ -321,15 +331,6 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return super.getConstructorParamInfo(innerFunctionSymbol, parameterNodes);
|
||||
}
|
||||
|
||||
protected getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null {
|
||||
// The necessary info is on the inner function declaration (inside the ES5 class IIFE).
|
||||
const innerFunctionSymbol =
|
||||
this.getInnerFunctionSymbolFromClassDeclaration(symbol.valueDeclaration);
|
||||
if (!innerFunctionSymbol) return null;
|
||||
|
||||
return super.getDecoratorsOfSymbol(innerFunctionSymbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type and decorators for the constructor of a class,
|
||||
* where the information is stored on a static method of the class.
|
||||
|
@ -6,8 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {ClassDeclaration, ClassSymbol, Declaration, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
import {DecoratedClass} from './decorated_class';
|
||||
import {ClassDeclaration, ClassSymbol, Declaration, Decorator, ReflectionHost} from '../../../src/ngtsc/reflection';
|
||||
|
||||
export const PRE_R3_MARKER = '__PRE_R3__';
|
||||
export const POST_R3_MARKER = '__POST_R3__';
|
||||
@ -64,11 +63,18 @@ export interface NgccReflectionHost extends ReflectionHost {
|
||||
getSwitchableDeclarations(module: ts.Node): SwitchableVariableDeclaration[];
|
||||
|
||||
/**
|
||||
* Find all the classes that contain decorations in a given file.
|
||||
* @param sourceFile The source file to search for decorated classes.
|
||||
* @returns An array of decorated classes.
|
||||
* Retrieves all decorators of a given class symbol.
|
||||
* @param symbol Class symbol that can refer to a declaration which can hold decorators.
|
||||
* @returns An array of decorators or null if none are declared.
|
||||
*/
|
||||
findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
|
||||
getDecoratorsOfSymbol(symbol: ClassSymbol): Decorator[]|null;
|
||||
|
||||
/**
|
||||
* Retrieves all class symbols of a given source file.
|
||||
* @param sourceFile The source file to search for classes.
|
||||
* @returns An array of found class symbols.
|
||||
*/
|
||||
findClassSymbols(sourceFile: ts.SourceFile): ClassSymbol[];
|
||||
|
||||
/**
|
||||
* Search the given source file for exported functions and static class methods that return
|
||||
|
@ -122,6 +122,10 @@ export class Renderer {
|
||||
private computeDecoratorsToRemove(classes: CompiledClass[]): RedundantDecoratorMap {
|
||||
const decoratorsToRemove = new RedundantDecoratorMap();
|
||||
classes.forEach(clazz => {
|
||||
if (clazz.decorators === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
clazz.decorators.forEach(dec => {
|
||||
const decoratorArray = dec.node.parent !;
|
||||
if (!decoratorsToRemove.has(decoratorArray)) {
|
||||
|
@ -95,8 +95,13 @@ describe('DecorationAnalyzer', () => {
|
||||
]);
|
||||
// Only detect the Component and Directive decorators
|
||||
handler.detect.and.callFake(
|
||||
(node: ts.Declaration, decorators: Decorator[]): DetectResult<any>| undefined => {
|
||||
logs.push(`detect: ${(node as any).name.text}@${decorators.map(d => d.name)}`);
|
||||
(node: ts.Declaration, decorators: Decorator[] | null): DetectResult<any>| undefined => {
|
||||
const className = (node as any).name.text;
|
||||
if (decorators === null) {
|
||||
logs.push(`detect: ${className} (no decorators)`);
|
||||
} else {
|
||||
logs.push(`detect: ${className}@${decorators.map(d => d.name)}`);
|
||||
}
|
||||
if (!decorators) {
|
||||
return undefined;
|
||||
}
|
||||
@ -160,12 +165,13 @@ describe('DecorationAnalyzer', () => {
|
||||
|
||||
it('should call detect on the decorator handlers with each class from the parsed file',
|
||||
() => {
|
||||
expect(testHandler.detect).toHaveBeenCalledTimes(4);
|
||||
expect(testHandler.detect.calls.allArgs().map(args => args[1][0])).toEqual([
|
||||
jasmine.objectContaining({name: 'Component'}),
|
||||
jasmine.objectContaining({name: 'Directive'}),
|
||||
jasmine.objectContaining({name: 'Injectable'}),
|
||||
jasmine.objectContaining({name: 'Component'}),
|
||||
expect(testHandler.detect).toHaveBeenCalledTimes(5);
|
||||
expect(testHandler.detect.calls.allArgs().map(args => args[1])).toEqual([
|
||||
null,
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]),
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Directive'})]),
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Injectable'})]),
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]),
|
||||
]);
|
||||
});
|
||||
|
||||
@ -190,6 +196,8 @@ describe('DecorationAnalyzer', () => {
|
||||
|
||||
it('should analyze, resolve and compile the classes that are detected', () => {
|
||||
expect(logs).toEqual([
|
||||
// Classes without decorators should also be detected.
|
||||
'detect: InjectionToken (no decorators)',
|
||||
// First detect and (potentially) analyze.
|
||||
'detect: MyComponent@Component',
|
||||
'analyze: MyComponent@Component',
|
||||
|
@ -1587,27 +1587,41 @@ describe('CommonJsReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecoratedClasses()', () => {
|
||||
it('should return an array of all decorated classes in the given source file', () => {
|
||||
describe('findClassSymbols()', () => {
|
||||
it('should return an array of all classes in the given source file', () => {
|
||||
const {program, host: compilerHost} = makeTestBundleProgram(DECORATED_FILES);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||
const primary = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const primaryDecoratedClasses = host.findDecoratedClasses(primary);
|
||||
expect(primaryDecoratedClasses.length).toEqual(2);
|
||||
const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
|
||||
expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
// Note that `B` is not exported from `primary.js`
|
||||
const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
|
||||
expect(classB.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
expect(classSymbolsPrimary.length).toEqual(2);
|
||||
expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']);
|
||||
|
||||
const secondary = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
const secondaryDecoratedClasses = host.findDecoratedClasses(secondary);
|
||||
expect(secondaryDecoratedClasses.length).toEqual(1);
|
||||
// Note that `D` is exported from `secondary.js` but not exported from `primary.js`
|
||||
const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
|
||||
expect(classD.name).toEqual('D');
|
||||
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
expect(classSymbolsSecondary.length).toEqual(1);
|
||||
expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecoratorsOfSymbol()', () => {
|
||||
it('should return decorators of class symbol', () => {
|
||||
const {program, host: compilerHost} = makeTestBundleProgram(DECORATED_FILES);
|
||||
const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsPrimary.length).toEqual(2);
|
||||
expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']);
|
||||
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
const classDecoratorsSecondary =
|
||||
classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsSecondary.length).toEqual(1);
|
||||
expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1539,30 +1539,42 @@ describe('Esm2015ReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecoratedClasses()', () => {
|
||||
it('should return an array of all decorated classes in the given source file', () => {
|
||||
describe('findClassSymbols()', () => {
|
||||
it('should return an array of all classes in the given source file', () => {
|
||||
const program = makeTestProgram(...DECORATED_FILES);
|
||||
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const primaryDecoratedClasses = host.findDecoratedClasses(primaryFile);
|
||||
expect(primaryDecoratedClasses.length).toEqual(2);
|
||||
const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
|
||||
expect(ts.isClassDeclaration(classA.declaration)).toBeTruthy();
|
||||
expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
// Note that `B` is not exported from `primary.js`
|
||||
const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
|
||||
expect(ts.isClassDeclaration(classB.declaration)).toBeTruthy();
|
||||
expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
expect(classSymbolsPrimary.length).toEqual(3);
|
||||
expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B', 'C']);
|
||||
|
||||
const secondaryDecoratedClasses = host.findDecoratedClasses(secondaryFile) !;
|
||||
expect(secondaryDecoratedClasses.length).toEqual(1);
|
||||
// Note that `D` is exported from `secondary.js` but not exported from `primary.js`
|
||||
const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
|
||||
expect(classD.name).toEqual('D');
|
||||
expect(ts.isClassDeclaration(classD.declaration)).toBeTruthy();
|
||||
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
expect(classSymbolsSecondary.length).toEqual(1);
|
||||
expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecoratorsOfSymbol()', () => {
|
||||
it('should return decorators of class symbol', () => {
|
||||
const program = makeTestProgram(...DECORATED_FILES);
|
||||
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsPrimary.length).toEqual(3);
|
||||
expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']);
|
||||
expect(classDecoratorsPrimary[2]).toBe(null);
|
||||
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
const classDecoratorsSecondary =
|
||||
classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsSecondary.length).toEqual(1);
|
||||
expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -309,24 +309,46 @@ describe('Esm5ReflectionHost [import helper style]', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecoratedClasses', () => {
|
||||
it('should return an array of all decorated classes in the given source file', () => {
|
||||
describe('findClassSymbols()', () => {
|
||||
it('should return an array of all classes in the given source file', () => {
|
||||
const program = makeTestProgram(...fileSystem.files);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
|
||||
const ngModuleFile = program.getSourceFile('/ngmodule.js') !;
|
||||
const ngModuleClasses = host.findDecoratedClasses(ngModuleFile);
|
||||
const ngModuleClasses = host.findClassSymbols(ngModuleFile);
|
||||
expect(ngModuleClasses.length).toEqual(1);
|
||||
const ngModuleClass = ngModuleClasses.find(c => c.name === 'HttpClientXsrfModule') !;
|
||||
expect(ngModuleClass.decorators.map(decorator => decorator.name)).toEqual(['NgModule']);
|
||||
expect(ngModuleClasses[0].name).toBe('HttpClientXsrfModule');
|
||||
|
||||
const someDirectiveFile = program.getSourceFile('/some_directive.js') !;
|
||||
const someDirectiveClasses = host.findDecoratedClasses(someDirectiveFile);
|
||||
expect(someDirectiveClasses.length).toEqual(1);
|
||||
const someDirectiveClass = someDirectiveClasses.find(c => c.name === 'SomeDirective') !;
|
||||
expect(someDirectiveClass.decorators.map(decorator => decorator.name)).toEqual([
|
||||
'Directive'
|
||||
]);
|
||||
const someDirectiveClasses = host.findClassSymbols(someDirectiveFile);
|
||||
expect(someDirectiveClasses.length).toEqual(3);
|
||||
expect(someDirectiveClasses[0].name).toBe('ViewContainerRef');
|
||||
expect(someDirectiveClasses[1].name).toBe('TemplateRef');
|
||||
expect(someDirectiveClasses[2].name).toBe('SomeDirective');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecoratorsOfSymbol()', () => {
|
||||
it('should return decorators of class symbol', () => {
|
||||
const program = makeTestProgram(...fileSystem.files);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
|
||||
const ngModuleFile = program.getSourceFile('/ngmodule.js') !;
|
||||
const ngModuleClasses = host.findClassSymbols(ngModuleFile);
|
||||
const ngModuleDecorators = ngModuleClasses.map(s => host.getDecoratorsOfSymbol(s));
|
||||
|
||||
expect(ngModuleClasses.length).toEqual(1);
|
||||
expect(ngModuleDecorators[0] !.map(d => d.name)).toEqual(['NgModule']);
|
||||
|
||||
const someDirectiveFile = program.getSourceFile('/some_directive.js') !;
|
||||
const someDirectiveClasses = host.findClassSymbols(someDirectiveFile);
|
||||
const someDirectiveDecorators =
|
||||
someDirectiveClasses.map(s => host.getDecoratorsOfSymbol(s));
|
||||
|
||||
expect(someDirectiveDecorators.length).toEqual(3);
|
||||
expect(someDirectiveDecorators[0]).toBe(null);
|
||||
expect(someDirectiveDecorators[1]).toBe(null);
|
||||
expect(someDirectiveDecorators[2] !.map(d => d.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1787,27 +1787,41 @@ describe('Esm5ReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecoratedClasses()', () => {
|
||||
it('should return an array of all decorated classes in the given source file', () => {
|
||||
describe('findClassSymbols()', () => {
|
||||
it('should return an array of all classes in the given source file', () => {
|
||||
const program = makeTestProgram(...DECORATED_FILES);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
const primary = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const primaryDecoratedClasses = host.findDecoratedClasses(primary);
|
||||
expect(primaryDecoratedClasses.length).toEqual(2);
|
||||
const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
|
||||
expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
// Note that `B` is not exported from `primary.js`
|
||||
const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
|
||||
expect(classB.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
expect(classSymbolsPrimary.length).toEqual(2);
|
||||
expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']);
|
||||
|
||||
const secondary = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
const secondaryDecoratedClasses = host.findDecoratedClasses(secondary);
|
||||
expect(secondaryDecoratedClasses.length).toEqual(1);
|
||||
// Note that `D` is exported from `secondary.js` but not exported from `primary.js`
|
||||
const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
|
||||
expect(classD.name).toEqual('D');
|
||||
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
expect(classSymbolsSecondary.length).toEqual(1);
|
||||
expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecoratorsOfSymbol()', () => {
|
||||
it('should return decorators of class symbol', () => {
|
||||
const program = makeTestProgram(...DECORATED_FILES);
|
||||
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsPrimary.length).toEqual(2);
|
||||
expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']);
|
||||
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
const classDecoratorsSecondary =
|
||||
classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsSecondary.length).toEqual(1);
|
||||
expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1681,27 +1681,41 @@ describe('UmdReflectionHost', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecoratedClasses()', () => {
|
||||
it('should return an array of all decorated classes in the given source file', () => {
|
||||
describe('findClassSymbols()', () => {
|
||||
it('should return an array of all classes in the given source file', () => {
|
||||
const {program, host: compilerHost} = makeTestBundleProgram(DECORATED_FILES);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||
const primary = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const primaryDecoratedClasses = host.findDecoratedClasses(primary);
|
||||
expect(primaryDecoratedClasses.length).toEqual(2);
|
||||
const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
|
||||
expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
// Note that `B` is not exported from `primary.js`
|
||||
const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
|
||||
expect(classB.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
expect(classSymbolsPrimary.length).toEqual(2);
|
||||
expect(classSymbolsPrimary.map(c => c.name)).toEqual(['A', 'B']);
|
||||
|
||||
const secondary = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
const secondaryDecoratedClasses = host.findDecoratedClasses(secondary);
|
||||
expect(secondaryDecoratedClasses.length).toEqual(1);
|
||||
// Note that `D` is exported from `secondary.js` but not exported from `primary.js`
|
||||
const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
|
||||
expect(classD.name).toEqual('D');
|
||||
expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
expect(classSymbolsSecondary.length).toEqual(1);
|
||||
expect(classSymbolsSecondary.map(c => c.name)).toEqual(['D']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecoratorsOfSymbol()', () => {
|
||||
it('should return decorators of class symbol', () => {
|
||||
const {program, host: compilerHost} = makeTestBundleProgram(DECORATED_FILES);
|
||||
const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||
const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !;
|
||||
const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !;
|
||||
|
||||
const classSymbolsPrimary = host.findClassSymbols(primaryFile);
|
||||
const classDecoratorsPrimary = classSymbolsPrimary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsPrimary.length).toEqual(2);
|
||||
expect(classDecoratorsPrimary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
expect(classDecoratorsPrimary[1] !.map(d => d.name)).toEqual(['Directive']);
|
||||
|
||||
const classSymbolsSecondary = host.findClassSymbols(secondaryFile);
|
||||
const classDecoratorsSecondary =
|
||||
classSymbolsSecondary.map(s => host.getDecoratorsOfSymbol(s));
|
||||
expect(classDecoratorsSecondary.length).toEqual(1);
|
||||
expect(classDecoratorsSecondary[0] !.map(d => d.name)).toEqual(['Directive']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -326,7 +326,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -345,7 +345,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -366,7 +366,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -389,7 +389,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -406,7 +406,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -424,7 +424,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -318,7 +318,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -336,7 +336,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -355,7 +355,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -376,7 +376,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -393,7 +393,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -411,7 +411,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -226,7 +226,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -245,7 +245,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -264,7 +264,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -324,7 +324,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -341,7 +341,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -359,7 +359,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -234,6 +234,38 @@ describe('Renderer', () => {
|
||||
.toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`);
|
||||
});
|
||||
|
||||
it('should render classes without decorators if handler matches', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
testFormatter} = createTestRenderer('test-package', [{
|
||||
name: '/src/file.js',
|
||||
contents: `
|
||||
import { Directive, ViewChild } from '@angular/core';
|
||||
|
||||
export class UndecoratedBase { test = null; }
|
||||
|
||||
UndecoratedBase.propDecorators = {
|
||||
test: [{
|
||||
type: ViewChild,
|
||||
args: ["test", {static: true}]
|
||||
}],
|
||||
};
|
||||
`
|
||||
}]);
|
||||
|
||||
renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toEqual(
|
||||
`UndecoratedBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ viewQuery: function (rf, ctx) { if (rf & 1) {
|
||||
ɵngcc0.ɵɵstaticViewQuery(_c0, true, null);
|
||||
} if (rf & 2) {
|
||||
var _t;
|
||||
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadViewQuery()) && (ctx.test = _t.first);
|
||||
} } });`);
|
||||
});
|
||||
|
||||
it('should call renderImports after other abstract methods', () => {
|
||||
// This allows the other methods to add additional imports if necessary
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
|
@ -377,7 +377,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -396,7 +396,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -417,7 +417,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -439,7 +439,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -456,7 +456,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -474,7 +474,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
Reference in New Issue
Block a user