diff --git a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts index 938bb5974f..e83a953710 100644 --- a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts @@ -30,6 +30,9 @@ export interface DecorationAnalysis { constantPool: ConstantPool; } +export type DecorationAnalyses = Map; +export const DecorationAnalyses = Map; + export interface MatchingHandler { handler: DecoratorHandler; match: M; @@ -63,12 +66,30 @@ export class DecorationAnalyzer { private typeChecker: ts.TypeChecker, private host: NgccReflectionHost, private rootDirs: string[], private isCore: boolean) {} + /** + * Analyze a program to find all the decorated files should be transformed. + * @param program The program whose files should be analysed. + * @returns a map of the source files to the analysis for those files. + */ + analyzeProgram(program: ts.Program): DecorationAnalyses { + const analyzedFiles = new DecorationAnalyses(); + program.getRootFileNames().forEach(fileName => { + const entryPoint = program.getSourceFile(fileName) !; + const decoratedFiles = this.host.findDecoratedFiles(entryPoint); + decoratedFiles.forEach( + decoratedFile => + analyzedFiles.set(decoratedFile.sourceFile, this.analyzeFile(decoratedFile))); + }); + return analyzedFiles; + } + /** * Analyze a decorated file to generate the information about decorated classes that * should be converted to use ivy definitions. * @param file The file to be analysed for decorated classes. + * @returns the analysis of the file */ - analyzeFile(file: DecoratedFile): DecorationAnalysis { + protected analyzeFile(file: DecoratedFile): DecorationAnalysis { const constantPool = new ConstantPool(); const analyzedClasses = file.decoratedClasses.map(clazz => this.analyzeClass(constantPool, clazz)) diff --git a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts index 8d4041a6f5..42133d1240 100644 --- a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -9,23 +9,21 @@ import * as ts from 'typescript'; import {Decorator} from '../../../ngtsc/host'; import {DecoratorHandler} from '../../../ngtsc/transform'; -import {DecorationAnalysis, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; -import {DecoratedClass} from '../../src/host/decorated_class'; -import {DecoratedFile} from '../../src/host/decorated_file'; +import {DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {Fesm2015ReflectionHost} from '../../src/host/fesm2015_host'; -import {getDeclaration, makeProgram} from '../helpers/utils'; +import {makeProgram} from '../helpers/utils'; const TEST_PROGRAM = { name: 'test.js', contents: ` import {Component, Injectable} from '@angular/core'; - @Component() export class MyComponent {} + MyComponent.decorators = [{type: Component}]; - @Injectable() export class MyService {} + MyService.decorators = [{type: Injectable}]; ` }; @@ -50,49 +48,26 @@ function createTestHandler() { return handler; } -function createParsedFile(program: ts.Program) { - const file = new DecoratedFile(program.getSourceFile('test.js') !); - - const componentClass = getDeclaration(program, 'test.js', 'MyComponent', ts.isClassDeclaration); - file.decoratedClasses.push( - new DecoratedClass('MyComponent', {} as any, [{ - name: 'Component', - import: {from: '@angular/core', name: 'Component'}, - node: null as any, - args: null - }])); - - const serviceClass = getDeclaration(program, 'test.js', 'MyService', ts.isClassDeclaration); - file.decoratedClasses.push( - new DecoratedClass('MyService', {} as any, [{ - name: 'Injectable', - import: {from: '@angular/core', name: 'Injectable'}, - node: null as any, - args: null - }])); - - return file; -} - describe('DecorationAnalyzer', () => { - describe('analyzeFile()', () => { + describe('analyzeProgram()', () => { let program: ts.Program; let testHandler: jasmine.SpyObj>; - let result: DecorationAnalysis; + let result: DecorationAnalyses; beforeEach(() => { program = makeProgram(TEST_PROGRAM); - const file = createParsedFile(program); const analyzer = new DecorationAnalyzer( program.getTypeChecker(), new Fesm2015ReflectionHost(false, program.getTypeChecker()), [''], false); testHandler = createTestHandler(); analyzer.handlers = [testHandler]; - result = analyzer.analyzeFile(file); + result = analyzer.analyzeProgram(program); }); - it('should return an object containing a reference to the original source file', - () => { expect(result.sourceFile).toBe(program.getSourceFile('test.js') !); }); + it('should return an object containing a reference to the original source file', () => { + const file = program.getSourceFile(TEST_PROGRAM.name) !; + expect(result.get(file) !.sourceFile).toBe(file); + }); it('should call detect on the decorator handlers with each class from the parsed file', () => { expect(testHandler.detect).toHaveBeenCalledTimes(2); @@ -103,8 +78,10 @@ describe('DecorationAnalyzer', () => { }); it('should return an object containing the classes that were analyzed', () => { - expect(result.analyzedClasses.length).toEqual(1); - expect(result.analyzedClasses[0].name).toEqual('MyComponent'); + const file = program.getSourceFile(TEST_PROGRAM.name) !; + const analysis = result.get(file) !; + expect(analysis.analyzedClasses.length).toEqual(1); + expect(analysis.analyzedClasses[0].name).toEqual('MyComponent'); }); it('should analyze and compile the classes that are detected', () => {