@angular-devkit/build-angular 17.2.0-next.0 → 17.2.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/package.json +24 -25
  2. package/src/builders/application/build-action.d.ts +1 -0
  3. package/src/builders/application/build-action.js +18 -18
  4. package/src/builders/application/execute-build.js +8 -15
  5. package/src/builders/application/index.js +20 -3
  6. package/src/builders/application/options.d.ts +13 -2
  7. package/src/builders/application/options.js +16 -3
  8. package/src/builders/application/schema.d.ts +13 -0
  9. package/src/builders/application/schema.json +12 -0
  10. package/src/builders/application/setup-bundling.js +2 -2
  11. package/src/builders/browser/index.js +3 -2
  12. package/src/builders/dev-server/builder.js +13 -2
  13. package/src/builders/dev-server/options.d.ts +1 -0
  14. package/src/builders/dev-server/options.js +3 -1
  15. package/src/builders/dev-server/schema.d.ts +19 -0
  16. package/src/builders/dev-server/schema.json +18 -0
  17. package/src/builders/dev-server/vite-server.js +6 -6
  18. package/src/builders/extract-i18n/application-extraction.js +3 -1
  19. package/src/builders/server/index.js +3 -2
  20. package/src/tools/babel/plugins/elide-angular-metadata.d.ts +1 -1
  21. package/src/tools/babel/plugins/elide-angular-metadata.js +38 -30
  22. package/src/tools/babel/plugins/pure-toplevel-functions.d.ts +1 -1
  23. package/src/tools/babel/plugins/pure-toplevel-functions.js +3 -4
  24. package/src/tools/esbuild/angular/compiler-plugin.js +1 -1
  25. package/src/tools/esbuild/application-code-bundle.js +7 -4
  26. package/src/tools/esbuild/budget-stats.js +5 -0
  27. package/src/tools/esbuild/bundler-context.d.ts +1 -0
  28. package/src/tools/esbuild/bundler-context.js +19 -5
  29. package/src/tools/esbuild/bundler-execution-result.d.ts +2 -0
  30. package/src/tools/esbuild/bundler-execution-result.js +6 -0
  31. package/src/tools/esbuild/compiler-plugin-options.js +2 -1
  32. package/src/tools/esbuild/external-packages-plugin.d.ts +3 -1
  33. package/src/tools/esbuild/external-packages-plugin.js +9 -5
  34. package/src/tools/esbuild/global-scripts.js +3 -4
  35. package/src/tools/esbuild/global-styles.js +2 -1
  36. package/src/tools/esbuild/index-html-generator.js +4 -2
  37. package/src/tools/esbuild/stylesheets/bundle-options.d.ts +2 -0
  38. package/src/tools/esbuild/stylesheets/bundle-options.js +1 -0
  39. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.d.ts +7 -0
  40. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +24 -8
  41. package/src/tools/esbuild/utils.d.ts +5 -8
  42. package/src/tools/esbuild/utils.js +64 -31
  43. package/src/tools/vite/angular-memory-plugin.js +13 -0
  44. package/src/tools/webpack/utils/stats.d.ts +1 -0
  45. package/src/tools/webpack/utils/stats.js +101 -49
  46. package/src/utils/environment-options.d.ts +1 -0
  47. package/src/utils/environment-options.js +3 -1
  48. package/src/utils/load-proxy-config.js +3 -3
  49. package/src/utils/postcss-configuration.d.ts +11 -0
  50. package/src/utils/postcss-configuration.js +77 -0
