@angular-devkit/build-angular 17.0.0-rc.1 → 17.0.0-rc.3

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 (47) hide show
  1. package/package.json +11 -10
  2. package/src/builders/app-shell/render-worker.d.ts +1 -1
  3. package/src/builders/app-shell/render-worker.js +16 -9
  4. package/src/builders/application/build-action.js +5 -2
  5. package/src/builders/application/execute-build.js +28 -18
  6. package/src/builders/browser-esbuild/index.js +9 -1
  7. package/src/builders/dev-server/vite-server.js +59 -20
  8. package/src/builders/extract-i18n/application-extraction.js +1 -0
  9. package/src/builders/prerender/routes-extractor-worker.js +1 -1
  10. package/src/tools/babel/plugins/elide-angular-metadata.js +14 -2
  11. package/src/tools/esbuild/angular/compiler-plugin.js +2 -1
  12. package/src/tools/esbuild/application-code-bundle.d.ts +3 -2
  13. package/src/tools/esbuild/application-code-bundle.js +8 -12
  14. package/src/tools/esbuild/bundler-context.d.ts +1 -1
  15. package/src/tools/esbuild/bundler-context.js +7 -2
  16. package/src/tools/esbuild/bundler-execution-result.d.ts +2 -0
  17. package/src/tools/esbuild/bundler-execution-result.js +14 -0
  18. package/src/tools/esbuild/commonjs-checker.js +3 -3
  19. package/src/tools/esbuild/global-scripts.d.ts +2 -3
  20. package/src/tools/esbuild/global-scripts.js +72 -69
  21. package/src/tools/esbuild/global-styles.d.ts +2 -3
  22. package/src/tools/esbuild/global-styles.js +35 -33
  23. package/src/tools/esbuild/javascript-transformer-worker.js +3 -2
  24. package/src/tools/esbuild/javascript-transformer.d.ts +1 -0
  25. package/src/tools/esbuild/javascript-transformer.js +25 -13
  26. package/src/tools/esbuild/utils.d.ts +1 -1
  27. package/src/tools/esbuild/utils.js +18 -4
  28. package/src/tools/esbuild/watcher.d.ts +1 -0
  29. package/src/tools/esbuild/watcher.js +3 -0
  30. package/src/utils/check-port.js +3 -1
  31. package/src/utils/environment-options.d.ts +1 -0
  32. package/src/utils/environment-options.js +3 -1
  33. package/src/utils/load-esm.d.ts +0 -2
  34. package/src/utils/routes-extractor/extractor.d.ts +1 -1
  35. package/src/utils/routes-extractor/extractor.js +2 -2
  36. package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +3 -2
  37. package/src/utils/server-rendering/fetch-patch.d.ts +8 -0
  38. package/src/utils/server-rendering/fetch-patch.js +66 -0
  39. package/src/utils/server-rendering/prerender.js +25 -30
  40. package/src/utils/server-rendering/render-worker.d.ts +4 -2
  41. package/src/utils/server-rendering/render-worker.js +8 -4
  42. package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -3
  43. package/src/utils/server-rendering/routes-extractor-worker.js +10 -4
  44. package/src/tools/esbuild/javascript-transfomer-plugin.d.ts +0 -19
  45. package/src/tools/esbuild/javascript-transfomer-plugin.js +0 -51
  46. package/src/utils/server-rendering/prerender-server.d.ts +0 -21
  47. package/src/utils/server-rendering/prerender-server.js +0 -102
