@angular-devkit/build-angular 17.1.0-next.0 → 17.1.0-next.2

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 (38) hide show
  1. package/package.json +22 -19
  2. package/src/builders/application/build-action.d.ts +1 -0
  3. package/src/builders/application/build-action.js +20 -12
  4. package/src/builders/application/execute-post-bundle.js +8 -4
  5. package/src/builders/application/index.js +9 -1
  6. package/src/builders/application/options.js +1 -1
  7. package/src/builders/browser-esbuild/builder-status-warnings.js +1 -3
  8. package/src/builders/dev-server/builder.js +5 -0
  9. package/src/builders/dev-server/vite-server.js +12 -5
  10. package/src/builders/karma/index.js +4 -1
  11. package/src/builders/karma/schema.d.ts +6 -2
  12. package/src/builders/karma/schema.json +12 -2
  13. package/src/builders/ssr-dev-server/index.js +27 -18
  14. package/src/tools/esbuild/angular/angular-host.js +6 -0
  15. package/src/tools/esbuild/angular/compiler-plugin.js +1 -1
  16. package/src/tools/esbuild/angular/component-stylesheets.js +1 -1
  17. package/src/tools/esbuild/angular/jit-plugin-callbacks.d.ts +2 -1
  18. package/src/tools/esbuild/angular/jit-plugin-callbacks.js +15 -15
  19. package/src/tools/esbuild/angular/source-file-cache.js +0 -1
  20. package/src/tools/esbuild/compiler-plugin-options.js +4 -2
  21. package/src/tools/esbuild/global-styles.js +4 -2
  22. package/src/tools/esbuild/i18n-locale-plugin.d.ts +4 -0
  23. package/src/tools/esbuild/i18n-locale-plugin.js +48 -18
  24. package/src/tools/esbuild/stylesheets/bundle-options.d.ts +3 -0
  25. package/src/tools/esbuild/stylesheets/bundle-options.js +11 -6
  26. package/src/tools/esbuild/stylesheets/css-inline-fonts-plugin.d.ts +25 -0
  27. package/src/tools/esbuild/stylesheets/css-inline-fonts-plugin.js +57 -0
  28. package/src/tools/esbuild/watcher.d.ts +1 -0
  29. package/src/tools/esbuild/watcher.js +56 -106
  30. package/src/tools/sass/rebasing-importer.js +29 -4
  31. package/src/utils/check-port.js +15 -29
  32. package/src/utils/delete-output-dir.d.ts +1 -1
  33. package/src/utils/delete-output-dir.js +11 -2
  34. package/src/utils/index-file/inline-fonts.d.ts +6 -1
  35. package/src/utils/index-file/inline-fonts.js +30 -14
  36. package/src/utils/server-rendering/esm-in-memory-loader/node-18-utils.js +6 -5
  37. package/src/utils/server-rendering/esm-in-memory-loader/register-hooks.js +1 -3
  38. package/src/utils/spinner.js +1 -1
package/package.json CHANGED
@@ -1,38 +1,36 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "17.1.0-next.0",
3
+ "version": "17.1.0-next.2",
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.1701.0-next.0",
11
- "@angular-devkit/build-webpack": "0.1701.0-next.0",
12
- "@angular-devkit/core": "17.1.0-next.0",
13
- "@babel/core": "7.23.5",
14
- "@babel/generator": "7.23.5",
10
+ "@angular-devkit/architect": "0.1701.0-next.2",
11
+ "@angular-devkit/build-webpack": "0.1701.0-next.2",
12
+ "@angular-devkit/core": "17.1.0-next.2",
13
+ "@babel/core": "7.23.6",
14
+ "@babel/generator": "7.23.6",
15
15
  "@babel/helper-annotate-as-pure": "7.22.5",
16
16
  "@babel/helper-split-export-declaration": "7.22.6",
17
17
  "@babel/plugin-transform-async-generator-functions": "7.23.4",
18
18
  "@babel/plugin-transform-async-to-generator": "7.23.3",