@@ -20,9 +20,11 @@ async function extractMessages(options, builderName, context, extractorConstruct
20
20
  // Setup the build options for the application based on the buildTarget option
21
21
  const buildOptions = (await context.validateOptions(await context.getTargetOptions(options.buildTarget), builderName));
22
22
  buildOptions.optimization = false;
23
- buildOptions.sourceMap = { scripts: true, vendor: true };
23
+ buildOptions.sourceMap = { scripts: true, vendor: true, styles: false };
24
24
  buildOptions.localize = false;
25
25
  buildOptions.budgets = undefined;
26
+ buildOptions.index = false;
27
+ buildOptions.serviceWorker = false;
26
28
  let build;
27
29
  if (builderName === '@angular-devkit/build-angular:application') {
28
30
  build = application_1.buildApplicationInternal;
@@ -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) => {
@@ -7,7 +7,7 @@
7
7
  */
8
8
  /// <reference path="../../../../../../../../../../packages/angular_devkit/build_angular/src/babel-bazel.d.ts" />
9
9
  /// <reference types="@angular/compiler-cli/private/babel" />
10
- import { PluginObj } from '@babel/core';
10
+ import type { PluginObj } from '@babel/core';
11
11
  /**
12
12
  * Provides one or more keywords that if found within the content of a source file indicate
13
13
  * that this plugin should be used with a source file.
@@ -8,7 +8,6 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.getKeywords = void 0;
11
- const core_1 = require("@babel/core");
12
11
  /**
13
12
  * The name of the Angular class metadata function created by the Angular compiler.
14
13
  */
@@ -28,9 +27,18 @@ const SET_CLASS_DEBUG_INFO_NAME = 'ɵsetClassDebugInfo';
28
27
  * @returns An a string iterable containing one or more keywords.
29
28
  */
30
29
  function getKeywords() {
31
- return [SET_CLASS_METADATA_NAME, SET_CLASS_METADATA_ASYNC_NAME, SET_CLASS_DEBUG_INFO_NAME];
30
+ return Object.keys(angularMetadataFunctions);
32
31
  }
33
32
  exports.getKeywords = getKeywords;
33
+ /**
34
+ * An object map of function names and related value checks for discovery of Angular generated
35
+ * metadata calls.
36
+ */
37
+ const angularMetadataFunctions = {
38
+ [SET_CLASS_METADATA_NAME]: isSetClassMetadataCall,
39
+ [SET_CLASS_METADATA_ASYNC_NAME]: isSetClassMetadataAsyncCall,
40
+ [SET_CLASS_DEBUG_INFO_NAME]: isSetClassDebugInfoCall,
41
+ };
34
42
  /**
35
43
  * A babel plugin factory function for eliding the Angular class metadata function (`ɵsetClassMetadata`).
36
44
  *
@@ -40,20 +48,23 @@ function default_1() {
40
48
  return {
41
49
  visitor: {
42
50
  CallExpression(path) {
43
- const callee = path.node.callee;
44
- const callArguments = path.node.arguments;
51
+ const callee = path.get('callee');
45
52
  // The function being called must be the metadata function name
46
53
  let calleeName;
47
- if (core_1.types.isMemberExpression(callee) && core_1.types.isIdentifier(callee.property)) {
48
- calleeName = callee.property.name;
54
+ if (callee.isMemberExpression()) {
55
+ const calleeProperty = callee.get('property');
56
+ if (calleeProperty.isIdentifier()) {
57
+ calleeName = calleeProperty.node.name;
58
+ }
59
+ }
60
+ else if (callee.isIdentifier()) {
61
+ calleeName = callee.node.name;
49
62
  }
50
- else if (core_1.types.isIdentifier(callee)) {
51
- calleeName = callee.name;
63
+ if (!calleeName) {
64
+ return;
52
65
  }
53
- if (calleeName !== undefined &&
54
- (isRemoveClassMetadataCall(calleeName, callArguments) ||
55
- isRemoveClassmetadataAsyncCall(calleeName, callArguments) ||
56
- isSetClassDebugInfoCall(calleeName, callArguments))) {
66
+ if (Object.hasOwn(angularMetadataFunctions, calleeName) &&
67
+ angularMetadataFunctions[calleeName](path.get('arguments'))) {
57
68
  // The metadata function is always emitted inside a function expression
58
69
  const parent = path.getFunctionParent();
59
70
  if (parent && (parent.isFunctionExpression() || parent.isArrowFunctionExpression())) {
@@ -68,35 +79,32 @@ function default_1() {
68
79
  }
69
80
  exports.default = default_1;
70
81
  /** Determines if a function call is a call to `setClassMetadata`. */
71
- function isRemoveClassMetadataCall(name, args) {
82
+ function isSetClassMetadataCall(callArguments) {
72
83
  // `setClassMetadata` calls have to meet the following criteria:
73
84
  // * First must be an identifier
74
85
  // * Second must be an array literal
75
- return (name === SET_CLASS_METADATA_NAME &&
76
- args.length === 4 &&
77
- core_1.types.isIdentifier(args[0]) &&
78
- core_1.types.isArrayExpression(args[1]));
86
+ return (callArguments.length === 4 &&
87
+ callArguments[0].isIdentifier() &&
88
+ callArguments[1].isArrayExpression());
79
89
  }
80
90
  /** Determines if a function call is a call to `setClassMetadataAsync`. */
81
- function isRemoveClassmetadataAsyncCall(name, args) {
91
+ function isSetClassMetadataAsyncCall(callArguments) {
82
92
  // `setClassMetadataAsync` calls have to meet the following criteria:
83
93
  // * First argument must be an identifier.
84
94
  // * Second argument must be an inline function.
85
95
  // * Third argument must be an inline function.
86
- return (name === SET_CLASS_METADATA_ASYNC_NAME &&
87
- args.length === 3 &&
88
- core_1.types.isIdentifier(args[0]) &&
89
- isInlineFunction(args[1]) &&
90
- isInlineFunction(args[2]));
96
+ return (callArguments.length === 3 &&
97
+ callArguments[0].isIdentifier() &&
98
+ isInlineFunction(callArguments[1]) &&
99
+ isInlineFunction(callArguments[2]));
91
100
  }
92
101
  /** Determines if a function call is a call to `setClassDebugInfo`. */
93
- function isSetClassDebugInfoCall(name, args) {
94
- return (name === SET_CLASS_DEBUG_INFO_NAME &&
95
- args.length === 2 &&
96
- core_1.types.isIdentifier(args[0]) &&
97
- core_1.types.isObjectExpression(args[1]));
102
+ function isSetClassDebugInfoCall(callArguments) {
103
+ return (callArguments.length === 2 &&
104
+ callArguments[0].isIdentifier() &&
105
+ callArguments[1].isObjectExpression());
98
106
  }
99
107
  /** Determines if a node is an inline function expression. */
100
- function isInlineFunction(node) {
101
- return core_1.types.isFunctionExpression(node) || core_1.types.isArrowFunctionExpression(node);
108
+ function isInlineFunction(path) {
109
+ return path.isFunctionExpression() || path.isArrowFunctionExpression();
102
110
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
  /// <reference path="../../../../../../../../../../packages/angular_devkit/build_angular/src/babel-bazel.d.ts" />
9
9
  /// <reference types="@angular/compiler-cli/private/babel" />
10
- import { PluginObj } from '@babel/core';
10
+ import type { PluginObj } from '@babel/core';
11
11
  /**
12
12
  * A babel plugin factory function for adding the PURE annotation to top-level new and call expressions.
13
13
  *
@@ -33,7 +33,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- const core_1 = require("@babel/core");
37
36
  const helper_annotate_as_pure_1 = __importDefault(require("@babel/helper-annotate-as-pure"));
38
37
  const tslib = __importStar(require("tslib"));
39
38
  /**
@@ -67,14 +66,14 @@ function default_1() {
67
66
  if (path.getFunctionParent()) {
68
67
  return;
69
68
  }
70
- const callee = path.node.callee;
71
- if ((core_1.types.isFunctionExpression(callee) || core_1.types.isArrowFunctionExpression(callee)) &&
69
+ const callee = path.get('callee');
70
+ if ((callee.isFunctionExpression() || callee.isArrowFunctionExpression()) &&
72
71
  path.node.arguments.length !== 0) {
73
72
  return;
74
73
  }
75
74
  // Do not annotate TypeScript helpers emitted by the TypeScript compiler.
76
75
  // TypeScript helpers are intended to cause side effects.
77
- if (core_1.types.isIdentifier(callee) && isTslibHelperName(callee.name)) {
76
+ if (callee.isIdentifier() && isTslibHelperName(callee.node.name)) {
78
77
  return;
79
78
  }
80
79
  (0, helper_annotate_as_pure_1.default)(path);
@@ -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
@@ -230,7 +232,7 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
230
232
  }
231
233
  exports.createServerPolyfillBundleOptions = createServerPolyfillBundleOptions;
232
234
  function getEsBuildCommonOptions(options) {
233
- const { workspaceRoot, outExtension, optimizationOptions, sourcemapOptions, tsconfig, externalDependencies, outputNames, preserveSymlinks, jit, loaderExtensions, } = options;
235
+ const { workspaceRoot, outExtension, optimizationOptions, sourcemapOptions, tsconfig, externalDependencies, outputNames, preserveSymlinks, jit, loaderExtensions, jsonLogs, } = options;
234
236
  // Ensure unique hashes for i18n translation changes when using post-process inlining.
235
237
  // This hash value is added as a footer to each file and ensures that the output file names (with hashes)
236
238
  // change when translation files have changed. If this is not done the post processed files may have
@@ -250,7 +252,7 @@ function getEsBuildCommonOptions(options) {
250
252
  resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
251
253
  metafile: true,
252
254
  legalComments: options.extractLicenses ? 'none' : 'eof',
253
- logLevel: options.verbose ? 'debug' : 'silent',
255
+ logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
254
256
  minifyIdentifiers: optimizationOptions.scripts && environment_options_1.allowMangle,
255
257
  minifySyntax: optimizationOptions.scripts,
256
258
  minifyWhitespace: optimizationOptions.scripts,
@@ -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.
@@ -26,6 +26,11 @@ function generateBudgetStats(metafile, initialFiles) {
26
26
  if (!file.endsWith('.js') && !file.endsWith('.css')) {
27
27
  continue;
28
28
  }
29
+ // Exclude server bundles
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ if (entry['ng-platform-server']) {
32
+ continue;
33
+ }
29
34
  const initialRecord = initialFiles.get(file);
30
35
  let name = initialRecord?.name;
31
36
  if (name === undefined && entry.entryPoint) {
@@ -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,6 +162,12 @@ 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.#platformIsServer) {
166
+ for (const entry of Object.values(result.metafile.outputs)) {
167
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
+ entry['ng-platform-server'] = true;
169
+ }
170
+ }
165
171
  }
166
172
  catch (failure) {
167
173
  // Build failures will throw an exception which contains errors/warnings
@@ -226,6 +232,7 @@ class BundlerContext {
226
232
  name,
227
233
  type,
228
234
  entrypoint: true,
235
+ serverFile: this.#platformIsServer,
229
236
  };
230
237
  if (!this.initialFilter || this.initialFilter(record)) {
231
238
  initialFiles.set(relativeFilePath, record);
@@ -245,6 +252,7 @@ class BundlerContext {
245
252
  type: initialImport.kind === 'import-rule' ? 'style' : 'script',
246
253
  entrypoint: false,
247
254
  external: initialImport.external,
255
+ serverFile: this.#platformIsServer,
248
256
  };
249
257
  if (!this.initialFilter || this.initialFilter(record)) {
250
258
  initialFiles.set(initialImport.path, record);
@@ -260,15 +268,16 @@ class BundlerContext {
260
268
  for (const { imports } of Object.values(result.metafile.outputs)) {
261
269
  for (const importData of imports) {
262
270
  if (!importData.external ||
263
- (importData.kind !== 'import-statement' && importData.kind !== 'dynamic-import')) {
271
+ (importData.kind !== 'import-statement' &&
272
+ importData.kind !== 'dynamic-import' &&
273
+ importData.kind !== 'require-call')) {
264
274
  continue;
265
275
  }
266
276
  externalImports.add(importData.path);
267
277
  }
268
278
  }
269
279
  (0, node_assert_1.default)(this.#esbuildOptions, 'esbuild options cannot be undefined.');
270
- const { platform, assetNames = '' } = this.#esbuildOptions;
271
- const platformIsServer = platform === 'node';
280
+ const { assetNames = '' } = this.#esbuildOptions;
272
281
  const mediaDirname = (0, node_path_1.dirname)(assetNames);
273
282
  const outputFiles = result.outputFiles.map((file) => {
274
283
  let fileType;
@@ -276,7 +285,9 @@ class BundlerContext {
276
285
  fileType = BuildOutputFileType.Media;
277
286
  }
278
287
  else {
279
- fileType = platformIsServer ? BuildOutputFileType.Server : BuildOutputFileType.Browser;
288
+ fileType = this.#platformIsServer
289
+ ? BuildOutputFileType.Server
290
+ : BuildOutputFileType.Browser;
280
291
  }
281
292
  return (0, utils_1.convertOutputFile)(file, fileType);
282
293
  });
@@ -286,7 +297,7 @@ class BundlerContext {
286
297
  outputFiles,
287
298
  initialFiles,
288
299
  externalImports: {
289
- [platformIsServer ? 'server' : 'browser']: externalImports,
300
+ [this.#platformIsServer ? 'server' : 'browser']: externalImports,
290
301
  },
291
302
  externalConfiguration: this.#esbuildOptions.external,
292
303
  errors: undefined,
@@ -306,6 +317,9 @@ class BundlerContext {
306
317
  }
307
318
  }
308
319
  }
320
+ get #platformIsServer() {
321
+ return this.#esbuildOptions?.platform === 'node';
322
+ }
309
323
  /**
310
324
  * Invalidate a stored bundler result based on the previous watch files
311
325
  * and a list of changed files.
@@ -33,6 +33,7 @@ export declare class ExecutionResult {
33
33
  outputFiles: BuildOutputFile[];
34
34
  assetFiles: BuildOutputAsset[];
35
35
  errors: (Message | PartialMessage)[];
36
+ prerenderedRoutes: string[];
36
37
  warnings: (Message | PartialMessage)[];
37
38
  externalMetadata?: ExternalResultMetadata;
38
39
  constructor(rebuildContexts: BundlerContext[], codeBundleCache?: SourceFileCache | undefined);
@@ -40,6 +41,7 @@ export declare class ExecutionResult {
40
41
  addAssets(assets: BuildOutputAsset[]): void;
41
42
  addError(error: PartialMessage | string): void;
42
43
  addErrors(errors: (PartialMessage | string)[]): void;
44
+ addPrerenderedRoutes(routes: string[]): void;
43
45
  addWarning(error: PartialMessage | string): void;
44
46
  addWarnings(errors: (PartialMessage | string)[]): void;
45
47
  /**
@@ -19,6 +19,7 @@ class ExecutionResult {
19
19
  outputFiles = [];
20
20
  assetFiles = [];
21
21
  errors = [];
22
+ prerenderedRoutes = [];
22
23
  warnings = [];
23
24
  externalMetadata;
24
25
  constructor(rebuildContexts, codeBundleCache) {
@@ -44,6 +45,11 @@ class ExecutionResult {
44
45
  this.addError(error);
45
46
  }
46
47
  }
48
+ addPrerenderedRoutes(routes) {
49
+ this.prerenderedRoutes.push(...routes);
50
+ // Sort the prerendered routes.
51
+ this.prerenderedRoutes.sort((a, b) => a.localeCompare(b));
52
+ }
47
53
  addWarning(error) {
48
54
  if (typeof error === 'string') {
49
55
  this.warnings.push({ text: error, location: null });
@@ -9,7 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createCompilerPluginOptions = void 0;
11
11
  function createCompilerPluginOptions(options, target, sourceFileCache) {
12
- const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, publicPath, } = options;
12
+ const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, postcssConfiguration, publicPath, } = options;
13
13
  return {
14
14
  // JS/TS options
15
15
  pluginOptions: {
@@ -40,6 +40,7 @@ function createCompilerPluginOptions(options, target, sourceFileCache) {
40
40
  inlineStyleLanguage,
41
41
  preserveSymlinks,
42
42
  tailwindConfiguration,
43
+ postcssConfiguration,
43
44
  cacheOptions,
44
45
  publicPath,
45
46
  },
@@ -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, {
@@ -49,7 +49,7 @@ const virtual_module_plugin_1 = require("./virtual-module-plugin");
49
49
  * @returns An esbuild BuildOptions object.
50
50
  */
51
51
  function createGlobalScriptsBundleOptions(options, target, initial) {
52
- const { globalScripts, optimizationOptions, outputNames, preserveSymlinks, sourcemapOptions, workspaceRoot, } = options;
52
+ const { globalScripts, optimizationOptions, outputNames, preserveSymlinks, sourcemapOptions, jsonLogs, workspaceRoot, } = options;
53
53
  const namespace = 'angular:script/global';
54
54
  const entryPoints = {};
55
55
  let found = false;
@@ -74,7 +74,7 @@ function createGlobalScriptsBundleOptions(options, target, initial) {
74
74
  mainFields: ['script', 'browser', 'main'],
75
75
  conditions: ['script'],
76
76
  resolveExtensions: ['.mjs', '.js'],
77
- logLevel: options.verbose ? 'debug' : 'silent',
77
+ logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
78
78
  metafile: true,
79
79
  minify: optimizationOptions.scripts,
80
80
  outdir: workspaceRoot,
@@ -91,8 +91,7 @@ function createGlobalScriptsBundleOptions(options, target, initial) {
91
91
  // Add the `js` extension here so that esbuild generates an output file with the extension
92
92
  transformPath: (path) => path.slice(namespace.length + 1) + '.js',
93
93
  loadContent: (args, build) => (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
94
- const files = globalScripts.find(({ name }) => name === args.path.slice(0, -3))
95
- ?.files;
94
+ const files = globalScripts.find(({ name }) => name === args.path.slice(0, -3))?.files;
96
95
  (0, node_assert_1.default)(files, `Invalid operation: global scripts name not found [${args.path}]`);
97
96
  // Global scripts are concatenated using magic-string instead of bundled via esbuild.
98
97
  const bundleContent = new magic_string_1.Bundle();
@@ -15,7 +15,7 @@ const node_assert_1 = __importDefault(require("node:assert"));
15
15
  const bundle_options_1 = require("./stylesheets/bundle-options");
16
16
  const virtual_module_plugin_1 = require("./virtual-module-plugin");
17
17
  function createGlobalStylesBundleOptions(options, target, initial) {
18
- const { workspaceRoot, optimizationOptions, sourcemapOptions, outputNames, globalStyles, preserveSymlinks, externalDependencies, stylePreprocessorOptions, tailwindConfiguration, cacheOptions, publicPath, } = options;
18
+ const { workspaceRoot, optimizationOptions, sourcemapOptions, outputNames, globalStyles, preserveSymlinks, externalDependencies, stylePreprocessorOptions, tailwindConfiguration, postcssConfiguration, cacheOptions, publicPath, } = options;
19
19
  const namespace = 'angular:styles/global';
20
20
  const entryPoints = {};
21
21
  let found = false;
@@ -46,6 +46,7 @@ function createGlobalStylesBundleOptions(options, target, initial) {
46
46
  },
47
47
  includePaths: stylePreprocessorOptions?.includePaths,
48
48
  tailwindConfiguration,
49
+ postcssConfiguration,
49
50
  cacheOptions,
50
51
  publicPath,
51
52
  }, loadCache);
@@ -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),
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { BuildOptions } from 'esbuild';
9
9
  import { NormalizedCachedOptions } from '../../../utils/normalize-cache';
10
+ import { PostcssConfiguration } from '../../../utils/postcss-configuration';
10
11
  import { LoadResultCache } from '../load-result-cache';
11
12
  export interface BundleStylesheetOptions {
12
13
  workspaceRoot: string;
@@ -25,6 +26,7 @@ export interface BundleStylesheetOptions {
25
26
  file: string;
26
27
  package: string;
27
28
  };
29
+ postcssConfiguration?: PostcssConfiguration;
28
30
  publicPath?: string;
29
31
  cacheOptions: NormalizedCachedOptions;
30
32
  }
@@ -26,6 +26,7 @@ function createStylesheetBundleOptions(options, cache, inlineComponentData) {
26
26
  includePaths,
27
27
  inlineComponentData,
28
28
  tailwindConfiguration: options.tailwindConfiguration,
29
+ postcssConfiguration: options.postcssConfiguration,
29
30
  }, cache);
30
31
  const plugins = [
31
32
  pluginFactory.create(sass_language_1.SassStylesheetLanguage),
@@ -6,6 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
9
+ import type { PostcssConfiguration } from '../../../utils/postcss-configuration';
9
10
  import { LoadResultCache } from '../load-result-cache';
10
11
  /**
11
12
  * An object containing the plugin options to use when processing stylesheets.
@@ -35,6 +36,12 @@ export interface StylesheetPluginOptions {
35
36
  file: string;
36
37
  package: string;
37
38
  };
39
+ /**
40
+ * Optional configuration object for custom postcss usage. If present, postcss will be
41
+ * initialized and used for every stylesheet. This overrides the tailwind integration
42
+ * and any tailwind usage must be manually configured in the custom postcss usage.
43
+ */
44
+ postcssConfiguration?: PostcssConfiguration;
38
45
  }
39
46
  export interface StylesheetLanguage {
40
47
  name: string;
@@ -72,7 +72,9 @@ class StylesheetPluginFactory {
72
72
  }
73
73
  create(language) {
74
74
  // Return a noop plugin if no load actions are required
75
- if (!language.process && !this.options.tailwindConfiguration) {
75
+ if (!language.process &&
76
+ !this.options.postcssConfiguration &&
77
+ !this.options.tailwindConfiguration) {
76
78
  return {
77
79
  name: 'angular-' + language.name,
78
80
  setup() { },
@@ -84,7 +86,23 @@ class StylesheetPluginFactory {
84
86
  if (this.postcssProcessor) {
85
87
  return this.postcssProcessor;
86
88
  }
87
- if (options.tailwindConfiguration) {
89
+ if (options.postcssConfiguration) {
90
+ const postCssInstanceKey = JSON.stringify(options.postcssConfiguration);
91
+ this.postcssProcessor = postcssProcessor.get(postCssInstanceKey)?.deref();
92
+ if (!this.postcssProcessor) {
93
+ postcss ??= (await Promise.resolve().then(() => __importStar(require('postcss')))).default;
94
+ this.postcssProcessor = postcss();
95
+ for (const [pluginName, pluginOptions] of options.postcssConfiguration.plugins) {
96
+ const { default: plugin } = await Promise.resolve(`${pluginName}`).then(s => __importStar(require(s)));
97
+ if (typeof plugin !== 'function' || plugin.postcss !== true) {
98
+ throw new Error(`Attempted to load invalid Postcss plugin: "${pluginName}"`);
99
+ }
100
+ this.postcssProcessor.use(plugin(pluginOptions));
101
+ }
102
+ postcssProcessor.set(postCssInstanceKey, new WeakRef(this.postcssProcessor));
103
+ }
104
+ }
105
+ else if (options.tailwindConfiguration) {
88
106
  const { package: tailwindPackage, file: config } = options.tailwindConfiguration;
89
107
  const postCssInstanceKey = tailwindPackage + ':' + config;
90
108
  this.postcssProcessor = postcssProcessor.get(postCssInstanceKey)?.deref();
@@ -132,14 +150,12 @@ async function processStylesheet(language, data, filename, format, options, buil
132
150
  watchFiles: [filename],
133
151
  };
134
152
  }
135
- // Return early if there are no contents to further process
136
- if (!result.contents) {
153
+ // Return early if there are no contents to further process or there are errors
154
+ if (!result.contents || result.errors?.length) {
137
155
  return result;
138
156
  }
139
- // Only use postcss if Tailwind processing is required.
140
- // NOTE: If postcss is used for more than just Tailwind in the future this check MUST
141
- // be updated to account for the additional use.
142
- if (postcssProcessor && !result.errors?.length && hasTailwindKeywords(result.contents)) {
157
+ // Only use postcss if Tailwind processing is required or custom postcss is present.
158
+ if (postcssProcessor && (options.postcssConfiguration || hasTailwindKeywords(result.contents))) {
143
159
  const postcssResult = await compileString(typeof result.contents === 'string'
144
160
  ? result.contents
145
161
  : Buffer.from(result.contents).toString('utf-8'), filename, postcssProcessor, options);
@@ -6,19 +6,15 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import { logging } from '@angular-devkit/core';
9
- import { BuildOptions, Metafile, OutputFile, PartialMessage } from 'esbuild';
10
- import { NormalizedOutputOptions } from '../../builders/application/options';
9
+ import { BuildOptions, Metafile, OutputFile } from 'esbuild';
10
+ import { NormalizedApplicationBuildOptions, NormalizedOutputOptions } from '../../builders/application/options';
11
11
  import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
12
12
  import { BuildOutputFile, BuildOutputFileType, InitialFileRecord } from './bundler-context';
13
- import { BuildOutputAsset } from './bundler-execution-result';
14
- export declare function logBuildStats(logger: logging.LoggerApi, metafile: Metafile, initial: Map<string, InitialFileRecord>, budgetFailures: BudgetCalculatorResult[] | undefined, changedFiles?: Set<string>, estimatedTransferSizes?: Map<string, number>): void;
13
+ import { BuildOutputAsset, ExecutionResult } from './bundler-execution-result';
14
+ export declare function logBuildStats(metafile: Metafile, initial: Map<string, InitialFileRecord>, budgetFailures: BudgetCalculatorResult[] | undefined, colors: boolean, changedFiles?: Set<string>, estimatedTransferSizes?: Map<string, number>, ssrOutputEnabled?: boolean, verbose?: boolean): string;
15
15
  export declare function calculateEstimatedTransferSizes(outputFiles: OutputFile[]): Promise<Map<string, number>>;
16
16
  export declare function withSpinner<T>(text: string, action: () => T | Promise<T>): Promise<T>;
17
17
  export declare function withNoProgress<T>(text: string, action: () => T | Promise<T>): Promise<T>;
18
- export declare function logMessages(logger: logging.LoggerApi, { errors, warnings }: {
19
- errors?: PartialMessage[];
20
- warnings?: PartialMessage[];
21
- }): Promise<void>;
22
18
  /**
23
19
  * Generates a syntax feature object map for Angular applications based on a list of targets.
24
20
  * A full set of feature names can be found here: https://esbuild.github.io/api/#supported
@@ -41,3 +37,4 @@ export declare function transformSupportedBrowsersToTargets(supportedBrowsers: s
41
37
  * @see https://esbuild.github.io/api/#target
42
38
  */
43
39
  export declare function getSupportedNodeTargets(): string[];
40
+ export declare function logMessages(logger: logging.LoggerApi, executionResult: ExecutionResult, options: NormalizedApplicationBuildOptions): Promise<void>;