@@ -0,0 +1,66 @@
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.patchFetchToLoadInMemoryAssets = void 0;
11
+ const mrmime_1 = require("mrmime");
12
+ const promises_1 = require("node:fs/promises");
13
+ const node_path_1 = require("node:path");
14
+ const node_worker_threads_1 = require("node:worker_threads");
15
+ const undici_1 = require("undici");
16
+ /**
17
+ * This is passed as workerData when setting up the worker via the `piscina` package.
18
+ */
19
+ const { assetFiles } = node_worker_threads_1.workerData;
20
+ const assetsCache = new Map();
21
+ const RESOLVE_PROTOCOL = 'resolve:';
22
+ function patchFetchToLoadInMemoryAssets() {
23
+ const global = globalThis;
24
+ const originalFetch = global.fetch;
25
+ const patchedFetch = async (input, init) => {
26
+ let url;
27
+ if (input instanceof URL) {
28
+ url = input;
29
+ }
30
+ else if (typeof input === 'string') {
31
+ url = new URL(input, RESOLVE_PROTOCOL + '//');
32
+ }
33
+ else if (typeof input === 'object' && 'url' in input) {
34
+ url = new URL(input.url, RESOLVE_PROTOCOL + '//');
35
+ }
36
+ else {
37
+ return originalFetch(input, init);
38
+ }
39
+ const { pathname, protocol } = url;
40
+ if (protocol !== RESOLVE_PROTOCOL || !assetFiles[pathname]) {
41
+ // Only handle relative requests or files that are in assets.
42
+ return originalFetch(input, init);
43
+ }
44
+ const cachedAsset = assetsCache.get(pathname);
45
+ if (cachedAsset) {
46
+ const { content, headers } = cachedAsset;
47
+ return new undici_1.Response(content, {
48
+ headers,
49
+ });
50
+ }
51
+ const extension = (0, node_path_1.extname)(pathname);
52
+ const mimeType = (0, mrmime_1.lookup)(extension);
53
+ const content = await (0, promises_1.readFile)(assetFiles[pathname]);
54
+ const headers = mimeType
55
+ ? {
56
+ 'Content-Type': mimeType,
57
+ }
58
+ : undefined;
59
+ assetsCache.set(pathname, { headers, content });
60
+ return new undici_1.Response(content, {
61
+ headers,
62
+ });
63
+ };
64
+ global.fetch = patchedFetch;
65
+ }
66
+ exports.patchFetchToLoadInMemoryAssets = patchFetchToLoadInMemoryAssets;
@@ -16,7 +16,6 @@ const node_path_1 = require("node:path");
16
16
  const piscina_1 = __importDefault(require("piscina"));
17
17
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
18
18
  const node_18_utils_1 = require("./esm-in-memory-loader/node-18-utils");
19
- const prerender_server_1 = require("./prerender-server");
20
19
  async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOptions = {}, outputFiles, assets, document, sourcemap = false, inlineCriticalCss = false, maxThreads = 1, verbose = false) {
21
20
  const outputFilesForWorker = {};
22
21
  const serverBundlesSourceMaps = new Map();
@@ -45,37 +44,33 @@ async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOpti
45
44
  }
46
45
  }
47
46
  serverBundlesSourceMaps.clear();
48
- // Start server to handle HTTP requests to assets.
49
- // TODO: consider starting this is a seperate process to avoid any blocks to the main thread.
50
- const { address: assetsServerAddress, close: closeAssetsServer } = await (0, prerender_server_1.startServer)(assets);
51
- try {
52
- // Get routes to prerender
53
- const { routes: allRoutes, warnings: routesWarnings } = await getAllRoutes(workspaceRoot, outputFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose, assetsServerAddress);
54
- if (routesWarnings?.length) {
55
- warnings.push(...routesWarnings);
56
- }
57
- if (allRoutes.size < 1) {
58
- return {
59
- errors,
60
- warnings,
61
- output: {},
62
- prerenderedRoutes: allRoutes,
63
- };
64
- }
65
- // Render routes
66
- const { warnings: renderingWarnings, errors: renderingErrors, output, } = await renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, inlineCriticalCss, document, assetsServerAddress, appShellOptions);
67
- errors.push(...renderingErrors);
68
- warnings.push(...renderingWarnings);
47
+ const assetsReversed = {};
48
+ for (const { source, destination } of assets) {
49
+ assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
50
+ }
51
+ // Get routes to prerender
52
+ const { routes: allRoutes, warnings: routesWarnings } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed, document, appShellOptions, prerenderOptions, sourcemap, verbose);
53
+ if (routesWarnings?.length) {
54
+ warnings.push(...routesWarnings);
55
+ }
56
+ if (allRoutes.size < 1) {
69
57
  return {
70
58
  errors,
71
59
  warnings,
72
- output,
60
+ output: {},
73
61
  prerenderedRoutes: allRoutes,
74
62
  };
75
63
  }
