@angular/build 19.1.6 → 19.1.7
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 +4 -4
- package/src/builders/application/execute-build.js +1 -1
- package/src/builders/application/i18n.js +6 -3
- package/src/builders/dev-server/schema.d.ts +4 -4
- package/src/builders/dev-server/schema.json +2 -2
- package/src/builders/dev-server/vite-server.js +15 -7
- package/src/tools/esbuild/i18n-inliner.d.ts +9 -0
- package/src/tools/esbuild/i18n-inliner.js +110 -21
- package/src/tools/esbuild/utils.js +4 -1
- package/src/tools/vite/plugins/angular-memory-plugin.d.ts +1 -1
- package/src/tools/vite/plugins/angular-memory-plugin.js +9 -3
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/manifest.js +7 -2
- package/src/utils/server-rendering/prerender.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/build",
|
|
3
|
-
"version": "19.1.
|
|
3
|
+
"version": "19.1.7",
|
|
4
4
|
"description": "Official build system for Angular",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Angular CLI",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"builders": "builders.json",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ampproject/remapping": "2.3.0",
|
|
26
|
-
"@angular-devkit/core": "19.1.
|
|
27
|
-
"@angular-devkit/architect": "0.1901.
|
|
26
|
+
"@angular-devkit/core": "19.1.7",
|
|
27
|
+
"@angular-devkit/architect": "0.1901.7",
|
|
28
28
|
"@babel/core": "7.26.0",
|
|
29
29
|
"@babel/helper-annotate-as-pure": "7.25.9",
|
|
30
30
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@angular/localize": "^19.0.0",
|
|
59
59
|
"@angular/platform-server": "^19.0.0",
|
|
60
60
|
"@angular/service-worker": "^19.0.0",
|
|
61
|
-
"@angular/ssr": "^19.1.
|
|
61
|
+
"@angular/ssr": "^19.1.7",
|
|
62
62
|
"less": "^4.2.0",
|
|
63
63
|
"ng-packagr": "^19.0.0",
|
|
64
64
|
"postcss": "^8.4.0",
|
|
@@ -148,7 +148,6 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
148
148
|
}
|
|
149
149
|
const { metafile, initialFiles, outputFiles } = bundlingResult;
|
|
150
150
|
executionResult.outputFiles.push(...outputFiles);
|
|
151
|
-
const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputInfo);
|
|
152
151
|
// Analyze files for bundle budget failures if present
|
|
153
152
|
let budgetFailures;
|
|
154
153
|
if (options.budgets) {
|
|
@@ -218,6 +217,7 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
218
217
|
executionResult.addOutputFile('stats.json', JSON.stringify(metafile, null, 2), bundler_context_1.BuildOutputFileType.Root);
|
|
219
218
|
}
|
|
220
219
|
if (!jsonLogs) {
|
|
220
|
+
const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputInfo);
|
|
221
221
|
executionResult.addLog((0, utils_1.logBuildStats)(metafile, outputFiles, initialFiles, budgetFailures, colors, changedFiles, estimatedTransferSizes, !!ssrOptions, verbose));
|
|
222
222
|
}
|
|
223
223
|
return executionResult;
|
|
@@ -26,12 +26,13 @@ const options_1 = require("./options");
|
|
|
26
26
|
* @param initialFiles A map containing initial file information for the executed build.
|
|
27
27
|
*/
|
|
28
28
|
async function inlineI18n(metafile, options, executionResult, initialFiles) {
|
|
29
|
-
const { i18nOptions, optimizationOptions, baseHref } = options;
|
|
29
|
+
const { i18nOptions, optimizationOptions, baseHref, cacheOptions } = options;
|
|
30
30
|
// Create the multi-threaded inliner with common options and the files generated from the build.
|
|
31
31
|
const inliner = new i18n_inliner_1.I18nInliner({
|
|
32
32
|
missingTranslation: i18nOptions.missingTranslationBehavior ?? 'warning',
|
|
33
33
|
outputFiles: executionResult.outputFiles,
|
|
34
34
|
shouldOptimize: optimizationOptions.scripts,
|
|
35
|
+
persistentCachePath: cacheOptions.enabled ? cacheOptions.path : undefined,
|
|
35
36
|
}, environment_options_1.maxWorkers);
|
|
36
37
|
const inlineResult = {
|
|
37
38
|
errors: [],
|
|
@@ -41,6 +42,8 @@ async function inlineI18n(metafile, options, executionResult, initialFiles) {
|
|
|
41
42
|
// For each active locale, use the inliner to process the output files of the build.
|
|
42
43
|
const updatedOutputFiles = [];
|
|
43
44
|
const updatedAssetFiles = [];
|
|
45
|
+
// Root and SSR entry files are not modified.
|
|
46
|
+
const unModifiedOutputFiles = executionResult.outputFiles.filter(({ type }) => type === bundler_context_1.BuildOutputFileType.Root || type === bundler_context_1.BuildOutputFileType.ServerRoot);
|
|
44
47
|
try {
|
|
45
48
|
for (const locale of i18nOptions.inlineLocales) {
|
|
46
49
|
// A locale specific set of files is returned from the inliner.
|
|
@@ -51,7 +54,7 @@ async function inlineI18n(metafile, options, executionResult, initialFiles) {
|
|
|
51
54
|
const { errors, warnings, additionalAssets, additionalOutputFiles, prerenderedRoutes: generatedRoutes, } = await (0, execute_post_bundle_1.executePostBundleSteps)(metafile, {
|
|
52
55
|
...options,
|
|
53
56
|
baseHref: (0, options_1.getLocaleBaseHref)(baseHref, i18nOptions, locale) ?? baseHref,
|
|
54
|
-
}, localeOutputFiles, executionResult.assetFiles, initialFiles, locale);
|
|
57
|
+
}, [...unModifiedOutputFiles, ...localeOutputFiles], executionResult.assetFiles, initialFiles, locale);
|
|
55
58
|
localeOutputFiles.push(...additionalOutputFiles);
|
|
56
59
|
inlineResult.errors.push(...errors);
|
|
57
60
|
inlineResult.warnings.push(...warnings);
|
|
@@ -81,7 +84,7 @@ async function inlineI18n(metafile, options, executionResult, initialFiles) {
|
|
|
81
84
|
// Update the result with all localized files.
|
|
82
85
|
executionResult.outputFiles = [
|
|
83
86
|
// Root and SSR entry files are not modified.
|
|
84
|
-
...
|
|
87
|
+
...unModifiedOutputFiles,
|
|
85
88
|
// Updated files for each locale.
|
|
86
89
|
...updatedOutputFiles,
|
|
87
90
|
];
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export type Schema = {
|
|
5
5
|
/**
|
|
6
|
-
* The hosts that
|
|
7
|
-
* same name. For further details:
|
|
6
|
+
* The hosts that the development server will respond to. This option sets the Vite option
|
|
7
|
+
* of the same name. For further details:
|
|
8
8
|
* https://vite.dev/config/server-options.html#server-allowedhosts
|
|
9
9
|
*/
|
|
10
10
|
allowedHosts?: AllowedHosts;
|
|
@@ -86,8 +86,8 @@ export type Schema = {
|
|
|
86
86
|
watch?: boolean;
|
|
87
87
|
};
|
|
88
88
|
/**
|
|
89
|
-
* The hosts that
|
|
90
|
-
* same name. For further details:
|
|
89
|
+
* The hosts that the development server will respond to. This option sets the Vite option
|
|
90
|
+
* of the same name. For further details:
|
|
91
91
|
* https://vite.dev/config/server-options.html#server-allowedhosts
|
|
92
92
|
*/
|
|
93
93
|
export type AllowedHosts = string[] | boolean;
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"description": "SSL certificate to use for serving HTTPS."
|
|
38
38
|
},
|
|
39
39
|
"allowedHosts": {
|
|
40
|
-
"description": "The hosts that
|
|
40
|
+
"description": "The hosts that the development server will respond to. This option sets the Vite option of the same name. For further details: https://vite.dev/config/server-options.html#server-allowedhosts",
|
|
41
41
|
"default": [],
|
|
42
42
|
"oneOf": [
|
|
43
43
|
{
|
|
44
44
|
"type": "array",
|
|
45
|
-
"description": "
|
|
45
|
+
"description": "A list of hosts that the development server will respond to.",
|
|
46
46
|
"items": {
|
|
47
47
|
"type": "string"
|
|
48
48
|
}
|
|
@@ -530,6 +530,7 @@ function updateResultRecord(outputPath, file, normalizePath, htmlIndexPath, gene
|
|
|
530
530
|
}
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
|
+
// eslint-disable-next-line max-lines-per-function
|
|
533
534
|
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, zoneless, componentStyles, templateUpdates, prebundleLoaderExtensions, define, extensionMiddleware, indexHtmlTransformer, thirdPartySourcemaps = false) {
|
|
534
535
|
const proxy = await (0, utils_2.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig);
|
|
535
536
|
// dynamically import Vite for ESM compatibility
|
|
@@ -546,6 +547,14 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
546
547
|
ssrFiles = ['./main.server.mjs', './server.mjs'];
|
|
547
548
|
break;
|
|
548
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Required when using `externalDependencies` to prevent Vite load errors.
|
|
552
|
+
*
|
|
553
|
+
* @note Can be removed if Vite introduces native support for externals.
|
|
554
|
+
* @note Vite misresolves browser modules in SSR when accessing URLs with multiple segments
|
|
555
|
+
* (e.g., 'foo/bar'), as they are not correctly re-based from the base href.
|
|
556
|
+
*/
|
|
557
|
+
const preTransformRequests = externalMetadata.explicitBrowser.length === 0 && ssrMode === plugins_1.ServerSsrMode.NoSsr;
|
|
549
558
|
const cacheDir = (0, node_path_1.join)(serverOptions.cacheOptions.path, serverOptions.buildTarget.project, 'vite');
|
|
550
559
|
const configuration = {
|
|
551
560
|
configFile: false,
|
|
@@ -575,14 +584,10 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
575
584
|
preserveSymlinks,
|
|
576
585
|
},
|
|
577
586
|
dev: {
|
|
578
|
-
|
|
579
|
-
// NOTE: If Vite adds direct support for externals, this can be removed.
|
|
580
|
-
// NOTE: Vite breaks the resolution of browser modules in SSR
|
|
581
|
-
// when accessing a url with two or more segments (e.g., 'foo/bar'),
|
|
582
|
-
// as they are not re-based from the base href.
|
|
583
|
-
preTransformRequests: externalMetadata.explicitBrowser.length === 0 && ssrMode === plugins_1.ServerSsrMode.NoSsr,
|
|
587
|
+
preTransformRequests,
|
|
584
588
|
},
|
|
585
589
|
server: {
|
|
590
|
+
preTransformRequests,
|
|
586
591
|
warmup: {
|
|
587
592
|
ssrFiles,
|
|
588
593
|
},
|
|
@@ -603,6 +608,9 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
603
608
|
? (proxy ?? {})
|
|
604
609
|
: proxy,
|
|
605
610
|
cors: {
|
|
611
|
+
// This will add the header `Access-Control-Allow-Origin: http://example.com`,
|
|
612
|
+
// where `http://example.com` is the requesting origin.
|
|
613
|
+
origin: true,
|
|
606
614
|
// Allow preflight requests to be proxied.
|
|
607
615
|
preflightContinue: true,
|
|
608
616
|
},
|
|
@@ -660,7 +668,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
660
668
|
outputFiles,
|
|
661
669
|
templateUpdates,
|
|
662
670
|
external: externalMetadata.explicitBrowser,
|
|
663
|
-
|
|
671
|
+
disableViteTransport: !serverOptions.liveReload,
|
|
664
672
|
}),
|
|
665
673
|
],
|
|
666
674
|
// Browser only optimizeDeps. (This does not run for SSR dependencies).
|
|
@@ -13,6 +13,7 @@ export interface I18nInlinerOptions {
|
|
|
13
13
|
missingTranslation: 'error' | 'warning' | 'ignore';
|
|
14
14
|
outputFiles: BuildOutputFile[];
|
|
15
15
|
shouldOptimize?: boolean;
|
|
16
|
+
persistentCachePath?: string;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* A class that performs i18n translation inlining of JavaScript code.
|
|
@@ -22,6 +23,7 @@ export interface I18nInlinerOptions {
|
|
|
22
23
|
*/
|
|
23
24
|
export declare class I18nInliner {
|
|
24
25
|
#private;
|
|
26
|
+
private readonly options;
|
|
25
27
|
constructor(options: I18nInlinerOptions, maxThreads?: number);
|
|
26
28
|
/**
|
|
27
29
|
* Performs inlining of translations for the provided locale and translations. The files that
|
|
@@ -41,4 +43,11 @@ export declare class I18nInliner {
|
|
|
41
43
|
* @returns A void promise that resolves when closing is complete.
|
|
42
44
|
*/
|
|
43
45
|
close(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Initializes the cache for storing translated bundles.
|
|
48
|
+
* If the cache is already initialized, it does nothing.
|
|
49
|
+
*
|
|
50
|
+
* @returns A promise that resolves once the cache initialization process is complete.
|
|
51
|
+
*/
|
|
52
|
+
private initCache;
|
|
44
53
|
}
|
|
@@ -6,12 +6,47 @@
|
|
|
6
6
|
* Use of this source code is governed by an MIT-style license that can be
|
|
7
7
|
* found in the LICENSE file at https://angular.dev/license
|
|
8
8
|
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
9
42
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
43
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
44
|
};
|
|
12
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
46
|
exports.I18nInliner = void 0;
|
|
14
47
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
48
|
+
const node_crypto_1 = require("node:crypto");
|
|
49
|
+
const node_path_1 = require("node:path");
|
|
15
50
|
const worker_pool_1 = require("../../utils/worker-pool");
|
|
16
51
|
const bundler_context_1 = require("./bundler-context");
|
|
17
52
|
const utils_1 = require("./utils");
|
|
@@ -27,37 +62,37 @@ const LOCALIZE_KEYWORD = '$localize';
|
|
|
27
62
|
* localize function (`$localize`).
|
|
28
63
|
*/
|
|
29
64
|
class I18nInliner {
|
|
65
|
+
options;
|
|
66
|
+
#cacheInitFailed = false;
|
|
30
67
|
#workerPool;
|
|
68
|
+
#cache;
|
|
31
69
|
#localizeFiles;
|
|
32
70
|
#unmodifiedFiles;
|
|
33
|
-
#fileToType = new Map();
|
|
34
71
|
constructor(options, maxThreads) {
|
|
72
|
+
this.options = options;
|
|
35
73
|
this.#unmodifiedFiles = [];
|
|
74
|
+
const { outputFiles, shouldOptimize, missingTranslation } = options;
|
|
36
75
|
const files = new Map();
|
|
37
76
|
const pendingMaps = [];
|
|
38
|
-
for (const file of
|
|
77
|
+
for (const file of outputFiles) {
|
|
39
78
|
if (file.type === bundler_context_1.BuildOutputFileType.Root || file.type === bundler_context_1.BuildOutputFileType.ServerRoot) {
|
|
40
79
|
// Skip also the server entry-point.
|
|
41
80
|
// Skip stats and similar files.
|
|
42
81
|
continue;
|
|
43
82
|
}
|
|
44
|
-
|
|
45
|
-
if (
|
|
83
|
+
const fileExtension = (0, node_path_1.extname)(file.path);
|
|
84
|
+
if (fileExtension === '.js' || fileExtension === '.mjs') {
|
|
46
85
|
// Check if localizations are present
|
|
47
86
|
const contentBuffer = Buffer.isBuffer(file.contents)
|
|
48
87
|
? file.contents
|
|
49
88
|
: Buffer.from(file.contents.buffer, file.contents.byteOffset, file.contents.byteLength);
|
|
50
89
|
const hasLocalize = contentBuffer.includes(LOCALIZE_KEYWORD);
|
|
51
90
|
if (hasLocalize) {
|
|
52
|
-
|
|
53
|
-
// without copying until the data is actually used within a Worker. This is useful here
|
|
54
|
-
// since each file may not actually be processed in each Worker and the Blob avoids
|
|
55
|
-
// unneeded repeat copying of potentially large JavaScript files.
|
|
56
|
-
files.set(file.path, new Blob([file.contents]));
|
|
91
|
+
files.set(file.path, file);
|
|
57
92
|
continue;
|
|
58
93
|
}
|
|
59
94
|
}
|
|
60
|
-
else if (
|
|
95
|
+
else if (fileExtension === '.map') {
|
|
61
96
|
// The related JS file may not have been checked yet. To ensure that map files are not
|
|
62
97
|
// missed, store any pending map files and check them after all output files.
|
|
63
98
|
pendingMaps.push(file);
|
|
@@ -68,7 +103,7 @@ class I18nInliner {
|
|
|
68
103
|
// Check if any pending map files should be processed by checking if the parent JS file is present
|
|
69
104
|
for (const file of pendingMaps) {
|
|
70
105
|
if (files.has(file.path.slice(0, -4))) {
|
|
71
|
-
files.set(file.path,
|
|
106
|
+
files.set(file.path, file);
|
|
72
107
|
}
|
|
73
108
|
else {
|
|
74
109
|
this.#unmodifiedFiles.push(file);
|
|
@@ -80,9 +115,13 @@ class I18nInliner {
|
|
|
80
115
|
maxThreads,
|
|
81
116
|
// Extract options to ensure only the named options are serialized and sent to the worker
|
|
82
117
|
workerData: {
|
|
83
|
-
missingTranslation
|
|
84
|
-
shouldOptimize
|
|
85
|
-
|
|
118
|
+
missingTranslation,
|
|
119
|
+
shouldOptimize,
|
|
120
|
+
// A Blob is an immutable data structure that allows sharing the data between workers
|
|
121
|
+
// without copying until the data is actually used within a Worker. This is useful here
|
|
122
|
+
// since each file may not actually be processed in each Worker and the Blob avoids
|
|
123
|
+
// unneeded repeat copying of potentially large JavaScript files.
|
|
124
|
+
files: new Map(Array.from(files, ([name, file]) => [name, new Blob([file.contents])])),
|
|
86
125
|
},
|
|
87
126
|
});
|
|
88
127
|
}
|
|
@@ -95,18 +134,41 @@ class I18nInliner {
|
|
|
95
134
|
* @returns A promise that resolves to an array of OutputFiles representing a translated result.
|
|
96
135
|
*/
|
|
97
136
|
async inlineForLocale(locale, translation) {
|
|
137
|
+
await this.initCache();
|
|
138
|
+
const { shouldOptimize, missingTranslation } = this.options;
|
|
98
139
|
// Request inlining for each file that contains localize calls
|
|
99
140
|
const requests = [];
|
|
100
|
-
|
|
141
|
+
let fileCacheKeyBase;
|
|
142
|
+
for (const [filename, file] of this.#localizeFiles) {
|
|
143
|
+
let cacheKey;
|
|
101
144
|
if (filename.endsWith('.map')) {
|
|
102
145
|
continue;
|
|
103
146
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
locale,
|
|
107
|
-
|
|
147
|
+
let cacheResultPromise = Promise.resolve(null);
|
|
148
|
+
if (this.#cache) {
|
|
149
|
+
fileCacheKeyBase ??= Buffer.from(JSON.stringify({ locale, translation, missingTranslation, shouldOptimize }), 'utf-8');
|
|
150
|
+
// NOTE: If additional options are added, this may need to be updated.
|
|
151
|
+
// TODO: Consider xxhash or similar instead of SHA256
|
|
152
|
+
cacheKey = (0, node_crypto_1.createHash)('sha256')
|
|
153
|
+
.update(file.hash)
|
|
154
|
+
.update(filename)
|
|
155
|
+
.update(fileCacheKeyBase)
|
|
156
|
+
.digest('hex');
|
|
157
|
+
// Failure to get the value should not fail the transform
|
|
158
|
+
cacheResultPromise = this.#cache.get(cacheKey).catch(() => null);
|
|
159
|
+
}
|
|
160
|
+
const fileResult = cacheResultPromise.then(async (cachedResult) => {
|
|
161
|
+
if (cachedResult) {
|
|
162
|
+
return cachedResult;
|
|
163
|
+
}
|
|
164
|
+
const result = await this.#workerPool.run({ filename, locale, translation });
|
|
165
|
+
if (this.#cache && cacheKey) {
|
|
166
|
+
// Failure to set the value should not fail the transform
|
|
167
|
+
await this.#cache.set(cacheKey, result).catch(() => { });
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
108
170
|
});
|
|
109
|
-
requests.push(
|
|
171
|
+
requests.push(fileResult);
|
|
110
172
|
}
|
|
111
173
|
// Wait for all file requests to complete
|
|
112
174
|
const rawResults = await Promise.all(requests);
|
|
@@ -115,7 +177,7 @@ class I18nInliner {
|
|
|
115
177
|
const warnings = [];
|
|
116
178
|
const outputFiles = [
|
|
117
179
|
...rawResults.flatMap(({ file, code, map, messages }) => {
|
|
118
|
-
const type = this.#
|
|
180
|
+
const type = this.#localizeFiles.get(file)?.type;
|
|
119
181
|
(0, node_assert_1.default)(type !== undefined, 'localized file should always have a type' + file);
|
|
120
182
|
const resultFiles = [(0, utils_1.createOutputFile)(file, code, type)];
|
|
121
183
|
if (map) {
|
|
@@ -146,5 +208,32 @@ class I18nInliner {
|
|
|
146
208
|
close() {
|
|
147
209
|
return this.#workerPool.destroy();
|
|
148
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Initializes the cache for storing translated bundles.
|
|
213
|
+
* If the cache is already initialized, it does nothing.
|
|
214
|
+
*
|
|
215
|
+
* @returns A promise that resolves once the cache initialization process is complete.
|
|
216
|
+
*/
|
|
217
|
+
async initCache() {
|
|
218
|
+
if (this.#cache || this.#cacheInitFailed) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const { persistentCachePath } = this.options;
|
|
222
|
+
// Webcontainers currently do not support this persistent cache store.
|
|
223
|
+
if (!persistentCachePath || process.versions.webcontainer) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
// Initialize a persistent cache for i18n transformations.
|
|
227
|
+
try {
|
|
228
|
+
const { LmbdCacheStore } = await Promise.resolve().then(() => __importStar(require('./lmdb-cache-store')));
|
|
229
|
+
this.#cache = new LmbdCacheStore((0, node_path_1.join)(persistentCachePath, 'angular-i18n.db'));
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
this.#cacheInitFailed = true;
|
|
233
|
+
// eslint-disable-next-line no-console
|
|
234
|
+
console.warn('Unable to initialize JavaScript cache storage.\n' +
|
|
235
|
+
'This will not affect the build output content but may result in slower builds.');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
149
238
|
}
|
|
150
239
|
exports.I18nInliner = I18nInliner;
|
|
@@ -35,6 +35,9 @@ const manifest_1 = require("../../utils/server-rendering/manifest");
|
|
|
35
35
|
const stats_table_1 = require("../../utils/stats-table");
|
|
36
36
|
const bundler_context_1 = require("./bundler-context");
|
|
37
37
|
function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, changedFiles, estimatedTransferSizes, ssrOutputEnabled, verbose) {
|
|
38
|
+
// Remove the i18n subpath in case the build is using i18n.
|
|
39
|
+
// en-US/main.js -> main.js
|
|
40
|
+
const normalizedChangedFiles = new Set([...(changedFiles ?? [])].map((f) => (0, node_path_1.basename)(f)));
|
|
38
41
|
const browserStats = [];
|
|
39
42
|
const serverStats = [];
|
|
40
43
|
let unchangedCount = 0;
|
|
@@ -45,7 +48,7 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
|
|
|
45
48
|
continue;
|
|
46
49
|
}
|
|
47
50
|
// Show only changed files if a changed list is provided
|
|
48
|
-
if (
|
|
51
|
+
if (normalizedChangedFiles.size && !normalizedChangedFiles.has(file)) {
|
|
49
52
|
++unchangedCount;
|
|
50
53
|
continue;
|
|
51
54
|
}
|
|
@@ -12,7 +12,7 @@ interface AngularMemoryPluginOptions {
|
|
|
12
12
|
outputFiles: AngularMemoryOutputFiles;
|
|
13
13
|
templateUpdates?: ReadonlyMap<string, string>;
|
|
14
14
|
external?: string[];
|
|
15
|
-
|
|
15
|
+
disableViteTransport?: boolean;
|
|
16
16
|
}
|
|
17
17
|
export declare function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): Promise<Plugin>;
|
|
18
18
|
export {};
|
|
@@ -73,7 +73,7 @@ async function createAngularMemoryPlugin(options) {
|
|
|
73
73
|
const codeContents = outputFiles.get(relativeFile)?.contents;
|
|
74
74
|
if (codeContents === undefined) {
|
|
75
75
|
if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) {
|
|
76
|
-
return
|
|
76
|
+
return loadViteClientCode(file, options.disableViteTransport);
|
|
77
77
|
}
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
@@ -96,9 +96,9 @@ async function createAngularMemoryPlugin(options) {
|
|
|
96
96
|
* @param file The absolute path to the Vite client code.
|
|
97
97
|
* @returns
|
|
98
98
|
*/
|
|
99
|
-
async function loadViteClientCode(file) {
|
|
99
|
+
async function loadViteClientCode(file, disableViteTransport = false) {
|
|
100
100
|
const originalContents = await (0, promises_1.readFile)(file, 'utf-8');
|
|
101
|
-
|
|
101
|
+
let updatedContents = originalContents.replace(`"You can also disable this overlay by setting ",
|
|
102
102
|
h("code", { part: "config-option-name" }, "server.hmr.overlay"),
|
|
103
103
|
" to ",
|
|
104
104
|
h("code", { part: "config-option-value" }, "false"),
|
|
@@ -106,5 +106,11 @@ async function loadViteClientCode(file) {
|
|
|
106
106
|
h("code", { part: "config-file-name" }, hmrConfigName),
|
|
107
107
|
"."`, '');
|
|
108
108
|
(0, node_assert_1.default)(originalContents !== updatedContents, 'Failed to update Vite client error overlay text.');
|
|
109
|
+
if (disableViteTransport) {
|
|
110
|
+
const previousUpdatedContents = updatedContents;
|
|
111
|
+
updatedContents = updatedContents.replace('transport.connect(handleMessage)', '');
|
|
112
|
+
(0, node_assert_1.default)(previousUpdatedContents !== updatedContents, 'Failed to update Vite client WebSocket disable.');
|
|
113
|
+
updatedContents = updatedContents.replace('console.debug("[vite] connecting...")', '');
|
|
114
|
+
}
|
|
109
115
|
return updatedContents;
|
|
110
116
|
}
|
|
@@ -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.1.
|
|
13
|
+
const VERSION = '19.1.7';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -11,6 +11,7 @@ exports.SERVER_APP_ENGINE_MANIFEST_FILENAME = exports.SERVER_APP_MANIFEST_FILENA
|
|
|
11
11
|
exports.generateAngularServerAppEngineManifest = generateAngularServerAppEngineManifest;
|
|
12
12
|
exports.generateAngularServerAppManifest = generateAngularServerAppManifest;
|
|
13
13
|
const node_path_1 = require("node:path");
|
|
14
|
+
const node_vm_1 = require("node:vm");
|
|
14
15
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
15
16
|
const utils_1 = require("../../tools/esbuild/utils");
|
|
16
17
|
const environment_options_1 = require("../environment-options");
|
|
@@ -115,9 +116,13 @@ function generateAngularServerAppManifest(additionalHtmlOutputFiles, outputFiles
|
|
|
115
116
|
const extension = (0, node_path_1.extname)(file.path);
|
|
116
117
|
if (extension === '.html' || (inlineCriticalCss && extension === '.css')) {
|
|
117
118
|
const jsChunkFilePath = `assets-chunks/${file.path.replace(/[./]/g, '_')}.mjs`;
|
|
118
|
-
|
|
119
|
+
const escapedContent = escapeUnsafeChars(file.text);
|
|
120
|
+
serverAssetsChunks.push((0, utils_1.createOutputFile)(jsChunkFilePath, `export default \`${escapedContent}\`;`, bundler_context_1.BuildOutputFileType.ServerApplication));
|
|
121
|
+
// This is needed because JavaScript engines script parser convert `\r\n` to `\n` in template literals,
|
|
122
|
+
// which can result in an incorrect byte length.
|
|
123
|
+
const size = (0, node_vm_1.runInThisContext)(`new TextEncoder().encode(\`${escapedContent}\`).byteLength`);
|
|
119
124
|
serverAssets[file.path] =
|
|
120
|
-
`{size: ${
|
|
125
|
+
`{size: ${size}, hash: '${file.hash}', text: () => import('./${jsChunkFilePath}').then(m => m.default)}`;
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
// When routes have been extracted, mappings are no longer needed, as preloads will be included in the metadata.
|
|
@@ -128,11 +128,11 @@ async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxTh
|
|
|
128
128
|
try {
|
|
129
129
|
const renderingPromises = [];
|
|
130
130
|
const appShellRouteWithLeadingSlash = appShellRoute && addLeadingSlash(appShellRoute);
|
|
131
|
-
const
|
|
131
|
+
const baseHrefPathnameWithLeadingSlash = new URL(baseHref, 'http://localhost').pathname;
|
|
132
132
|
for (const { route, redirectTo } of serializableRouteTreeNode) {
|
|
133
133
|
// Remove the base href from the file output path.
|
|
134
|
-
const routeWithoutBaseHref = addTrailingSlash(route).startsWith(
|
|
135
|
-
? addLeadingSlash(route.slice(
|
|
134
|
+
const routeWithoutBaseHref = addTrailingSlash(route).startsWith(baseHrefPathnameWithLeadingSlash)
|
|
135
|
+
? addLeadingSlash(route.slice(baseHrefPathnameWithLeadingSlash.length))
|
|
136
136
|
: route;
|
|
137
137
|
const outPath = node_path_1.posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html');
|
|
138
138
|
if (typeof redirectTo === 'string') {
|