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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user