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
 |   // Detect if the component inherits from another class
 | ||||||
|   const usesInheritance = clazz.heritageClauses !== undefined && |   const usesInheritance = reflector.hasBaseClass(clazz); | ||||||
|       clazz.heritageClauses.some(hc => hc.token === ts.SyntaxKind.ExtendsKeyword); |  | ||||||
|   const metadata: R3DirectiveMetadata = { |   const metadata: R3DirectiveMetadata = { | ||||||
|     name: clazz.name !.text, |     name: clazz.name !.text, | ||||||
|     deps: getValidConstructorDependencies(clazz, reflector, isCore), host, |     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