19
- "@babel/plugin-transform-runtime": "7.23.4",
20
- "@babel/preset-env": "7.23.5",
21
- "@babel/runtime": "7.23.5",
19
+ "@babel/plugin-transform-runtime": "7.23.6",
20
+ "@babel/preset-env": "7.23.6",
21
+ "@babel/runtime": "7.23.6",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "17.1.0-next.0",
23
+ "@ngtools/webpack": "17.1.0-next.2",
24
24
  "@vitejs/plugin-basic-ssl": "1.0.2",
25
25
  "ansi-colors": "4.1.3",
26
26
  "autoprefixer": "10.4.16",
27
27
  "babel-loader": "9.1.3",
28
28
  "babel-plugin-istanbul": "6.1.1",
29
29
  "browserslist": "^4.21.5",
30
- "browser-sync": "2.29.3",
31
- "chokidar": "3.5.3",
32
30
  "copy-webpack-plugin": "11.0.0",
33
31
  "critters": "0.0.20",
34
32
  "css-loader": "6.8.1",
35
- "esbuild-wasm": "0.19.8",
33
+ "esbuild-wasm": "0.19.9",
36
34
  "fast-glob": "3.3.2",
37
35
  "https-proxy-agent": "7.0.2",
38
36
  "http-proxy-middleware": "2.0.6",
@@ -50,8 +48,8 @@
50
48
  "ora": "5.4.1",
51
49
  "parse5-html-rewriting-stream": "7.0.0",
52
50
  "picomatch": "3.0.1",
53
- "piscina": "4.2.0",
54
- "postcss": "8.4.31",
51
+ "piscina": "4.2.1",
52
+ "postcss": "8.4.32",
55
53
  "postcss-loader": "7.3.3",
56
54
  "resolve-url-loader": "5.0.0",
57
55
  "rxjs": "7.8.1",
@@ -60,12 +58,13 @@
60
58
  "semver": "7.5.4",
61
59
  "source-map-loader": "4.0.1",
62
60
  "source-map-support": "0.5.21",
63
- "terser": "5.24.0",
61
+ "terser": "5.26.0",
64
62
  "text-table": "0.2.0",
65
63
  "tree-kill": "1.2.2",
66
64
  "tslib": "2.6.2",
67
- "undici": "5.28.1",
68
- "vite": "5.0.4",
65
+ "undici": "6.0.1",
66
+ "vite": "5.0.7",
67
+ "watchpack": "2.4.0",
69
68
  "webpack": "5.89.0",
70
69
  "webpack-dev-middleware": "6.1.1",
71
70
  "webpack-dev-server": "4.15.1",
@@ -73,13 +72,14 @@
73
72
  "webpack-subresource-integrity": "5.1.0"
74
73
  },
75
74
  "optionalDependencies": {
76
- "esbuild": "0.19.8"
75
+ "esbuild": "0.19.9"
77
76
  },
78
77
  "peerDependencies": {
79
78
  "@angular/compiler-cli": "^17.0.0 || ^17.1.0-next.0",
80
79
  "@angular/localize": "^17.0.0 || ^17.1.0-next.0",
81
80
  "@angular/platform-server": "^17.0.0 || ^17.1.0-next.0",
82
81
  "@angular/service-worker": "^17.0.0 || ^17.1.0-next.0",
82
+ "browser-sync": "^2.29.3",
83
83
  "jest": "^29.5.0",
84
84
  "jest-environment-jsdom": "^29.5.0",
85
85
  "karma": "^6.3.0",
@@ -98,6 +98,9 @@
98
98
  "@angular/service-worker": {
99
99
  "optional": true
100
100
  },
101
+ "browser-sync": {
102
+ "optional": true
103
+ },
101
104
  "jest": {
102
105
  "optional": true
103
106
  },
@@ -24,4 +24,5 @@ export declare function runEsBuildBuildAction(action: (rebuildState?: RebuildSta
24
24
  deleteOutputPath?: boolean;
25
25
  poll?: number;
26
26
  signal?: AbortSignal;
27
+ preserveSymlinks?: boolean;
27
28
  }): AsyncIterable<(ExecutionResult['outputWithFiles'] | ExecutionResult['output']) & BuilderOutput>;
@@ -34,15 +34,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runEsBuildBuildAction = void 0;
37
+ const node_fs_1 = require("node:fs");
37
38
  const node_path_1 = __importDefault(require("node:path"));
38
39
  const sass_language_1 = require("../../tools/esbuild/stylesheets/sass-language");
39
40
  const utils_1 = require("../../tools/esbuild/utils");
40
41
  const delete_output_dir_1 = require("../../utils/delete-output-dir");
41
42
  const environment_options_1 = require("../../utils/environment-options");
42
43
  async function* runEsBuildBuildAction(action, options) {
43
- const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, } = options;
44
+ const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, preserveSymlinks, } = options;
44
45
  if (deleteOutputPath && writeToFileSystem) {
45
- await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputPath);
46
+ await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputPath, ['browser', 'server']);
46
47
  }
