@angular-devkit/build-angular 17.1.0-next.2 → 17.1.0-rc.0
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/builders.json +5 -0
- package/package.json +24 -20
- package/src/builders/app-shell/index.js +7 -0
- package/src/builders/application/build-action.d.ts +4 -3
- package/src/builders/application/build-action.js +8 -5
- package/src/builders/application/execute-build.js +10 -4
- package/src/builders/application/index.d.ts +20 -10
- package/src/builders/application/index.js +38 -26
- package/src/builders/application/options.d.ts +11 -3
- package/src/builders/application/options.js +42 -30
- package/src/builders/application/schema.d.ts +32 -2
- package/src/builders/application/schema.json +40 -2
- package/src/builders/browser-esbuild/index.js +8 -4
- package/src/builders/dev-server/vite-server.js +7 -13
- package/src/builders/jest/index.js +2 -2
- package/src/builders/prerender/index.js +7 -0
- package/src/builders/ssr-dev-server/index.js +17 -31
- package/src/builders/web-test-runner/builder-status-warnings.d.ts +11 -0
- package/src/builders/web-test-runner/builder-status-warnings.js +46 -0
- package/src/builders/web-test-runner/index.d.ts +10 -0
- package/src/builders/web-test-runner/index.js +151 -0
- package/src/builders/web-test-runner/jasmine_runner.js +88 -0
- package/src/builders/web-test-runner/options.d.ts +24 -0
- package/src/builders/web-test-runner/options.js +26 -0
- package/src/builders/web-test-runner/schema.d.ts +188 -0
- package/src/builders/web-test-runner/schema.js +15 -0
- package/src/builders/web-test-runner/schema.json +291 -0
- package/src/builders/web-test-runner/test_page.html +40 -0
- package/src/tools/esbuild/angular/angular-host.js +1 -1
- package/src/tools/esbuild/angular/compiler-plugin.js +10 -26
- package/src/tools/esbuild/angular/component-stylesheets.d.ts +3 -6
- package/src/tools/esbuild/angular/component-stylesheets.js +46 -60
- package/src/tools/esbuild/angular/jit-plugin-callbacks.js +2 -2
- package/src/tools/esbuild/bundler-context.d.ts +1 -1
- package/src/tools/esbuild/bundler-context.js +18 -2
- package/src/tools/esbuild/cache.d.ts +88 -0
- package/src/tools/esbuild/cache.js +92 -0
- package/src/tools/esbuild/compiler-plugin-options.js +1 -1
- package/src/tools/esbuild/index-html-generator.js +3 -1
- package/src/tools/esbuild/javascript-transformer-worker.d.ts +2 -2
- package/src/tools/esbuild/javascript-transformer-worker.js +12 -5
- package/src/tools/esbuild/javascript-transformer.d.ts +3 -1
- package/src/tools/esbuild/javascript-transformer.js +42 -17
- package/src/tools/esbuild/stylesheets/bundle-options.d.ts +1 -1
- package/src/tools/esbuild/stylesheets/sass-language.js +3 -12
- package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +9 -1
- package/src/tools/esbuild/utils.d.ts +2 -2
- package/src/tools/esbuild/utils.js +61 -74
- package/src/tools/sass/lexer.d.ts +0 -11
- package/src/tools/sass/lexer.js +1 -87
- package/src/utils/index-file/index-html-generator.js +15 -28
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +1 -0
- package/src/{builders/dev-server → utils}/load-proxy-config.js +2 -2
- package/src/{builders/jest → utils}/test-files.d.ts +1 -2
- package/src/{builders/jest → utils}/test-files.js +3 -3
- /package/src/{builders/dev-server → utils}/load-proxy-config.d.ts +0 -0
|
@@ -14,17 +14,8 @@ exports.ComponentStylesheetBundler = void 0;
|
|
|
14
14
|
const node_crypto_1 = require("node:crypto");
|
|
15
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
16
|
const bundler_context_1 = require("../bundler-context");
|
|
17
|
+
const cache_1 = require("../cache");
|
|
17
18
|
const bundle_options_1 = require("../stylesheets/bundle-options");
|
|
18
|
-
class BundlerContextCache extends Map {
|
|
19
|
-
getOrCreate(key, creator) {
|
|
20
|
-
let value = this.get(key);
|
|
21
|
-
if (value === undefined) {
|
|
22
|
-
value = creator();
|
|
23
|
-
this.set(key, value);
|
|
24
|
-
}
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
19
|
/**
|
|
29
20
|
* Bundles component stylesheets. A stylesheet can be either an inline stylesheet that
|
|
30
21
|
* is contained within the Component's metadata definition or an external file referenced
|
|
@@ -33,8 +24,8 @@ class BundlerContextCache extends Map {
|
|
|
33
24
|
class ComponentStylesheetBundler {
|
|
34
25
|
options;
|
|
35
26
|
incremental;
|
|
36
|
-
#fileContexts = new
|
|
37
|
-
#inlineContexts = new
|
|
27
|
+
#fileContexts = new cache_1.MemoryCache();
|
|
28
|
+
#inlineContexts = new cache_1.MemoryCache();
|
|
38
29
|
/**
|
|
39
30
|
*
|
|
40
31
|
* @param options An object containing the stylesheet bundling options.
|
|
@@ -45,14 +36,14 @@ class ComponentStylesheetBundler {
|
|
|
45
36
|
this.incremental = incremental;
|
|
46
37
|
}
|
|
47
38
|
async bundleFile(entry) {
|
|
48
|
-
const bundlerContext = this.#fileContexts.getOrCreate(entry, () => {
|
|
39
|
+
const bundlerContext = await this.#fileContexts.getOrCreate(entry, () => {
|
|
49
40
|
return new bundler_context_1.BundlerContext(this.options.workspaceRoot, this.incremental, (loadCache) => {
|
|
50
41
|
const buildOptions = (0, bundle_options_1.createStylesheetBundleOptions)(this.options, loadCache);
|
|
51
42
|
buildOptions.entryPoints = [entry];
|
|
52
43
|
return buildOptions;
|
|
53
44
|
});
|
|
54
45
|
});
|
|
55
|
-
return extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
|
|
46
|
+
return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
|
|
56
47
|
}
|
|
57
48
|
async bundleInline(data, filename, language) {
|
|
58
49
|
// Use a hash of the inline stylesheet content to ensure a consistent identifier. External stylesheets will resolve
|
|
@@ -60,7 +51,7 @@ class ComponentStylesheetBundler {
|
|
|
60
51
|
// TODO: Consider xxhash instead for hashing
|
|
61
52
|
const id = (0, node_crypto_1.createHash)('sha256').update(data).digest('hex');
|
|
62
53
|
const entry = [language, id, filename].join(';');
|
|
63
|
-
const bundlerContext = this.#inlineContexts.getOrCreate(entry, () => {
|
|
54
|
+
const bundlerContext = await this.#inlineContexts.getOrCreate(entry, () => {
|
|
64
55
|
const namespace = 'angular:styles/component';
|
|
65
56
|
return new bundler_context_1.BundlerContext(this.options.workspaceRoot, this.incremental, (loadCache) => {
|
|
66
57
|
const buildOptions = (0, bundle_options_1.createStylesheetBundleOptions)(this.options, loadCache, {
|
|
@@ -92,7 +83,7 @@ class ComponentStylesheetBundler {
|
|
|
92
83
|
});
|
|
93
84
|
});
|
|
94
85
|
// Extract the result of the bundling from the output files
|
|
95
|
-
return extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
|
|
86
|
+
return this.extractResult(await bundlerContext.bundle(), bundlerContext.watchFiles);
|
|
96
87
|
}
|
|
97
88
|
invalidate(files) {
|
|
98
89
|
if (!this.incremental) {
|
|
@@ -112,51 +103,46 @@ class ComponentStylesheetBundler {
|
|
|
112
103
|
this.#inlineContexts.clear();
|
|
113
104
|
await Promise.allSettled(contexts.map((context) => context.dispose()));
|
|
114
105
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
throw new Error(`Unexpected non CSS/Media file "${filename}" outputted during component stylesheet processing.`);
|
|
106
|
+
extractResult(result, referencedFiles) {
|
|
107
|
+
let contents = '';
|
|
108
|
+
let metafile;
|
|
109
|
+
const outputFiles = [];
|
|
110
|
+
if (!result.errors) {
|
|
111
|
+
for (const outputFile of result.outputFiles) {
|
|
112
|
+
const filename = node_path_1.default.basename(outputFile.path);
|
|
113
|
+
// Needed for Bazel as otherwise the files will not be written in the correct place.
|
|
114
|
+
outputFile.path = node_path_1.default.join(this.options.workspaceRoot, outputFile.path);
|
|
115
|
+
if (outputFile.type === bundler_context_1.BuildOutputFileType.Media) {
|
|
116
|
+
// The output files could also contain resources (images/fonts/etc.) that were referenced
|
|
117
|
+
outputFiles.push(outputFile);
|
|
118
|
+
}
|
|
119
|
+
else if (filename.endsWith('.css')) {
|
|
120
|
+
contents = outputFile.text;
|
|
121
|
+
}
|
|
122
|
+
else if (filename.endsWith('.css.map')) {
|
|
123
|
+
outputFiles.push(outputFile);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw new Error(`Unexpected non CSS/Media file "${filename}" outputted during component stylesheet processing.`);
|
|
127
|
+
}
|
|
138
128
|
}
|
|
129
|
+
metafile = result.metafile;
|
|
130
|
+
// Remove entryPoint fields from outputs to prevent the internal component styles from being
|
|
131
|
+
// treated as initial files. Also mark the entry as a component resource for stat reporting.
|
|
132
|
+
Object.values(metafile.outputs).forEach((output) => {
|
|
133
|
+
delete output.entryPoint;
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
135
|
+
output['ng-component'] = true;
|
|
136
|
+
});
|
|
139
137
|
}
|
|
138
|
+
return {
|
|
139
|
+
errors: result.errors,
|
|
140
|
+
warnings: result.warnings,
|
|
141
|
+
contents,
|
|
142
|
+
outputFiles,
|
|
143
|
+
metafile,
|
|
144
|
+
referencedFiles,
|
|
145
|
+
};
|
|
140
146
|
}
|
|
141
|
-
let metafile;
|
|
142
|
-
if (!result.errors) {
|
|
143
|
-
metafile = result.metafile;
|
|
144
|
-
// Remove entryPoint fields from outputs to prevent the internal component styles from being
|
|
145
|
-
// treated as initial files. Also mark the entry as a component resource for stat reporting.
|
|
146
|
-
Object.values(metafile.outputs).forEach((output) => {
|
|
147
|
-
delete output.entryPoint;
|
|
148
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
149
|
-
output['ng-component'] = true;
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
errors: result.errors,
|
|
154
|
-
warnings: result.warnings,
|
|
155
|
-
contents,
|
|
156
|
-
map,
|
|
157
|
-
path: outputPath,
|
|
158
|
-
resourceFiles,
|
|
159
|
-
metafile,
|
|
160
|
-
referencedFiles,
|
|
161
|
-
};
|
|
162
147
|
}
|
|
148
|
+
exports.ComponentStylesheetBundler = ComponentStylesheetBundler;
|
|
@@ -92,8 +92,8 @@ function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles
|
|
|
92
92
|
else {
|
|
93
93
|
stylesheetResult = await stylesheetBundler.bundleInline(entry.contents, entry.path, inlineStyleLanguage);
|
|
94
94
|
}
|
|
95
|
-
const { contents,
|
|
96
|
-
additionalResultFiles.set(entry.path, { outputFiles
|
|
95
|
+
const { contents, outputFiles, errors, warnings, metafile, referencedFiles } = stylesheetResult;
|
|
96
|
+
additionalResultFiles.set(entry.path, { outputFiles, metafile });
|
|
97
97
|
return {
|
|
98
98
|
errors,
|
|
99
99
|
warnings,
|
|
@@ -20,6 +20,7 @@ export type BundleContextResult = {
|
|
|
20
20
|
server?: Set<string>;
|
|
21
21
|
browser?: Set<string>;
|
|
22
22
|
};
|
|
23
|
+
externalConfiguration?: string[];
|
|
23
24
|
};
|
|
24
25
|
export interface InitialFileRecord {
|
|
25
26
|
entrypoint: boolean;
|
|
@@ -35,7 +36,6 @@ export declare enum BuildOutputFileType {
|
|
|
35
36
|
}
|
|
36
37
|
export interface BuildOutputFile extends OutputFile {
|
|
37
38
|
type: BuildOutputFileType;
|
|
38
|
-
fullOutputPath: string;
|
|
39
39
|
clone: () => BuildOutputFile;
|
|
40
40
|
}
|
|
41
41
|
export type BundlerOptionsFactory<T extends BuildOptions = BuildOptions> = (loadCache: LoadResultCache | undefined) => T;
|
|
@@ -6,9 +6,13 @@
|
|
|
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.io/license
|
|
8
8
|
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
9
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
13
|
exports.BundlerContext = exports.BuildOutputFileType = void 0;
|
|
11
14
|
const esbuild_1 = require("esbuild");
|
|
15
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
12
16
|
const node_path_1 = require("node:path");
|
|
13
17
|
const load_result_cache_1 = require("./load-result-cache");
|
|
14
18
|
const utils_1 = require("./utils");
|
|
@@ -71,6 +75,7 @@ class BundlerContext {
|
|
|
71
75
|
const externalImportsBrowser = new Set();
|
|
72
76
|
const externalImportsServer = new Set();
|
|
73
77
|
const outputFiles = [];
|
|
78
|
+
let externalConfiguration;
|
|
74
79
|
for (const result of individualResults) {
|
|
75
80
|
warnings.push(...result.warnings);
|
|
76
81
|
if (result.errors) {
|
|
@@ -87,6 +92,12 @@ class BundlerContext {
|
|
|
87
92
|
outputFiles.push(...result.outputFiles);
|
|
88
93
|
result.externalImports.browser?.forEach((value) => externalImportsBrowser.add(value));
|
|
89
94
|
result.externalImports.server?.forEach((value) => externalImportsServer.add(value));
|
|
95
|
+
if (result.externalConfiguration) {
|
|
96
|
+
externalConfiguration ??= new Set();
|
|
97
|
+
for (const value of result.externalConfiguration) {
|
|
98
|
+
externalConfiguration.add(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
90
101
|
}
|
|
91
102
|
if (errors !== undefined) {
|
|
92
103
|
return { errors, warnings };
|
|
@@ -101,6 +112,7 @@ class BundlerContext {
|
|
|
101
112
|
browser: externalImportsBrowser,
|
|
102
113
|
server: externalImportsServer,
|
|
103
114
|
},
|
|
115
|
+
externalConfiguration: externalConfiguration ? [...externalConfiguration] : undefined,
|
|
104
116
|
};
|
|
105
117
|
}
|
|
106
118
|
/**
|
|
@@ -250,10 +262,13 @@ class BundlerContext {
|
|
|
250
262
|
externalImports.add(importData.path);
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
|
-
|
|
265
|
+
(0, node_assert_1.default)(this.#esbuildOptions, 'esbuild options cannot be undefined.');
|
|
266
|
+
const { platform, assetNames = '' } = this.#esbuildOptions;
|
|
267
|
+
const platformIsServer = platform === 'node';
|
|
268
|
+
const mediaDirname = (0, node_path_1.dirname)(assetNames);
|
|
254
269
|
const outputFiles = result.outputFiles.map((file) => {
|
|
255
270
|
let fileType;
|
|
256
|
-
if ((0, node_path_1.dirname)(file.path) ===
|
|
271
|
+
if ((0, node_path_1.dirname)(file.path) === mediaDirname) {
|
|
257
272
|
fileType = BuildOutputFileType.Media;
|
|
258
273
|
}
|
|
259
274
|
else {
|
|
@@ -269,6 +284,7 @@ class BundlerContext {
|
|
|
269
284
|
externalImports: {
|
|
270
285
|
[platformIsServer ? 'server' : 'browser']: externalImports,
|
|
271
286
|
},
|
|
287
|
+
externalConfiguration: this.#esbuildOptions.external,
|
|
272
288
|
errors: undefined,
|
|
273
289
|
};
|
|
274
290
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
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.io/license
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @fileoverview
|
|
10
|
+
* Provides infrastructure for common caching functionality within the build system.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* A backing data store for one or more Cache instances.
|
|
14
|
+
* The interface is intentionally designed to support using a JavaScript
|
|
15
|
+
* Map instance as a potential cache store.
|
|
16
|
+
*/
|
|
17
|
+
export interface CacheStore<V> {
|
|
18
|
+
/**
|
|
19
|
+
* Returns the specified value from the cache store or `undefined` if not found.
|
|
20
|
+
* @param key The key to retrieve from the store.
|
|
21
|
+
*/
|
|
22
|
+
get(key: string): V | undefined | Promise<V | undefined>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns whether the provided key is present in the cache store.
|
|
25
|
+
* @param key The key to check from the store.
|
|
26
|
+
*/
|
|
27
|
+
has(key: string): boolean | Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Adds a new value to the cache store if the key is not present.
|
|
30
|
+
* Updates the value for the key if already present.
|
|
31
|
+
* @param key The key to associate with the value in the cache store.
|
|
32
|
+
* @param value The value to add to the cache store.
|
|
33
|
+
*/
|
|
34
|
+
set(key: string, value: V): this | Promise<this>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A cache object that allows accessing and storing key/value pairs in
|
|
38
|
+
* an underlying CacheStore. This class is the primary method for consumers
|
|
39
|
+
* to use a cache.
|
|
40
|
+
*/
|
|
41
|
+
export declare class Cache<V, S extends CacheStore<V> = CacheStore<V>> {
|
|
42
|
+
protected readonly store: S;
|
|
43
|
+
readonly namespace?: string | undefined;
|
|
44
|
+
constructor(store: S, namespace?: string | undefined);
|
|
45
|
+
/**
|
|
46
|
+
* Prefixes a key with the cache namespace if present.
|
|
47
|
+
* @param key A key string to prefix.
|
|
48
|
+
* @returns A prefixed key if a namespace is present. Otherwise the provided key.
|
|
49
|
+
*/
|
|
50
|
+
protected withNamespace(key: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Gets the value associated with a provided key if available.
|
|
53
|
+
* Otherwise, creates a value using the factory creator function, puts the value
|
|
54
|
+
* in the cache, and returns the new value.
|
|
55
|
+
* @param key A key associated with the value.
|
|
56
|
+
* @param creator A factory function for the value if no value is present.
|
|
57
|
+
* @returns A value associated with the provided key.
|
|
58
|
+
*/
|
|
59
|
+
getOrCreate(key: string, creator: () => V | Promise<V>): Promise<V>;
|
|
60
|
+
/**
|
|
61
|
+
* Gets the value associated with a provided key if available.
|
|
62
|
+
* @param key A key associated with the value.
|
|
63
|
+
* @returns A value associated with the provided key if present. Otherwise, `undefined`.
|
|
64
|
+
*/
|
|
65
|
+
get(key: string): Promise<V | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Puts a value in the cache and associates it with the provided key.
|
|
68
|
+
* If the key is already present, the value is updated instead.
|
|
69
|
+
* @param key A key associated with the value.
|
|
70
|
+
* @param value A value to put in the cache.
|
|
71
|
+
*/
|
|
72
|
+
put(key: string, value: V): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A lightweight in-memory cache implementation based on a JavaScript Map object.
|
|
76
|
+
*/
|
|
77
|
+
export declare class MemoryCache<V> extends Cache<V, Map<string, V>> {
|
|
78
|
+
constructor();
|
|
79
|
+
/**
|
|
80
|
+
* Removes all entries from the cache instance.
|
|
81
|
+
*/
|
|
82
|
+
clear(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Provides all the values currently present in the cache instance.
|
|
85
|
+
* @returns An iterable of all values in the cache.
|
|
86
|
+
*/
|
|
87
|
+
values(): IterableIterator<V>;
|
|
88
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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.io/license
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.MemoryCache = exports.Cache = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* A cache object that allows accessing and storing key/value pairs in
|
|
13
|
+
* an underlying CacheStore. This class is the primary method for consumers
|
|
14
|
+
* to use a cache.
|
|
15
|
+
*/
|
|
16
|
+
class Cache {
|
|
17
|
+
store;
|
|
18
|
+
namespace;
|
|
19
|
+
constructor(store, namespace) {
|
|
20
|
+
this.store = store;
|
|
21
|
+
this.namespace = namespace;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Prefixes a key with the cache namespace if present.
|
|
25
|
+
* @param key A key string to prefix.
|
|
26
|
+
* @returns A prefixed key if a namespace is present. Otherwise the provided key.
|
|
27
|
+
*/
|
|
28
|
+
withNamespace(key) {
|
|
29
|
+
if (this.namespace) {
|
|
30
|
+
return `${this.namespace}:${key}`;
|
|
31
|
+
}
|
|
32
|
+
return key;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets the value associated with a provided key if available.
|
|
36
|
+
* Otherwise, creates a value using the factory creator function, puts the value
|
|
37
|
+
* in the cache, and returns the new value.
|
|
38
|
+
* @param key A key associated with the value.
|
|
39
|
+
* @param creator A factory function for the value if no value is present.
|
|
40
|
+
* @returns A value associated with the provided key.
|
|
41
|
+
*/
|
|
42
|
+
async getOrCreate(key, creator) {
|
|
43
|
+
const namespacedKey = this.withNamespace(key);
|
|
44
|
+
let value = await this.store.get(namespacedKey);
|
|
45
|
+
if (value === undefined) {
|
|
46
|
+
value = await creator();
|
|
47
|
+
await this.store.set(namespacedKey, value);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets the value associated with a provided key if available.
|
|
53
|
+
* @param key A key associated with the value.
|
|
54
|
+
* @returns A value associated with the provided key if present. Otherwise, `undefined`.
|
|
55
|
+
*/
|
|
56
|
+
async get(key) {
|
|
57
|
+
const value = await this.store.get(this.withNamespace(key));
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Puts a value in the cache and associates it with the provided key.
|
|
62
|
+
* If the key is already present, the value is updated instead.
|
|
63
|
+
* @param key A key associated with the value.
|
|
64
|
+
* @param value A value to put in the cache.
|
|
65
|
+
*/
|
|
66
|
+
async put(key, value) {
|
|
67
|
+
await this.store.set(this.withNamespace(key), value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.Cache = Cache;
|
|
71
|
+
/**
|
|
72
|
+
* A lightweight in-memory cache implementation based on a JavaScript Map object.
|
|
73
|
+
*/
|
|
74
|
+
class MemoryCache extends Cache {
|
|
75
|
+
constructor() {
|
|
76
|
+
super(new Map());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Removes all entries from the cache instance.
|
|
80
|
+
*/
|
|
81
|
+
clear() {
|
|
82
|
+
this.store.clear();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Provides all the values currently present in the cache instance.
|
|
86
|
+
* @returns An iterable of all values in the cache.
|
|
87
|
+
*/
|
|
88
|
+
values() {
|
|
89
|
+
return this.store.values();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.MemoryCache = MemoryCache;
|
|
@@ -32,7 +32,7 @@ function createCompilerPluginOptions(options, target, sourceFileCache) {
|
|
|
32
32
|
// Hidden component stylesheet sourcemaps are inaccessible which is effectively
|
|
33
33
|
// the same as being disabled. Disabling has the advantage of avoiding the overhead
|
|
34
34
|
// of sourcemap processing.
|
|
35
|
-
|
|
35
|
+
sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
|
|
36
36
|
outputNames,
|
|
37
37
|
includePaths: stylePreprocessorOptions?.includePaths,
|
|
38
38
|
externalDependencies,
|
|
@@ -45,7 +45,7 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
|
|
|
45
45
|
const hints = [];
|
|
46
46
|
const { indexHtmlOptions, externalPackages, optimizationOptions, crossOrigin, subresourceIntegrity, baseHref, } = buildOptions;
|
|
47
47
|
(0, node_assert_1.default)(indexHtmlOptions, 'indexHtmlOptions cannot be undefined.');
|
|
48
|
-
if (!externalPackages) {
|
|
48
|
+
if (!externalPackages && indexHtmlOptions.preloadInitial) {
|
|
49
49
|
for (const [key, value] of initialFiles) {
|
|
50
50
|
if (value.entrypoint) {
|
|
51
51
|
// Entry points are already referenced in the HTML
|
|
@@ -87,6 +87,7 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
|
|
|
87
87
|
},
|
|
88
88
|
crossOrigin: crossOrigin,
|
|
89
89
|
deployUrl: buildOptions.publicPath,
|
|
90
|
+
postTransform: indexHtmlOptions.transformer,
|
|
90
91
|
});
|
|
91
92
|
indexHtmlGenerator.readAsset = readAsset;
|
|
92
93
|
const transformResult = await indexHtmlGenerator.process({
|
|
@@ -110,6 +111,7 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
|
|
|
110
111
|
const { InlineCriticalCssProcessor } = await Promise.resolve().then(() => __importStar(require('../../utils/index-file/inline-critical-css')));
|
|
111
112
|
const inlineCriticalCssProcessor = new InlineCriticalCssProcessor({
|
|
112
113
|
minify: false, // CSS has already been minified during the build.
|
|
114
|
+
deployUrl: buildOptions.publicPath,
|
|
113
115
|
readAsset,
|
|
114
116
|
});
|
|
115
117
|
const { content, errors, warnings } = await inlineCriticalCssProcessor.process(contentWithoutCriticalCssInlined, {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
interface JavaScriptTransformRequest {
|
|
9
9
|
filename: string;
|
|
10
|
-
data: string;
|
|
10
|
+
data: string | Uint8Array;
|
|
11
11
|
sourcemap: boolean;
|
|
12
12
|
thirdPartySourcemaps: boolean;
|
|
13
13
|
advancedOptimizations: boolean;
|
|
@@ -15,5 +15,5 @@ interface JavaScriptTransformRequest {
|
|
|
15
15
|
sideEffects?: boolean;
|
|
16
16
|
jit: boolean;
|
|
17
17
|
}
|
|
18
|
-
export default function transformJavaScript(request: JavaScriptTransformRequest): Promise<
|
|
18
|
+
export default function transformJavaScript(request: JavaScriptTransformRequest): Promise<unknown>;
|
|
19
19
|
export {};
|
|
@@ -29,19 +29,26 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
29
29
|
__setModuleDefault(result, mod);
|
|
30
30
|
return result;
|
|
31
31
|
};
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
32
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
36
|
const core_1 = require("@babel/core");
|
|
34
|
-
const
|
|
37
|
+
const piscina_1 = __importDefault(require("piscina"));
|
|
35
38
|
const application_1 = __importStar(require("../../tools/babel/presets/application"));
|
|
36
39
|
const load_esm_1 = require("../../utils/load-esm");
|
|
40
|
+
const textDecoder = new TextDecoder();
|
|
41
|
+
const textEncoder = new TextEncoder();
|
|
37
42
|
async function transformJavaScript(request) {
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
43
|
+
const { filename, data, ...options } = request;
|
|
44
|
+
const textData = typeof data === 'string' ? data : textDecoder.decode(data);
|
|
45
|
+
const transformedData = await transformWithBabel(filename, textData, options);
|
|
46
|
+
// Transfer the data via `move` instead of cloning
|
|
47
|
+
return piscina_1.default.move(textEncoder.encode(transformedData));
|
|
41
48
|
}
|
|
42
49
|
exports.default = transformJavaScript;
|
|
43
50
|
let linkerPluginCreator;
|
|
44
|
-
async function transformWithBabel(
|
|
51
|
+
async function transformWithBabel(filename, data, options) {
|
|
45
52
|
const shouldLink = !options.skipLinker && (await (0, application_1.requiresLinking)(filename, data));
|
|
46
53
|
const useInputSourcemap = options.sourcemap &&
|
|
47
54
|
(!!options.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
|
+
import { Cache } from './cache';
|
|
8
9
|
/**
|
|
9
10
|
* Transformation options that should apply to all transformed files and data.
|
|
10
11
|
*/
|
|
@@ -24,7 +25,8 @@ export interface JavaScriptTransformerOptions {
|
|
|
24
25
|
export declare class JavaScriptTransformer {
|
|
25
26
|
#private;
|
|
26
27
|
readonly maxThreads: number;
|
|
27
|
-
|
|
28
|
+
private readonly cache?;
|
|
29
|
+
constructor(options: JavaScriptTransformerOptions, maxThreads: number, cache?: Cache<Uint8Array, import("./cache").CacheStore<Uint8Array>> | undefined);
|
|
28
30
|
/**
|
|
29
31
|
* Performs JavaScript transformations on a file from the filesystem.
|
|
30
32
|
* If no transformations are required, the data for the original file will be returned.
|
|
@@ -11,6 +11,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.JavaScriptTransformer = void 0;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const promises_1 = require("node:fs/promises");
|
|
14
16
|
const piscina_1 = __importDefault(require("piscina"));
|
|
15
17
|
/**
|
|
16
18
|
* A class that performs transformation of JavaScript files and raw data.
|
|
@@ -21,11 +23,13 @@ const piscina_1 = __importDefault(require("piscina"));
|
|
|
21
23
|
*/
|
|
22
24
|
class JavaScriptTransformer {
|
|
23
25
|
maxThreads;
|
|
26
|
+
cache;
|
|
24
27
|
#workerPool;
|
|
25
28
|
#commonOptions;
|
|
26
|
-
#
|
|
27
|
-
constructor(options, maxThreads,
|
|
29
|
+
#fileCacheKeyBase;
|
|
30
|
+
constructor(options, maxThreads, cache) {
|
|
28
31
|
this.maxThreads = maxThreads;
|
|
32
|
+
this.cache = cache;
|
|
29
33
|
// Extract options to ensure only the named options are serialized and sent to the worker
|
|
30
34
|
const { sourcemap, thirdPartySourcemaps = false, advancedOptimizations = false, jit = false, } = options;
|
|
31
35
|
this.#commonOptions = {
|
|
@@ -34,10 +38,7 @@ class JavaScriptTransformer {
|
|
|
34
38
|
advancedOptimizations,
|
|
35
39
|
jit,
|
|
36
40
|
};
|
|
37
|
-
|
|
38
|
-
if (reuseResults) {
|
|
39
|
-
this.#pendingfileResults = new Map();
|
|
40
|
-
}
|
|
41
|
+
this.#fileCacheKeyBase = Buffer.from(JSON.stringify(this.#commonOptions), 'utf-8');
|
|
41
42
|
}
|
|
42
43
|
#ensureWorkerPool() {
|
|
43
44
|
this.#workerPool ??= new piscina_1.default({
|
|
@@ -57,21 +58,46 @@ class JavaScriptTransformer {
|
|
|
57
58
|
* @param sideEffects If false, and `advancedOptimizations` is enabled tslib decorators are wrapped.
|
|
58
59
|
* @returns A promise that resolves to a UTF-8 encoded Uint8Array containing the result.
|
|
59
60
|
*/
|
|
60
|
-
transformFile(filename, skipLinker, sideEffects) {
|
|
61
|
-
const
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
61
|
+
async transformFile(filename, skipLinker, sideEffects) {
|
|
62
|
+
const data = await (0, promises_1.readFile)(filename);
|
|
63
|
+
let result;
|
|
64
|
+
let cacheKey;
|
|
65
|
+
if (this.cache) {
|
|
66
|
+
// Create a cache key from the file data and options that effect the output.
|
|
67
|
+
// NOTE: If additional options are added, this may need to be updated.
|
|
68
|
+
// TODO: Consider xxhash or similar instead of SHA256
|
|
69
|
+
const hash = (0, node_crypto_1.createHash)('sha256');
|
|
70
|
+
hash.update(`${!!skipLinker}--${!!sideEffects}`);
|
|
71
|
+
hash.update(data);
|
|
72
|
+
hash.update(this.#fileCacheKeyBase);
|
|
73
|
+
cacheKey = hash.digest('hex');
|
|
74
|
+
try {
|
|
75
|
+
result = await this.cache?.get(cacheKey);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Failure to get the value should not fail the transform
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (result === undefined) {
|
|
82
|
+
// If there is no cache or no cached entry, process the file
|
|
83
|
+
result = (await this.#ensureWorkerPool().run({
|
|
67
84
|
filename,
|
|
85
|
+
data,
|
|
68
86
|
skipLinker,
|
|
69
87
|
sideEffects,
|
|
70
88
|
...this.#commonOptions,
|
|
71
|
-
});
|
|
72
|
-
|
|
89
|
+
}, { transferList: [data.buffer] }));
|
|
90
|
+
// If there is a cache then store the result
|
|
91
|
+
if (this.cache && cacheKey) {
|
|
92
|
+
try {
|
|
93
|
+
await this.cache.put(cacheKey, result);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Failure to store the value in the cache should not fail the transform
|
|
97
|
+
}
|
|
98
|
+
}
|
|
73
99
|
}
|
|
74
|
-
return
|
|
100
|
+
return result;
|
|
75
101
|
}
|
|
76
102
|
/**
|
|
77
103
|
* Performs JavaScript transformations on the provided data of a file. The file does not need
|
|
@@ -103,7 +129,6 @@ class JavaScriptTransformer {
|
|
|
103
129
|
* @returns A void promise that resolves when closing is complete.
|
|
104
130
|
*/
|
|
105
131
|
async close() {
|
|
106
|
-
this.#pendingfileResults?.clear();
|
|
107
132
|
if (this.#workerPool) {
|
|
108
133
|
try {
|
|
109
134
|
await this.#workerPool.destroy();
|
|
@@ -13,7 +13,7 @@ export interface BundleStylesheetOptions {
|
|
|
13
13
|
optimization: boolean;
|
|
14
14
|
inlineFonts: boolean;
|
|
15
15
|
preserveSymlinks?: boolean;
|
|
16
|
-
sourcemap: boolean | 'external' | 'inline';
|
|
16
|
+
sourcemap: boolean | 'external' | 'inline' | 'linked';
|
|
17
17
|
outputNames: {
|
|
18
18
|
bundles: string;
|
|
19
19
|
media: string;
|