@angular/build 19.0.0-next.0 → 19.0.0-next.2
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/package.json +7 -7
- package/src/builders/extract-i18n/application-extraction.js +4 -0
- package/src/tools/angular/compilation/jit-compilation.js +2 -1
- package/src/tools/esbuild/angular/compiler-plugin.js +3 -1
- package/src/tools/esbuild/application-code-bundle.js +0 -25
- package/src/typings.d.ts +19 -0
- package/src/utils/index-file/inline-critical-css.js +43 -33
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/prerender.js +16 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/build",
|
|
3
|
-
"version": "19.0.0-next.
|
|
3
|
+
"version": "19.0.0-next.2",
|
|
4
4
|
"description": "Official build system for Angular",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Angular CLI",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"builders": "builders.json",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ampproject/remapping": "2.3.0",
|
|
26
|
-
"@angular-devkit/architect": "0.1900.0-next.
|
|
26
|
+
"@angular-devkit/architect": "0.1900.0-next.2",
|
|
27
27
|
"@babel/core": "7.25.2",
|
|
28
28
|
"@babel/helper-annotate-as-pure": "7.24.7",
|
|
29
29
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
"@vitejs/plugin-basic-ssl": "1.1.0",
|
|
33
33
|
"browserslist": "^4.23.0",
|
|
34
34
|
"critters": "0.0.24",
|
|
35
|
-
"esbuild": "0.23.
|
|
35
|
+
"esbuild": "0.23.1",
|
|
36
36
|
"fast-glob": "3.3.2",
|
|
37
37
|
"https-proxy-agent": "7.0.5",
|
|
38
38
|
"listr2": "8.2.4",
|
|
39
|
-
"lmdb": "3.0.
|
|
39
|
+
"lmdb": "3.0.14",
|
|
40
40
|
"magic-string": "0.30.11",
|
|
41
41
|
"mrmime": "2.0.0",
|
|
42
42
|
"parse5-html-rewriting-stream": "7.0.0",
|
|
43
43
|
"picomatch": "4.0.2",
|
|
44
44
|
"piscina": "4.6.1",
|
|
45
|
-
"rollup": "4.
|
|
45
|
+
"rollup": "4.21.1",
|
|
46
46
|
"sass": "1.77.8",
|
|
47
47
|
"semver": "7.6.3",
|
|
48
|
-
"vite": "5.4.
|
|
48
|
+
"vite": "5.4.2",
|
|
49
49
|
"watchpack": "2.4.2"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@angular/localize": "^19.0.0-next.0",
|
|
54
54
|
"@angular/platform-server": "^19.0.0-next.0",
|
|
55
55
|
"@angular/service-worker": "^19.0.0-next.0",
|
|
56
|
-
"@angular/ssr": "^19.0.0-next.
|
|
56
|
+
"@angular/ssr": "^19.0.0-next.2",
|
|
57
57
|
"less": "^4.2.0",
|
|
58
58
|
"postcss": "^8.4.0",
|
|
59
59
|
"tailwindcss": "^2.0.0 || ^3.0.0",
|
|
@@ -11,6 +11,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.extractMessages = extractMessages;
|
|
14
|
+
const node_fs_1 = require("node:fs");
|
|
14
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
16
|
const application_1 = require("../application");
|
|
16
17
|
const results_1 = require("../application/results");
|
|
@@ -72,6 +73,9 @@ function setupLocalizeExtractor(extractorConstructor, files, context) {
|
|
|
72
73
|
if (file?.origin === 'memory') {
|
|
73
74
|
content = textDecoder.decode(file.contents);
|
|
74
75
|
}
|
|
76
|
+
else if (file?.origin === 'disk') {
|
|
77
|
+
content = (0, node_fs_1.readFileSync)(file.inputPath, 'utf-8');
|
|
78
|
+
}
|
|
75
79
|
if (content === undefined) {
|
|
76
80
|
throw new Error('Unknown file requested: ' + requestedPath);
|
|
77
81
|
}
|
|
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.JitCompilation = void 0;
|
|
14
14
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
15
|
const typescript_1 = __importDefault(require("typescript"));
|
|
16
|
+
const load_esm_1 = require("../../../utils/load-esm");
|
|
16
17
|
const profiling_1 = require("../../esbuild/profiling");
|
|
17
18
|
const angular_host_1 = require("../angular-host");
|
|
18
19
|
const jit_resource_transformer_1 = require("../transformers/jit-resource-transformer");
|
|
@@ -36,7 +37,7 @@ class JitCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
36
37
|
#state;
|
|
37
38
|
async initialize(tsconfig, hostOptions, compilerOptionsTransformer) {
|
|
38
39
|
// Dynamically load the Angular compiler CLI package
|
|
39
|
-
const { constructorParametersDownlevelTransform } = await
|
|
40
|
+
const { constructorParametersDownlevelTransform } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/private/tooling');
|
|
40
41
|
// Load the compiler configuration and transform as needed
|
|
41
42
|
const { options: originalCompilerOptions, rootNames, errors: configurationDiagnostics, } = await this.loadConfiguration(tsconfig);
|
|
42
43
|
const compilerOptions = compilerOptionsTransformer?.(originalCompilerOptions) ?? originalCompilerOptions;
|
|
@@ -145,7 +145,9 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
145
145
|
stylesheetResult = await stylesheetBundler.bundleFile(stylesheetFile);
|
|
146
146
|
}
|
|
147
147
|
else {
|
|
148
|
-
stylesheetResult = await stylesheetBundler.bundleInline(data, containingFile,
|
|
148
|
+
stylesheetResult = await stylesheetBundler.bundleInline(data, containingFile,
|
|
149
|
+
// Inline stylesheets from a template style element are always CSS
|
|
150
|
+
containingFile.endsWith('.html') ? 'css' : styleOptions.inlineStyleLanguage);
|
|
149
151
|
}
|
|
150
152
|
const { contents, outputFiles, metafile, referencedFiles, errors, warnings } = stylesheetResult;
|
|
151
153
|
if (errors) {
|
|
@@ -322,12 +322,10 @@ function getEsBuildCommonPolyfillsOptions(options, namespace, tryToResolvePolyfi
|
|
|
322
322
|
namespace,
|
|
323
323
|
cache: sourceFileCache?.loadResultCache,
|
|
324
324
|
loadContent: async (_, build) => {
|
|
325
|
-
let hasLocalizePolyfill = false;
|
|
326
325
|
let polyfillPaths = polyfills;
|
|
327
326
|
let warnings;
|
|
328
327
|
if (tryToResolvePolyfillsAsRelative) {
|
|
329
328
|
polyfillPaths = await Promise.all(polyfills.map(async (path) => {
|
|
330
|
-
hasLocalizePolyfill ||= path.startsWith('@angular/localize');
|
|
331
329
|
if (path.startsWith('zone.js') || !(0, node_path_1.extname)(path)) {
|
|
332
330
|
return path;
|
|
333
331
|
}
|
|
@@ -339,29 +337,6 @@ function getEsBuildCommonPolyfillsOptions(options, namespace, tryToResolvePolyfi
|
|
|
339
337
|
return result.path ? potentialPathRelative : path;
|
|
340
338
|
}));
|
|
341
339
|
}
|
|
342
|
-
else {
|
|
343
|
-
hasLocalizePolyfill = polyfills.some((p) => p.startsWith('@angular/localize'));
|
|
344
|
-
}
|
|
345
|
-
// Add localize polyfill if needed.
|
|
346
|
-
// TODO: remove in version 19 or later.
|
|
347
|
-
if (!i18nOptions.shouldInline && !hasLocalizePolyfill) {
|
|
348
|
-
const result = await build.resolve('@angular/localize', {
|
|
349
|
-
kind: 'import-statement',
|
|
350
|
-
resolveDir: workspaceRoot,
|
|
351
|
-
});
|
|
352
|
-
if (result.path) {
|
|
353
|
-
polyfillPaths.push('@angular/localize/init');
|
|
354
|
-
(warnings ??= []).push({
|
|
355
|
-
text: 'Polyfill for "@angular/localize/init" was added automatically.',
|
|
356
|
-
notes: [
|
|
357
|
-
{
|
|
358
|
-
text: 'In the future, this functionality will be removed. ' +
|
|
359
|
-
'Please add this polyfill in the "polyfills" section of your "angular.json" instead.',
|
|
360
|
-
},
|
|
361
|
-
],
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
340
|
// Generate module contents with an import statement per defined polyfill
|
|
366
341
|
let contents = polyfillPaths
|
|
367
342
|
.map((file) => `import '${file.replace(/\\/g, '/')}';`)
|
package/src/typings.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
// The `bundled_critters` causes issues with module mappings in Bazel,
|
|
10
|
+
// leading to unexpected behavior with esbuild. Specifically, the problem occurs
|
|
11
|
+
// when esbuild resolves to a different module or version than expected, due to
|
|
12
|
+
// how Bazel handles module mappings.
|
|
13
|
+
//
|
|
14
|
+
// This change aims to resolve esbuild types correctly and maintain consistency
|
|
15
|
+
// in the Bazel build process.
|
|
16
|
+
|
|
17
|
+
declare module 'esbuild' {
|
|
18
|
+
export * from 'esbuild-wasm';
|
|
19
|
+
}
|
|
@@ -22,38 +22,53 @@ const MEDIA_SET_HANDLER_PATTERN = /^this\.media=["'](.*)["'];?$/;
|
|
|
22
22
|
*/
|
|
23
23
|
const CSP_MEDIA_ATTR = 'ngCspMedia';
|
|
24
24
|
/**
|
|
25
|
-
* Script
|
|
25
|
+
* Script that dynamically updates the `media` attribute of `<link>` tags based on a custom attribute (`CSP_MEDIA_ATTR`).
|
|
26
26
|
*
|
|
27
27
|
* NOTE:
|
|
28
28
|
* We do not use `document.querySelectorAll('link').forEach((s) => s.addEventListener('load', ...)`
|
|
29
|
-
* because
|
|
29
|
+
* because load events are not always triggered reliably on Chrome.
|
|
30
30
|
* See: https://github.com/angular/angular-cli/issues/26932 and https://crbug.com/1521256
|
|
31
|
+
*
|
|
32
|
+
* The script:
|
|
33
|
+
* - Ensures the event target is a `<link>` tag with the `CSP_MEDIA_ATTR` attribute.
|
|
34
|
+
* - Updates the `media` attribute with the value of `CSP_MEDIA_ATTR` and then removes the attribute.
|
|
35
|
+
* - Removes the event listener when all relevant `<link>` tags have been processed.
|
|
36
|
+
* - Uses event capturing (the `true` parameter) since load events do not bubble up the DOM.
|
|
31
37
|
*/
|
|
32
|
-
const LINK_LOAD_SCRIPT_CONTENT =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
const LINK_LOAD_SCRIPT_CONTENT = `
|
|
39
|
+
(() => {
|
|
40
|
+
const CSP_MEDIA_ATTR = '${CSP_MEDIA_ATTR}';
|
|
41
|
+
const documentElement = document.documentElement;
|
|
42
|
+
|
|
43
|
+
// Listener for load events on link tags.
|
|
44
|
+
const listener = (e) => {
|
|
45
|
+
const target = e.target;
|
|
46
|
+
if (
|
|
47
|
+
!target ||
|
|
48
|
+
target.tagName !== 'LINK' ||
|
|
49
|
+
!target.hasAttribute(CSP_MEDIA_ATTR)
|
|
50
|
+
) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
target.media = target.getAttribute(CSP_MEDIA_ATTR);
|
|
55
|
+
target.removeAttribute(CSP_MEDIA_ATTR);
|
|
56
|
+
|
|
57
|
+
if (!document.head.querySelector(\`link[\${CSP_MEDIA_ATTR}]\`)) {
|
|
58
|
+
documentElement.removeEventListener('load', listener);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
documentElement.addEventListener('load', listener, true);
|
|
63
|
+
})();
|
|
64
|
+
`.trim();
|
|
65
|
+
class CrittersBase extends critters_1.default {
|
|
66
|
+
}
|
|
67
|
+
/* eslint-enable @typescript-eslint/no-unsafe-declaration-merging */
|
|
68
|
+
class CrittersExtended extends CrittersBase {
|
|
53
69
|
optionsExtended;
|
|
54
70
|
warnings = [];
|
|
55
71
|
errors = [];
|
|
56
|
-
initialEmbedLinkedStylesheet;
|
|
57
72
|
addedCspScriptsDocuments = new WeakSet();
|
|
58
73
|
documentNonces = new WeakMap();
|
|
59
74
|
constructor(optionsExtended) {
|
|
@@ -71,17 +86,12 @@ class CrittersExtended extends critters_1.default {
|
|
|
71
86
|
reduceInlineStyles: false,
|
|
72
87
|
mergeStylesheets: false,
|
|
73
88
|
// Note: if `preload` changes to anything other than `media`, the logic in
|
|
74
|
-
// `
|
|
89
|
+
// `embedLinkedStylesheet` will have to be updated.
|
|
75
90
|
preload: 'media',
|
|
76
91
|
noscriptFallback: true,
|
|
77
92
|
inlineFonts: true,
|
|
78
93
|
});
|
|
79
94
|
this.optionsExtended = optionsExtended;
|
|
80
|
-
// We can't use inheritance to override `embedLinkedStylesheet`, because it's not declared in
|
|
81
|
-
// the `Critters` .d.ts which means that we can't call the `super` implementation. TS doesn't
|
|
82
|
-
// allow for `super` to be cast to a different type.
|
|
83
|
-
this.initialEmbedLinkedStylesheet = this.embedLinkedStylesheet;
|
|
84
|
-
this.embedLinkedStylesheet = this.embedLinkedStylesheetOverride;
|
|
85
95
|
}
|
|
86
96
|
readFile(path) {
|
|
87
97
|
const readAsset = this.optionsExtended.readAsset;
|
|
@@ -91,7 +101,7 @@ class CrittersExtended extends critters_1.default {
|
|
|
91
101
|
* Override of the Critters `embedLinkedStylesheet` method
|
|
92
102
|
* that makes it work with Angular's CSP APIs.
|
|
93
103
|
*/
|
|
94
|
-
|
|
104
|
+
async embedLinkedStylesheet(link, document) {
|
|
95
105
|
if (link.getAttribute('media') === 'print' && link.next?.name === 'noscript') {
|
|
96
106
|
// Workaround for https://github.com/GoogleChromeLabs/critters/issues/64
|
|
97
107
|
// NB: this is only needed for the webpack based builders.
|
|
@@ -102,7 +112,7 @@ class CrittersExtended extends critters_1.default {
|
|
|
102
112
|
link?.next?.remove();
|
|
103
113
|
}
|
|
104
114
|
}
|
|
105
|
-
const returnValue = await
|
|
115
|
+
const returnValue = await super.embedLinkedStylesheet(link, document);
|
|
106
116
|
const cspNonce = this.findCspNonce(document);
|
|
107
117
|
if (cspNonce) {
|
|
108
118
|
const crittersMedia = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
|
|
@@ -126,7 +136,7 @@ class CrittersExtended extends critters_1.default {
|
|
|
126
136
|
});
|
|
127
137
|
}
|
|
128
138
|
return returnValue;
|
|
129
|
-
}
|
|
139
|
+
}
|
|
130
140
|
/**
|
|
131
141
|
* Finds the CSP nonce for a specific document.
|
|
132
142
|
*/
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.normalizeCacheOptions = normalizeCacheOptions;
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
/** Version placeholder is replaced during the build process with actual package version */
|
|
13
|
-
const VERSION = '19.0.0-next.
|
|
13
|
+
const VERSION = '19.0.0-next.2';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -49,11 +49,14 @@ async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOpti
|
|
|
49
49
|
assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
|
|
50
50
|
}
|
|
51
51
|
// Get routes to prerender
|
|
52
|
-
const { routes: allRoutes, warnings: routesWarnings } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed, document, appShellOptions, prerenderOptions, sourcemap, verbose);
|
|
52
|
+
const { routes: allRoutes, warnings: routesWarnings, errors: routesErrors, } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed, document, appShellOptions, prerenderOptions, sourcemap, verbose);
|
|
53
|
+
if (routesErrors?.length) {
|
|
54
|
+
errors.push(...routesErrors);
|
|
55
|
+
}
|
|
53
56
|
if (routesWarnings?.length) {
|
|
54
57
|
warnings.push(...routesWarnings);
|
|
55
58
|
}
|
|
56
|
-
if (allRoutes.size < 1) {
|
|
59
|
+
if (allRoutes.size < 1 || errors.length > 0) {
|
|
57
60
|
return {
|
|
58
61
|
errors,
|
|
59
62
|
warnings,
|
|
@@ -109,7 +112,8 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
109
112
|
const isAppShellRoute = appShellRoute === route;
|
|
110
113
|
const serverContext = isAppShellRoute ? 'app-shell' : 'ssg';
|
|
111
114
|
const render = renderWorker.run({ route, serverContext });
|
|
112
|
-
const renderResult = render
|
|
115
|
+
const renderResult = render
|
|
116
|
+
.then(({ content, warnings, errors }) => {
|
|
113
117
|
if (content !== undefined) {
|
|
114
118
|
const outPath = isAppShellRoute
|
|
115
119
|
? 'index.html'
|
|
@@ -122,6 +126,10 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
122
126
|
if (errors) {
|
|
123
127
|
errors.push(...errors);
|
|
124
128
|
}
|
|
129
|
+
})
|
|
130
|
+
.catch((err) => {
|
|
131
|
+
errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack}`);
|
|
132
|
+
void renderWorker.destroy();
|
|
125
133
|
});
|
|
126
134
|
renderingPromises.push(renderResult);
|
|
127
135
|
}
|
|
@@ -173,15 +181,19 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWo
|
|
|
173
181
|
execArgv: workerExecArgv,
|
|
174
182
|
recordTiming: false,
|
|
175
183
|
});
|
|
184
|
+
const errors = [];
|
|
176
185
|
const { routes: extractedRoutes, warnings } = await renderWorker
|
|
177
186
|
.run({})
|
|
187
|
+
.catch((err) => {
|
|
188
|
+
errors.push(`An error occurred while extracting routes.\n\n${err.stack}`);
|
|
189
|
+
})
|
|
178
190
|
.finally(() => {
|
|
179
191
|
void renderWorker.destroy();
|
|
180
192
|
});
|
|
181
193
|
for (const route of extractedRoutes) {
|
|
182
194
|
routes.add(route);
|
|
183
195
|
}
|
|
184
|
-
return { routes, warnings };
|
|
196
|
+
return { routes, warnings, errors };
|
|
185
197
|
}
|
|
186
198
|
function addLeadingSlash(value) {
|
|
187
199
|
return value.charAt(0) === '/' ? value : '/' + value;
|