47
48
  const withProgress = progress ? utils_1.withSpinner : utils_1.withNoProgress;
48
49
  // Initial build
@@ -62,20 +63,25 @@ async function* runEsBuildBuildAction(action, options) {
62
63
  if (progress) {
63
64
  logger.info('Watch mode enabled. Watching for file changes...');
64
65
  }
66
+ const ignored = [
67
+ // Ignore the output and cache paths to avoid infinite rebuild cycles
68
+ outputPath,
69
+ cacheOptions.basePath,
70
+ `${workspaceRoot.replace(/\\/g, '/')}/**/.*/**`,
71
+ ];
72
+ if (!preserveSymlinks) {
73
+ // Ignore all node modules directories to avoid excessive file watchers.
74
+ // Package changes are handled below by watching manifest and lock files.
75
+ // NOTE: this is not enable when preserveSymlinks is true as this would break `npm link` usages.
76
+ ignored.push('**/node_modules/**');
77
+ }
65
78
  // Setup a watcher
66
79
  const { createWatcher } = await Promise.resolve().then(() => __importStar(require('../../tools/esbuild/watcher')));
67
80
  watcher = createWatcher({
68
81
  polling: typeof poll === 'number',
69
82
  interval: poll,
70
- ignored: [
71
- // Ignore the output and cache paths to avoid infinite rebuild cycles
72
- outputPath,
73
- cacheOptions.basePath,
74
- // Ignore all node modules directories to avoid excessive file watchers.
75
- // Package changes are handled below by watching manifest and lock files.
76
- '**/node_modules/**',
77
- `${workspaceRoot.replace(/\\/g, '/')}/**/.*/**`,
78
- ],
83
+ followSymlinks: preserveSymlinks,
84
+ ignored,
79
85
  });
80
86
  // Setup abort support
81
87
  options.signal?.addEventListener('abort', () => void watcher?.close());
@@ -96,7 +102,9 @@ async function* runEsBuildBuildAction(action, options) {
96
102
  '.pnp.cjs',
97
103
  '.pnp.data.json',
98
104
  ];
99
- watcher.add(packageWatchFiles.map((file) => node_path_1.default.join(workspaceRoot, file)));
105
+ watcher.add(packageWatchFiles
106
+ .map((file) => node_path_1.default.join(workspaceRoot, file))
107
+ .filter((file) => (0, node_fs_1.existsSync)(file)));
100
108
  // Watch locations provided by the initial build result
101
109
  watcher.add(result.watchFiles);
102
110
  }
@@ -39,9 +39,11 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
39
39
  * NOTE: we don't perform critical CSS inlining as this will be done during server rendering.
40
40
  */
41
41
  let indexContentOutputNoCssInlining;
42
+ // When using prerender/app-shell the index HTML file can be regenerated.
43
+ // Thus, we use a Map so that we do not generate 2 files with the same filename.
44
+ const additionalHtmlOutputFiles = new Map();
42
45
  // Generate index HTML file
43
46
  // If localization is enabled, index generation is handled in the inlining process.
