Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1888,22 +1888,32 @@ export class ComponentDecoratorHandler
private resolveAllDeferredDependencies(
resolution: Readonly<ComponentResolutionData>,
): R3DeferPerComponentDependency[] {
const seenDeps = new Set<ClassDeclaration>();
const deferrableTypes: R3DeferPerComponentDependency[] = [];
// Go over all dependencies of all defer blocks and update the value of
// the `isDeferrable` flag and the `importPath` to reflect the current
// state after visiting all components during the `resolve` phase.
for (const [_, deps] of resolution.deferPerBlockDependencies) {
for (const deferBlockDep of deps) {
const importDecl =
resolution.deferrableDeclToImportDecl.get(deferBlockDep.declaration.node) ?? null;
const node = deferBlockDep.declaration.node;
const importDecl = resolution.deferrableDeclToImportDecl.get(node) ?? null;
if (importDecl !== null && this.deferredSymbolTracker.canDefer(importDecl)) {
deferBlockDep.isDeferrable = true;
deferBlockDep.importPath = (importDecl.moduleSpecifier as ts.StringLiteral).text;
deferBlockDep.isDefaultImport = isDefaultImport(importDecl);
deferrableTypes.push(deferBlockDep as R3DeferPerComponentDependency);

// The same dependency may be used across multiple deferred blocks. De-duplicate it
// because it can throw off other logic further down the compilation pipeline.
// Note that the logic above needs to run even if the dependency is seen before,
// because the object literals are different between each block.
if (!seenDeps.has(node)) {
seenDeps.add(node);
deferrableTypes.push(deferBlockDep as R3DeferPerComponentDependency);
}
}
}
}

return deferrableTypes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1122,3 +1122,99 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
****************************************************************************************************/
export {};

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep_lazy.js
****************************************************************************************************/
import { Directive } from '@angular/core';
import * as i0 from "@angular/core";
export class DuplicateLazyDep {
}
DuplicateLazyDep.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DuplicateLazyDep, deps: [], target: i0.ɵɵFactoryTarget.Directive });
DuplicateLazyDep.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: DuplicateLazyDep, isStandalone: true, selector: "duplicate-lazy-dep", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DuplicateLazyDep, decorators: [{
type: Directive,
args: [{ selector: 'duplicate-lazy-dep' }]
}] });

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep_lazy.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class DuplicateLazyDep {
static ɵfac: i0.ɵɵFactoryDeclaration<DuplicateLazyDep, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<DuplicateLazyDep, "duplicate-lazy-dep", never, {}, {}, never, never, true, never>;
}

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep_other.js
****************************************************************************************************/
import { Directive } from '@angular/core';
import * as i0 from "@angular/core";
export class OtherLazyDep {
}
OtherLazyDep.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: OtherLazyDep, deps: [], target: i0.ɵɵFactoryTarget.Directive });
OtherLazyDep.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: OtherLazyDep, isStandalone: true, selector: "other-lazy-dep", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: OtherLazyDep, decorators: [{
type: Directive,
args: [{ selector: 'other-lazy-dep' }]
}] });

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep_other.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class OtherLazyDep {
static ɵfac: i0.ɵɵFactoryDeclaration<OtherLazyDep, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<OtherLazyDep, "other-lazy-dep", never, {}, {}, never, never, true, never>;
}

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep.js
****************************************************************************************************/
import { Component } from '@angular/core';
import * as i0 from "@angular/core";
export class MyApp {
}
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
@defer {
<duplicate-lazy-dep/>
}

@defer {
<duplicate-lazy-dep/>
}

@defer {
<other-lazy-dep/>
}
`, isInline: true, deferBlockDependencies: [() => [import("./deferred_with_duplicate_external_dep_lazy").then(m => m.DuplicateLazyDep)], () => [import("./deferred_with_duplicate_external_dep_lazy").then(m => m.DuplicateLazyDep)], () => [import("./deferred_with_duplicate_external_dep_other").then(m => m.OtherLazyDep)]] });
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, resolveDeferredDeps: () => [import("./deferred_with_duplicate_external_dep_lazy").then(m => m.DuplicateLazyDep), import("./deferred_with_duplicate_external_dep_other").then(m => m.OtherLazyDep)], resolveMetadata: (DuplicateLazyDep, OtherLazyDep) => ({ decorators: [{
type: Component,
args: [{
template: `
@defer {
<duplicate-lazy-dep/>
}

