66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { AnimationTriggerNames , BoundTarget , compileClassDebugInfo , compileComponentClassMetadata , compileComponentFromMetadata , compileDeclareClassMetadata , compileDeclareComponentFromMetadata , ConstantPool , CssSelector , DeclarationListEmitMode , DeclareComponentTemplateInfo , DEFAULT_INTERPOLATION_CONFIG , DomElementSchemaRegistry , Expression , FactoryTarget , makeBindingParser , R3ComponentMetadata , R3DeferBlockMetadata , R3DeferBlockTemplateDependency , R3DirectiveDependencyMetadata , R3NgModuleDependencyMetadata , R3PipeDependencyMetadata , R3TargetBinder , R3TemplateDependency , R3TemplateDependencyKind , R3TemplateDependencyMetadata , SchemaMetadata , SelectorMatcher , TmplAstDeferredBlock , TmplAstDeferredBlockTriggers , TmplAstDeferredTrigger , TmplAstElement , ViewEncapsulation , WrappedNodeExpr } from '@angular/compiler' ;
9+ import { AnimationTriggerNames , BoundTarget , compileClassDebugInfo , compileComponentClassMetadata , compileComponentFromMetadata , compileDeclareClassMetadata , compileDeclareComponentFromMetadata , ConstantPool , CssSelector , DeclarationListEmitMode , DeclareComponentTemplateInfo , DEFAULT_INTERPOLATION_CONFIG , DeferBlockDepsEmitMode , DomElementSchemaRegistry , Expression , FactoryTarget , makeBindingParser , R3ComponentMetadata , R3DeferBlockMetadata , R3DeferBlockTemplateDependency , R3DirectiveDependencyMetadata , R3NgModuleDependencyMetadata , R3PipeDependencyMetadata , R3TargetBinder , R3TemplateDependency , R3TemplateDependencyKind , R3TemplateDependencyMetadata , SchemaMetadata , SelectorMatcher , TmplAstDeferredBlock , TmplAstDeferredBlockTriggers , TmplAstDeferredTrigger , TmplAstElement , ViewEncapsulation , WrappedNodeExpr } from '@angular/compiler' ;
1010import ts from 'typescript' ;
1111
1212import { Cycle , CycleAnalyzer , CycleHandlingStrategy } from '../../../cycles' ;
@@ -19,7 +19,7 @@ import {IndexingContext} from '../../../indexer';
1919import { DirectiveMeta , extractDirectiveTypeCheckMeta , HostDirectivesResolver , MatchSource , MetadataReader , MetadataRegistry , MetaKind , NgModuleMeta , PipeMeta , ResourceRegistry } from '../../../metadata' ;
2020import { PartialEvaluator } from '../../../partial_evaluator' ;
2121import { PerfEvent , PerfRecorder } from '../../../perf' ;
22- import { ClassDeclaration , DeclarationNode , Decorator , isNamedClassDeclaration , ReflectionHost , reflectObjectLiteral } from '../../../reflection' ;
22+ import { ClassDeclaration , DeclarationNode , Decorator , Import , isNamedClassDeclaration , ReflectionHost , reflectObjectLiteral } from '../../../reflection' ;
2323import { ComponentScopeKind , ComponentScopeReader , DtsModuleScopeResolver , LocalModuleScope , LocalModuleScopeRegistry , makeNotStandaloneDiagnostic , makeUnknownComponentImportDiagnostic , StandaloneScope , TypeCheckScopeRegistry } from '../../../scope' ;
2424import { getDiagnosticNode , makeUnknownComponentDeferredImportDiagnostic } from '../../../scope/src/util' ;
2525import { AnalysisOutput , CompilationMode , CompileResult , DecoratorHandler , DetectResult , HandlerPrecedence , ResolveResult } from '../../../transform' ;
@@ -477,6 +477,21 @@ export class ComponentDecoratorHandler implements
477477 styles . push ( ...template . styles ) ;
478478 }
479479
480+ // Collect all explicitly deferred symbols from the `@Component.deferredImports` field
481+ // (if it exists) and populate the `DeferredSymbolTracker` state. These operations are safe
482+ // for the local compilation mode, since they don't require accessing/resolving symbols
483+ // outside of the current source file.
484+ let explicitlyDeferredTypes : Map < string , string > | null = null ;
485+ if ( metadata . isStandalone && rawDeferredImports !== null ) {
486+ const deferredTypes = this . collectExplicitlyDeferredSymbols ( rawDeferredImports ) ;
487+ for ( const [ deferredType , importDetails ] of deferredTypes ) {
488+ explicitlyDeferredTypes ??= new Map ( ) ;
489+ explicitlyDeferredTypes . set ( importDetails . name , importDetails . from ) ;
490+ this . deferredSymbolTracker . markAsDeferrableCandidate (
491+ deferredType , importDetails . node , node , true /* isExplicitlyDeferred */ ) ;
492+ }
493+ }
494+
480495 const output : AnalysisOutput < ComponentAnalysisData > = {
481496 analysis : {
482497 baseClass : readBaseClass ( node , this . reflector , this . evaluator ) ,
@@ -524,6 +539,7 @@ export class ComponentDecoratorHandler implements
524539 resolvedImports,
525540 rawDeferredImports,
526541 resolvedDeferredImports,
542+ explicitlyDeferredTypes,
527543 schemas,
528544 decorator : decorator ?. node as ts . Decorator | null ?? null ,
529545 } ,
@@ -656,8 +672,43 @@ export class ComponentDecoratorHandler implements
656672 resolve (
657673 node : ClassDeclaration , analysis : Readonly < ComponentAnalysisData > ,
658674 symbol : ComponentSymbol ) : ResolveResult < ComponentResolutionData > {
675+ const metadata = analysis . meta as Readonly < R3ComponentMetadata < R3TemplateDependencyMetadata > > ;
676+ const diagnostics : ts . Diagnostic [ ] = [ ] ;
677+ const context = getSourceFile ( node ) ;
678+
679+ // Check if there are some import declarations that contain symbols used within
680+ // the `@Component.deferredImports` field, but those imports contain other symbols
681+ // and thus the declaration can not be removed.
682+ const nonRemovableImports =
683+ this . deferredSymbolTracker . getNonRemovableDeferredImports ( context , node ) ;
684+ if ( nonRemovableImports . length > 0 ) {
685+ for ( const importDecl of nonRemovableImports ) {
686+ const diagnostic = makeDiagnostic (
687+ ErrorCode . DEFERRED_DEPENDENCY_IMPORTED_EAGERLY , importDecl ,
688+ `This import contains symbols used in the \`@Component.deferredImports\` array ` +
689+ `of the \`${ node . name . getText ( ) } \` component, but also some other symbols that ` +
690+ `are not in any \`@Component.deferredImports\` array. This renders all these ` +
691+ `defer imports useless as this import remains and its module is eagerly loaded. ` +
692+ `To fix this, make sure that this import contains *only* symbols ` +
693+ `that are used within \`@Component.deferredImports\` arrays.` ) ;
694+ diagnostics . push ( diagnostic ) ;
695+ }
696+ return { diagnostics} ;
697+ }
698+
659699 if ( this . compilationMode === CompilationMode . LOCAL ) {
660- return { } ;
700+ return {
701+ data : {
702+ declarationListEmitMode : ( ! analysis . meta . isStandalone || analysis . rawImports !== null ) ?
703+ DeclarationListEmitMode . RuntimeResolved :
704+ DeclarationListEmitMode . Direct ,
705+ declarations : EMPTY_ARRAY ,
706+ deferBlocks : this . locateDeferBlocksWithoutScope ( analysis . template ) ,
707+ deferBlockDepsEmitMode : DeferBlockDepsEmitMode . PerComponent ,
708+ deferrableDeclToImportDecl : new Map ( ) ,
709+ deferrableTypes : new Map ( ) ,
710+ } ,
711+ } ;
661712 }
662713
663714 if ( this . semanticDepGraphUpdater !== null && analysis . baseClass instanceof Reference ) {
@@ -668,18 +719,14 @@ export class ComponentDecoratorHandler implements
668719 return { } ;
669720 }
670721
671- const context = getSourceFile ( node ) ;
672- const metadata = analysis . meta as Readonly < R3ComponentMetadata < R3TemplateDependencyMetadata > > ;
673-
674-
675722 const data : ComponentResolutionData = {
676723 declarations : EMPTY_ARRAY ,
677724 declarationListEmitMode : DeclarationListEmitMode . Direct ,
678725 deferBlocks : new Map ( ) ,
726+ deferBlockDepsEmitMode : DeferBlockDepsEmitMode . PerBlock ,
679727 deferrableDeclToImportDecl : new Map ( ) ,
680728 deferrableTypes : new Map ( ) ,
681729 } ;
682- const diagnostics : ts . Diagnostic [ ] = [ ] ;
683730
684731 const scope = this . scopeReader . getScopeForComponent ( node ) ;
685732 if ( scope !== null ) {
@@ -849,7 +896,7 @@ export class ComponentDecoratorHandler implements
849896 eagerlyUsed . has ( decl . ref . node ) ) ;
850897
851898 // Process information related to defer blocks
852- this . resolveDeferBlocks ( deferBlocks , declarations , data , analysis , eagerlyUsed , bound ) ;
899+ this . resolveDeferBlocks ( node , deferBlocks , declarations , data , analysis , eagerlyUsed , bound ) ;
853900
854901 const cyclesFromDirectives = new Map < UsedDirective , Cycle > ( ) ;
855902 const cyclesFromPipes = new Map < UsedPipe , Cycle > ( ) ;
@@ -1055,13 +1102,13 @@ export class ComponentDecoratorHandler implements
10551102 return [ ] ;
10561103 }
10571104
1058- // Collect all explicitly deferred symbols from the `@Component.deferredImports` field
1059- // if it exists. As a part of that process we also populate the `DeferredSymbolTracker` state,
1060- // which is then used within the `collectDeferredSymbols` call.
1061- this . collectExplicitlyDeferredSymbols ( analysis ) ;
10621105 const deferrableTypes = this . collectDeferredSymbols ( resolution ) ;
10631106
1064- const meta : R3ComponentMetadata < R3TemplateDependency > = { ...analysis . meta , ...resolution } ;
1107+ const meta : R3ComponentMetadata < R3TemplateDependency > = {
1108+ ...analysis . meta ,
1109+ ...resolution ,
1110+ deferrableTypes,
1111+ } ;
10651112 const fac = compileNgFactoryDefField ( toFactoryMetadata ( meta , FactoryTarget . Component ) ) ;
10661113
10671114 removeDeferrableTypesFromComponentDecorator ( analysis , deferrableTypes ) ;
@@ -1113,19 +1160,20 @@ export class ComponentDecoratorHandler implements
11131160 return [ ] ;
11141161 }
11151162
1116- const deferrableTypes = this . collectExplicitlyDeferredSymbols ( analysis ) ;
1117- const meta : R3ComponentMetadata < R3TemplateDependency > = {
1163+ // In the local compilation mode we can only rely on the information available
1164+ // within the `@Component.deferredImports` array, because in this mode compiler
1165+ // doesn't have information on which dependencies belong to which defer blocks.
1166+ const deferrableTypes = analysis . explicitlyDeferredTypes ;
1167+
1168+ const meta = {
11181169 ...analysis . meta ,
1119- declarationListEmitMode : ( ! analysis . meta . isStandalone || analysis . rawImports !== null ) ?
1120- DeclarationListEmitMode . RuntimeResolved :
1121- DeclarationListEmitMode . Direct ,
1122- declarations : EMPTY_ARRAY ,
1123- deferBlocks : this . locateDeferBlocksWithoutScope ( analysis . template ) ,
1124- deferrableDeclToImportDecl : new Map ( ) ,
1125- deferrableTypes,
1126- } ;
1170+ ...resolution ,
1171+ deferrableTypes : deferrableTypes ?? new Map ( ) ,
1172+ } as R3ComponentMetadata < R3TemplateDependency > ;
11271173
1128- removeDeferrableTypesFromComponentDecorator ( analysis , deferrableTypes ) ;
1174+ if ( analysis . explicitlyDeferredTypes !== null ) {
1175+ removeDeferrableTypesFromComponentDecorator ( analysis , analysis . explicitlyDeferredTypes ) ;
1176+ }
11291177
11301178 const fac = compileNgFactoryDefField ( toFactoryMetadata ( meta , FactoryTarget . Component ) ) ;
11311179 const def = compileComponentFromMetadata ( meta , pool , makeBindingParser ( ) ) ;
@@ -1191,16 +1239,16 @@ export class ComponentDecoratorHandler implements
11911239 }
11921240
11931241 /**
1194- * Collects a list of deferrable symbols based on the `@Component.deferredImports` field.
1242+ * Collects deferrable symbols from the `@Component.deferredImports` field.
11951243 */
1196- private collectExplicitlyDeferredSymbols ( analysis : Readonly < ComponentAnalysisData > ) :
1197- Map < string , string > {
1198- const deferrableTypes = new Map < string , string > ( ) ;
1199- if ( ! analysis . meta . isStandalone || analysis . rawDeferredImports === null ||
1200- ! ts . isArrayLiteralExpression ( analysis . rawDeferredImports ) )
1201- return deferrableTypes ;
1244+ private collectExplicitlyDeferredSymbols ( rawDeferredImports : ts . Expression ) :
1245+ Map < ts . Identifier , Import > {
1246+ const deferredTypes = new Map < ts . Identifier , Import > ( ) ;
1247+ if ( ! ts . isArrayLiteralExpression ( rawDeferredImports ) ) {
1248+ return deferredTypes ;
1249+ }
12021250
1203- for ( const element of analysis . rawDeferredImports . elements ) {
1251+ for ( const element of rawDeferredImports . elements ) {
12041252 const node = tryUnwrapForwardRef ( element , this . reflector ) || element ;
12051253
12061254 if ( ! ts . isIdentifier ( node ) ) {
@@ -1210,11 +1258,10 @@ export class ComponentDecoratorHandler implements
12101258
12111259 const imp = this . reflector . getImportOfIdentifier ( node ) ;
12121260 if ( imp !== null ) {
1213- deferrableTypes . set ( imp . name , imp . from ) ;
1214- this . deferredSymbolTracker . markAsExplicitlyDeferred ( imp . node ) ;
1261+ deferredTypes . set ( node , imp ) ;
12151262 }
12161263 }
1217- return deferrableTypes ;
1264+ return deferredTypes ;
12181265 }
12191266
12201267 /**
@@ -1248,6 +1295,7 @@ export class ComponentDecoratorHandler implements
12481295 * available for the final `compile` step.
12491296 */
12501297 private resolveDeferBlocks (
1298+ componentClassDecl : ClassDeclaration ,
12511299 deferBlocks : Map < TmplAstDeferredBlock , BoundTarget < DirectiveMeta > > ,
12521300 deferrableDecls : Map < ClassDeclaration , AnyUsedType > ,
12531301 resolutionData : ComponentResolutionData ,
@@ -1306,13 +1354,13 @@ export class ComponentDecoratorHandler implements
13061354 if ( analysisData . meta . isStandalone ) {
13071355 if ( analysisData . rawImports !== null ) {
13081356 this . registerDeferrableCandidates (
1309- analysisData . rawImports , false /* isDeferredImport */ , allDeferredDecls ,
1310- eagerlyUsedDecls , resolutionData ) ;
1357+ componentClassDecl , analysisData . rawImports , false /* isDeferredImport */ ,
1358+ allDeferredDecls , eagerlyUsedDecls , resolutionData ) ;
13111359 }
13121360 if ( analysisData . rawDeferredImports !== null ) {
13131361 this . registerDeferrableCandidates (
1314- analysisData . rawDeferredImports , true /* isDeferredImport */ , allDeferredDecls ,
1315- eagerlyUsedDecls , resolutionData ) ;
1362+ componentClassDecl , analysisData . rawDeferredImports , true /* isDeferredImport */ ,
1363+ allDeferredDecls , eagerlyUsedDecls , resolutionData ) ;
13161364 }
13171365 }
13181366 }
@@ -1323,7 +1371,7 @@ export class ComponentDecoratorHandler implements
13231371 * candidates.
13241372 */
13251373 private registerDeferrableCandidates (
1326- importsExpr : ts . Expression , isDeferredImport : boolean ,
1374+ componentClassDecl : ClassDeclaration , importsExpr : ts . Expression , isDeferredImport : boolean ,
13271375 allDeferredDecls : Set < ClassDeclaration > , eagerlyUsedDecls : Set < ClassDeclaration > ,
13281376 resolutionData : ComponentResolutionData ) {
13291377 if ( ! ts . isArrayLiteralExpression ( importsExpr ) ) {
@@ -1388,11 +1436,8 @@ export class ComponentDecoratorHandler implements
13881436 resolutionData . deferrableDeclToImportDecl . set (
13891437 decl . node as unknown as Expression , imp . node as unknown as Expression ) ;
13901438
1391- if ( isDeferredImport ) {
1392- this . deferredSymbolTracker . markAsExplicitlyDeferred ( imp . node ) ;
1393- } else {
1394- this . deferredSymbolTracker . markAsDeferrableCandidate ( node , imp . node ) ;
1395- }
1439+ this . deferredSymbolTracker . markAsDeferrableCandidate (
1440+ node , imp . node , componentClassDecl , isDeferredImport ) ;
13961441 }
13971442 }
13981443
0 commit comments