@angular-devkit/build-angular 17.2.0-next.1 → 17.2.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/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "17.2.0-next.1",
3
+ "version": "17.2.0",
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.1702.0-next.1",
11
- "@angular-devkit/build-webpack": "0.1702.0-next.1",
12
- "@angular-devkit/core": "17.2.0-next.1",
10
+ "@angular-devkit/architect": "0.1702.0",
11
+ "@angular-devkit/build-webpack": "0.1702.0",
12
+ "@angular-devkit/core": "17.2.0",
13
13
  "@babel/core": "7.23.9",
14
14
  "@babel/generator": "7.23.6",
15
15
  "@babel/helper-annotate-as-pure": "7.22.5",
@@ -20,51 +20,51 @@
20
20
  "@babel/preset-env": "7.23.9",
21
21
  "@babel/runtime": "7.23.9",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "17.2.0-next.1",
23
+ "@ngtools/webpack": "17.2.0",
24
24
  "@vitejs/plugin-basic-ssl": "1.1.0",
25
25
  "ansi-colors": "4.1.3",
26
26
  "autoprefixer": "10.4.17",
27
27
  "babel-loader": "9.1.3",
28
28
  "babel-plugin-istanbul": "6.1.1",
29
29
  "browserslist": "^4.21.5",
30
- "copy-webpack-plugin": "12.0.2",
30
+ "copy-webpack-plugin": "11.0.0",
31
31
  "critters": "0.0.20",
32
32
  "css-loader": "6.10.0",
33
33
  "esbuild-wasm": "0.20.0",
34
34
  "fast-glob": "3.3.2",
35
35
  "https-proxy-agent": "7.0.2",
36
36
  "http-proxy-middleware": "2.0.6",
37
- "inquirer": "9.2.13",
37
+ "inquirer": "9.2.14",
38
38
  "jsonc-parser": "3.2.1",
39
39
  "karma-source-map-support": "1.4.0",
40
40
  "less": "4.2.0",
41
41
  "less-loader": "11.1.0",
42
42
  "license-webpack-plugin": "4.0.2",
43
43
  "loader-utils": "3.2.1",
44
- "magic-string": "0.30.5",
45
- "mini-css-extract-plugin": "2.7.7",
44
+ "magic-string": "0.30.7",
45
+ "mini-css-extract-plugin": "2.8.0",
46
46
  "mrmime": "2.0.0",
47
47
  "open": "8.4.2",
48
48
  "ora": "5.4.1",
49
49
  "parse5-html-rewriting-stream": "7.0.0",
50
- "picomatch": "3.0.1",
50
+ "picomatch": "4.0.1",
51
51
  "piscina": "4.3.1",
52
- "postcss": "8.4.33",
52
+ "postcss": "8.4.35",
53
53
  "postcss-loader": "8.1.0",
54
54
  "resolve-url-loader": "5.0.0",
55
55
  "rxjs": "7.8.1",
56
56
  "sass": "1.70.0",
57
57
  "sass-loader": "14.1.0",
58
- "semver": "7.5.4",
58
+ "semver": "7.6.0",
59
59
  "source-map-loader": "5.0.0",
60
60
  "source-map-support": "0.5.21",
61
61
  "terser": "5.27.0",
62
62
  "tree-kill": "1.2.2",
63
63
  "tslib": "2.6.2",
64
- "undici": "6.5.0",
64
+ "undici": "6.6.2",
65
65
  "vite": "5.0.12",
66
66
  "watchpack": "2.4.0",
67
- "webpack": "5.90.0",
67
+ "webpack": "5.90.1",
68
68
  "webpack-dev-middleware": "6.1.1",
69
69
  "webpack-dev-server": "4.15.1",
70
70
  "webpack-merge": "5.10.0",
@@ -74,16 +74,16 @@
74
74
  "esbuild": "0.20.0"
75
75
  },
