@@ -1146,5 +1146,218 @@ runInEachFileSystem(() => {
11461146 'inputs: { x: [i0.ɵɵInputFlags.HasDecoratorInputTransform, "x", "x", v => v + \'TRANSFORMED!\'] }' ) ;
11471147 } ) ;
11481148 } ) ;
1149+
1150+ describe ( '@defer' , ( ) => {
1151+ it ( 'should handle `@Component.deferredImports` field' , ( ) => {
1152+ env . write ( 'deferred-a.ts' , `
1153+ import {Component} from '@angular/core';
1154+ @Component({
1155+ standalone: true,
1156+ selector: 'deferred-cmp-a',
1157+ template: 'DeferredCmpA contents',
1158+ })
1159+ export class DeferredCmpA {
1160+ }
1161+ ` ) ;
1162+
1163+ env . write ( 'deferred-b.ts' , `
1164+ import {Component} from '@angular/core';
1165+ @Component({
1166+ standalone: true,
1167+ selector: 'deferred-cmp-b',
1168+ template: 'DeferredCmpB contents',
1169+ })
1170+ export class DeferredCmpB {
1171+ }
1172+ ` ) ;
1173+
1174+ env . write ( 'test.ts' , `
1175+ import {Component} from '@angular/core';
1176+ import {DeferredCmpA} from './deferred-a';
1177+ import {DeferredCmpB} from './deferred-b';
1178+ @Component({
1179+ standalone: true,
1180+ deferredImports: [DeferredCmpA, DeferredCmpB],
1181+ template: \`
1182+ @defer {
1183+ <deferred-cmp-a />
1184+ }
1185+ @defer {
1186+ <deferred-cmp-b />
1187+ }
1188+ \`,
1189+ })
1190+ export class AppCmp {
1191+ }
1192+ ` ) ;
1193+
1194+ env . driveMain ( ) ;
1195+ const jsContents = env . getContents ( 'test.js' ) ;
1196+
1197+ // Expect that all deferrableImports in local compilation mode
1198+ // are located in a single function (since we can't detect in
1199+ // the local mode which components belong to which block).
1200+ expect ( jsContents )
1201+ . toContain (
1202+ 'const AppCmp_DeferFn = () => [' +
1203+ 'import("./deferred-a").then(m => m.DeferredCmpA), ' +
1204+ 'import("./deferred-b").then(m => m.DeferredCmpB)];' ) ;
1205+
1206+ // Make sure there are no eager imports present in the output.
1207+ expect ( jsContents ) . not . toContain ( `from './deferred-a'` ) ;
1208+ expect ( jsContents ) . not . toContain ( `from './deferred-b'` ) ;
1209+
1210+ // All defer instructions use the same dependency function.
1211+ expect ( jsContents ) . toContain ( 'ɵɵdefer(1, 0, AppCmp_DeferFn);' ) ;
1212+ expect ( jsContents ) . toContain ( 'ɵɵdefer(4, 3, AppCmp_DeferFn);' ) ;
1213+
1214+ // Expect `ɵsetClassMetadataAsync` to contain dynamic imports too.
1215+ expect ( jsContents )
1216+ . toContain (
1217+ 'ɵsetClassMetadataAsync(AppCmp, () => [' +
1218+ 'import("./deferred-a").then(m => m.DeferredCmpA), ' +
1219+ 'import("./deferred-b").then(m => m.DeferredCmpB)], ' +
1220+ '(DeferredCmpA, DeferredCmpB) => {' ) ;
1221+ } ) ;
1222+
1223+ it ( 'should handle `@Component.imports` field' , ( ) => {
1224+ env . write ( 'deferred-a.ts' , `
1225+ import {Component} from '@angular/core';
1226+ @Component({
1227+ standalone: true,
1228+ selector: 'deferred-cmp-a',
1229+ template: 'DeferredCmpA contents',
1230+ })
1231+ export class DeferredCmpA {
1232+ }
1233+ ` ) ;
1234+
1235+ env . write ( 'test.ts' , `
1236+ import {Component} from '@angular/core';
1237+ import {DeferredCmpA} from './deferred-a';
1238+ @Component({
1239+ standalone: true,
1240+ imports: [DeferredCmpA],
1241+ template: \`
1242+ @defer {
1243+ <deferred-cmp-a />
1244+ }
1245+ \`,
1246+ })
1247+ export class AppCmp {
1248+ }
1249+ ` ) ;
1250+
1251+ env . driveMain ( ) ;
1252+ const jsContents = env . getContents ( 'test.js' ) ;
1253+
1254+ // In local compilation mode we can't detect which components
1255+ // belong to `@defer` blocks, thus can't determine whether corresponding
1256+ // classes can be defer-loaded. In this case we retain eager imports
1257+ // and do not generate defer dependency functions for `@defer` instructions.
1258+
1259+ // Eager imports are retained in the output.
1260+ expect ( jsContents ) . toContain ( `from './deferred-a'` ) ;
1261+
1262+ // Defer instructions do not have a dependency function,
1263+ // since all dependencies were defined in `@Component.imports`.
1264+ expect ( jsContents ) . toContain ( 'ɵɵdefer(1, 0);' ) ;
1265+
1266+ // Expect `ɵsetClassMetadata` (sync) to be generated.
1267+ expect ( jsContents ) . toContain ( 'ɵsetClassMetadata(AppCmp,' ) ;
1268+ } ) ;
1269+
1270+ it ( 'should handle defer blocks that rely on deps from `deferredImports` and `imports`' ,
1271+ ( ) => {
1272+ env . write ( 'eager-a.ts' , `
1273+ import {Component} from '@angular/core';
1274+ @Component({
1275+ standalone: true,
1276+ selector: 'eager-cmp-a',
1277+ template: 'EagerCmpA contents',
1278+ })
1279+ export class EagerCmpA {
1280+ }
1281+ ` ) ;
1282+
1283+ env . write ( 'deferred-a.ts' , `
1284+ import {Component} from '@angular/core';
1285+ @Component({
1286+ standalone: true,
1287+ selector: 'deferred-cmp-a',
1288+ template: 'DeferredCmpA contents',
1289+ })
1290+ export class DeferredCmpA {
1291+ }
1292+ ` ) ;
1293+
1294+ env . write ( 'deferred-b.ts' , `
1295+ import {Component} from '@angular/core';
1296+ @Component({
1297+ standalone: true,
1298+ selector: 'deferred-cmp-b',
1299+ template: 'DeferredCmpB contents',
1300+ })
1301+ export class DeferredCmpB {
1302+ }
1303+ ` ) ;
1304+
1305+ env . write ( 'test.ts' , `
1306+ import {Component} from '@angular/core';
1307+ import {DeferredCmpA} from './deferred-a';
1308+ import {DeferredCmpB} from './deferred-b';
1309+ import {EagerCmpA} from './eager-a';
1310+ @Component({
1311+ standalone: true,
1312+ imports: [EagerCmpA],
1313+ deferredImports: [DeferredCmpA, DeferredCmpB],
1314+ template: \`
1315+ @defer {
1316+ <eager-cmp-a />
1317+ <deferred-cmp-a />
1318+ }
1319+ @defer {
1320+ <eager-cmp-a />
1321+ <deferred-cmp-b />
1322+ }
1323+ \`,
1324+ })
1325+ export class AppCmp {
1326+ }
1327+ ` ) ;
1328+
1329+ env . driveMain ( ) ;
1330+ const jsContents = env . getContents ( 'test.js' ) ;
1331+
1332+ // Expect that all deferrableImports in local compilation mode
1333+ // are located in a single function (since we can't detect in
1334+ // the local mode which components belong to which block).
1335+ // Eager dependencies are **not* included here.
1336+ expect ( jsContents )
1337+ . toContain (
1338+ 'const AppCmp_DeferFn = () => [' +
1339+ 'import("./deferred-a").then(m => m.DeferredCmpA), ' +
1340+ 'import("./deferred-b").then(m => m.DeferredCmpB)];' ) ;
1341+
1342+ // Make sure there are no eager imports present in the output.
1343+ expect ( jsContents ) . not . toContain ( `from './deferred-a'` ) ;
1344+ expect ( jsContents ) . not . toContain ( `from './deferred-b'` ) ;
1345+
1346+ // Eager dependencies retain their imports.
1347+ expect ( jsContents ) . toContain ( `from './eager-a';` ) ;
1348+
1349+ // All defer instructions use the same dependency function.
1350+ expect ( jsContents ) . toContain ( 'ɵɵdefer(1, 0, AppCmp_DeferFn);' ) ;
1351+ expect ( jsContents ) . toContain ( 'ɵɵdefer(4, 3, AppCmp_DeferFn);' ) ;
1352+
1353+ // Expect `ɵsetClassMetadataAsync` to contain dynamic imports too.
1354+ expect ( jsContents )
1355+ . toContain (
1356+ 'ɵsetClassMetadataAsync(AppCmp, () => [' +
1357+ 'import("./deferred-a").then(m => m.DeferredCmpA), ' +
1358+ 'import("./deferred-b").then(m => m.DeferredCmpB)], ' +
1359+ '(DeferredCmpA, DeferredCmpB) => {' ) ;
1360+ } ) ;
1361+ } ) ;
11491362 } ) ;
11501363} ) ;
0 commit comments