Skip to content

Commit 12f9ed0

Browse files
AndrewKushniratscott
authored andcommitted
refactor(compiler-cli): add tests for defer blocks and local compilation (#53591)
This commit adds tests to cover local compilation support for `@defer` blocks. PR Close #53591
1 parent 6971341 commit 12f9ed0

File tree

2 files changed

+565
-0
lines changed

2 files changed

+565
-0
lines changed

packages/compiler-cli/test/ngtsc/local_compilation_spec.ts

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)