76
76
  "peerDependencies": {
77
- "@angular/compiler-cli": "^17.0.0 || ^17.2.0-next.0",
78
- "@angular/localize": "^17.0.0 || ^17.2.0-next.0",
79
- "@angular/platform-server": "^17.0.0 || ^17.2.0-next.0",
80
- "@angular/service-worker": "^17.0.0 || ^17.2.0-next.0",
77
+ "@angular/compiler-cli": "^17.0.0",
78
+ "@angular/localize": "^17.0.0",
79
+ "@angular/platform-server": "^17.0.0",
80
+ "@angular/service-worker": "^17.0.0",
81
81
  "@web/test-runner": "^0.18.0",
82
82
  "browser-sync": "^3.0.2",
83
83
  "jest": "^29.5.0",
84
84
  "jest-environment-jsdom": "^29.5.0",
85
85
  "karma": "^6.3.0",
86
- "ng-packagr": "^17.0.0 || ^17.2.0-next.0",
86
+ "ng-packagr": "^17.0.0",
87
87
  "protractor": "^7.0.0",
88
88
  "tailwindcss": "^2.0.0 || ^3.0.0",
89
89
  "typescript": ">=5.2 <5.4"
@@ -26,4 +26,5 @@ export declare function runEsBuildBuildAction(action: (rebuildState?: RebuildSta
26
26
  poll?: number;
27
27
  signal?: AbortSignal;
28
28
  preserveSymlinks?: boolean;
29
+ clearScreen?: boolean;
29
30
  }): AsyncIterable<(ExecutionResult['outputWithFiles'] | ExecutionResult['output']) & BuilderOutput>;
@@ -40,8 +40,21 @@ const sass_language_1 = require("../../tools/esbuild/stylesheets/sass-language")
40
40
  const utils_1 = require("../../tools/esbuild/utils");
41
41
  const delete_output_dir_1 = require("../../utils/delete-output-dir");
42
42
  const environment_options_1 = require("../../utils/environment-options");
43
+ // Watch workspace for package manager changes
44
+ const packageWatchFiles = [
45
+ // manifest can affect module resolution
46
+ 'package.json',
47
+ // npm lock file
48
+ 'package-lock.json',
49
+ // pnpm lock file
50
+ 'pnpm-lock.yaml',
51
+ // yarn lock file including Yarn PnP manifest files (https://yarnpkg.com/advanced/pnp-spec/)
52
+ 'yarn.lock',
53
+ '.pnp.cjs',
54
+ '.pnp.data.json',
55
+ ];
43
56
  async function* runEsBuildBuildAction(action, options) {
44
- const { writeToFileSystemFilter, writeToFileSystem, watch, poll, logger, deleteOutputPath, cacheOptions, outputOptions, verbose, projectRoot, workspaceRoot, progress, preserveSymlinks, } = options;
57
+ const { writeToFileSystemFilter, writeToFileSystem, watch, poll, clearScreen, logger, deleteOutputPath, cacheOptions, outputOptions, verbose, projectRoot, workspaceRoot, progress, preserveSymlinks, } = options;
45
58
  if (deleteOutputPath && writeToFileSystem) {
46
59
  await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputOptions.base, [
47
60
  outputOptions.browser,
@@ -93,19 +106,6 @@ async function* runEsBuildBuildAction(action, options) {
93
106
  if (environment_options_1.shouldWatchRoot) {
94
107
  watcher.add(projectRoot);
95
108
  }
96
- // Watch workspace for package manager changes
97
- const packageWatchFiles = [
98
- // manifest can affect module resolution
99
- 'package.json',
100
- // npm lock file
101
- 'package-lock.json',
102
- // pnpm lock file
103
- 'pnpm-lock.yaml',
104
- // yarn lock file including Yarn PnP manifest files (https://yarnpkg.com/advanced/pnp-spec/)
105
- 'yarn.lock',
106
- '.pnp.cjs',
107
- '.pnp.data.json',
108
- ];
109
109
  watcher.add(packageWatchFiles
110
110
  .map((file) => node_path_1.default.join(workspaceRoot, file))
111
111
  .filter((file) => (0, node_fs_1.existsSync)(file)));
@@ -136,6 +136,10 @@ async function* runEsBuildBuildAction(action, options) {
136
136
  if (options.signal?.aborted) {
137
137
  break;
138
138
  }
139
+ if (clearScreen) {
140
+ // eslint-disable-next-line no-console
141
+ console.clear();
142
+ }
139
143
  if (verbose) {
140
144
  logger.info(changes.toDebugString());
141
145
  }
@@ -90,6 +90,7 @@ context, infrastructureSettings, extensions) {
90
90
  projectRoot: normalizedOptions.projectRoot,
91
91
  workspaceRoot: normalizedOptions.workspaceRoot,
92
92
  progress: normalizedOptions.progress,
93
+ clearScreen: normalizedOptions.clearScreen,
93
94
  writeToFileSystem,
94
95
  // For app-shell and SSG server files are not required by users.
95
96
  // Omit these when SSR is not enabled.
@@ -31,7 +31,9 @@ interface InternalOptions {
31
31
  * Indicates whether all node packages should be marked as external.
32
32
  * Currently used by the dev-server to support prebundling.
33
33
  */
34
- externalPackages?: boolean;
34
+ externalPackages?: boolean | {
35
+ exclude: string[];
36
+ };
35
37
  /**
36
38
  * Forces the output from the localize post-processing to not create nested directories per locale output.
37
39
  * This is only used by the development server which currently only supports a single locale per build.
@@ -72,7 +74,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
72
74
  polyfills: string[] | undefined;
73
75
  poll: number | undefined;
74
76
  progress: boolean;
75
- externalPackages: boolean | undefined;
77
+ externalPackages: boolean | {
78
+ exclude: string[];
79
+ } | undefined;
76
80
  preserveSymlinks: boolean;
77
81
  stylePreprocessorOptions: import("./schema").StylePreprocessorOptions | undefined;
78
82
  subresourceIntegrity: boolean | undefined;
@@ -139,5 +143,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
139
143
  loaderExtensions: Record<string, "binary" | "file" | "text"> | undefined;
140
144
  jsonLogs: boolean;
141
145
  colors: boolean;
146
+ clearScreen: boolean | undefined;
147
+ define: {
148
+ [key: string]: string;
149
+ } | undefined;
142
150
  }>;
143
151
  export {};
@@ -178,7 +178,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
178
178
  };
179
179
  }
180
180
  // Initial options to keep
181
- const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, deleteOutputPath, namedChunks, budgets, deployUrl, } = options;
181
+ const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, deleteOutputPath, namedChunks, budgets, deployUrl, clearScreen, define, } = options;
182
182
  // Return all the normalized options
183
183
  return {
184
184
  advancedOptimizations: !!aot && optimizationOptions.scripts,
@@ -230,6 +230,8 @@ async function normalizeOptions(context, projectName, options, extensions) {
230
230
  loaderExtensions,
231
231
  jsonLogs: environment_options_1.useJSONBuildLogs,
232
232
  colors: color_1.colors.enabled,
233
+ clearScreen,
234
+ define,
233
235
  };
234
236
  }
235
237
  exports.normalizeOptions = normalizeOptions;
@@ -33,10 +33,23 @@ export interface Schema {
33
33
  * set.
34
34
  */
35
35
  budgets?: Budget[];
36
+ /**
37
+ * Automatically clear the terminal screen during rebuilds.
38
+ */
39
+ clearScreen?: boolean;
36
40
  /**
37
41
  * Define the crossorigin attribute setting of elements that provide CORS support.
38
42
  */
39
43
  crossOrigin?: CrossOrigin;
44
+ /**
45
+ * Defines global identifiers that will be replaced with a specified constant value when
46
+ * found in any JavaScript or TypeScript code including libraries. The value will be used
47
+ * directly. String values must be put in quotes. Identifiers within Angular metadata such
48
+ * as Component Decorators will not be replaced.
49
+ */
50
+ define?: {
51
+ [key: string]: string;
52
+ };
40
53
  /**
41
54
  * Delete the output path before building.
42
55
  */
@@ -133,6 +133,11 @@
133
133
  },
134
134
  "default": []
135
135
  },
136
+ "clearScreen": {
137
+ "type": "boolean",
138
+ "default": false,
139
+ "description": "Automatically clear the terminal screen during rebuilds."
140
+ },
136
141
  "optimization": {
137
142
  "description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.io/guide/workspace-config#optimization-configuration.",
138
143
  "default": true,
@@ -211,6 +216,13 @@
211
216
  "^\\.\\S+$": { "enum": ["text", "binary", "file", "empty"] }
212
217
  }
213
218
  },
219
+ "define": {
220
+ "description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced.",
221
+ "type": "object",
222
+ "additionalProperties": {
223
+ "type": "string"
224
+ }
225
+ },
214
226
  "fileReplacements": {
215
227
  "description": "Replace compilation source files with other compilation source files in the build.",
216
228
  "type": "array",
@@ -120,8 +120,9 @@ function buildWebpackBrowser(options, context, transforms = {}) {
120
120
  webpackFactory: require('webpack'),
121
121
  logging: transforms.logging ||
122
122
  ((stats, config) => {
123
- if (options.verbose) {
124
- context.logger.info(stats.toString(config.stats));
123
+ if (options.verbose && config.stats !== false) {
124
+ const statsOptions = config.stats === true ? undefined : config.stats;
125
+ context.logger.info(stats.toString(statsOptions));
125
126
  }
126
127
  }),
127
128
  }).pipe((0, rxjs_1.concatMap)(
@@ -63,8 +63,16 @@ function execute(options, context, transforms = {}, extensions) {
63
63
  if (transforms?.logging || transforms?.webpackConfiguration) {
64
64
  throw new Error('The `application` and `browser-esbuild` builders do not support Webpack transforms.');
65
65
  }
66
+ // Warn if the initial options provided by the user enable prebundling but caching is disabled
67
+ if (options.prebundle && !normalizedOptions.cacheOptions.enabled) {
68
+ context.logger.warn(`Prebundling has been configured but will not be used because caching has been disabled.`);
69
+ }
66
70
  return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./vite-server')))).pipe((0, rxjs_1.switchMap)(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context, transforms, extensions)));
67
71
  }
72
+ // Warn if the initial options provided by the user enable prebundling with Webpack-based builders
73
+ if (options.prebundle) {
74
+ context.logger.warn(`Prebundling has been configured but will not be used because it is not supported by the "${builderName}" builder.`);
75
+ }
68
76
  if (extensions?.buildPlugins?.length) {
69
77
  throw new Error('Only the `application` and `browser-esbuild` builders support plugins.');
70
78
  }
@@ -80,7 +88,10 @@ async function initialize(initialOptions, projectName, context, builderSelector
80
88
  // Purge old build disk cache.
81
89
  await (0, purge_cache_1.purgeStaleBuildCache)(context);
82
90
  const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, initialOptions);
83
- const builderName = await context.getBuilderNameForTarget(normalizedOptions.buildTarget);
91
+ const builderName = builderSelector({
92
+ builderName: await context.getBuilderNameForTarget(normalizedOptions.buildTarget),
93
+ forceEsbuild: !!normalizedOptions.forceEsbuild,
94
+ }, context.logger);
84
95
  if (!normalizedOptions.disableHostCheck &&
85
96
  !/^127\.\d+\.\d+\.\d+/g.test(normalizedOptions.host) &&
86
97
  normalizedOptions.host !== 'localhost') {
@@ -100,7 +111,7 @@ case.
100
111
  }
101
112
  normalizedOptions.port = await (0, check_port_1.checkPort)(normalizedOptions.port, normalizedOptions.host);
102
113
  return {
103
- builderName: builderSelector({ builderName, forceEsbuild: !!normalizedOptions.forceEsbuild }, context.logger),
114
+ builderName,
104
115
  normalizedOptions,
105
116
  };
106
117
  }
@@ -43,4 +43,5 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
43
43
  sslCert: string | undefined;
44
44
  sslKey: string | undefined;
45
45
  forceEsbuild: boolean | undefined;
46
+ prebundle: import("./schema").PrebundleUnion;
46
47
  }>;
@@ -33,7 +33,7 @@ async function normalizeOptions(context, projectName, options) {
33
33
  const buildTargetSpecifier = options.buildTarget ?? options.browserTarget ?? `::development`;
34
34
  const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, projectName, 'build');
35
35
  // Initial options to keep
36
- const { host, port, poll, open, verbose, watch, allowedHosts, disableHostCheck, liveReload, hmr, headers, proxyConfig, servePath, publicHost, ssl, sslCert, sslKey, forceEsbuild, } = options;
36
+ const { host, port, poll, open, verbose, watch, allowedHosts, disableHostCheck, liveReload, hmr, headers, proxyConfig, servePath, publicHost, ssl, sslCert, sslKey, forceEsbuild, prebundle, } = options;
37
37
  // Return all the normalized options
38
38
  return {
39
39
  buildTarget,
@@ -58,6 +58,8 @@ async function normalizeOptions(context, projectName, options) {
58
58
  sslCert,
59
59
  sslKey,
60
60
  forceEsbuild,
61
+ // Prebundling defaults to true but requires caching to function
62
+ prebundle: cacheOptions.enabled && (prebundle ?? true),
61
63
  };
62
64
  }
63
65
  exports.normalizeOptions = normalizeOptions;
@@ -58,6 +58,12 @@ export interface Schema {
58
58
  * Port to listen on.
59
59
  */
60
60
  port?: number;
61
+ /**
62
+ * Enable and control the Vite-based development server's prebundling capabilities. To
63
+ * enable prebundling, the Angular CLI cache must also be enabled. This option has no effect
64
+ * when using the 'browser' or other Webpack-based builders.
65
+ */
66
+ prebundle?: PrebundleUnion;
61
67
  /**
62
68
  * Proxy configuration file. For more information, see
63
69
  * https://angular.io/guide/build#proxying-to-a-backend-server.
@@ -94,3 +100,16 @@ export interface Schema {
94
100
  */
95
101
  watch?: boolean;
96
102
  }
103
+ /**
104
+ * Enable and control the Vite-based development server's prebundling capabilities. To
105
+ * enable prebundling, the Angular CLI cache must also be enabled. This option has no effect
106
+ * when using the 'browser' or other Webpack-based builders.
107
+ */
108
+ export type PrebundleUnion = boolean | PrebundleClass;
109
+ export interface PrebundleClass {
110
+ /**
111
+ * List of package imports that should not be prebundled by the development server. The
112
+ * packages will be bundled into the application code itself.
113
+ */
114
+ exclude: string[];
115
+ }
@@ -106,6 +106,24 @@
106
106
  "type": "boolean",
107
107
  "description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system.",
108
108
  "default": false
109
+ },
110
+ "prebundle": {
111
+ "description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders.",
112
+ "oneOf": [
113
+ { "type": "boolean" },
114
+ {
115
+ "type": "object",
116
+ "properties": {
117
+ "exclude": {
118
+ "description": "List of package imports that should not be prebundled by the development server. The packages will be bundled into the application code itself.",
119
+ "type": "array",
120
+ "items": { "type": "string" }
121
+ }
122
+ },
123
+ "additionalProperties": false,
124
+ "required": ["exclude"]
125
+ }
126
+ ]
109
127
  }
110
128
  },
111
129
  "additionalProperties": false,
@@ -66,16 +66,15 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
66
66
  // Avoid bundling and processing the ssr entry-point as this is not used by the dev-server.
67
67
  browserOptions.ssr = true;
68
68
  // https://nodejs.org/api/process.html#processsetsourcemapsenabledval
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
69
  process.setSourceMapsEnabled(true);
71
70
  }
72
71
  // Set all packages as external to support Vite's prebundle caching
73
- browserOptions.externalPackages = serverOptions.cacheOptions.enabled;
72
+ browserOptions.externalPackages = serverOptions.prebundle;
74
73
  const baseHref = browserOptions.baseHref;
75
74
  if (serverOptions.servePath === undefined && baseHref !== undefined) {
76
75
  // Remove trailing slash
77
76
  serverOptions.servePath =
78
- baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
77
+ baseHref !== './' && baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
79
78
  }
80
79
  // The development server currently only supports a single locale when localizing.
81
80
  // This matches the behavior of the Webpack-based development server but could be expanded in the future.
@@ -164,8 +163,9 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
164
163
  externalMetadata.implicitServer.length = 0;
165
164
  externalMetadata.implicitBrowser.length = 0;
166
165
  externalMetadata.explicit.push(...explicit);
167
- externalMetadata.implicitServer.push(...implicitServer);
168
- externalMetadata.implicitBrowser.push(...implicitBrowser);
166
+ // Remove any absolute URLs (http://, https://, //) to avoid Vite's prebundling from processing them as files
167
+ externalMetadata.implicitServer.push(...implicitServer.filter((value) => !/^(?:https?:)?\/\//.test(value)));
168
+ externalMetadata.implicitBrowser.push(...implicitBrowser.filter((value) => !/^(?:https?:)?\/\//.test(value)));
169
169
  // The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm.
170
170
  // See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239
171
171
  externalMetadata.explicit.sort();
@@ -341,7 +341,8 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
341
341
  publicDir: false,
342
342
  esbuild: false,
343
343
  mode: 'development',
344
- appType: 'mpa',
344
+ // We use custom as we do not rely on Vite's htmlFallbackMiddleware and indexHtmlMiddleware.
345
+ appType: 'custom',
345
346
  css: {
346
347
  devSourcemap: true,
347
348
  },
@@ -392,7 +393,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
392
393
  * - Breaks RxJs (Unless it is added as external). See: https://github.com/angular/angular-cli/issues/26235
393
394
  */
394
395
  // Only enable with caching since it causes prebundle dependencies to be cached
395
- disabled: true, // !serverOptions.cacheOptions.enabled,
396
+ disabled: true, // serverOptions.prebundle === false,
396
397
  // Exclude any explicitly defined dependencies (currently build defined externals and node.js built-ins)
397
398
  exclude: serverExplicitExternal,
398
399
  // Include all implict dependencies from the external packages internal option
@@ -422,7 +423,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
422
423
  // Browser only optimizeDeps. (This does not run for SSR dependencies).
423
424
  optimizeDeps: getDepOptimizationConfig({
424
425
  // Only enable with caching since it causes prebundle dependencies to be cached
425
- disabled: !serverOptions.cacheOptions.enabled,
426
+ disabled: serverOptions.prebundle === false,
426
427
  // Exclude any explicitly defined dependencies (currently build defined externals)
427
428
  exclude: externalMetadata.explicit,
428
429
  // Include all implict dependencies from the external packages internal option
@@ -62,8 +62,9 @@ function execute(options, context, transforms = {}) {
62
62
  return (0, build_webpack_1.runWebpack)(config, context, {
63
63
  webpackFactory: require('webpack'),
64
64
  logging: (stats, config) => {
65
- if (options.verbose) {
66
- context.logger.info(stats.toString(config.stats));
65
+ if (options.verbose && config.stats !== false) {
66
+ const statsOptions = config.stats === true ? undefined : config.stats;
67
+ context.logger.info(stats.toString(statsOptions));
67
68
  }
68
69
  },
69
70
  }).pipe((0, rxjs_1.concatMap)(async (output) => {
@@ -162,7 +162,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
162
162
  });
163
163
  referencedFileTracker.add(containingFile, Object.keys(workerResult.metafile.inputs).map((input) => path.join(build.initialOptions.absWorkingDir ?? '', input)));
164
164
  // Return bundled worker file entry name to be used in the built output
165
- const workerCodeFile = workerResult.outputFiles.find((file) => file.path.endsWith('.js'));
165
+ const workerCodeFile = workerResult.outputFiles.find((file) => /^worker-[A-Z0-9]{8}.[cm]?js$/.test(path.basename(file.path)));
166
166
  (0, node_assert_1.default)(workerCodeFile, 'Web Worker bundled code file should always be present.');
167
167
  const workerCodePath = path.relative(build.initialOptions.outdir ?? '', workerCodeFile.path);
168
168
  return workerCodePath.replaceAll('\\', '/');
@@ -53,9 +53,11 @@ function createBrowserCodeBundleOptions(options, target, sourceFileCache) {
53
53
  }
54
54
  if (options.externalPackages) {
55
55
  // Package files affected by a customized loader should not be implicitly marked as external
56
- if (options.loaderExtensions || options.plugins) {
56
+ if (options.loaderExtensions ||
57
+ options.plugins ||
58
+ typeof options.externalPackages === 'object') {
57
59
  // Plugin must be added after custom plugins to ensure any added loader options are considered
58
- buildOptions.plugins?.push((0, external_packages_plugin_1.createExternalPackagesPlugin)());
60
+ buildOptions.plugins?.push((0, external_packages_plugin_1.createExternalPackagesPlugin)(options.externalPackages !== true ? options.externalPackages : undefined));
59
61
  }
60
62
  else {
61
63
  // Safe to use the packages external option directly
@@ -265,6 +267,7 @@ function getEsBuildCommonOptions(options) {
265
267
  write: false,
266
268
  preserveSymlinks,
267
269
  define: {
270
+ ...options.define,
268
271
  // Only set to false when script optimizations are enabled. It should not be set to true because
269
272
  // Angular turns `ngDevMode` into an object for development debugging purposes when not defined
270
273
  // which a constant true value would break.
@@ -27,6 +27,7 @@ export interface InitialFileRecord {
27
27
  name?: string;
28
28
  type: 'script' | 'style';
29
29
  external?: boolean;
30
+ serverFile: boolean;
30
31
  }
31
32
  export declare enum BuildOutputFileType {
32
33
  Browser = 1,
@@ -162,7 +162,7 @@ class BundlerContext {
162
162
  // For non-incremental builds, perform a single build
163
163
  result = await (0, esbuild_1.build)(this.#esbuildOptions);
164
164
  }
165
- if (this.#esbuildOptions?.platform === 'node') {
165
+ if (this.#platformIsServer) {
166
166
  for (const entry of Object.values(result.metafile.outputs)) {
167
167
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
168
  entry['ng-platform-server'] = true;
@@ -232,6 +232,7 @@ class BundlerContext {
232
232
  name,
233
233
  type,
234
234
  entrypoint: true,
235
+ serverFile: this.#platformIsServer,
235
236
  };
236
237
  if (!this.initialFilter || this.initialFilter(record)) {
237
238
  initialFiles.set(relativeFilePath, record);
@@ -251,6 +252,7 @@ class BundlerContext {
251
252
  type: initialImport.kind === 'import-rule' ? 'style' : 'script',
252
253
  entrypoint: false,
253
254
  external: initialImport.external,
255
+ serverFile: this.#platformIsServer,
254
256
  };
255
257
  if (!this.initialFilter || this.initialFilter(record)) {
256
258
  initialFiles.set(initialImport.path, record);
@@ -275,8 +277,7 @@ class BundlerContext {
275
277
  }
276
278
  }
277
279
  (0, node_assert_1.default)(this.#esbuildOptions, 'esbuild options cannot be undefined.');
278
- const { platform, assetNames = '' } = this.#esbuildOptions;
279
- const platformIsServer = platform === 'node';
280
+ const { assetNames = '' } = this.#esbuildOptions;
280
281
  const mediaDirname = (0, node_path_1.dirname)(assetNames);
281
282
  const outputFiles = result.outputFiles.map((file) => {
282
283
  let fileType;
@@ -284,7 +285,9 @@ class BundlerContext {
284
285
  fileType = BuildOutputFileType.Media;
285
286
  }
286
287
  else {
287
- fileType = platformIsServer ? BuildOutputFileType.Server : BuildOutputFileType.Browser;
288
+ fileType = this.#platformIsServer
289
+ ? BuildOutputFileType.Server
290
+ : BuildOutputFileType.Browser;
288
291
  }
289
292
  return (0, utils_1.convertOutputFile)(file, fileType);
290
293
  });
@@ -294,7 +297,7 @@ class BundlerContext {
294
297
  outputFiles,
295
298
  initialFiles,
296
299
  externalImports: {
297
- [platformIsServer ? 'server' : 'browser']: externalImports,
300
+ [this.#platformIsServer ? 'server' : 'browser']: externalImports,
298
301
  },
299
302
  externalConfiguration: this.#esbuildOptions.external,
300
303
  errors: undefined,
@@ -314,6 +317,9 @@ class BundlerContext {
314
317
  }
315
318
  }
316
319
  }
320
+ get #platformIsServer() {
321
+ return this.#esbuildOptions?.platform === 'node';
322
+ }
317
323
  /**
318
324
  * Invalidate a stored bundler result based on the previous watch files
319
325
  * and a list of changed files.
@@ -13,4 +13,6 @@ import type { Plugin } from 'esbuild';
13
13
  *
14
14
  * @returns An esbuild plugin.
15
15
  */
16
- export declare function createExternalPackagesPlugin(): Plugin;
16
+ export declare function createExternalPackagesPlugin(options?: {
17
+ exclude?: string[];
18
+ }): Plugin;
@@ -17,22 +17,26 @@ const EXTERNAL_PACKAGE_RESOLUTION = Symbol('EXTERNAL_PACKAGE_RESOLUTION');
17
17
  *
18
18
  * @returns An esbuild plugin.
19
19
  */
20
- function createExternalPackagesPlugin() {
20
+ function createExternalPackagesPlugin(options) {
21
+ const exclusions = options?.exclude?.length ? new Set(options.exclude) : undefined;
21
22
  return {
22
23
  name: 'angular-external-packages',
23
24
  setup(build) {
24
- // Safe to use native packages external option if no loader options present
25
- if (build.initialOptions.loader === undefined ||
26
- Object.keys(build.initialOptions.loader).length === 0) {
25
+ const loaderOptionKeys = build.initialOptions.loader && Object.keys(build.initialOptions.loader);
26
+ // Safe to use native packages external option if no loader options or exclusions present
27
+ if (!exclusions && !loaderOptionKeys?.length) {
27
28
  build.initialOptions.packages = 'external';
28
29
  return;
29
30
  }
30
- const loaderFileExtensions = new Set(Object.keys(build.initialOptions.loader));
31
+ const loaderFileExtensions = new Set(loaderOptionKeys);
31
32
  // Only attempt resolve of non-relative and non-absolute paths
32
33
  build.onResolve({ filter: /^[^./]/ }, async (args) => {
33
34
  if (args.pluginData?.[EXTERNAL_PACKAGE_RESOLUTION]) {
34
35
  return null;
35
36
  }
37
+ if (exclusions?.has(args.path)) {
38
+ return null;
39
+ }
36
40
  const { importer, kind, resolveDir, namespace, pluginData = {} } = args;
37
41
  pluginData[EXTERNAL_PACKAGE_RESOLUTION] = true;
38
42
  const result = await build.resolve(args.path, {
@@ -47,7 +47,7 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
47
47
  (0, node_assert_1.default)(indexHtmlOptions, 'indexHtmlOptions cannot be undefined.');
48
48
  if (!externalPackages && indexHtmlOptions.preloadInitial) {
49
49
  for (const [key, value] of initialFiles) {
50
- if (value.entrypoint) {
50
+ if (value.entrypoint || value.serverFile) {
51
51
  // Entry points are already referenced in the HTML
52
52
  continue;
53
53
  }
@@ -94,7 +94,9 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
94
94
  baseHref,
95
95
  lang,
96
96
  outputPath: virtualOutputPath,
97
- files: [...initialFiles].map(([file, record]) => ({
97
+ files: [...initialFiles]
98
+ .filter(([, file]) => !file.serverFile)
99
+ .map(([file, record]) => ({
98
100
  name: record.name ?? '',
99
101
  file,
100
102
  extension: node_path_1.default.extname(file),
@@ -99,6 +99,19 @@ function createAngularMemoryPlugin(options) {
99
99
  next();
100
100
  return;
101
101
  }
102
+ // HTML fallbacking
103
+ // This matches what happens in the vite html fallback middleware.
104
+ // ref: https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/htmlFallback.ts#L9
105
+ const htmlAssetSourcePath = pathname[pathname.length - 1] === '/'
106
+ ? // Trailing slash check for `index.html`.
107
+ assets.get(pathname + 'index.html')
108
+ : // Non-trailing slash check for fallback `.html`
109
+ assets.get(pathname + '.html');
110
+ if (htmlAssetSourcePath) {
111
+ req.url = `${server.config.base}@fs/${encodeURI(htmlAssetSourcePath)}`;
112
+ next();
113
+ return;
114
+ }
102
115
  // Resource files are handled directly.
103
116
  // Global stylesheets (CSS files) are currently considered resources to workaround
104
117
  // dev server sourcemap issues with stylesheets.
@@ -419,8 +419,9 @@ function createWebpackLoggingCallback(options, logger) {
419
419
  ...(0, helpers_1.normalizeExtraEntryPoints)(scripts, 'scripts'),
420
420
  ];
421
421
  return (stats, config) => {
422
- if (verbose) {
423
- logger.info(stats.toString(config.stats));
422
+ if (verbose && config.stats !== false) {
423
+ const statsOptions = config.stats === true ? undefined : config.stats;
424
+ logger.info(stats.toString(statsOptions));
424
425
  }
425
426
  const rawStats = stats.toJson((0, helpers_1.getStatsOptions)(false));
426
427
  const webpackStats = {
@@ -6,6 +6,6 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  export interface PostcssConfiguration {
9
- plugins: [name: string, options?: object][];
9
+ plugins: [name: string, options?: object | string][];
10
10
  }
11
11
  export declare function loadPostcssConfiguration(workspaceRoot: string, projectRoot: string): Promise<PostcssConfiguration | undefined>;
@@ -67,7 +67,7 @@ async function loadPostcssConfiguration(workspaceRoot, projectRoot) {
67
67
  }
68
68
  const config = { plugins: [] };
69
69
  for (const [name, options] of entries) {
70
- if (!options || typeof options !== 'object') {
70
+ if (!options || (typeof options !== 'object' && typeof options !== 'string')) {
71
71
  continue;
72
72
  }
73
73
  config.plugins.push([name, options]);