76
- finally {
77
- void closeAssetsServer?.();
78
- }
64
+ // Render routes
65
+ const { warnings: renderingWarnings, errors: renderingErrors, output, } = await renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, inlineCriticalCss, document, appShellOptions);
66
+ errors.push(...renderingErrors);
67
+ warnings.push(...renderingWarnings);
68
+ return {
69
+ errors,
70
+ warnings,
71
+ output,
72
+ prerenderedRoutes: allRoutes,
73
+ };
79
74
  }
80
75
  exports.prerenderPages = prerenderPages;
81
76
  class RoutesSet extends Set {
@@ -83,7 +78,7 @@ class RoutesSet extends Set {
83
78
  return super.add(addLeadingSlash(value));
84
79
  }
85
80
  }
86
- async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, inlineCriticalCss, document, baseUrl, appShellOptions) {
81
+ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, inlineCriticalCss, document, appShellOptions) {
87
82
  const output = {};
88
83
  const warnings = [];
89
84
  const errors = [];
@@ -97,9 +92,9 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
97
92
  workerData: {
98
93
  workspaceRoot,
99
94
  outputFiles: outputFilesForWorker,
95
+ assetFiles: assetFilesForWorker,
100
96
  inlineCriticalCss,
101
97
  document,
102
- baseUrl,
103
98
  },
104
99
  execArgv: workerExecArgv,
105
100
  });
@@ -139,7 +134,7 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
139
134
  output,
140
135
  };
141
136
  }
142
- async function getAllRoutes(workspaceRoot, outputFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose, assetsServerAddress) {
137
+ async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose) {
143
138
  const { routesFile, discoverRoutes } = prerenderOptions;
144
139
  const routes = new RoutesSet();
145
140
  const { route: appShellRoute } = appShellOptions;
@@ -165,9 +160,9 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, document, appSh
165
160
  workerData: {
166
161
  workspaceRoot,
167
162
  outputFiles: outputFilesForWorker,
163
+ assetFiles: assetFilesForWorker,
168
164
  document,
169
165
  verbose,
170
- url: assetsServerAddress,
171
166
  },
172
167
  execArgv: workerExecArgv,
173
168
  });
@@ -10,11 +10,13 @@ import { RenderResult, ServerContext } from './render-page';
10
10
  export interface RenderWorkerData extends ESMInMemoryFileLoaderWorkerData {
11
11
  document: string;
12
12
  inlineCriticalCss?: boolean;
13
- baseUrl: string;
13
+ assetFiles: Record</** Destination */ string, /** Source */ string>;
14
14
  }
15
15
  export interface RenderOptions {
16
16
  route: string;
17
17
  serverContext: ServerContext;
18
18
  }
19
19
  /** Renders an application based on a provided options. */
20
- export default function (options: RenderOptions): Promise<RenderResult>;
20
+ declare function render(options: RenderOptions): Promise<RenderResult>;
21
+ declare const _default: typeof render;
22
+ export default _default;
@@ -8,19 +8,23 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const node_worker_threads_1 = require("node:worker_threads");
11
+ const fetch_patch_1 = require("./fetch-patch");
11
12
  const render_page_1 = require("./render-page");
12
13
  /**
13
14
  * This is passed as workerData when setting up the worker via the `piscina` package.
14
15
  */
15
- const { outputFiles, document, inlineCriticalCss, baseUrl } = node_worker_threads_1.workerData;
16
+ const { outputFiles, document, inlineCriticalCss } = node_worker_threads_1.workerData;
16
17
  /** Renders an application based on a provided options. */
