@angular/build 19.0.0-next.7 → 19.0.0-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -5
- package/package.json +10 -9
- package/src/builders/application/execute-build.js +19 -2
- package/src/builders/application/execute-post-bundle.d.ts +2 -2
- package/src/builders/application/execute-post-bundle.js +30 -11
- package/src/builders/application/i18n.d.ts +2 -2
- package/src/builders/application/i18n.js +4 -5
- package/src/builders/application/index.js +8 -5
- package/src/builders/application/options.d.ts +25 -1
- package/src/builders/application/options.js +31 -2
- package/src/builders/application/schema.d.ts +15 -0
- package/src/builders/application/schema.js +11 -1
- package/src/builders/application/schema.json +5 -0
- package/src/builders/application/setup-bundling.js +6 -3
- package/src/builders/dev-server/vite-server.d.ts +2 -1
- package/src/builders/dev-server/vite-server.js +71 -53
- package/src/builders/extract-i18n/application-extraction.js +3 -3
- package/src/tools/angular/angular-host.d.ts +2 -1
- package/src/tools/angular/angular-host.js +20 -1
- package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/aot-compilation.js +9 -1
- package/src/tools/angular/compilation/parallel-compilation.d.ts +1 -0
- package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
- package/src/tools/angular/compilation/parallel-worker.js +2 -1
- package/src/tools/babel/plugins/add-code-coverage.d.ts +14 -0
- package/src/tools/babel/plugins/add-code-coverage.js +44 -0
- package/src/tools/babel/plugins/types.d.ts +20 -0
- package/src/tools/esbuild/angular/compiler-plugin.d.ts +2 -0
- package/src/tools/esbuild/angular/compiler-plugin.js +44 -4
- package/src/tools/esbuild/angular/component-stylesheets.d.ts +8 -3
- package/src/tools/esbuild/angular/component-stylesheets.js +46 -11
- package/src/tools/esbuild/application-code-bundle.d.ts +1 -0
- package/src/tools/esbuild/application-code-bundle.js +109 -2
- package/src/tools/esbuild/budget-stats.js +1 -1
- package/src/tools/esbuild/bundler-context.d.ts +4 -3
- package/src/tools/esbuild/bundler-context.js +8 -4
- package/src/tools/esbuild/bundler-execution-result.d.ts +5 -2
- package/src/tools/esbuild/bundler-execution-result.js +7 -3
- package/src/tools/esbuild/cache.d.ts +5 -0
- package/src/tools/esbuild/cache.js +7 -0
- package/src/tools/esbuild/compiler-plugin-options.js +3 -1
- package/src/tools/esbuild/i18n-inliner.js +2 -1
- package/src/tools/esbuild/javascript-transformer-worker.d.ts +1 -0
- package/src/tools/esbuild/javascript-transformer-worker.js +5 -1
- package/src/tools/esbuild/javascript-transformer.d.ts +2 -2
- package/src/tools/esbuild/javascript-transformer.js +5 -3
- package/src/tools/esbuild/utils.js +7 -3
- package/src/tools/vite/middlewares/assets-middleware.js +2 -5
- package/src/tools/vite/middlewares/html-fallback-middleware.js +22 -6
- package/src/tools/vite/middlewares/index.d.ts +1 -1
- package/src/tools/vite/middlewares/index.js +3 -2
- package/src/tools/vite/middlewares/ssr-middleware.d.ts +2 -1
- package/src/tools/vite/middlewares/ssr-middleware.js +62 -15
- package/src/tools/vite/plugins/angular-memory-plugin.d.ts +16 -0
- package/src/tools/vite/{angular-memory-plugin.js → plugins/angular-memory-plugin.js} +19 -41
- package/src/tools/vite/{i18n-locale-plugin.d.ts → plugins/i18n-locale-plugin.d.ts} +0 -4
- package/src/tools/vite/{i18n-locale-plugin.js → plugins/i18n-locale-plugin.js} +2 -3
- package/src/tools/vite/plugins/index.d.ts +12 -0
- package/src/tools/vite/plugins/index.js +21 -0
- package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +41 -0
- package/src/tools/vite/plugins/setup-middlewares-plugin.js +62 -0
- package/src/tools/vite/plugins/ssr-transform-plugin.d.ts +9 -0
- package/src/tools/vite/plugins/ssr-transform-plugin.js +38 -0
- package/src/utils/environment-options.d.ts +2 -0
- package/src/utils/environment-options.js +5 -1
- package/src/utils/index-file/index-html-generator.js +5 -0
- package/src/utils/index-file/ngcm-attribute.d.ts +15 -0
- package/src/utils/index-file/ngcm-attribute.js +37 -0
- package/src/utils/index-file/valid-self-closing-tags.js +28 -0
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/fetch-patch.d.ts +1 -1
- package/src/utils/server-rendering/fetch-patch.js +2 -2
- package/src/utils/server-rendering/launch-server.d.ts +14 -0
- package/src/utils/server-rendering/launch-server.js +63 -0
- package/src/utils/server-rendering/load-esm-from-memory.d.ts +7 -0
- package/src/utils/server-rendering/manifest.d.ts +8 -2
- package/src/utils/server-rendering/manifest.js +52 -12
- package/src/utils/server-rendering/models.d.ts +27 -0
- package/src/utils/server-rendering/models.js +22 -0
- package/src/utils/server-rendering/prerender.d.ts +6 -10
- package/src/utils/server-rendering/prerender.js +102 -63
- package/src/utils/server-rendering/render-worker.d.ts +4 -1
- package/src/utils/server-rendering/render-worker.js +13 -3
- package/src/utils/server-rendering/routes-extractor-worker.d.ts +6 -10
- package/src/utils/server-rendering/routes-extractor-worker.js +14 -5
- package/src/utils/server-rendering/utils.d.ts +11 -0
- package/src/utils/server-rendering/utils.js +17 -0
- package/src/tools/vite/angular-memory-plugin.d.ts +0 -22
- /package/src/tools/vite/{id-prefix-plugin.d.ts → plugins/id-prefix-plugin.d.ts} +0 -0
- /package/src/tools/vite/{id-prefix-plugin.js → plugins/id-prefix-plugin.js} +0 -0
|
@@ -39,10 +39,9 @@ const node_assert_1 = __importDefault(require("node:assert"));
|
|
|
39
39
|
const promises_1 = require("node:fs/promises");
|
|
40
40
|
const node_module_1 = require("node:module");
|
|
41
41
|
const node_path_1 = require("node:path");
|
|
42
|
-
const
|
|
43
|
-
const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
|
|
44
|
-
const id_prefix_plugin_1 = require("../../tools/vite/id-prefix-plugin");
|
|
42
|
+
const plugins_1 = require("../../tools/vite/plugins");
|
|
45
43
|
const utils_1 = require("../../utils");
|
|
44
|
+
const environment_options_1 = require("../../utils/environment-options");
|
|
46
45
|
const load_esm_1 = require("../../utils/load-esm");
|
|
47
46
|
const results_1 = require("../application/results");
|
|
48
47
|
const internal_1 = require("./internal");
|
|
@@ -66,17 +65,17 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
66
65
|
}
|
|
67
66
|
// TODO: Adjust architect to not force a JsonObject derived return type
|
|
68
67
|
const browserOptions = (await context.validateOptions(rawBrowserOptions, builderName));
|
|
69
|
-
if (browserOptions.prerender
|
|
68
|
+
if (browserOptions.prerender) {
|
|
70
69
|
// Disable prerendering if enabled and force SSR.
|
|
71
70
|
// This is so instead of prerendering all the routes for every change, the page is "prerendered" when it is requested.
|
|
72
71
|
browserOptions.prerender = false;
|
|
73
|
-
|
|
74
|
-
browserOptions.ssr = true;
|
|
75
|
-
// https://nodejs.org/api/process.html#processsetsourcemapsenabledval
|
|
76
|
-
process.setSourceMapsEnabled(true);
|
|
72
|
+
browserOptions.ssr ||= true;
|
|
77
73
|
}
|
|
78
74
|
// Set all packages as external to support Vite's prebundle caching
|
|
79
75
|
browserOptions.externalPackages = serverOptions.prebundle;
|
|
76
|
+
// Disable generating a full manifest with routes.
|
|
77
|
+
// This is done during runtime when using the dev-server.
|
|
78
|
+
browserOptions.partialSSRBuild = true;
|
|
80
79
|
// The development server currently only supports a single locale when localizing.
|
|
81
80
|
// This matches the behavior of the Webpack-based development server but could be expanded in the future.
|
|
82
81
|
if (browserOptions.localize === true ||
|
|
@@ -88,7 +87,13 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
88
87
|
// When localization is enabled with a single locale, force a flat path to maintain behavior with the existing Webpack-based dev server.
|
|
89
88
|
browserOptions.forceI18nFlatOutput = true;
|
|
90
89
|
}
|
|
91
|
-
const { vendor: thirdPartySourcemaps } = (0, utils_1.normalizeSourceMaps)(browserOptions.sourceMap ?? false);
|
|
90
|
+
const { vendor: thirdPartySourcemaps, scripts: scriptsSourcemaps } = (0, utils_1.normalizeSourceMaps)(browserOptions.sourceMap ?? false);
|
|
91
|
+
if (scriptsSourcemaps && browserOptions.server) {
|
|
92
|
+
// https://nodejs.org/api/process.html#processsetsourcemapsenabledval
|
|
93
|
+
process.setSourceMapsEnabled(true);
|
|
94
|
+
}
|
|
95
|
+
// TODO: Enable by default once full support across CLI and FW is integrated
|
|
96
|
+
browserOptions.externalRuntimeStyles = environment_options_1.useComponentStyleHmr;
|
|
92
97
|
// Setup the prebundling transformer that will be shared across Vite prebundling requests
|
|
93
98
|
const prebundleTransformer = new internal_1.JavaScriptTransformer(
|
|
94
99
|
// Always enable JIT linking to support applications built with and without AOT.
|
|
@@ -124,7 +129,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
124
129
|
case results_1.ResultKind.Failure:
|
|
125
130
|
if (result.errors.length && server) {
|
|
126
131
|
hadError = true;
|
|
127
|
-
server.
|
|
132
|
+
server.ws.send({
|
|
128
133
|
type: 'error',
|
|
129
134
|
err: {
|
|
130
135
|
message: result.errors[0].text,
|
|
@@ -171,7 +176,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
171
176
|
if (hadError && server) {
|
|
172
177
|
hadError = false;
|
|
173
178
|
// Send an empty update to clear the error overlay
|
|
174
|
-
server.
|
|
179
|
+
server.ws.send({
|
|
175
180
|
'type': 'update',
|
|
176
181
|
updates: [],
|
|
177
182
|
});
|
|
@@ -180,8 +185,8 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
180
185
|
let requiresServerRestart = false;
|
|
181
186
|
if (result.detail?.['externalMetadata']) {
|
|
182
187
|
const { implicitBrowser, implicitServer, explicit } = result.detail['externalMetadata'];
|
|
183
|
-
const implicitServerFiltered = implicitServer.filter((m) =>
|
|
184
|
-
const implicitBrowserFiltered = implicitBrowser.filter(
|
|
188
|
+
const implicitServerFiltered = implicitServer.filter((m) => !(0, node_module_1.isBuiltin)(m) && !isAbsoluteUrl(m));
|
|
189
|
+
const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m));
|
|
185
190
|
if (browserOptions.ssr && serverOptions.prebundle !== false) {
|
|
186
191
|
const previousImplicitServer = new Set(externalMetadata.implicitServer);
|
|
187
192
|
// Restart the server to force SSR dep re-optimization when a dependency has been added.
|
|
@@ -194,7 +199,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
194
199
|
externalMetadata.implicitServer.length = 0;
|
|
195
200
|
externalMetadata.implicitBrowser.length = 0;
|
|
196
201
|
externalMetadata.explicitBrowser.push(...explicit);
|
|
197
|
-
externalMetadata.explicitServer.push(...explicit, ...
|
|
202
|
+
externalMetadata.explicitServer.push(...explicit, ...node_module_1.builtinModules);
|
|
198
203
|
externalMetadata.implicitServer.push(...implicitServerFiltered);
|
|
199
204
|
externalMetadata.implicitBrowser.push(...implicitBrowserFiltered);
|
|
200
205
|
// The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm.
|
|
@@ -238,15 +243,19 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
238
243
|
const polyfills = Array.isArray((browserOptions.polyfills ??= []))
|
|
239
244
|
? browserOptions.polyfills
|
|
240
245
|
: [browserOptions.polyfills];
|
|
246
|
+
let ssrMode = plugins_1.ServerSsrMode.NoSsr;
|
|
247
|
+
if (browserOptions.outputMode &&
|
|
248
|
+
typeof browserOptions.ssr === 'object' &&
|
|
249
|
+
browserOptions.ssr.entry) {
|
|
250
|
+
ssrMode = plugins_1.ServerSsrMode.ExternalSsrMiddleware;
|
|
251
|
+
}
|
|
252
|
+
else if (browserOptions.server) {
|
|
253
|
+
ssrMode = plugins_1.ServerSsrMode.InternalSsrMiddleware;
|
|
254
|
+
}
|
|
241
255
|
// Setup server and start listening
|
|
242
|
-
const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata,
|
|
256
|
+
const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, (0, internal_1.isZonelessApp)(polyfills), usedComponentStyles, browserOptions.loader, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps);
|
|
243
257
|
server = await createServer(serverConfiguration);
|
|
244
258
|
await server.listen();
|
|
245
|
-
if (browserOptions.ssr && serverOptions.prebundle !== false) {
|
|
246
|
-
// Warm up the SSR request and begin optimizing dependencies.
|
|
247
|
-
// Without this, Vite will only start optimizing SSR modules when the first request is made.
|
|
248
|
-
void server.warmupRequest('./main.server.mjs', { ssr: true });
|
|
249
|
-
}
|
|
250
259
|
const urls = server.resolvedUrls;
|
|
251
260
|
if (urls && (urls.local.length || urls.network.length)) {
|
|
252
261
|
serverUrl = new URL(urls.local[0] ?? urls.network[0]);
|
|
@@ -260,7 +269,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
260
269
|
key: 'r',
|
|
261
270
|
description: 'force reload browser',
|
|
262
271
|
action(server) {
|
|
263
|
-
server.
|
|
272
|
+
server.ws.send({
|
|
264
273
|
type: 'full-reload',
|
|
265
274
|
path: '*',
|
|
266
275
|
});
|
|
@@ -280,28 +289,30 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
280
289
|
}
|
|
281
290
|
async function handleUpdate(normalizePath, generatedFiles, server, serverOptions, logger, usedComponentStyles) {
|
|
282
291
|
const updatedFiles = [];
|
|
283
|
-
let
|
|
292
|
+
let destroyAngularServerAppCalled = false;
|
|
284
293
|
// Invalidate any updated files
|
|
285
|
-
for (const [file,
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
for (const [file, { updated, type }] of generatedFiles) {
|
|
295
|
+
if (!updated) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (type === internal_1.BuildOutputFileType.ServerApplication && !destroyAngularServerAppCalled) {
|
|
299
|
+
// Clear the server app cache
|
|
300
|
+
// This must be done before module invalidation.
|
|
301
|
+
const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
|
|
302
|
+
ɵdestroyAngularServerApp();
|
|
303
|
+
destroyAngularServerAppCalled = true;
|
|
291
304
|
}
|
|
305
|
+
updatedFiles.push(file);
|
|
306
|
+
const updatedModules = server.moduleGraph.getModulesByFile(normalizePath((0, node_path_1.join)(server.config.root, file)));
|
|
307
|
+
updatedModules?.forEach((m) => server.moduleGraph.invalidateModule(m));
|
|
292
308
|
}
|
|
293
309
|
if (!updatedFiles.length) {
|
|
294
310
|
return;
|
|
295
311
|
}
|
|
296
|
-
// clean server apps cache
|
|
297
|
-
if (isServerFileUpdated) {
|
|
298
|
-
const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
|
|
299
|
-
ɵdestroyAngularServerApp();
|
|
300
|
-
}
|
|
301
312
|
if (serverOptions.liveReload || serverOptions.hmr) {
|
|
302
313
|
if (updatedFiles.every((f) => f.endsWith('.css'))) {
|
|
303
314
|
const timestamp = Date.now();
|
|
304
|
-
server.
|
|
315
|
+
server.ws.send({
|
|
305
316
|
type: 'update',
|
|
306
317
|
updates: updatedFiles.flatMap((filePath) => {
|
|
307
318
|
// For component styles, an HMR update must be sent for each one with the corresponding
|
|
@@ -332,7 +343,7 @@ async function handleUpdate(normalizePath, generatedFiles, server, serverOptions
|
|
|
332
343
|
}
|
|
333
344
|
// Send reload command to clients
|
|
334
345
|
if (serverOptions.liveReload) {
|
|
335
|
-
server.
|
|
346
|
+
server.ws.send({
|
|
336
347
|
type: 'full-reload',
|
|
337
348
|
path: '*',
|
|
338
349
|
});
|
|
@@ -392,13 +403,13 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
|
|
|
392
403
|
}
|
|
393
404
|
}
|
|
394
405
|
}
|
|
395
|
-
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata,
|
|
406
|
+
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, zoneless, usedComponentStyles, prebundleLoaderExtensions, extensionMiddleware, indexHtmlTransformer, thirdPartySourcemaps = false) {
|
|
396
407
|
const proxy = await (0, utils_1.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig);
|
|
397
408
|
// dynamically import Vite for ESM compatibility
|
|
398
409
|
const { normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
|
|
399
410
|
// Path will not exist on disk and only used to provide separate path for Vite requests
|
|
400
411
|
const virtualProjectRoot = normalizePath((0, node_path_1.join)(serverOptions.workspaceRoot, `.angular/vite-root`, serverOptions.buildTarget.project));
|
|
401
|
-
const cacheDir = (0, node_path_1.join)(serverOptions.cacheOptions.path, 'vite');
|
|
412
|
+
const cacheDir = (0, node_path_1.join)(serverOptions.cacheOptions.path, serverOptions.buildTarget.project, 'vite');
|
|
402
413
|
const configuration = {
|
|
403
414
|
configFile: false,
|
|
404
415
|
envFile: false,
|
|
@@ -427,6 +438,9 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
427
438
|
preserveSymlinks,
|
|
428
439
|
},
|
|
429
440
|
server: {
|
|
441
|
+
warmup: {
|
|
442
|
+
ssrFiles: ['./main.server.mjs', './server.mjs'],
|
|
443
|
+
},
|
|
430
444
|
port: serverOptions.port,
|
|
431
445
|
strictPort: true,
|
|
432
446
|
host: serverOptions.host,
|
|
@@ -475,20 +489,22 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
475
489
|
}),
|
|
476
490
|
},
|
|
477
491
|
plugins: [
|
|
478
|
-
(0,
|
|
479
|
-
(0,
|
|
480
|
-
workspaceRoot: serverOptions.workspaceRoot,
|
|
481
|
-
virtualProjectRoot,
|
|
492
|
+
(0, plugins_1.createAngularLocaleDataPlugin)(),
|
|
493
|
+
(0, plugins_1.createAngularSetupMiddlewaresPlugin)({
|
|
482
494
|
outputFiles,
|
|
483
495
|
assets,
|
|
484
|
-
ssr,
|
|
485
|
-
external: externalMetadata.explicitBrowser,
|
|
486
496
|
indexHtmlTransformer,
|
|
487
497
|
extensionMiddleware,
|
|
488
|
-
normalizePath,
|
|
489
498
|
usedComponentStyles,
|
|
499
|
+
ssrMode,
|
|
500
|
+
}),
|
|
501
|
+
(0, plugins_1.createRemoveIdPrefixPlugin)(externalMetadata.explicitBrowser),
|
|
502
|
+
await (0, plugins_1.createAngularSsrTransformPlugin)(serverOptions.workspaceRoot),
|
|
503
|
+
await (0, plugins_1.createAngularMemoryPlugin)({
|
|
504
|
+
virtualProjectRoot,
|
|
505
|
+
outputFiles,
|
|
506
|
+
external: externalMetadata.explicitBrowser,
|
|
490
507
|
}),
|
|
491
|
-
(0, id_prefix_plugin_1.createRemoveIdPrefixPlugin)(externalMetadata.explicitBrowser),
|
|
492
508
|
],
|
|
493
509
|
// Browser only optimizeDeps. (This does not run for SSR dependencies).
|
|
494
510
|
optimizeDeps: getDepOptimizationConfig({
|
|
@@ -556,12 +572,14 @@ function getDepOptimizationConfig({ disabled, exclude, include, target, zoneless
|
|
|
556
572
|
},
|
|
557
573
|
};
|
|
558
574
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
575
|
+
/**
|
|
576
|
+
* Checks if the given value is an absolute URL.
|
|
577
|
+
*
|
|
578
|
+
* This function helps in avoiding Vite's prebundling from processing absolute URLs (http://, https://, //) as files.
|
|
579
|
+
*
|
|
580
|
+
* @param value - The URL or path to check.
|
|
581
|
+
* @returns `true` if the value is not an absolute URL; otherwise, `false`.
|
|
582
|
+
*/
|
|
583
|
+
function isAbsoluteUrl(value) {
|
|
584
|
+
return /^(?:https?:)?\/\//.test(value);
|
|
567
585
|
}
|
|
@@ -15,6 +15,7 @@ const node_fs_1 = require("node:fs");
|
|
|
15
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
16
|
const application_1 = require("../application");
|
|
17
17
|
const results_1 = require("../application/results");
|
|
18
|
+
const schema_1 = require("../application/schema");
|
|
18
19
|
async function extractMessages(options, builderName, context, extractorConstructor, extensions) {
|
|
19
20
|
const messages = [];
|
|
20
21
|
// Setup the build options for the application based on the buildTarget option
|
|
@@ -25,9 +26,8 @@ async function extractMessages(options, builderName, context, extractorConstruct
|
|
|
25
26
|
buildOptions.budgets = undefined;
|
|
26
27
|
buildOptions.index = false;
|
|
27
28
|
buildOptions.serviceWorker = false;
|
|
28
|
-
buildOptions.
|
|
29
|
-
buildOptions.
|
|
30
|
-
buildOptions.prerender = false;
|
|
29
|
+
buildOptions.outputMode = schema_1.OutputMode.Static;
|
|
30
|
+
buildOptions.server = undefined;
|
|
31
31
|
// Build the application with the build options
|
|
32
32
|
const builderResult = await first((0, application_1.buildApplicationInternal)(buildOptions, context, extensions));
|
|
33
33
|
let success = false;
|
|
@@ -13,7 +13,8 @@ export interface AngularHostOptions {
|
|
|
13
13
|
fileReplacements?: Record<string, string>;
|
|
14
14
|
sourceFileCache?: Map<string, ts.SourceFile>;
|
|
15
15
|
modifiedFiles?: Set<string>;
|
|
16
|
-
|
|
16
|
+
externalStylesheets?: Map<string, string>;
|
|
17
|
+
transformStylesheet(data: string, containingFile: string, stylesheetFile?: string, order?: number): Promise<string | null>;
|
|
17
18
|
processWebWorker(workerFile: string, containingFile: string): string;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.ensureSourceFileVersions = ensureSourceFileVersions;
|
|
14
14
|
exports.createAngularCompilerHost = createAngularCompilerHost;
|
|
15
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
16
|
const node_crypto_1 = require("node:crypto");
|
|
16
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
18
|
/**
|
|
@@ -107,13 +108,31 @@ function createAngularCompilerHost(typescript, compilerOptions, hostOptions) {
|
|
|
107
108
|
if (context.type !== 'style') {
|
|
108
109
|
return null;
|
|
109
110
|
}
|
|
111
|
+
(0, node_assert_1.default)(!context.resourceFile || !hostOptions.externalStylesheets?.has(context.resourceFile), 'External runtime stylesheets should not be transformed: ' + context.resourceFile);
|
|
110
112
|
// No transformation required if the resource is empty
|
|
111
113
|
if (data.trim().length === 0) {
|
|
112
114
|
return { content: '' };
|
|
113
115
|
}
|
|
114
|
-
const result = await hostOptions.transformStylesheet(data, context.containingFile, context.resourceFile ?? undefined
|
|
116
|
+
const result = await hostOptions.transformStylesheet(data, context.containingFile, context.resourceFile ?? undefined,
|
|
117
|
+
// TODO: Remove once available in compiler-cli types
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
context.order);
|
|
115
120
|
return typeof result === 'string' ? { content: result } : null;
|
|
116
121
|
};
|
|
122
|
+
host.resourceNameToFileName = function (resourceName, containingFile) {
|
|
123
|
+
const resolvedPath = node_path_1.default.join(node_path_1.default.dirname(containingFile), resourceName);
|
|
124
|
+
// All resource names that have HTML file extensions are assumed to be templates
|
|
125
|
+
if (resourceName.endsWith('.html') || !hostOptions.externalStylesheets) {
|
|
126
|
+
return resolvedPath;
|
|
127
|
+
}
|
|
128
|
+
// For external stylesheets, create a unique identifier and store the mapping
|
|
129
|
+
let externalId = hostOptions.externalStylesheets.get(resolvedPath);
|
|
130
|
+
if (externalId === undefined) {
|
|
131
|
+
externalId = (0, node_crypto_1.createHash)('sha256').update(resolvedPath).digest('hex');
|
|
132
|
+
hostOptions.externalStylesheets.set(resolvedPath, externalId);
|
|
133
|
+
}
|
|
134
|
+
return externalId + '.css';
|
|
135
|
+
};
|
|
117
136
|
// Allow the AOT compiler to request the set of changed templates and styles
|
|
118
137
|
host.getModifiedResourceFiles = function () {
|
|
119
138
|
return hostOptions.modifiedFiles;
|
|
@@ -30,6 +30,7 @@ export declare abstract class AngularCompilation {
|
|
|
30
30
|
affectedFiles: ReadonlySet<ts.SourceFile>;
|
|
31
31
|
compilerOptions: ng.CompilerOptions;
|
|
32
32
|
referencedFiles: readonly string[];
|
|
33
|
+
externalStylesheets?: ReadonlyMap<string, string>;
|
|
33
34
|
}>;
|
|
34
35
|
abstract emitAffectedFiles(): Iterable<EmitFileResult> | Promise<Iterable<EmitFileResult>>;
|
|
35
36
|
protected abstract collectDiagnostics(modes: DiagnosticModes): Iterable<ts.Diagnostic> | Promise<Iterable<ts.Diagnostic>>;
|
|
@@ -15,6 +15,7 @@ export declare class AotCompilation extends AngularCompilation {
|
|
|
15
15
|
affectedFiles: ReadonlySet<ts.SourceFile>;
|
|
16
16
|
compilerOptions: ng.CompilerOptions;
|
|
17
17
|
referencedFiles: readonly string[];
|
|
18
|
+
externalStylesheets?: ReadonlyMap<string, string>;
|
|
18
19
|
}>;
|
|
19
20
|
collectDiagnostics(modes: DiagnosticModes): Iterable<ts.Diagnostic>;
|
|
20
21
|
emitAffectedFiles(): Iterable<EmitFileResult>;
|
|
@@ -47,6 +47,9 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
47
47
|
// Load the compiler configuration and transform as needed
|
|
48
48
|
const { options: originalCompilerOptions, rootNames, errors: configurationDiagnostics, } = await this.loadConfiguration(tsconfig);
|
|
49
49
|
const compilerOptions = compilerOptionsTransformer?.(originalCompilerOptions) ?? originalCompilerOptions;
|
|
50
|
+
if (compilerOptions.externalRuntimeStyles) {
|
|
51
|
+
hostOptions.externalStylesheets ??= new Map();
|
|
52
|
+
}
|
|
50
53
|
// Create Angular compiler host
|
|
51
54
|
const host = (0, angular_host_1.createAngularCompilerHost)(typescript_1.default, compilerOptions, hostOptions);
|
|
52
55
|
// Create the Angular specific program that contains the Angular compiler
|
|
@@ -82,7 +85,12 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
82
85
|
return [sourceFile.fileName, ...resourceDependencies];
|
|
83
86
|
});
|
|
84
87
|
this.#state = new AngularCompilationState(angularProgram, host, typeScriptProgram, affectedFiles, affectedFiles.size === 1 ? OptimizeFor.SingleFile : OptimizeFor.WholeProgram, (0, web_worker_transformer_1.createWorkerTransformer)(hostOptions.processWebWorker.bind(hostOptions)), this.#state?.diagnosticCache);
|
|
85
|
-
return {
|
|
88
|
+
return {
|
|
89
|
+
affectedFiles,
|
|
90
|
+
compilerOptions,
|
|
91
|
+
referencedFiles,
|
|
92
|
+
externalStylesheets: hostOptions.externalStylesheets,
|
|
93
|
+
};
|
|
86
94
|
}
|
|
87
95
|
*collectDiagnostics(modes) {
|
|
88
96
|
(0, node_assert_1.default)(this.#state, 'Angular compilation must be initialized prior to collecting diagnostics.');
|
|
@@ -26,6 +26,7 @@ export declare class ParallelCompilation extends AngularCompilation {
|
|
|
26
26
|
affectedFiles: ReadonlySet<SourceFile>;
|
|
27
27
|
compilerOptions: CompilerOptions;
|
|
28
28
|
referencedFiles: readonly string[];
|
|
29
|
+
externalStylesheets?: ReadonlyMap<string, string>;
|
|
29
30
|
}>;
|
|
30
31
|
/**
|
|
31
32
|
* This is not needed with this compilation type since the worker will already send a response
|
|
@@ -19,6 +19,7 @@ export interface InitRequest {
|
|
|
19
19
|
webWorkerSignal: Int32Array;
|
|
20
20
|
}
|
|
21
21
|
export declare function initialize(request: InitRequest): Promise<{
|
|
22
|
+
externalStylesheets: ReadonlyMap<string, string> | undefined;
|
|
22
23
|
referencedFiles: readonly string[];
|
|
23
24
|
compilerOptions: {
|
|
24
25
|
allowJs: boolean | undefined;
|
|
@@ -33,7 +33,7 @@ async function initialize(request) {
|
|
|
33
33
|
stylesheetRequests.get(requestId)?.[0](value);
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
-
const { compilerOptions, referencedFiles } = await compilation.initialize(request.tsconfig, {
|
|
36
|
+
const { compilerOptions, referencedFiles, externalStylesheets } = await compilation.initialize(request.tsconfig, {
|
|
37
37
|
fileReplacements: request.fileReplacements,
|
|
38
38
|
sourceFileCache,
|
|
39
39
|
modifiedFiles: sourceFileCache.modifiedFiles,
|
|
@@ -69,6 +69,7 @@ async function initialize(request) {
|
|
|
69
69
|
return result?.transformedOptions ?? compilerOptions;
|
|
70
70
|
});
|
|
71
71
|
return {
|
|
72
|
+
externalStylesheets,
|
|
72
73
|
referencedFiles,
|
|
73
74
|
// TODO: Expand? `allowJs`, `isolatedModules`, `sourceMap`, `inlineSourceMap` are the only fields needed currently.
|
|
74
75
|
compilerOptions: {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
import { PluginObj } from '@babel/core';
|
|
9
|
+
/**
|
|
10
|
+
* A babel plugin factory function for adding istanbul instrumentation.
|
|
11
|
+
*
|
|
12
|
+
* @returns A babel plugin object instance.
|
|
13
|
+
*/
|
|
14
|
+
export default function (): PluginObj;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.default = default_1;
|
|
14
|
+
const core_1 = require("@babel/core");
|
|
15
|
+
const istanbul_lib_instrument_1 = require("istanbul-lib-instrument");
|
|
16
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
17
|
+
/**
|
|
18
|
+
* A babel plugin factory function for adding istanbul instrumentation.
|
|
19
|
+
*
|
|
20
|
+
* @returns A babel plugin object instance.
|
|
21
|
+
*/
|
|
22
|
+
function default_1() {
|
|
23
|
+
const visitors = new WeakMap();
|
|
24
|
+
return {
|
|
25
|
+
visitor: {
|
|
26
|
+
Program: {
|
|
27
|
+
enter(path, state) {
|
|
28
|
+
const visitor = (0, istanbul_lib_instrument_1.programVisitor)(core_1.types, state.filename, {
|
|
29
|
+
// Babel returns a Converter object from the `convert-source-map` package
|
|
30
|
+
inputSourceMap: state.file.inputMap?.toObject(),
|
|
31
|
+
});
|
|
32
|
+
visitors.set(path, visitor);
|
|
33
|
+
visitor.enter(path);
|
|
34
|
+
},
|
|
35
|
+
exit(path) {
|
|
36
|
+
const visitor = visitors.get(path);
|
|
37
|
+
(0, node_assert_1.default)(visitor, 'Instrumentation visitor should always be present for program path.');
|
|
38
|
+
visitor.exit(path);
|
|
39
|
+
visitors.delete(path);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare module 'istanbul-lib-instrument' {
|
|
10
|
+
export interface Visitor {
|
|
11
|
+
enter(path: import('@babel/core').NodePath<types.Program>): void;
|
|
12
|
+
exit(path: import('@babel/core').NodePath<types.Program>): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function programVisitor(
|
|
16
|
+
types: typeof import('@babel/core').types,
|
|
17
|
+
filePath?: string,
|
|
18
|
+
options?: { inputSourceMap?: object | null },
|
|
19
|
+
): Visitor;
|
|
20
|
+
}
|
|
@@ -21,6 +21,8 @@ export interface CompilerPluginOptions {
|
|
|
21
21
|
sourceFileCache?: SourceFileCache;
|
|
22
22
|
loadResultCache?: LoadResultCache;
|
|
23
23
|
incremental: boolean;
|
|
24
|
+
externalRuntimeStyles?: boolean;
|
|
25
|
+
instrumentForCoverage?: (request: string) => boolean;
|
|
24
26
|
}
|
|
25
27
|
export declare function createCompilerPlugin(pluginOptions: CompilerPluginOptions, styleOptions: BundleStylesheetOptions & {
|
|
26
28
|
inlineStyleLanguage: string;
|
|
@@ -35,6 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createCompilerPlugin = createCompilerPlugin;
|
|
37
37
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
38
|
+
const node_crypto_1 = require("node:crypto");
|
|
38
39
|
const path = __importStar(require("node:path"));
|
|
39
40
|
const environment_options_1 = require("../../../utils/environment-options");
|
|
40
41
|
const compilation_1 = require("../../angular/compilation");
|
|
@@ -120,13 +121,14 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
120
121
|
// Angular compiler which does not have direct knowledge of transitive resource
|
|
121
122
|
// dependencies or web worker processing.
|
|
122
123
|
let modifiedFiles;
|
|
124
|
+
let invalidatedStylesheetEntries;
|
|
123
125
|
if (pluginOptions.sourceFileCache?.modifiedFiles.size &&
|
|
124
126
|
referencedFileTracker &&
|
|
125
127
|
!pluginOptions.noopTypeScriptCompilation) {
|
|
126
128
|
// TODO: Differentiate between changed input files and stale output files
|
|
127
129
|
modifiedFiles = referencedFileTracker.update(pluginOptions.sourceFileCache.modifiedFiles);
|
|
128
130
|
pluginOptions.sourceFileCache.invalidate(modifiedFiles);
|
|
129
|
-
stylesheetBundler.invalidate(modifiedFiles);
|
|
131
|
+
invalidatedStylesheetEntries = stylesheetBundler.invalidate(modifiedFiles);
|
|
130
132
|
}
|
|
131
133
|
if (!pluginOptions.noopTypeScriptCompilation &&
|
|
132
134
|
compilation.update &&
|
|
@@ -138,7 +140,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
138
140
|
fileReplacements: pluginOptions.fileReplacements,
|
|
139
141
|
modifiedFiles,
|
|
140
142
|
sourceFileCache: pluginOptions.sourceFileCache,
|
|
141
|
-
async transformStylesheet(data, containingFile, stylesheetFile) {
|
|
143
|
+
async transformStylesheet(data, containingFile, stylesheetFile, order) {
|
|
142
144
|
let stylesheetResult;
|
|
143
145
|
// Stylesheet file only exists for external stylesheets
|
|
144
146
|
if (stylesheetFile) {
|
|
@@ -147,7 +149,17 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
147
149
|
else {
|
|
148
150
|
stylesheetResult = await stylesheetBundler.bundleInline(data, containingFile,
|
|
149
151
|
// Inline stylesheets from a template style element are always CSS
|
|
150
|
-
containingFile.endsWith('.html') ? 'css' : styleOptions.inlineStyleLanguage
|
|
152
|
+
containingFile.endsWith('.html') ? 'css' : styleOptions.inlineStyleLanguage,
|
|
153
|
+
// When external runtime styles are enabled, an identifier for the style that does not change
|
|
154
|
+
// based on the content is required to avoid emitted JS code changes. Any JS code changes will
|
|
155
|
+
// invalid the output and force a full page reload for HMR cases. The containing file and order
|
|
156
|
+
// of the style within the containing file is used.
|
|
157
|
+
pluginOptions.externalRuntimeStyles
|
|
158
|
+
? (0, node_crypto_1.createHash)('sha-256')
|
|
159
|
+
.update(containingFile)
|
|
160
|
+
.update((order ?? 0).toString())
|
|
161
|
+
.digest('hex')
|
|
162
|
+
: undefined);
|
|
151
163
|
}
|
|
152
164
|
const { contents, outputFiles, metafile, referencedFiles, errors, warnings } = stylesheetResult;
|
|
153
165
|
if (errors) {
|
|
@@ -201,6 +213,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
201
213
|
// Initialize the Angular compilation for the current build.
|
|
202
214
|
// In watch mode, previous build state will be reused.
|
|
203
215
|
let referencedFiles;
|
|
216
|
+
let externalStylesheets;
|
|
204
217
|
try {
|
|
205
218
|
const initializationResult = await compilation.initialize(pluginOptions.tsconfig, hostOptions, createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks));
|
|
206
219
|
shouldTsIgnoreJs = !initializationResult.compilerOptions.allowJs;
|
|
@@ -211,6 +224,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
211
224
|
!!initializationResult.compilerOptions.sourceMap ||
|
|
212
225
|
!!initializationResult.compilerOptions.inlineSourceMap;
|
|
213
226
|
referencedFiles = initializationResult.referencedFiles;
|
|
227
|
+
externalStylesheets = initializationResult.externalStylesheets;
|
|
214
228
|
}
|
|
215
229
|
catch (error) {
|
|
216
230
|
(result.errors ??= []).push({
|
|
@@ -231,6 +245,19 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
231
245
|
hasCompilationErrors = await sharedTSCompilationState.waitUntilReady;
|
|
232
246
|
return result;
|
|
233
247
|
}
|
|
248
|
+
if (externalStylesheets) {
|
|
249
|
+
// Process any new external stylesheets
|
|
250
|
+
for (const [stylesheetFile, externalId] of externalStylesheets) {
|
|
251
|
+
await bundleExternalStylesheet(stylesheetBundler, stylesheetFile, externalId, result, additionalResults);
|
|
252
|
+
}
|
|
253
|
+
// Process any updated stylesheets
|
|
254
|
+
if (invalidatedStylesheetEntries) {
|
|
255
|
+
for (const stylesheetFile of invalidatedStylesheetEntries) {
|
|
256
|
+
// externalId is already linked in the bundler context so only enabling is required here
|
|
257
|
+
await bundleExternalStylesheet(stylesheetBundler, stylesheetFile, true, result, additionalResults);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
234
261
|
// Update TypeScript file output cache for all affected files
|
|
235
262
|
try {
|
|
236
263
|
await (0, profiling_1.profileAsync)('NG_EMIT_TS', async () => {
|
|
@@ -312,7 +339,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
312
339
|
// A string indicates untransformed output from the TS/NG compiler.
|
|
313
340
|
// This step is unneeded when using esbuild transpilation.
|
|
314
341
|
const sideEffects = await hasSideEffects(request);
|
|
315
|
-
|
|
342
|
+
const instrumentForCoverage = pluginOptions.instrumentForCoverage?.(request);
|
|
343
|
+
contents = await javascriptTransformer.transformData(request, contents, true /* skipLinker */, sideEffects, instrumentForCoverage);
|
|
316
344
|
// Store as the returned Uint8Array to allow caching the fully transformed code
|
|
317
345
|
typeScriptFileCache.set(request, contents);
|
|
318
346
|
}
|
|
@@ -376,6 +404,17 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
376
404
|
},
|
|
377
405
|
};
|
|
378
406
|
}
|
|
407
|
+
async function bundleExternalStylesheet(stylesheetBundler, stylesheetFile, externalId, result, additionalResults) {
|
|
408
|
+
const { outputFiles, metafile, errors, warnings } = await stylesheetBundler.bundleFile(stylesheetFile, externalId);
|
|
409
|
+
if (errors) {
|
|
410
|
+
(result.errors ??= []).push(...errors);
|
|
411
|
+
}
|
|
412
|
+
(result.warnings ??= []).push(...warnings);
|
|
413
|
+
additionalResults.set(stylesheetFile, {
|
|
414
|
+
outputFiles,
|
|
415
|
+
metafile,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
379
418
|
function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks) {
|
|
380
419
|
return (compilerOptions) => {
|
|
381
420
|
// target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum)
|
|
@@ -433,6 +472,7 @@ function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserve
|
|
|
433
472
|
mapRoot: undefined,
|
|
434
473
|
sourceRoot: undefined,
|
|
435
474
|
preserveSymlinks,
|
|
475
|
+
externalRuntimeStyles: pluginOptions.externalRuntimeStyles,
|
|
436
476
|
};
|
|
437
477
|
};
|
|
438
478
|
}
|