refactor(ivy): ngcc - `DecorationAnalyzer` acts on whole program (#26082)
PR Close #26082
This commit is contained in:
parent
9562324ea4
commit
f7b17a4784
|
@ -30,6 +30,9 @@ export interface DecorationAnalysis {
|
||||||
constantPool: ConstantPool;
|
constantPool: ConstantPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DecorationAnalyses = Map<ts.SourceFile, DecorationAnalysis>;
|
||||||
|
export const DecorationAnalyses = Map;
|
||||||
|
|
||||||
export interface MatchingHandler<A, M> {
|
export interface MatchingHandler<A, M> {
|
||||||
handler: DecoratorHandler<A, M>;
|
handler: DecoratorHandler<A, M>;
|
||||||
match: M;
|
match: M;
|
||||||
|
@ -63,12 +66,30 @@ export class DecorationAnalyzer {
|
||||||
private typeChecker: ts.TypeChecker, private host: NgccReflectionHost,
|
private typeChecker: ts.TypeChecker, private host: NgccReflectionHost,
|
||||||
private rootDirs: string[], private isCore: boolean) {}
|
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
|
* Analyze a decorated file to generate the information about decorated classes that
|
||||||
* should be converted to use ivy definitions.
|
* should be converted to use ivy definitions.
|
||||||
* @param file The file to be analysed for decorated classes.
|
* @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 constantPool = new ConstantPool();
|
||||||
const analyzedClasses =
|
const analyzedClasses =
|
||||||
file.decoratedClasses.map(clazz => this.analyzeClass(constantPool, clazz))
|
file.decoratedClasses.map(clazz => this.analyzeClass(constantPool, clazz))
|
||||||
|
|
|
@ -9,23 +9,21 @@ import * as ts from 'typescript';
|
||||||
|
|
||||||
import {Decorator} from '../../../ngtsc/host';
|
import {Decorator} from '../../../ngtsc/host';
|
||||||
import {DecoratorHandler} from '../../../ngtsc/transform';
|
import {DecoratorHandler} from '../../../ngtsc/transform';
|
||||||
import {DecorationAnalysis, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
import {DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
import {DecoratedClass} from '../../src/host/decorated_class';
|
|
||||||
import {DecoratedFile} from '../../src/host/decorated_file';
|
|
||||||
import {Fesm2015ReflectionHost} from '../../src/host/fesm2015_host';
|
import {Fesm2015ReflectionHost} from '../../src/host/fesm2015_host';
|
||||||
|
|
||||||
import {getDeclaration, makeProgram} from '../helpers/utils';
|
import {makeProgram} from '../helpers/utils';
|
||||||
|
|
||||||
const TEST_PROGRAM = {
|
const TEST_PROGRAM = {
|
||||||
name: 'test.js',
|
name: 'test.js',
|
||||||
contents: `
|
contents: `
|
||||||
import {Component, Injectable} from '@angular/core';
|
import {Component, Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Component()
|
|
||||||
export class MyComponent {}
|
export class MyComponent {}
|
||||||
|
MyComponent.decorators = [{type: Component}];
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class MyService {}
|
export class MyService {}
|
||||||
|
MyService.decorators = [{type: Injectable}];
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,49 +48,26 @@ function createTestHandler() {
|
||||||
return handler;
|
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('DecorationAnalyzer', () => {
|
||||||
describe('analyzeFile()', () => {
|
describe('analyzeProgram()', () => {
|
||||||
let program: ts.Program;
|
let program: ts.Program;
|
||||||
let testHandler: jasmine.SpyObj<DecoratorHandler<any, any>>;
|
let testHandler: jasmine.SpyObj<DecoratorHandler<any, any>>;
|
||||||
let result: DecorationAnalysis;
|
let result: DecorationAnalyses;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
program = makeProgram(TEST_PROGRAM);
|
program = makeProgram(TEST_PROGRAM);
|
||||||
const file = createParsedFile(program);
|
|
||||||
const analyzer = new DecorationAnalyzer(
|
const analyzer = new DecorationAnalyzer(
|
||||||
program.getTypeChecker(), new Fesm2015ReflectionHost(false, program.getTypeChecker()),
|
program.getTypeChecker(), new Fesm2015ReflectionHost(false, program.getTypeChecker()),
|
||||||
[''], false);
|
[''], false);
|
||||||
testHandler = createTestHandler();
|
testHandler = createTestHandler();
|
||||||
analyzer.handlers = [testHandler];
|
analyzer.handlers = [testHandler];
|
||||||
result = analyzer.analyzeFile(file);
|
result = analyzer.analyzeProgram(program);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an object containing a reference to the original source file',
|
it('should return an object containing a reference to the original source file', () => {
|
||||||
() => { expect(result.sourceFile).toBe(program.getSourceFile('test.js') !); });
|
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', () => {
|
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);
|
||||||
|
@ -103,8 +78,10 @@ describe('DecorationAnalyzer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an object containing the classes that were analyzed', () => {
|
it('should return an object containing the classes that were analyzed', () => {
|
||||||
expect(result.analyzedClasses.length).toEqual(1);
|
const file = program.getSourceFile(TEST_PROGRAM.name) !;
|
||||||
expect(result.analyzedClasses[0].name).toEqual('MyComponent');
|
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', () => {
|
it('should analyze and compile the classes that are detected', () => {
|
||||||
|
|
Loading…
Reference in New Issue