44
- // NOTE: Localization with SSR is not currently supported.
45
47
  if (indexHtmlOptions) {
46
48
  const { content, contentWithoutCriticalCssInlined, errors, warnings } = await (0, index_html_generator_1.generateIndexHtml)(initialFiles, outputFiles, {
47
49
  ...options,
@@ -50,9 +52,10 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
50
52
  indexContentOutputNoCssInlining = contentWithoutCriticalCssInlined;
51
53
  allErrors.push(...errors);
52
54
  allWarnings.push(...warnings);
53
- additionalOutputFiles.push((0, utils_1.createOutputFileFromText)(indexHtmlOptions.output, content, bundler_context_1.BuildOutputFileType.Browser));
55
+ additionalHtmlOutputFiles.set(indexHtmlOptions.output, (0, utils_1.createOutputFileFromText)(indexHtmlOptions.output, content, bundler_context_1.BuildOutputFileType.Browser));
54
56
  if (ssrOptions) {
55
- additionalOutputFiles.push((0, utils_1.createOutputFileFromText)('index.server.html', contentWithoutCriticalCssInlined, bundler_context_1.BuildOutputFileType.Server));
57
+ const serverIndexHtmlFilename = 'index.server.html';
58
+ additionalHtmlOutputFiles.set(serverIndexHtmlFilename, (0, utils_1.createOutputFileFromText)(serverIndexHtmlFilename, contentWithoutCriticalCssInlined, bundler_context_1.BuildOutputFileType.Server));
56
59
  }
57
60
  }
58
61
  // Pre-render (SSG) and App-shell
@@ -64,9 +67,10 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
64
67
  allWarnings.push(...warnings);
65
68
  prerenderedRoutes.push(...Array.from(generatedRoutes));
66
69
  for (const [path, content] of Object.entries(output)) {
67
- additionalOutputFiles.push((0, utils_1.createOutputFileFromText)(path, content, bundler_context_1.BuildOutputFileType.Browser));
70
+ additionalHtmlOutputFiles.set(path, (0, utils_1.createOutputFileFromText)(path, content, bundler_context_1.BuildOutputFileType.Browser));
68
71
  }
69
72
  }
73
+ additionalOutputFiles.push(...additionalHtmlOutputFiles.values());
70
74
  // Augment the application with service worker support
71
75
  // If localization is enabled, service worker is handled in the inlining process.
72
76
  if (serviceWorker) {
@@ -29,6 +29,13 @@ context, infrastructureSettings, plugins) {
29
29
  return;
30
30
  }
31
31
  const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, options, plugins);
32
+ // Setup an abort controller with a builder teardown if no signal is present
33
+ let signal = context.signal;
34
+ if (!signal) {
35
+ const controller = new AbortController();
36
+ signal = controller.signal;
37
+ context.addTeardown(() => controller.abort('builder-teardown'));
38
+ }
32
39
  yield* (0, build_action_1.runEsBuildBuildAction)(async (rebuildState) => {
33
40
  const startTime = process.hrtime.bigint();
34
41
  const result = await (0, execute_build_1.executeBuild)(normalizedOptions, context, rebuildState);
@@ -38,6 +45,7 @@ context, infrastructureSettings, plugins) {
38
45
  return result;
39
46
  }, {
40
47
  watch: normalizedOptions.watch,
48
+ preserveSymlinks: normalizedOptions.preserveSymlinks,
41
49
  poll: normalizedOptions.poll,
42
50
  deleteOutputPath: normalizedOptions.deleteOutputPath,
43
51
  cacheOptions: normalizedOptions.cacheOptions,
@@ -53,7 +61,7 @@ context, infrastructureSettings, plugins) {
53
61
  ? undefined
54
62
  : (file) => file.type !== bundler_context_1.BuildOutputFileType.Server,
55
63
  logger: context.logger,
56
- signal: context.signal,
64
+ signal,
57
65
  });
58
66
  }
59
67
  exports.buildApplicationInternal = buildApplicationInternal;
