fix(ivy): fix class inheritance detection for ES5 code in ngtsc (#28773)
				
					
				
			Previously, `ngtsc` detected class inheritance in a way that only worked in TS or ES2015 code. As a result, inheritance would not be detected for code in ES5 format, such as when running `ngtsc` through `ngcc` to transform old-style Angular code to ivy format. This commit fixes it by delegating class inheritance detection to the current `ReflectionHost`, which is able to correctly interpret the used code format. PR Close #28773
This commit is contained in:
		
							parent
							
								
									3c7ce823a3
								
							
						
					
					
						commit
						6b511a33f6
					
				| @ -202,8 +202,7 @@ export function extractDirectiveMetadata( | ||||
|   } | ||||
| 
 | ||||
|   // Detect if the component inherits from another class
 | ||||
|   const usesInheritance = clazz.heritageClauses !== undefined && | ||||
|       clazz.heritageClauses.some(hc => hc.token === ts.SyntaxKind.ExtendsKeyword); | ||||
|   const usesInheritance = reflector.hasBaseClass(clazz); | ||||
|   const metadata: R3DirectiveMetadata = { | ||||
|     name: clazz.name !.text, | ||||
|     deps: getValidConstructorDependencies(clazz, reflector, isCore), host, | ||||
|  | ||||
| @ -0,0 +1,80 @@ | ||||
| /** | ||||
|  * @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 * as ts from 'typescript'; | ||||
| 
 | ||||
| import {ReferenceEmitter} from '../../imports'; | ||||
| import {PartialEvaluator} from '../../partial_evaluator'; | ||||
| import {TypeScriptReflectionHost} from '../../reflection'; | ||||
| import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript'; | ||||
| import {DirectiveDecoratorHandler} from '../src/directive'; | ||||
| import {SelectorScopeRegistry} from '../src/selector_scope'; | ||||
| 
 | ||||
| 
 | ||||
| describe('DirectiveDecoratorHandler', () => { | ||||
|   it('should use the `ReflectionHost` to detect class inheritance', () => { | ||||
|     const {program, options, host} = makeProgram([ | ||||
|       { | ||||
|         name: 'node_modules/@angular/core/index.d.ts', | ||||
|         contents: 'export const Directive: any;', | ||||
|       }, | ||||
|       { | ||||
|         name: 'entry.ts', | ||||
|         contents: ` | ||||
|           import {Directive} from '@angular/core'; | ||||
| 
 | ||||
|           @Directive({selector: 'test-dir-1'}) | ||||
|           export class TestDir1 {} | ||||
| 
 | ||||
|           @Directive({selector: 'test-dir-2'}) | ||||
|           export class TestDir2 {} | ||||
|         `,
 | ||||
|       }, | ||||
|     ]); | ||||
| 
 | ||||
|     const checker = program.getTypeChecker(); | ||||
|     const reflectionHost = new TestReflectionHost(checker); | ||||
|     const evaluator = new PartialEvaluator(reflectionHost, checker); | ||||
|     const handler = new DirectiveDecoratorHandler( | ||||
|         reflectionHost, evaluator, | ||||
|         new SelectorScopeRegistry(checker, reflectionHost, new ReferenceEmitter([])), false); | ||||
| 
 | ||||
|     const analyzeDirective = (dirName: string) => { | ||||
|       const DirNode = getDeclaration(program, 'entry.ts', dirName, ts.isClassDeclaration); | ||||
| 
 | ||||
|       const detected = handler.detect(DirNode, reflectionHost.getDecoratorsOfDeclaration(DirNode)); | ||||
|       if (detected === undefined) { | ||||
|         throw new Error(`Failed to recognize @Directive (${dirName}).`); | ||||
|       } | ||||
| 
 | ||||
|       const {analysis} = handler.analyze(DirNode, detected.metadata); | ||||
|       if (analysis === undefined) { | ||||
|         throw new Error(`Failed to analyze @Directive (${dirName}).`); | ||||
|       } | ||||
| 
 | ||||
|       return analysis; | ||||
|     }; | ||||
| 
 | ||||
|     // By default, `TestReflectionHost#hasBaseClass()` returns `false`.
 | ||||
|     const analysis1 = analyzeDirective('TestDir1'); | ||||
|     expect(analysis1.meta.usesInheritance).toBe(false); | ||||
| 
 | ||||
|     // Tweak `TestReflectionHost#hasBaseClass()` to return true.
 | ||||
|     reflectionHost.hasBaseClassReturnValue = true; | ||||
| 
 | ||||
|     const analysis2 = analyzeDirective('TestDir2'); | ||||
|     expect(analysis2.meta.usesInheritance).toBe(true); | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| // Helpers
 | ||||
| class TestReflectionHost extends TypeScriptReflectionHost { | ||||
|   hasBaseClassReturnValue = false; | ||||
| 
 | ||||
|   hasBaseClass(node: ts.Declaration): boolean { return this.hasBaseClassReturnValue; } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user