|
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | 8 |
|
9 | | -import {AST, CssSelector, DomElementSchemaRegistry, LiteralPrimitive, ParseSourceSpan, PropertyRead, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute} from '@angular/compiler'; |
| 9 | +import {AST, CssSelector, DomElementSchemaRegistry, ExternalExpr, LiteralPrimitive, ParseSourceSpan, PropertyRead, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute} from '@angular/compiler'; |
10 | 10 | import ts from 'typescript'; |
11 | 11 |
|
12 | 12 | import {ErrorCode, ngErrorCode} from '../../diagnostics'; |
13 | 13 | import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath, getSourceFileOrError} from '../../file_system'; |
14 | | -import {Reference, ReferenceEmitter} from '../../imports'; |
| 14 | +import {Reference, ReferenceEmitKind, ReferenceEmitter} from '../../imports'; |
15 | 15 | import {IncrementalBuild} from '../../incremental/api'; |
16 | | -import {DirectiveMeta, MetadataReader, MetadataReaderWithIndex, MetaKind} from '../../metadata'; |
| 16 | +import {DirectiveMeta, MetadataReader, MetadataReaderWithIndex, MetaKind, NgModuleMeta} from '../../metadata'; |
17 | 17 | import {PerfCheckpoint, PerfEvent, PerfPhase, PerfRecorder} from '../../perf'; |
18 | 18 | import {ProgramDriver, UpdateMode} from '../../program_driver'; |
19 | 19 | import {ClassDeclaration, DeclarationNode, isNamedClassDeclaration, ReflectionHost} from '../../reflection'; |
20 | 20 | import {ComponentScopeKind, ComponentScopeReader, TypeCheckScopeRegistry} from '../../scope'; |
21 | 21 | import {isShim} from '../../shims'; |
22 | 22 | import {getSourceFileOrNull, isSymbolWithValueDeclaration} from '../../util/src/typescript'; |
23 | | -import {ElementSymbol, FullTemplateMapping, GlobalCompletion, NgTemplateDiagnostic, OptimizeFor, PotentialDirective, PotentialPipe, ProgramTypeCheckAdapter, Symbol, TcbLocation, TemplateDiagnostic, TemplateId, TemplateSymbol, TemplateTypeChecker, TypeCheckableDirectiveMeta, TypeCheckingConfig} from '../api'; |
| 23 | +import {ElementSymbol, FullTemplateMapping, GlobalCompletion, NgTemplateDiagnostic, OptimizeFor, PotentialDirective, PotentialImport, PotentialImportKind, PotentialPipe, ProgramTypeCheckAdapter, Symbol, TcbLocation, TemplateDiagnostic, TemplateId, TemplateSymbol, TemplateTypeChecker, TypeCheckableDirectiveMeta, TypeCheckingConfig} from '../api'; |
24 | 24 | import {makeTemplateDiagnostic} from '../diagnostics'; |
25 | 25 |
|
26 | 26 | import {CompletionEngine} from './completion'; |
@@ -673,6 +673,39 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker { |
673 | 673 | return scope.ngModule; |
674 | 674 | } |
675 | 675 |
|
| 676 | + getPotentialImportsFor(toImport: PotentialDirective, inContext: ts.ClassDeclaration): |
| 677 | + ReadonlyArray<PotentialImport> { |
| 678 | + // Look up the original reference associated with the trait's ngModule, so we don't lose the |
| 679 | + // Reference context (such as identifiers). If the trait is standalone, this will be |
| 680 | + // `undefined`. |
| 681 | + let ngModuleRef: Reference<ClassDeclaration<DeclarationNode>>|undefined; |
| 682 | + if (toImport.ngModule !== null) { |
| 683 | + ngModuleRef = this.metaReader.getNgModuleMetadata(new Reference(toImport.ngModule))?.ref; |
| 684 | + } |
| 685 | + |
| 686 | + // Import the ngModule if one exists. Otherwise, import the standalone trait directly. |
| 687 | + const importTarget = ngModuleRef ?? toImport.ref; |
| 688 | + |
| 689 | + // Using the compiler's ReferenceEmitter, try to emit a reference to the trait. |
| 690 | + // TODO(dylhunn): In the future, we can use a more sophisticated strategy for generating and |
| 691 | + // ranking references, such as keeping a record of import specifiers used in existing code. |
| 692 | + const emittedRef = this.refEmitter.emit(importTarget, inContext.getSourceFile()); |
| 693 | + if (emittedRef.kind === ReferenceEmitKind.Failed) return []; |
| 694 | + |
| 695 | + // The resulting import expression should have a module name and an identifier name. |
| 696 | + const emittedExpression: ExternalExpr = emittedRef.expression as ExternalExpr; |
| 697 | + if (emittedExpression.value.moduleName === null || emittedExpression.value.name === null) |
| 698 | + return []; |
| 699 | + |
| 700 | + // Extract and return the TS module and identifier names. |
| 701 | + const preferredImport: PotentialImport = { |
| 702 | + kind: ngModuleRef ? PotentialImportKind.NgModule : PotentialImportKind.Standalone, |
| 703 | + moduleSpecifier: emittedExpression.value.moduleName, |
| 704 | + symbolName: emittedExpression.value.name, |
| 705 | + }; |
| 706 | + return [preferredImport]; |
| 707 | + } |
| 708 | + |
676 | 709 | private getScopeData(component: ts.ClassDeclaration): ScopeData|null { |
677 | 710 | if (this.scopeCache.has(component)) { |
678 | 711 | return this.scopeCache.get(component)!; |
|
0 commit comments