@angular-devkit/build-angular 17.0.0-rc.3 → 17.0.0-rc.5
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 +6 -6
- package/src/builders/application/execute-build.js +37 -28
- package/src/builders/dev-server/builder.d.ts +12 -1
- package/src/builders/dev-server/builder.js +13 -4
- package/src/builders/dev-server/vite-server.d.ts +9 -6
- package/src/builders/dev-server/vite-server.js +141 -63
- package/src/builders/karma/find-tests-plugin.js +17 -7
- package/src/builders/ssr-dev-server/index.d.ts +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +3 -1
- package/src/tools/esbuild/angular/compiler-plugin.js +103 -56
- package/src/tools/esbuild/application-code-bundle.js +1 -16
- package/src/tools/esbuild/bundler-context.d.ts +4 -1
- package/src/tools/esbuild/bundler-context.js +13 -8
- package/src/tools/esbuild/bundler-execution-result.d.ts +14 -14
- package/src/tools/esbuild/bundler-execution-result.js +4 -3
- package/src/tools/esbuild/commonjs-checker.js +9 -4
- package/src/tools/esbuild/global-scripts.d.ts +1 -1
- package/src/tools/esbuild/global-scripts.js +2 -1
- package/src/tools/esbuild/javascript-transformer.d.ts +1 -1
- package/src/tools/esbuild/javascript-transformer.js +20 -8
- package/src/utils/bundle-calculator.js +1 -1
- package/src/utils/environment-options.js +4 -1
- package/src/utils/load-esm.js +6 -1
- package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.d.ts +0 -4
- package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +77 -28
- package/src/utils/server-rendering/render-worker.js +3 -1
- package/src/utils/server-rendering/routes-extractor-worker.js +2 -2
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-devkit/build-angular",
|
|
3
|
-
"version": "17.0.0-rc.
|
|
3
|
+
"version": "17.0.0-rc.5",
|
|
4
4
|
"description": "Angular Webpack Build Facade",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"typings": "src/index.d.ts",
|
|
7
7
|
"builders": "builders.json",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@ampproject/remapping": "2.2.1",
|
|
10
|
-
"@angular-devkit/architect": "0.1700.0-rc.
|
|
11
|
-
"@angular-devkit/build-webpack": "0.1700.0-rc.
|
|
12
|
-
"@angular-devkit/core": "17.0.0-rc.
|
|
10
|
+
"@angular-devkit/architect": "0.1700.0-rc.5",
|
|
11
|
+
"@angular-devkit/build-webpack": "0.1700.0-rc.5",
|
|
12
|
+
"@angular-devkit/core": "17.0.0-rc.5",
|
|
13
13
|
"@babel/core": "7.23.2",
|
|
14
14
|
"@babel/generator": "7.23.0",
|
|
15
15
|
"@babel/helper-annotate-as-pure": "7.22.5",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@babel/preset-env": "7.23.2",
|
|
21
21
|
"@babel/runtime": "7.23.2",
|
|
22
22
|
"@discoveryjs/json-ext": "0.5.7",
|
|
23
|
-
"@ngtools/webpack": "17.0.0-rc.
|
|
23
|
+
"@ngtools/webpack": "17.0.0-rc.5",
|
|
24
24
|
"@vitejs/plugin-basic-ssl": "1.0.1",
|
|
25
25
|
"ansi-colors": "4.1.3",
|
|
26
26
|
"autoprefixer": "10.4.16",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"text-table": "0.2.0",
|
|
65
65
|
"tree-kill": "1.2.2",
|
|
66
66
|
"tslib": "2.6.2",
|
|
67
|
-
"undici": "5.27.
|
|
67
|
+
"undici": "5.27.2",
|
|
68
68
|
"vite": "4.5.0",
|
|
69
69
|
"webpack": "5.89.0",
|
|
70
70
|
"webpack-dev-middleware": "6.1.1",
|
|
@@ -59,7 +59,7 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
59
59
|
// Global Scripts
|
|
60
60
|
if (options.globalScripts.length > 0) {
|
|
61
61
|
for (const initial of [true, false]) {
|
|
62
|
-
const bundleOptions = (0, global_scripts_1.createGlobalScriptsBundleOptions)(options, initial);
|
|
62
|
+
const bundleOptions = (0, global_scripts_1.createGlobalScriptsBundleOptions)(options, target, initial);
|
|
63
63
|
if (bundleOptions) {
|
|
64
64
|
bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, !!options.watch, bundleOptions, () => initial));
|
|
65
65
|
}
|
|
@@ -69,7 +69,12 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
69
69
|
if (serverEntryPoint && (prerenderOptions || appShellOptions || ssrOptions)) {
|
|
70
70
|
const nodeTargets = [...target, ...(0, utils_1.getSupportedNodeTargets)()];
|
|
71
71
|
// Server application code
|
|
72
|
-
bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, !!options.watch, (0, application_code_bundle_1.createServerCodeBundleOptions)(
|
|
72
|
+
bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, !!options.watch, (0, application_code_bundle_1.createServerCodeBundleOptions)({
|
|
73
|
+
...options,
|
|
74
|
+
// Disable external deps for server bundles.
|
|
75
|
+
// This is because it breaks Vite 'optimizeDeps' for SSR.
|
|
76
|
+
externalPackages: false,
|
|
77
|
+
}, nodeTargets, codeBundleCache), () => false));
|
|
73
78
|
// Server polyfills code
|
|
74
79
|
const serverPolyfillBundleOptions = (0, application_code_bundle_1.createServerPolyfillBundleOptions)(options, nodeTargets, codeBundleCache);
|
|
75
80
|
if (serverPolyfillBundleOptions) {
|
|
@@ -88,11 +93,33 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
88
93
|
}
|
|
89
94
|
// Analyze external imports if external options are enabled
|
|
90
95
|
if (options.externalPackages || options.externalDependencies?.length) {
|
|
96
|
+
const { browser = new Set(), server = new Set() } = bundlingResult.externalImports;
|
|
91
97
|
// TODO: Filter externalImports to generate second argument to support wildcard externalDependency values
|
|
92
|
-
executionResult.setExternalMetadata([...
|
|
98
|
+
executionResult.setExternalMetadata([...browser], [...server], options.externalDependencies);
|
|
93
99
|
}
|
|
94
100
|
const { metafile, initialFiles, outputFiles } = bundlingResult;
|
|
95
101
|
executionResult.outputFiles.push(...outputFiles);
|
|
102
|
+
const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputHashes);
|
|
103
|
+
// Analyze files for bundle budget failures if present
|
|
104
|
+
let budgetFailures;
|
|
105
|
+
if (options.budgets) {
|
|
106
|
+
const compatStats = (0, budget_stats_1.generateBudgetStats)(metafile, initialFiles);
|
|
107
|
+
budgetFailures = [...(0, bundle_calculator_1.checkBudgets)(options.budgets, compatStats, true)];
|
|
108
|
+
if (budgetFailures.length > 0) {
|
|
109
|
+
const errors = budgetFailures
|
|
110
|
+
.filter((failure) => failure.severity === 'error')
|
|
111
|
+
.map(({ message }) => message);
|
|
112
|
+
const warnings = budgetFailures
|
|
113
|
+
.filter((failure) => failure.severity !== 'error')
|
|
114
|
+
.map(({ message }) => message);
|
|
115
|
+
await printWarningsAndErrorsToConsoleAndAddToResult(context, executionResult, warnings, errors);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Calculate estimated transfer size if scripts are optimized
|
|
119
|
+
let estimatedTransferSizes;
|
|
120
|
+
if (optimizationOptions.scripts || optimizationOptions.styles.minify) {
|
|
121
|
+
estimatedTransferSizes = await (0, utils_1.calculateEstimatedTransferSizes)(executionResult.outputFiles);
|
|
122
|
+
}
|
|
96
123
|
// Check metafile for CommonJS module usage if optimizing scripts
|
|
97
124
|
if (optimizationOptions.scripts) {
|
|
98
125
|
const messages = (0, commonjs_checker_1.checkCommonJSModules)(metafile, options.allowedCommonJsDependencies);
|
|
@@ -108,27 +135,6 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
108
135
|
if (options.extractLicenses) {
|
|
109
136
|
executionResult.addOutputFile('3rdpartylicenses.txt', await (0, license_extractor_1.extractLicenses)(metafile, workspaceRoot), bundler_context_1.BuildOutputFileType.Root);
|
|
110
137
|
}
|
|
111
|
-
// Analyze files for bundle budget failures if present
|
|
112
|
-
let budgetFailures;
|
|
113
|
-
if (options.budgets) {
|
|
114
|
-
const compatStats = (0, budget_stats_1.generateBudgetStats)(metafile, initialFiles);
|
|
115
|
-
budgetFailures = [...(0, bundle_calculator_1.checkBudgets)(options.budgets, compatStats, true)];
|
|
116
|
-
if (budgetFailures.length > 0) {
|
|
117
|
-
await (0, utils_1.logMessages)(context, {
|
|
118
|
-
errors: budgetFailures
|
|
119
|
-
.filter((failure) => failure.severity === 'error')
|
|
120
|
-
.map((failure) => ({ text: failure.message, location: null })),
|
|
121
|
-
warnings: budgetFailures
|
|
122
|
-
.filter((failure) => failure.severity !== 'error')
|
|
123
|
-
.map((failure) => ({ text: failure.message, location: null })),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// Calculate estimated transfer size if scripts are optimized
|
|
128
|
-
let estimatedTransferSizes;
|
|
129
|
-
if (optimizationOptions.scripts || optimizationOptions.styles.minify) {
|
|
130
|
-
estimatedTransferSizes = await (0, utils_1.calculateEstimatedTransferSizes)(executionResult.outputFiles);
|
|
131
|
-
}
|
|
132
138
|
// Perform i18n translation inlining if enabled
|
|
133
139
|
let prerenderedRoutes;
|
|
134
140
|
let errors;
|
|
@@ -149,7 +155,7 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
149
155
|
executionResult.outputFiles.push(...result.additionalOutputFiles);
|
|
150
156
|
executionResult.assetFiles.push(...result.additionalAssets);
|
|
151
157
|
}
|
|
152
|
-
await
|
|
158
|
+
await printWarningsAndErrorsToConsoleAndAddToResult(context, executionResult, warnings, errors);
|
|
153
159
|
if (prerenderOptions) {
|
|
154
160
|
executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: prerenderedRoutes.sort((a, b) => a.localeCompare(b)) }, null, 2), bundler_context_1.BuildOutputFileType.Root);
|
|
155
161
|
let prerenderMsg = `Prerendered ${prerenderedRoutes.length} static route`;
|
|
@@ -161,7 +167,6 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
161
167
|
}
|
|
162
168
|
context.logger.info(color_1.colors.magenta(prerenderMsg) + '\n');
|
|
163
169
|
}
|
|
164
|
-
const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputHashes);
|
|
165
170
|
(0, utils_1.logBuildStats)(context, metafile, initialFiles, budgetFailures, changedFiles, estimatedTransferSizes);
|
|
166
171
|
// Write metafile if stats option is enabled
|
|
167
172
|
if (options.stats) {
|
|
@@ -170,9 +175,13 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
170
175
|
return executionResult;
|
|
171
176
|
}
|
|
172
177
|
exports.executeBuild = executeBuild;
|
|
173
|
-
async function
|
|
178
|
+
async function printWarningsAndErrorsToConsoleAndAddToResult(context, executionResult, warnings, errors) {
|
|
179
|
+
const errorMessages = errors.map((text) => ({ text, location: null }));
|
|
180
|
+
if (errorMessages.length) {
|
|
181
|
+
executionResult.addErrors(errorMessages);
|
|
182
|
+
}
|
|
174
183
|
await (0, utils_1.logMessages)(context, {
|
|
175
|
-
errors:
|
|
184
|
+
errors: errorMessages,
|
|
176
185
|
warnings: warnings.map((text) => ({ text, location: null })),
|
|
177
186
|
});
|
|
178
187
|
}
|
|
@@ -5,8 +5,10 @@
|
|
|
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
|
+
/// <reference types="node" />
|
|
8
9
|
import type { BuilderContext } from '@angular-devkit/architect';
|
|
9
10
|
import type { Plugin } from 'esbuild';
|
|
11
|
+
import type http from 'node:http';
|
|
10
12
|
import { Observable } from 'rxjs';
|
|
11
13
|
import type { ExecutionTransformer } from '../../transforms';
|
|
12
14
|
import type { IndexHtmlTransform } from '../../utils/index-file/index-html-generator';
|
|
@@ -14,10 +16,16 @@ import type { Schema as DevServerBuilderOptions } from './schema';
|
|
|
14
16
|
import type { DevServerBuilderOutput } from './webpack-server';
|
|
15
17
|
/**
|
|
16
18
|
* A Builder that executes a development server based on the provided browser target option.
|
|
19
|
+
*
|
|
20
|
+
* Usage of the `transforms` and/or `extensions` parameters is NOT supported and may cause
|
|
21
|
+
* unexpected build output or build failures.
|
|
22
|
+
*
|
|
17
23
|
* @param options Dev Server options.
|
|
18
24
|
* @param context The build context.
|
|
19
25
|
* @param transforms A map of transforms that can be used to hook into some logic (such as
|
|
20
26
|
* transforming webpack configuration before passing it to webpack).
|
|
27
|
+
* @param extensions An optional object containing an array of build plugins (esbuild-based)
|
|
28
|
+
* and/or HTTP request middleware.
|
|
21
29
|
*
|
|
22
30
|
* @experimental Direct usage of this function is considered experimental.
|
|
23
31
|
*/
|
|
@@ -25,4 +33,7 @@ export declare function execute(options: DevServerBuilderOptions, context: Build
|
|
|
25
33
|
webpackConfiguration?: ExecutionTransformer<import('webpack').Configuration>;
|
|
26
34
|
logging?: import('@angular-devkit/build-webpack').WebpackLoggingCallback;
|
|
27
35
|
indexHtml?: IndexHtmlTransform;
|
|
28
|
-
},
|
|
36
|
+
}, extensions?: {
|
|
37
|
+
buildPlugins?: Plugin[];
|
|
38
|
+
middleware?: ((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: unknown) => void) => void)[];
|
|
39
|
+
}): Observable<DevServerBuilderOutput>;
|
|
@@ -37,14 +37,20 @@ const purge_cache_1 = require("../../utils/purge-cache");
|
|
|
37
37
|
const options_1 = require("./options");
|
|
38
38
|
/**
|
|
39
39
|
* A Builder that executes a development server based on the provided browser target option.
|
|
40
|
+
*
|
|
41
|
+
* Usage of the `transforms` and/or `extensions` parameters is NOT supported and may cause
|
|
42
|
+
* unexpected build output or build failures.
|
|
43
|
+
*
|
|
40
44
|
* @param options Dev Server options.
|
|
41
45
|
* @param context The build context.
|
|
42
46
|
* @param transforms A map of transforms that can be used to hook into some logic (such as
|
|
43
47
|
* transforming webpack configuration before passing it to webpack).
|
|
48
|
+
* @param extensions An optional object containing an array of build plugins (esbuild-based)
|
|
49
|
+
* and/or HTTP request middleware.
|
|
44
50
|
*
|
|
45
51
|
* @experimental Direct usage of this function is considered experimental.
|
|
46
52
|
*/
|
|
47
|
-
function execute(options, context, transforms = {},
|
|
53
|
+
function execute(options, context, transforms = {}, extensions) {
|
|
48
54
|
// Determine project name from builder context target
|
|
49
55
|
const projectName = context.target?.project;
|
|
50
56
|
if (!projectName) {
|
|
@@ -56,14 +62,17 @@ function execute(options, context, transforms = {}, plugins) {
|
|
|
56
62
|
if (builderName === '@angular-devkit/build-angular:application' ||
|
|
57
63
|
builderName === '@angular-devkit/build-angular:browser-esbuild' ||
|
|
58
64
|
normalizedOptions.forceEsbuild) {
|
|
59
|
-
if (
|
|
65
|
+
if (transforms?.logging || transforms?.webpackConfiguration) {
|
|
60
66
|
throw new Error('The `application` and `browser-esbuild` builders do not support Webpack transforms.');
|
|
61
67
|
}
|
|
62
|
-
return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./vite-server')))).pipe((0, rxjs_1.switchMap)(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context,
|
|
68
|
+
return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./vite-server')))).pipe((0, rxjs_1.switchMap)(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context, transforms, extensions)));
|
|
63
69
|
}
|
|
64
|
-
if (
|
|
70
|
+
if (extensions?.buildPlugins?.length) {
|
|
65
71
|
throw new Error('Only the `application` and `browser-esbuild` builders support plugins.');
|
|
66
72
|
}
|
|
73
|
+
if (extensions?.middleware?.length) {
|
|
74
|
+
throw new Error('Only the `application` and `browser-esbuild` builders support middleware.');
|
|
75
|
+
}
|
|
67
76
|
// Use Webpack for all other browser targets
|
|
68
77
|
return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./webpack-server')))).pipe((0, rxjs_1.switchMap)(({ serveWebpackBrowser }) => serveWebpackBrowser(normalizedOptions, builderName, context, transforms)));
|
|
69
78
|
}));
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { BuilderContext } from '@angular-devkit/architect';
|
|
9
9
|
import type { Plugin } from 'esbuild';
|
|
10
|
-
import type { InlineConfig } from 'vite';
|
|
10
|
+
import type { Connect, InlineConfig } from 'vite';
|
|
11
|
+
import { ExternalResultMetadata } from '../../tools/esbuild/bundler-execution-result';
|
|
11
12
|
import { JavaScriptTransformer } from '../../tools/esbuild/javascript-transformer';
|
|
12
13
|
import type { NormalizedDevServerOptions } from './options';
|
|
13
14
|
import type { DevServerBuilderOutput } from './webpack-server';
|
|
@@ -18,9 +19,11 @@ interface OutputFileRecord {
|
|
|
18
19
|
updated: boolean;
|
|
19
20
|
servable: boolean;
|
|
20
21
|
}
|
|
21
|
-
export declare function serveWithVite(serverOptions: NormalizedDevServerOptions, builderName: string, context: BuilderContext,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
export declare function serveWithVite(serverOptions: NormalizedDevServerOptions, builderName: string, context: BuilderContext, transformers?: {
|
|
23
|
+
indexHtml?: (content: string) => Promise<string>;
|
|
24
|
+
}, extensions?: {
|
|
25
|
+
middleware?: Connect.NextHandleFunction[];
|
|
26
|
+
buildPlugins?: Plugin[];
|
|
27
|
+
}): AsyncIterableIterator<DevServerBuilderOutput>;
|
|
28
|
+
export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: ExternalResultMetadata, ssr: boolean, prebundleTransformer: JavaScriptTransformer, target: string[], extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>): Promise<InlineConfig>;
|
|
26
29
|
export {};
|
|
@@ -37,11 +37,11 @@ exports.setupServer = exports.serveWithVite = void 0;
|
|
|
37
37
|
const remapping_1 = __importDefault(require("@ampproject/remapping"));
|
|
38
38
|
const mrmime_1 = require("mrmime");
|
|
39
39
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
40
|
-
const node_crypto_1 = require("node:crypto");
|
|
41
40
|
const promises_1 = require("node:fs/promises");
|
|
42
|
-
const node_path_1 =
|
|
41
|
+
const node_path_1 = require("node:path");
|
|
43
42
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
44
43
|
const javascript_transformer_1 = require("../../tools/esbuild/javascript-transformer");
|
|
44
|
+
const rxjs_esm_resolution_plugin_1 = require("../../tools/esbuild/rxjs-esm-resolution-plugin");
|
|
45
45
|
const utils_1 = require("../../tools/esbuild/utils");
|
|
46
46
|
const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
|
|
47
47
|
const render_page_1 = require("../../utils/server-rendering/render-page");
|
|
@@ -50,7 +50,8 @@ const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
|
|
|
50
50
|
const application_1 = require("../application");
|
|
51
51
|
const browser_esbuild_1 = require("../browser-esbuild");
|
|
52
52
|
const load_proxy_config_1 = require("./load-proxy-config");
|
|
53
|
-
|
|
53
|
+
// eslint-disable-next-line max-lines-per-function
|
|
54
|
+
async function* serveWithVite(serverOptions, builderName, context, transformers, extensions) {
|
|
54
55
|
// Get the browser configuration from the target name.
|
|
55
56
|
const rawBrowserOptions = (await context.getTargetOptions(serverOptions.buildTarget));
|
|
56
57
|
const browserOptions = (await context.validateOptions({
|
|
@@ -89,7 +90,7 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
|
|
|
89
90
|
// Always enable JIT linking to support applications built with and without AOT.
|
|
90
91
|
// In a development environment the additional scope information does not
|
|
91
92
|
// have a negative effect unlike production where final output size is relevant.
|
|
92
|
-
{ sourcemap: true, jit: true }, 1);
|
|
93
|
+
{ sourcemap: true, jit: true }, 1, true);
|
|
93
94
|
// Extract output index from options
|
|
94
95
|
// TODO: Provide this info from the build results
|
|
95
96
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -97,12 +98,13 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
|
|
|
97
98
|
// dynamically import Vite for ESM compatibility
|
|
98
99
|
const { createServer, normalizePath } = await Promise.resolve().then(() => __importStar(require('vite')));
|
|
99
100
|
let server;
|
|
100
|
-
let
|
|
101
|
+
let serverUrl;
|
|
101
102
|
let hadError = false;
|
|
102
103
|
const generatedFiles = new Map();
|
|
103
104
|
const assetFiles = new Map();
|
|
104
105
|
const externalMetadata = {
|
|
105
|
-
|
|
106
|
+
implicitBrowser: [],
|
|
107
|
+
implicitServer: [],
|
|
106
108
|
explicit: [],
|
|
107
109
|
};
|
|
108
110
|
const build = builderName === '@angular-devkit/build-angular:application'
|
|
@@ -113,7 +115,7 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
|
|
|
113
115
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
116
|
browserOptions, context, {
|
|
115
117
|
write: false,
|
|
116
|
-
},
|
|
118
|
+
}, extensions?.buildPlugins)) {
|
|
117
119
|
(0, node_assert_1.default)(result.outputFiles, 'Builder did not provide result files.');
|
|
118
120
|
// If build failed, nothing to serve
|
|
119
121
|
if (!result.success) {
|
|
@@ -147,15 +149,21 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
|
|
|
147
149
|
assetFiles.set('/' + normalizePath(asset.destination), asset.source);
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
|
-
// To avoid disconnecting the array objects from the option, these arrays need to be mutated
|
|
151
|
-
// instead of replaced.
|
|
152
|
+
// To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
|
|
152
153
|
if (result.externalMetadata) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
const { implicitBrowser, implicitServer, explicit } = result.externalMetadata;
|
|
155
|
+
// Empty Arrays to avoid growing unlimited with every re-build.
|
|
156
|
+
externalMetadata.explicit.length = 0;
|
|
157
|
+
externalMetadata.implicitServer.length = 0;
|
|
158
|
+
externalMetadata.implicitBrowser.length = 0;
|
|
159
|
+
externalMetadata.explicit.push(...explicit);
|
|
160
|
+
externalMetadata.implicitServer.push(...implicitServer);
|
|
161
|
+
externalMetadata.implicitBrowser.push(...implicitBrowser);
|
|
162
|
+
// The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm.
|
|
163
|
+
// See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239
|
|
164
|
+
externalMetadata.explicit.sort();
|
|
165
|
+
externalMetadata.implicitServer.sort();
|
|
166
|
+
externalMetadata.implicitBrowser.sort();
|
|
159
167
|
}
|
|
160
168
|
if (server) {
|
|
161
169
|
handleUpdate(normalizePath, generatedFiles, server, serverOptions, context.logger);
|
|
@@ -166,19 +174,37 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
|
|
|
166
174
|
throw new Error('The builder requires a target.');
|
|
167
175
|
}
|
|
168
176
|
const { root = '' } = await context.getProjectMetadata(projectName);
|
|
169
|
-
const projectRoot = node_path_1.
|
|
177
|
+
const projectRoot = (0, node_path_1.join)(context.workspaceRoot, root);
|
|
170
178
|
const browsers = (0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger);
|
|
171
179
|
const target = (0, utils_1.transformSupportedBrowsersToTargets)(browsers);
|
|
172
180
|
// Setup server and start listening
|
|
173
|
-
const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, !!browserOptions.ssr, prebundleTransformer, target);
|
|
181
|
+
const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, !!browserOptions.ssr, prebundleTransformer, target, extensions?.middleware, transformers?.indexHtml);
|
|
174
182
|
server = await createServer(serverConfiguration);
|
|
175
183
|
await server.listen();
|
|
176
|
-
|
|
184
|
+
if (serverConfiguration.ssr?.optimizeDeps?.disabled === false) {
|
|
185
|
+
/**
|
|
186
|
+
* Vite will only start dependency optimization of SSR modules when the first request comes in.
|
|
187
|
+
* In some cases, this causes a long waiting time. To mitigate this, we call `ssrLoadModule` to
|
|
188
|
+
* initiate this process before the first request.
|
|
189
|
+
*
|
|
190
|
+
* NOTE: This will intentionally fail from the unknown module, but currently there is no other way
|
|
191
|
+
* to initiate the SSR dep optimizer.
|
|
192
|
+
*/
|
|
193
|
+
void server.ssrLoadModule('<deps-caller>').catch(() => { });
|
|
194
|
+
}
|
|
195
|
+
const urls = server.resolvedUrls;
|
|
196
|
+
if (urls && (urls.local.length || urls.network.length)) {
|
|
197
|
+
serverUrl = new URL(urls.local[0] ?? urls.network[0]);
|
|
198
|
+
}
|
|
177
199
|
// log connection information
|
|
178
200
|
server.printUrls();
|
|
179
201
|
}
|
|
180
202
|
// TODO: adjust output typings to reflect both development servers
|
|
181
|
-
yield {
|
|
203
|
+
yield {
|
|
204
|
+
success: true,
|
|
205
|
+
port: serverUrl?.port,
|
|
206
|
+
baseUrl: serverUrl?.href,
|
|
207
|
+
};
|
|
182
208
|
}
|
|
183
209
|
// Add cleanup logic via a builder teardown
|
|
184
210
|
let deferred;
|
|
@@ -196,7 +222,7 @@ function handleUpdate(normalizePath, generatedFiles, server, serverOptions, logg
|
|
|
196
222
|
for (const [file, record] of generatedFiles) {
|
|
197
223
|
if (record.updated) {
|
|
198
224
|
updatedFiles.push(file);
|
|
199
|
-
const updatedModules = server.moduleGraph.getModulesByFile(normalizePath(node_path_1.
|
|
225
|
+
const updatedModules = server.moduleGraph.getModulesByFile(normalizePath((0, node_path_1.join)(server.config.root, file)));
|
|
200
226
|
updatedModules?.forEach((m) => server?.moduleGraph.invalidateModule(m));
|
|
201
227
|
}
|
|
202
228
|
}
|
|
@@ -240,7 +266,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
|
|
|
240
266
|
filePath = '/index.html';
|
|
241
267
|
}
|
|
242
268
|
else {
|
|
243
|
-
filePath = normalizePath(file.path);
|
|
269
|
+
filePath = '/' + normalizePath(file.path);
|
|
244
270
|
}
|
|
245
271
|
seen.add(filePath);
|
|
246
272
|
// Skip analysis of sourcemaps
|
|
@@ -278,16 +304,20 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
|
|
|
278
304
|
}
|
|
279
305
|
}
|
|
280
306
|
// eslint-disable-next-line max-lines-per-function
|
|
281
|
-
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssr, prebundleTransformer, target) {
|
|
307
|
+
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssr, prebundleTransformer, target, extensionMiddleware, indexHtmlTransformer) {
|
|
282
308
|
const proxy = await (0, load_proxy_config_1.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig, true);
|
|
283
309
|
// dynamically import Vite for ESM compatibility
|
|
284
310
|
const { normalizePath } = await Promise.resolve().then(() => __importStar(require('vite')));
|
|
285
311
|
// Path will not exist on disk and only used to provide separate path for Vite requests
|
|
286
|
-
const virtualProjectRoot = normalizePath(node_path_1.
|
|
312
|
+
const virtualProjectRoot = normalizePath((0, node_path_1.join)(serverOptions.workspaceRoot, `.angular/vite-root`, serverOptions.buildTarget.project));
|
|
313
|
+
const serverExplicitExternal = [
|
|
314
|
+
...(await Promise.resolve().then(() => __importStar(require('node:module')))).builtinModules,
|
|
315
|
+
...externalMetadata.explicit,
|
|
316
|
+
];
|
|
287
317
|
const configuration = {
|
|
288
318
|
configFile: false,
|
|
289
319
|
envFile: false,
|
|
290
|
-
cacheDir: node_path_1.
|
|
320
|
+
cacheDir: (0, node_path_1.join)(serverOptions.cacheOptions.path, 'vite'),
|
|
291
321
|
root: virtualProjectRoot,
|
|
292
322
|
publicDir: false,
|
|
293
323
|
esbuild: false,
|
|
@@ -317,8 +347,31 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
317
347
|
preTransformRequests: externalMetadata.explicit.length === 0,
|
|
318
348
|
},
|
|
319
349
|
ssr: {
|
|
320
|
-
//
|
|
321
|
-
|
|
350
|
+
// Note: `true` and `/.*/` have different sematics. When true, the `external` option is ignored.
|
|
351
|
+
noExternal: /.*/,
|
|
352
|
+
// Exclude any Node.js built in module and provided dependencies (currently build defined externals)
|
|
353
|
+
external: serverExplicitExternal,
|
|
354
|
+
optimizeDeps: getDepOptimizationConfig({
|
|
355
|
+
/**
|
|
356
|
+
* *********************************************
|
|
357
|
+
* NOTE: Temporary disable 'optimizeDeps' for SSR.
|
|
358
|
+
* *********************************************
|
|
359
|
+
*
|
|
360
|
+
* Currently this causes a number of issues.
|
|
361
|
+
* - Deps are re-optimized everytime the server is started.
|
|
362
|
+
* - Added deps after a rebuild are not optimized.
|
|
363
|
+
* - Breaks RxJs (Unless it is added as external). See: https://github.com/angular/angular-cli/issues/26235
|
|
364
|
+
*/
|
|
365
|
+
// Only enable with caching since it causes prebundle dependencies to be cached
|
|
366
|
+
disabled: true,
|
|
367
|
+
// Exclude any explicitly defined dependencies (currently build defined externals and node.js built-ins)
|
|
368
|
+
exclude: serverExplicitExternal,
|
|
369
|
+
// Include all implict dependencies from the external packages internal option
|
|
370
|
+
include: externalMetadata.implicitServer,
|
|
371
|
+
ssr: true,
|
|
372
|
+
prebundleTransformer,
|
|
373
|
+
target,
|
|
374
|
+
}),
|
|
322
375
|
},
|
|
323
376
|
plugins: [
|
|
324
377
|
(0, i18n_locale_plugin_1.createAngularLocaleDataPlugin)(),
|
|
@@ -336,19 +389,18 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
336
389
|
if (importer && source[0] === '.' && importer.startsWith(virtualProjectRoot)) {
|
|
337
390
|
// Remove query if present
|
|
338
391
|
const [importerFile] = importer.split('?', 1);
|
|
339
|
-
source =
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
source = source.slice(1);
|
|
392
|
+
source =
|
|
393
|
+
'/' +
|
|
394
|
+
normalizePath((0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.relative)(virtualProjectRoot, importerFile)), source));
|
|
343
395
|
}
|
|
344
396
|
const [file] = source.split('?', 1);
|
|
345
397
|
if (outputFiles.has(file)) {
|
|
346
|
-
return node_path_1.
|
|
398
|
+
return (0, node_path_1.join)(virtualProjectRoot, source);
|
|
347
399
|
}
|
|
348
400
|
},
|
|
349
401
|
load(id) {
|
|
350
402
|
const [file] = id.split('?', 1);
|
|
351
|
-
const relativeFile = normalizePath(node_path_1.
|
|
403
|
+
const relativeFile = '/' + normalizePath((0, node_path_1.relative)(virtualProjectRoot, file));
|
|
352
404
|
const codeContents = outputFiles.get(relativeFile)?.contents;
|
|
353
405
|
if (codeContents === undefined) {
|
|
354
406
|
if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) {
|
|
@@ -392,7 +444,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
392
444
|
// Parse the incoming request.
|
|
393
445
|
// The base of the URL is unused but required to parse the URL.
|
|
394
446
|
const pathname = pathnameWithoutServePath(req.url, serverOptions);
|
|
395
|
-
const extension = node_path_1.
|
|
447
|
+
const extension = (0, node_path_1.extname)(pathname);
|
|
396
448
|
// Rewrite all build assets to a vite raw fs URL
|
|
397
449
|
const assetSourcePath = assets.get(pathname);
|
|
398
450
|
if (assetSourcePath !== undefined) {
|
|
@@ -422,16 +474,24 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
422
474
|
}
|
|
423
475
|
next();
|
|
424
476
|
});
|
|
477
|
+
if (extensionMiddleware?.length) {
|
|
478
|
+
extensionMiddleware.forEach((middleware) => server.middlewares.use(middleware));
|
|
479
|
+
}
|
|
425
480
|
// Returning a function, installs middleware after the main transform middleware but
|
|
426
481
|
// before the built-in HTML middleware
|
|
427
482
|
return () => {
|
|
428
483
|
function angularSSRMiddleware(req, res, next) {
|
|
429
484
|
const url = req.originalUrl;
|
|
430
|
-
if (
|
|
485
|
+
if (
|
|
486
|
+
// Skip if path is not defined.
|
|
487
|
+
!url ||
|
|
488
|
+
// Skip if path is like a file.
|
|
489
|
+
// NOTE: We use a regexp to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
|
|
490
|
+
/^\.[a-z]{2,4}$/i.test((0, node_path_1.extname)(url.split('?')[0]))) {
|
|
431
491
|
next();
|
|
432
492
|
return;
|
|
433
493
|
}
|
|
434
|
-
const rawHtml = outputFiles.get('index.server.html')?.contents;
|
|
494
|
+
const rawHtml = outputFiles.get('/index.server.html')?.contents;
|
|
435
495
|
if (!rawHtml) {
|
|
436
496
|
next();
|
|
437
497
|
return;
|
|
@@ -443,15 +503,17 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
443
503
|
document: html,
|
|
444
504
|
route,
|
|
445
505
|
serverContext: 'ssr',
|
|
446
|
-
loadBundle: (
|
|
506
|
+
loadBundle: (uri) =>
|
|
447
507
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
448
|
-
server.ssrLoadModule(
|
|
508
|
+
server.ssrLoadModule(uri.slice(1)),
|
|
449
509
|
// Files here are only needed for critical CSS inlining.
|
|
450
510
|
outputFiles: {},
|
|
451
511
|
// TODO: add support for critical css inlining.
|
|
452
512
|
inlineCriticalCss: false,
|
|
453
513
|
});
|
|
454
|
-
return content
|
|
514
|
+
return indexHtmlTransformer && content
|
|
515
|
+
? await indexHtmlTransformer(content)
|
|
516
|
+
: content;
|
|
455
517
|
});
|
|
456
518
|
}
|
|
457
519
|
if (ssr) {
|
|
@@ -468,7 +530,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
468
530
|
if (pathname === '/' || pathname === `/index.html`) {
|
|
469
531
|
const rawHtml = outputFiles.get('/index.html')?.contents;
|
|
470
532
|
if (rawHtml) {
|
|
471
|
-
transformIndexHtmlAndAddHeaders(req.url, rawHtml, res, next);
|
|
533
|
+
transformIndexHtmlAndAddHeaders(req.url, rawHtml, res, next, indexHtmlTransformer);
|
|
472
534
|
return;
|
|
473
535
|
}
|
|
474
536
|
}
|
|
@@ -499,35 +561,18 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
499
561
|
},
|
|
500
562
|
},
|
|
501
563
|
],
|
|
502
|
-
optimizeDeps
|
|
564
|
+
// Browser only optimizeDeps. (This does not run for SSR dependencies).
|
|
565
|
+
optimizeDeps: getDepOptimizationConfig({
|
|
503
566
|
// Only enable with caching since it causes prebundle dependencies to be cached
|
|
504
567
|
disabled: !serverOptions.cacheOptions.enabled,
|
|
505
568
|
// Exclude any explicitly defined dependencies (currently build defined externals)
|
|
506
569
|
exclude: externalMetadata.explicit,
|
|
507
570
|
// Include all implict dependencies from the external packages internal option
|
|
508
|
-
include: externalMetadata.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
// Set esbuild supported targets.
|
|
514
|
-
target,
|
|
515
|
-
supported: (0, utils_1.getFeatureSupport)(target),
|
|
516
|
-
plugins: [
|
|
517
|
-
{
|
|
518
|
-
name: 'angular-vite-optimize-deps',
|
|
519
|
-
setup(build) {
|
|
520
|
-
build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
|
|
521
|
-
return {
|
|
522
|
-
contents: await prebundleTransformer.transformFile(args.path),
|
|
523
|
-
loader: 'js',
|
|
524
|
-
};
|
|
525
|
-
});
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
],
|
|
529
|
-
},
|
|
530
|
-
},
|
|
571
|
+
include: externalMetadata.implicitBrowser,
|
|
572
|
+
ssr: false,
|
|
573
|
+
prebundleTransformer,
|
|
574
|
+
target,
|
|
575
|
+
}),
|
|
531
576
|
};
|
|
532
577
|
if (serverOptions.ssl) {
|
|
533
578
|
if (serverOptions.sslCert && serverOptions.sslKey) {
|
|
@@ -575,3 +620,36 @@ function pathnameWithoutServePath(url, serverOptions) {
|
|
|
575
620
|
}
|
|
576
621
|
return pathname;
|
|
577
622
|
}
|
|
623
|
+
function getDepOptimizationConfig({ disabled, exclude, include, target, prebundleTransformer, ssr, }) {
|
|
624
|
+
const plugins = [
|
|
625
|
+
{
|
|
626
|
+
name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}`,
|
|
627
|
+
setup(build) {
|
|
628
|
+
build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
|
|
629
|
+
return {
|
|
630
|
+
contents: await prebundleTransformer.transformFile(args.path),
|
|
631
|
+
loader: 'js',
|
|
632
|
+
};
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
];
|
|
637
|
+
if (ssr) {
|
|
638
|
+
plugins.unshift((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
// Only enable with caching since it causes prebundle dependencies to be cached
|
|
642
|
+
disabled,
|
|
643
|
+
// Exclude any explicitly defined dependencies (currently build defined externals)
|
|
644
|
+
exclude,
|
|
645
|
+
// Include all implict dependencies from the external packages internal option
|
|
646
|
+
include,
|
|
647
|
+
// Add an esbuild plugin to run the Angular linker on dependencies
|
|
648
|
+
esbuildOptions: {
|
|
649
|
+
// Set esbuild supported targets.
|
|
650
|
+
target,
|
|
651
|
+
supported: (0, utils_1.getFeatureSupport)(target),
|
|
652
|
+
plugins,
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
}
|