@defer {
<duplicate-lazy-dep/>
}

@defer {
<other-lazy-dep/>
}
`,
imports: [DuplicateLazyDep, OtherLazyDep],
}]
}], ctorParameters: null, propDecorators: null }) });

/****************************************************************************************************
* PARTIAL FILE: deferred_with_duplicate_external_dep.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class MyApp {
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
}

Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,25 @@
"failureMessage": "Incorrect template"
}
]
},
{
"description": "should handle a component with deferred blocks that share the same dependency",
"inputFiles": [
"deferred_with_duplicate_external_dep.ts",
"deferred_with_duplicate_external_dep_lazy.ts",
"deferred_with_duplicate_external_dep_other.ts"
],
"expectations": [
{
"files": [
{
"expected": "deferred_with_duplicate_external_dep_template.js",
"generated": "deferred_with_duplicate_external_dep.js"
}
],
"failureMessage": "Incorrect template"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {Component} from '@angular/core';
import {DuplicateLazyDep} from './deferred_with_duplicate_external_dep_lazy';
import {OtherLazyDep} from './deferred_with_duplicate_external_dep_other';

@Component({
template: `
@defer {
<duplicate-lazy-dep/>
}

@defer {
<duplicate-lazy-dep/>
}

@defer {
<other-lazy-dep/>
}
`,
imports: [DuplicateLazyDep, OtherLazyDep],
})
export class MyApp {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {Directive} from '@angular/core';

@Directive({selector: 'duplicate-lazy-dep'})
export class DuplicateLazyDep {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {Directive} from '@angular/core';

@Directive({selector: 'other-lazy-dep'})
export class OtherLazyDep {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const MyApp_Defer_1_DepsFn = () => [import("./deferred_with_duplicate_external_dep_lazy").then(m => m.DuplicateLazyDep)];
// NOTE: in linked tests there is one more loader here, because linked compilation doesn't have the ability to de-dupe identical functions.
const MyApp_Defer_7_DepsFn = () => [import("./deferred_with_duplicate_external_dep_other").then(m => m.OtherLazyDep)];


$r3$.ɵɵdefineComponent({
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵtemplate(0, MyApp_Defer_0_Template, 1, 0);
$r3$.ɵɵdefer(1, 0, MyApp_Defer_1_DepsFn);
$r3$.ɵɵdeferOnIdle();
$r3$.ɵɵtemplate(3, MyApp_Defer_3_Template, 1, 0);
// NOTE: does not check the function name, because linked compilation doesn't have the ability to de-dupe identical functions.
$r3$.ɵɵdefer(4, 3, …);
$r3$.ɵɵdeferOnIdle();
$r3$.ɵɵtemplate(6, MyApp_Defer_6_Template, 1, 0);
$r3$.ɵɵdefer(7, 6, MyApp_Defer_7_DepsFn);
$r3$.ɵɵdeferOnIdle();
}
},
encapsulation: 2
});


(() => {
(typeof ngDevMode === "undefined" || ngDevMode) && $r3$.ɵsetClassMetadataAsync(MyApp, () => [
import("./deferred_with_duplicate_external_dep_lazy").then(m => m.DuplicateLazyDep),
import("./deferred_with_duplicate_external_dep_other").then(m => m.OtherLazyDep)
], (DuplicateLazyDep, OtherLazyDep) => {
$r3$.ɵsetClassMetadata(MyApp, [{
type: Component,
args: [{
template: …,
// NOTE: there's a ... after the `imports`, because linked compilation produces a trailing comma while full compilation doesn't.
imports: [DuplicateLazyDep, OtherLazyDep]…
}]
}], null, null);
});
})();