@angular-devkit/build-angular 17.1.0-next.3 → 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.
Files changed (33) hide show
  1. package/builders.json +5 -0
  2. package/package.json +22 -18
  3. package/src/builders/app-shell/index.js +7 -0
  4. package/src/builders/application/execute-build.js +9 -3
  5. package/src/builders/application/options.js +3 -4
  6. package/src/builders/application/schema.d.ts +1 -1
  7. package/src/builders/application/schema.json +1 -1
  8. package/src/builders/dev-server/vite-server.js +2 -1
  9. package/src/builders/jest/index.js +2 -2
  10. package/src/builders/prerender/index.js +7 -0
  11. package/src/builders/web-test-runner/builder-status-warnings.d.ts +11 -0
  12. package/src/builders/web-test-runner/builder-status-warnings.js +46 -0
  13. package/src/builders/web-test-runner/index.d.ts +10 -0
  14. package/src/builders/web-test-runner/index.js +151 -0
  15. package/src/builders/web-test-runner/jasmine_runner.js +88 -0
  16. package/src/builders/web-test-runner/options.d.ts +24 -0
  17. package/src/builders/web-test-runner/options.js +26 -0
  18. package/src/builders/web-test-runner/schema.d.ts +188 -0
  19. package/src/builders/web-test-runner/schema.js +15 -0
  20. package/src/builders/web-test-runner/schema.json +291 -0
  21. package/src/builders/web-test-runner/test_page.html +40 -0
  22. package/src/tools/esbuild/angular/component-stylesheets.js +5 -14
  23. package/src/tools/esbuild/cache.d.ts +88 -0
  24. package/src/tools/esbuild/cache.js +92 -0
  25. package/src/tools/esbuild/javascript-transformer-worker.d.ts +2 -2
  26. package/src/tools/esbuild/javascript-transformer-worker.js +12 -5
  27. package/src/tools/esbuild/javascript-transformer.d.ts +3 -1
  28. package/src/tools/esbuild/javascript-transformer.js +42 -17
  29. package/src/tools/esbuild/stylesheets/sass-language.js +3 -12
  30. package/src/tools/sass/lexer.d.ts +0 -11
  31. package/src/tools/sass/lexer.js +1 -87
  32. package/src/{builders/jest → utils}/test-files.d.ts +1 -2
  33. package/src/{builders/jest → utils}/test-files.js +3 -3