17
- function default_1(options) {
18
+ async function render(options) {
18
19
  return (0, render_page_1.renderPage)({
19
20
  ...options,
20
- route: baseUrl + options.route,
21
21
  outputFiles,
22
22
  document,
23
23
  inlineCriticalCss,
24
24
  });
25
25
  }
26
- exports.default = default_1;
26
+ function initialize() {
27
+ (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
28
+ return render;
29
+ }
30
+ exports.default = initialize();
@@ -9,11 +9,13 @@ import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loa
9
9
  export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
10
10
  document: string;
11
11
  verbose: boolean;
12
- url: string;
13
- assetsServerAddress: string;
12
+ assetFiles: Record</** Destination */ string, /** Source */ string>;
14
13
  }
15
14
  export interface RoutersExtractorWorkerResult {
16
15
  routes: string[];
17
16
  warnings?: string[];
18
17
  }
19
- export default function (): Promise<RoutersExtractorWorkerResult>;
18
+ /** Renders an application based on a provided options. */
19
+ declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
20
+ declare const _default: typeof extractRoutes;
21
+ export default _default;
@@ -9,17 +9,19 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const node_worker_threads_1 = require("node:worker_threads");
11
11
  const load_esm_1 = require("../load-esm");
12
+ const fetch_patch_1 = require("./fetch-patch");
12
13
  /**
13
14
  * This is passed as workerData when setting up the worker via the `piscina` package.
14
15
  */
15
- const { document, verbose, url } = node_worker_threads_1.workerData;
16
- async function default_1() {
16
+ const { document, verbose } = node_worker_threads_1.workerData;
17
+ /** Renders an application based on a provided options. */
18
+ async function extractRoutes() {
17
19
  const { extractRoutes } = await (0, load_esm_1.loadEsmModule)('./render-utils.server.mjs');
18
20
  const { default: bootstrapAppFnOrModule } = await (0, load_esm_1.loadEsmModule)('./main.server.mjs');
19
21
  const skippedRedirects = [];
20
22
  const skippedOthers = [];
21
23
  const routes = [];
22
- for await (const { route, success, redirect } of extractRoutes(bootstrapAppFnOrModule, document, url)) {
24
+ for await (const { route, success, redirect } of extractRoutes(bootstrapAppFnOrModule, document)) {
23
25
  if (success) {
24
26
  routes.push(route);
25
27
  continue;
@@ -44,4 +46,8 @@ async function default_1() {
44
46
  }
45
47
  return { routes, warnings };
46
48
  }
47
- exports.default = default_1;
49
+ function initialize() {
50
+ (0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
51
+ return extractRoutes;
52
+ }
53
+ exports.default = initialize();
@@ -1,19 +0,0 @@
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
- import type { Plugin } from 'esbuild';
9
- import { JavaScriptTransformerOptions } from './javascript-transformer';
10
- export interface JavaScriptTransformerPluginOptions extends JavaScriptTransformerOptions {
11
- babelFileCache?: Map<string, Uint8Array>;
12
- maxWorkers: number;
13
- }
14
- /**
15
- * Creates a plugin that Transformers JavaScript using Babel.
16
- *
17
- * @returns An esbuild plugin.
18
- */
19
- export declare function createJavaScriptTransformerPlugin(options: JavaScriptTransformerPluginOptions): Plugin;
@@ -1,51 +0,0 @@
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.createJavaScriptTransformerPlugin = void 0;
11
- const javascript_transformer_1 = require("./javascript-transformer");
12
- /**
13
- * Creates a plugin that Transformers JavaScript using Babel.
14
- *
15
- * @returns An esbuild plugin.
16
- */
17
- function createJavaScriptTransformerPlugin(options) {
18
- return {
19
- name: 'angular-javascript-transformer',
20
- setup(build) {
21
- let javascriptTransformer;
22
- const { sourcemap, thirdPartySourcemaps, advancedOptimizations, jit, babelFileCache, maxWorkers, } = options;
23
- build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
24
- // The filename is currently used as a cache key. Since the cache is memory only,
25
- // the options cannot change and do not need to be represented in the key. If the
26
- // cache is later stored to disk, then the options that affect transform output
27
- // would need to be added to the key as well as a check for any change of content.
28
- let contents = babelFileCache?.get(args.path);
29
- if (contents === undefined) {
30
- // Initialize a worker pool for JavaScript transformations
31
- javascriptTransformer ??= new javascript_transformer_1.JavaScriptTransformer({
32
- sourcemap,
33
- thirdPartySourcemaps,
34
- advancedOptimizations,
35
- jit,
36
- }, maxWorkers);
37
- contents = await javascriptTransformer.transformFile(args.path, jit);
38
- babelFileCache?.set(args.path, contents);
39
- }
40
- return {
41
- contents,
42
- loader: 'js',
43
- };
44
- });
45
- build.onDispose(() => {
46
- void javascriptTransformer?.close();
47
- });
48
- },
49
- };
50
- }
51
- exports.createJavaScriptTransformerPlugin = createJavaScriptTransformerPlugin;
@@ -1,21 +0,0 @@
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
- import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
9
- /**
10
- * Start a server that can handle HTTP requests to assets.
11
- *
12
- * @example
13
- * ```ts
14
- * httpClient.get('/assets/content.json');
15
- * ```
16
- * @returns the server address.
17
- */
18
- export declare function startServer(assets: Readonly<BuildOutputAsset[]>): Promise<{
19
- address: string;
20
- close?: () => void;
21
- }>;
@@ -1,102 +0,0 @@
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.startServer = void 0;
11
- const mrmime_1 = require("mrmime");
12
- const promises_1 = require("node:fs/promises");
13
- const node_http_1 = require("node:http");
14
- const node_path_1 = require("node:path");
15
- /**
16
- * Start a server that can handle HTTP requests to assets.
17
- *
18
- * @example
19
- * ```ts
20
- * httpClient.get('/assets/content.json');
21
- * ```
22
- * @returns the server address.
23
- */
24
- async function startServer(assets) {
25
- if (Object.keys(assets).length === 0) {
26
- return {
27
- address: '',
28
- };
29
- }
30
- const assetsReversed = {};
31
- for (const { source, destination } of assets) {
32
- assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
33
- }
34
- const assetsCache = new Map();
35
- const server = (0, node_http_1.createServer)(requestHandler(assetsReversed, assetsCache));
36
- await new Promise((resolve) => {
37
- server.listen(0, '127.0.0.1', resolve);
38
- });
39
- const serverAddress = server.address();
40
- let address;
41
- if (!serverAddress) {
42
- address = '';
43
- }
44
- else if (typeof serverAddress === 'string') {
45
- address = serverAddress;
46
- }
47
- else {
48
- const { port, address: host } = serverAddress;
49
- address = `http://${host}:${port}`;
50
- }
51
- return {
52
- address,
53
- close: () => {
54
- assetsCache.clear();
55
- server.unref();
56
- server.close();
57
- },
58
- };
59
- }
60
- exports.startServer = startServer;
61
- function requestHandler(assetsReversed, assetsCache) {
62
- return (req, res) => {
63
- if (!req.url) {
64
- res.destroy(new Error('Request url was empty.'));
65
- return;
66
- }
67
- const { pathname } = new URL(req.url, 'resolve://');
68
- const asset = assetsReversed[pathname];
69
- if (!asset) {
70
- res.statusCode = 404;
71
- res.statusMessage = 'Asset not found.';
72
- res.end();
73
- return;
74
- }
75
- const cachedAsset = assetsCache.get(pathname);
76
- if (cachedAsset) {
77
- const { content, mimeType } = cachedAsset;
78
- if (mimeType) {
79
- res.setHeader('Content-Type', mimeType);
80
- }
81
- res.end(content);
82
- return;
83
- }
84
- (0, promises_1.readFile)(asset)
85
- .then((content) => {
86
- const extension = (0, node_path_1.extname)(pathname);
87
- const mimeType = (0, mrmime_1.lookup)(extension);
88
- assetsCache.set(pathname, {
89
- mimeType,
90
- content,
91
- });
92
- if (mimeType) {
93
- res.setHeader('Content-Type', mimeType);
94
- }
95
- res.end(content);
96
- })
97
- .catch((e) => res.destroy(e));
98
- };
99
- }
100
- function addLeadingSlash(value) {
101
- return value.charAt(0) === '/' ? value : '/' + value;
102
- }