@@ -181,7 +181,7 @@ async function normalizeOptions(context, projectName, options, plugins) {
181
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;
182
182
  // Return all the normalized options
183
183
  return {
184
- advancedOptimizations: !!aot,
184
+ advancedOptimizations: !!aot && optimizationOptions.scripts,
185
185
  allowedCommonJsDependencies,
186
186
  baseHref,
187
187
  cacheOptions,
@@ -30,9 +30,7 @@ function logBuilderStatusWarnings(options, { logger }) {
30
30
  if (typeof value === 'object' && Object.keys(value).length === 0) {
31
31
  continue;
32
32
  }
33
- if (unsupportedOption === 'vendorChunk' ||
34
- unsupportedOption === 'resourcesOutputPath' ||
35
- unsupportedOption === 'deployUrl') {
33
+ if (unsupportedOption === 'vendorChunk' || unsupportedOption === 'resourcesOutputPath') {
36
34
  logger.warn(`The '${unsupportedOption}' option is not used by this builder and will be ignored.`);
37
35
  continue;
38
36
  }
@@ -65,6 +65,11 @@ function execute(options, context, transforms = {}, extensions) {
65
65
  if (transforms?.logging || transforms?.webpackConfiguration) {
66
66
  throw new Error('The `application` and `browser-esbuild` builders do not support Webpack transforms.');
67
67
  }
68
+ if (normalizedOptions.forceEsbuild &&
69
+ builderName === '@angular-devkit/build-angular:browser') {
70
+ // The compatibility builder should be used if esbuild is force enabled with the official Webpack-based builder.
71
+ builderName = '@angular-devkit/build-angular:browser-esbuild';
72
+ }
68
73
  return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./vite-server')))).pipe((0, rxjs_1.switchMap)(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context, transforms, extensions)));
69
74
  }
70
75
  if (extensions?.buildPlugins?.length) {
@@ -74,8 +74,11 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
74
74
  }
75
75
  // Set all packages as external to support Vite's prebundle caching
76
76
  browserOptions.externalPackages = serverOptions.cacheOptions.enabled;
77
- if (serverOptions.servePath === undefined && browserOptions.baseHref !== undefined) {
78
- serverOptions.servePath = browserOptions.baseHref;
77
+ const baseHref = browserOptions.baseHref;
78
+ if (serverOptions.servePath === undefined && baseHref !== undefined) {
79
+ // Remove trailing slash
80
+ serverOptions.servePath =
81
+ baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
79
82
  }
80
83
  // The development server currently only supports a single locale when localizing.
81
84
  // This matches the behavior of the Webpack-based development server but could be expanded in the future.
@@ -157,7 +160,7 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
157
160
  assetFiles.clear();
158
161
  if (result.assetFiles) {
159
162
  for (const asset of result.assetFiles) {
160
- assetFiles.set('/' + normalizePath(asset.destination), asset.source);
163
+ assetFiles.set('/' + normalizePath(asset.destination), normalizePath(asset.source));
161
164
  }
162
165
  }
163
166
  // To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
@@ -179,7 +182,7 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
179
182
  if (server) {
180
183
  // Update fs allow list to include any new assets from the build option.
181
184
  server.config.server.fs.allow = [
182
- ...new Set(...server.config.server.fs.allow, ...assetFiles.values()),
185
+ ...new Set([...server.config.server.fs.allow, ...assetFiles.values()]),
183
186
  ];
184
187
  handleUpdate(normalizePath, generatedFiles, server, serverOptions, context.logger);
185
188
  }
@@ -350,7 +353,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
350
353
  css: {
351
354
  devSourcemap: true,
352
355
  },
353
- // Vite will normalize the `base` option by adding a leading and trailing forward slash.
356
+ // Vite will normalize the `base` option by adding a leading slash.
354
357
  base: serverOptions.servePath,
355
358
  resolve: {
356
359
  mainFields: ['es2020', 'browser', 'module', 'main'],
@@ -476,6 +479,10 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
476
479
  // Rewrite all build assets to a vite raw fs URL
477
480
  const assetSourcePath = assets.get(pathname);
478
481
  if (assetSourcePath !== undefined) {
482
+ // Workaround to disable Vite transformer middleware.
483
+ // See: https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/middlewares/transform.ts#L201 and
484
+ // https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/transformRequest.ts#L204-L206
485
+ req.headers.accept = 'text/html';
479
486
  // The encoding needs to match what happens in the vite static middleware.
480
487
  // ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
481
488
  req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`;
@@ -90,9 +90,12 @@ function execute(options, context, transforms = {}) {
90
90
  : getBuiltInKarmaConfig(context.workspaceRoot, projectName);
91
91
  karmaOptions.singleRun = singleRun;
92
92
  // Convert browsers from a string to an array
93
- if (options.browsers) {
93
+ if (typeof options.browsers === 'string' && options.browsers) {
94
94
  karmaOptions.browsers = options.browsers.split(',');
95
95
  }
96
+ else if (options.browsers === false) {
97
+ karmaOptions.browsers = [];
98
+ }
96
99
  if (options.reporters) {
97
100
  // Split along commas to make it more natural, and remove empty strings.
98
101
  const reporters = options.reporters
@@ -7,9 +7,9 @@ export interface Schema {
7
7
  */
8
8
  assets?: AssetPattern[];
9
9
  /**
10
- * Override which browsers tests are run against.
10
+ * Override which browsers tests are run against. Set to `false` to not use any browser.
11
11
  */
12
- browsers?: string;
12
+ browsers?: Browsers;
13
13
  /**
14
14
  * Output a code coverage report.
15
15
  */
@@ -117,6 +117,10 @@ export interface AssetPatternClass {
117
117
  */
118
118
  output: string;
119
119
  }
120
+ /**
121
+ * Override which browsers tests are run against. Set to `false` to not use any browser.
122
+ */
123
+ export type Browsers = boolean | string;
120
124
  export interface FileReplacement {
121
125
  replace?: string;
122
126
  replaceWith?: string;
@@ -199,8 +199,18 @@
199
199
  "description": "Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set."
200
200
  },
201
201
  "browsers": {
202
- "type": "string",
203
- "description": "Override which browsers tests are run against."
202
+ "description": "Override which browsers tests are run against. Set to `false` to not use any browser.",
203
+ "oneOf": [
204
+ {
205
+ "type": "string",
206
+ "description": "A comma seperate list of browsers to run tests against."
207
+ },
208
+ {
209
+ "const": false,
210
+ "type": "boolean",
211
+ "description": "Does use run tests against a browser."
212
+ }
213
+ ]
204
214
  },
205
215
  "codeCoverage": {
206
216
  "type": "boolean",
@@ -35,7 +35,6 @@ const architect_1 = require("@angular-devkit/architect");
35
35
  const core_1 = require("@angular-devkit/core");
36
36
  const path_1 = require("path");
37
37
  const rxjs_1 = require("rxjs");
38
- const operators_1 = require("rxjs/operators");
39
38
  const url = __importStar(require("url"));
40
39
  const error_1 = require("../../utils/error");
41
40
  const utils_1 = require("./utils");
@@ -60,7 +59,17 @@ function execute(options, context) {
60
59
  progress: options.progress,
61
60
  verbose: options.verbose,
62
61
  });
63
- const bsInstance = require('browser-sync').create();
62
+ let browserSync;
63
+ try {
64
+ browserSync = require('browser-sync');
65
+ }
66
+ catch {
67
+ return (0, rxjs_1.of)({
68
+ success: false,
69
+ error: '"browser-sync" is not installed, most likely you need to run `npm install browser-sync --save-dev` in your project.',
70
+ });
71
+ }
72
+ const bsInstance = browserSync.create();
64
73
  context.logger.error(core_1.tags.stripIndents `
65
74
  ****************************************************************************************
66
75
  This is a simple server for use in testing or debugging Angular applications locally.
@@ -69,35 +78,35 @@ function execute(options, context) {
69
78
  DON'T USE IT FOR PRODUCTION!
70
79
  ****************************************************************************************
71
80
  `);
72
- return (0, rxjs_1.zip)(browserTargetRun, serverTargetRun, (0, utils_1.getAvailablePort)()).pipe((0, operators_1.switchMap)(([br, sr, nodeServerPort]) => {
81
+ return (0, rxjs_1.zip)(browserTargetRun, serverTargetRun, (0, utils_1.getAvailablePort)()).pipe((0, rxjs_1.switchMap)(([br, sr, nodeServerPort]) => {
73
82
  return (0, rxjs_1.combineLatest)([br.output, sr.output]).pipe(
74
83
  // This is needed so that if both server and browser emit close to each other
75
84
  // we only emit once. This typically happens on the first build.
76
- (0, operators_1.debounceTime)(120), (0, operators_1.switchMap)(([b, s]) => {
85
+ (0, rxjs_1.debounceTime)(120), (0, rxjs_1.switchMap)(([b, s]) => {
77
86
  if (!s.success || !b.success) {
78
87
  return (0, rxjs_1.of)([b, s]);
79
88
  }
80
- return startNodeServer(s, nodeServerPort, context.logger, !!options.inspect).pipe((0, operators_1.mapTo)([b, s]), (0, operators_1.catchError)((err) => {
89
+ return startNodeServer(s, nodeServerPort, context.logger, !!options.inspect).pipe((0, rxjs_1.map)(() => [b, s]), (0, rxjs_1.catchError)((err) => {
81
90
  context.logger.error(`A server error has occurred.\n${mapErrorToMessage(err)}`);
82
91
  return rxjs_1.EMPTY;
83
92
  }));
84
- }), (0, operators_1.map)(([b, s]) => [
93
+ }), (0, rxjs_1.map)(([b, s]) => [
85
94
  {
86
95
  success: b.success && s.success,
87
96
  error: b.error || s.error,
88
97
  },
89
98
  nodeServerPort,
90
- ]), (0, operators_1.tap)(([builderOutput]) => {
99
+ ]), (0, rxjs_1.tap)(([builderOutput]) => {
91
100
  if (builderOutput.success) {
92
101
  context.logger.info('\nCompiled successfully.');
93
102
  }
94
- }), (0, operators_1.debounce)(([builderOutput]) => builderOutput.success && !options.inspect
103
+ }), (0, rxjs_1.debounce)(([builderOutput]) => builderOutput.success && !options.inspect
95
104
  ? (0, utils_1.waitUntilServerIsListening)(nodeServerPort)
96
- : rxjs_1.EMPTY), (0, operators_1.finalize)(() => {
105
+ : rxjs_1.EMPTY), (0, rxjs_1.finalize)(() => {
97
106
  void br.stop();
98
107
  void sr.stop();
99
108
  }));
100
- }), (0, operators_1.concatMap)(([builderOutput, nodeServerPort]) => {
109
+ }), (0, rxjs_1.concatMap)(([builderOutput, nodeServerPort]) => {
101
110
  if (!builderOutput.success) {
102
111
  return (0, rxjs_1.of)(builderOutput);
103
112
  }
@@ -106,7 +115,7 @@ function execute(options, context) {
106
115
  return (0, rxjs_1.of)(builderOutput);
107
116
  }
108
117
  else {
109
- return (0, rxjs_1.from)(initBrowserSync(bsInstance, nodeServerPort, options, context)).pipe((0, operators_1.tap)((bs) => {
118
+ return (0, rxjs_1.from)(initBrowserSync(bsInstance, nodeServerPort, options, context)).pipe((0, rxjs_1.tap)((bs) => {
110
119
  const baseUrl = getBaseUrl(bs);
111
120
  context.logger.info(core_1.tags.oneLine `
112
121
  **
@@ -114,19 +123,19 @@ function execute(options, context) {
114
123
  open your browser on ${baseUrl}
115
124
  **
116
125
  `);
117
- }), (0, operators_1.mapTo)(builderOutput));
126
+ }), (0, rxjs_1.map)(() => builderOutput));
118
127
  }
119
- }), (0, operators_1.map)((builderOutput) => ({
128
+ }), (0, rxjs_1.map)((builderOutput) => ({
120
129
  success: builderOutput.success,
121
130
  error: builderOutput.error,
122
131
  baseUrl: getBaseUrl(bsInstance),
123
132
  port: bsInstance.getOption('port'),
124
- })), (0, operators_1.finalize)(() => {
133
+ })), (0, rxjs_1.finalize)(() => {
125
134
  if (bsInstance) {
126
135
  bsInstance.exit();
127
136
  bsInstance.cleanup();
128
137
  }
129
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)({
138
+ }), (0, rxjs_1.catchError)((error) => (0, rxjs_1.of)({
130
139
  success: false,
131
140
  error: mapErrorToMessage(error),
132
141
  })));
@@ -152,10 +161,10 @@ function startNodeServer(serverOutput, port, logger, inspectMode = false) {
152
161
  if (inspectMode) {
153
162
  args.unshift('--inspect-brk');
154
163
  }
155
- return (0, rxjs_1.of)(null).pipe((0, operators_1.delay)(0), // Avoid EADDRINUSE error since it will cause the kill event to be finish.
156
- (0, operators_1.switchMap)(() => (0, utils_1.spawnAsObservable)('node', args, { env, shell: true })), (0, operators_1.tap)((res) => log({ stderr: res.stderr, stdout: res.stdout }, logger)), (0, operators_1.ignoreElements)(),
164
+ return (0, rxjs_1.of)(null).pipe((0, rxjs_1.delay)(0), // Avoid EADDRINUSE error since it will cause the kill event to be finish.
165
+ (0, rxjs_1.switchMap)(() => (0, utils_1.spawnAsObservable)('node', args, { env, shell: true })), (0, rxjs_1.tap)((res) => log({ stderr: res.stderr, stdout: res.stdout }, logger)), (0, rxjs_1.ignoreElements)(),
157
166
  // Emit a signal after the process has been started
158
- (0, operators_1.startWith)(undefined));
167
+ (0, rxjs_1.startWith)(undefined));
159
168
  }
160
169
  async function initBrowserSync(browserSyncInstance, nodeServerPort, options, context) {
161
170
  if (browserSyncInstance.active) {
@@ -28,6 +28,12 @@ exports.ensureSourceFileVersions = ensureSourceFileVersions;
28
28
  function createAngularCompilerHost(compilerOptions, hostOptions) {
29
29
  // Create TypeScript compiler host
30
30
  const host = typescript_1.default.createIncrementalCompilerHost(compilerOptions);
31
+ // Set the parsing mode to the same as TS 5.3 default for tsc. This provides a parse
32
+ // performance improvement by skipping non-type related JSDoc parsing.
33
+ // NOTE: The check for this enum can be removed when TS 5.3 support is the minimum.
34
+ if (typescript_1.default.JSDocParsingMode) {
35
+ host.jsDocParsingMode = typescript_1.default.JSDocParsingMode.ParseForTypeErrors;
36
+ }
31
37
  // The AOT compiler currently requires this hook to allow for a transformResource hook.
32
38
  // Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
33
39
  host.readResource = async function (filename) {
@@ -308,7 +308,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
308
308
  }));
309
309
  // Setup bundling of component templates and stylesheets when in JIT mode
310
310
  if (pluginOptions.jit) {
311
- (0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage);
311
+ (0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage, pluginOptions.loadResultCache);
312
312
  }
313
313
  build.onEnd((result) => {
314
314
  // Ensure other compilations are unblocked if the main compilation throws during start
@@ -79,7 +79,7 @@ class ComponentStylesheetBundler {
79
79
  namespace,
80
80
  };
81
81
  });
82
- build.onLoad({ filter: /^css;/, namespace }, async () => {
82
+ build.onLoad({ filter: /^css;/, namespace }, () => {
83
83
  return {
84
84
  contents: data,
85
85
  loader: 'css',
@@ -6,6 +6,7 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import type { Metafile, OutputFile, PluginBuild } from 'esbuild';
9
+ import { LoadResultCache } from '../load-result-cache';
9
10
  import { ComponentStylesheetBundler } from './component-stylesheets';
10
11
  /**
11
12
  * Sets up esbuild resolve and load callbacks to support Angular JIT mode processing
@@ -19,4 +20,4 @@ import { ComponentStylesheetBundler } from './component-stylesheets';
19
20
  export declare function setupJitPluginCallbacks(build: PluginBuild, stylesheetBundler: ComponentStylesheetBundler, additionalResultFiles: Map<string, {
20
21
  outputFiles?: OutputFile[];
21
22
  metafile?: Metafile;
22
- }>, inlineStyleLanguage: string): void;
23
+ }>, inlineStyleLanguage: string, loadCache?: LoadResultCache): void;