@@ -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;
@@ -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<Uint8Array>;
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 promises_1 = require("node:fs/promises");
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
- request.data ??= await (0, promises_1.readFile)(request.filename, 'utf-8');
39
- const transformedData = await transformWithBabel(request);
40
- return Buffer.from(transformedData, 'utf-8');
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({ filename, data, ...options }) {
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
- constructor(options: JavaScriptTransformerOptions, maxThreads: number, reuseResults?: boolean);
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
- #pendingfileResults;
27
- constructor(options, maxThreads, reuseResults) {
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
- // Currently only tracks pending file transform results
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 pendingKey = `${!!skipLinker}--${filename}`;
62
- let pending = this.#pendingfileResults?.get(pendingKey);
63
- if (pending === undefined) {
64
- // Always send the request to a worker. Files are almost always from node modules which means
65
- // they may need linking. The data is also not yet available to perform most transformation checks.
66
- pending = this.#ensureWorkerPool().run({
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
- this.#pendingfileResults?.set(pendingKey, pending);
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 pending;
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();
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.SassStylesheetLanguage = exports.shutdownSassWorkerPool = void 0;
34
34
  const node_path_1 = require("node:path");
35
35
  const node_url_1 = require("node:url");
36
+ const cache_1 = require("../cache");
36
37
  let sassWorkerPool;
37
38
  let sassWorkerPoolPromise;
38
39
  function isSassException(error) {
@@ -81,16 +82,6 @@ function parsePackageName(url) {
81
82
  },
82
83
  };
83
84
  }
84
- class Cache extends Map {
85
- async getOrCreate(key, creator) {
86
- let value = this.get(key);
87
- if (value === undefined) {
88
- value = await creator();
89
- this.set(key, value);
90
- }
91
- return value;
92
- }
93
- }
94
85
  async function compileString(data, filePath, syntax, options, resolveUrl) {
95
86
  // Lazily load Sass when a Sass file is found
96
87
  if (sassWorkerPool === undefined) {
@@ -105,8 +96,8 @@ async function compileString(data, filePath, syntax, options, resolveUrl) {
105
96
  // A null value indicates that the cached resolution attempt failed to find a location and
106
97
  // later stage resolution should be attempted. This avoids potentially expensive repeat
107
98
  // failing resolution attempts.
108
- const resolutionCache = new Cache();
109
- const packageRootCache = new Cache();
99
+ const resolutionCache = new cache_1.MemoryCache();
100
+ const packageRootCache = new cache_1.MemoryCache();
110
101
  const warnings = [];
111
102
  try {
112
103
  const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
@@ -16,14 +16,3 @@ export declare function findUrls(contents: string): Iterable<{
16
16
  end: number;
17
17
  value: string;
18
18
  }>;
19
- /**
20
- * Scans a CSS or Sass file and locates all valid import/use directive values as defined by the
21
- * syntax specification.
22
- * @param contents A string containing a CSS or Sass file to scan.
23
- * @returns An iterable that yields each CSS directive value found.
24
- */
25
- export declare function findImports(contents: string): Iterable<{
26
- start: number;
27
- end: number;
28
- specifier: string;
29
- }>;
@@ -7,7 +7,7 @@
7
7
  * found in the LICENSE file at https://angular.io/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.findImports = exports.findUrls = void 0;
10
+ exports.findUrls = void 0;
11
11
  // TODO: Combine everything into a single pass lexer
12
12
  /**
13
13
  * Determines if a unicode code point is a CSS whitespace character.
@@ -159,89 +159,3 @@ function* findUrls(contents) {
159
159
  }
160
160
  }
161
161
  exports.findUrls = findUrls;
162
- /**
163
- * Scans a CSS or Sass file and locates all valid import/use directive values as defined by the
164
- * syntax specification.
165
- * @param contents A string containing a CSS or Sass file to scan.
166
- * @returns An iterable that yields each CSS directive value found.
167
- */
168
- function* findImports(contents) {
169
- yield* find(contents, '@import ');
170
- yield* find(contents, '@use ');
171
- }
172
- exports.findImports = findImports;
173
- /**
174
- * Scans a CSS or Sass file and locates all valid function/directive values as defined by the
175
- * syntax specification.
176
- * @param contents A string containing a CSS or Sass file to scan.
177
- * @param prefix The prefix to start a valid segment.
178
- * @returns An iterable that yields each CSS url function value found.
179
- */
180
- function* find(contents, prefix) {
181
- let pos = 0;
182
- let width = 1;
183
- let current = -1;
184
- const next = () => {
185
- pos += width;
186
- current = contents.codePointAt(pos) ?? -1;
187
- width = current > 0xffff ? 2 : 1;
188
- return current;
189
- };
190
- // Based on https://www.w3.org/TR/css-syntax-3/#consume-ident-like-token
191
- while ((pos = contents.indexOf(prefix, pos)) !== -1) {
192
- // Set to position of the last character in prefix
193
- pos += prefix.length - 1;
194
- width = 1;
195
- // Consume all leading whitespace
196
- while (isWhitespace(next())) {
197
- /* empty */
198
- }
199
- // Initialize URL state
200
- const url = { start: pos, end: -1, specifier: '' };
201
- let complete = false;
202
- // If " or ', then consume the value as a string
203
- if (current === 0x0022 || current === 0x0027) {
204
- const ending = current;
205
- // Based on https://www.w3.org/TR/css-syntax-3/#consume-string-token
206
- while (!complete) {
207
- switch (next()) {
208
- case -1: // EOF
209
- return;
210
- case 0x000a: // line feed
211
- case 0x000c: // form feed
212
- case 0x000d: // carriage return
213
- // Invalid
214
- complete = true;
215
- break;
216
- case 0x005c: // \ -- character escape
217
- // If not EOF or newline, add the character after the escape
218
- switch (next()) {
219
- case -1:
220
- return;
221
- case 0x000a: // line feed
222
- case 0x000c: // form feed
223
- case 0x000d: // carriage return
224
- // Skip when inside a string
225
- break;
226
- default:
227
- // TODO: Handle hex escape codes
228
- url.specifier += String.fromCodePoint(current);
229
- break;
230
- }
231
- break;
232
- case ending:
233
- // Full string position should include the quotes for replacement
234
- url.end = pos + 1;
235
- complete = true;
236
- yield url;
237
- break;
238
- default:
239
- url.specifier += String.fromCodePoint(current);
240
- break;
241
- }
242
- }
243
- next();
244
- continue;
245
- }
246
- }
247
- }
@@ -6,7 +6,6 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import fastGlob from 'fast-glob';
9
- import { JestBuilderOptions } from './options';
10
9
  /**
11
10
  * Finds all test files in the project.
12
11
  *
@@ -16,4 +15,4 @@ import { JestBuilderOptions } from './options';
16
15
  * testing purposes.
17
16
  * @returns A set of all test files in the project.
18
17
  */
19
- export declare function findTestFiles(options: JestBuilderOptions, workspaceRoot: string, glob?: typeof fastGlob): Promise<Set<string>>;
18
+ export declare function findTestFiles(include: string[], exclude: string[], workspaceRoot: string, glob?: typeof fastGlob): Promise<Set<string>>;
@@ -21,14 +21,14 @@ const fast_glob_1 = __importDefault(require("fast-glob"));
21
21
  * testing purposes.
22
22
  * @returns A set of all test files in the project.
23
23
  */
24
- async function findTestFiles(options, workspaceRoot, glob = fast_glob_1.default) {
24
+ async function findTestFiles(include, exclude, workspaceRoot, glob = fast_glob_1.default) {
25
25
  const globOptions = {
26
26
  cwd: workspaceRoot,
27
- ignore: ['node_modules/**'].concat(options.exclude),
27
+ ignore: ['node_modules/**'].concat(exclude),
28
28
  braceExpansion: false, // Do not expand `a{b,c}` to `ab,ac`.
29
29
  extglob: false, // Disable "extglob" patterns.
30
30
  };
31
- const included = await Promise.all(options.include.map((pattern) => glob(pattern, globOptions)));
31
+ const included = await Promise.all(include.map((pattern) => glob(pattern, globOptions)));
32
32
  // Flatten and deduplicate any files found in multiple include patterns.
33
33
  return new Set(included.flat());
34
34
  }