@angular-devkit/build-angular 15.0.0 → 15.1.0-next.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.
@@ -25,6 +25,27 @@ function createSassPlugin(options) {
25
25
  return {
26
26
  name: 'angular-sass',
27
27
  setup(build) {
28
+ const resolveUrl = async (url, previousResolvedModules) => {
29
+ let result = await build.resolve(url, {
30
+ kind: 'import-rule',
31
+ // This should ideally be the directory of the importer file from Sass
32
+ // but that is not currently available from the Sass importer API.
33
+ resolveDir: build.initialOptions.absWorkingDir,
34
+ });
35
+ // Workaround to support Yarn PnP without access to the importer file from Sass
36
+ if (!result.path && (previousResolvedModules === null || previousResolvedModules === void 0 ? void 0 : previousResolvedModules.size)) {
37
+ for (const previous of previousResolvedModules) {
38
+ result = await build.resolve(url, {
39
+ kind: 'import-rule',
40
+ resolveDir: previous,
41
+ });
42
+ if (result.path) {
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ return result;
48
+ };
28
49
  build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
29
50
  // Lazily load Sass when a Sass file is found
30
51
  sassWorkerPool !== null && sassWorkerPool !== void 0 ? sassWorkerPool : (sassWorkerPool = new sass_service_1.SassWorkerImplementation(true));
@@ -41,39 +62,18 @@ function createSassPlugin(options) {
41
62
  importers: [
42
63
  {
43
64
  findFileUrl: async (url, { previousResolvedModules }) => {
44
- let result = await build.resolve(url, {
45
- kind: 'import-rule',
46
- // This should ideally be the directory of the importer file from Sass
47
- // but that is not currently available from the Sass importer API.
48
- resolveDir: build.initialOptions.absWorkingDir,
49
- });
50
- // Workaround to support Yarn PnP without access to the importer file from Sass
51
- if (!result.path && (previousResolvedModules === null || previousResolvedModules === void 0 ? void 0 : previousResolvedModules.size)) {
52
- for (const previous of previousResolvedModules) {
53
- result = await build.resolve(url, {
54
- kind: 'import-rule',
55
- resolveDir: previous,
56
- });
57
- }
58
- }
65
+ const result = await resolveUrl(url, previousResolvedModules);
59
66
  // Check for package deep imports
60
67
  if (!result.path) {
61
68
  const parts = url.split('/');
62
- const hasScope = parts.length > 2 && parts[0].startsWith('@');
63
- if (hasScope || parts.length > 1) {
64
- const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
65
- const packageName = hasScope
66
- ? `${nameOrScope}/${nameOrFirstPath}`
67
- : nameOrScope;
68
- const packageResult = await build.resolve(packageName + '/package.json', {
69
- kind: 'import-rule',
70
- // This should ideally be the directory of the importer file from Sass
71
- // but that is not currently available from the Sass importer API.
72
- resolveDir: build.initialOptions.absWorkingDir,
73
- });
74
- if (packageResult.path) {
75
- return (0, node_url_1.pathToFileURL)((0, node_path_1.join)((0, node_path_1.dirname)(packageResult.path), !hasScope ? nameOrFirstPath : '', ...pathPart));
76
- }
69
+ const hasScope = parts.length >= 2 && parts[0].startsWith('@');
70
+ const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
71
+ const packageName = hasScope
72
+ ? `${nameOrScope}/${nameOrFirstPath}`
73
+ : nameOrScope;
74
+ const packageResult = await resolveUrl(packageName + '/package.json', previousResolvedModules);
75
+ if (packageResult.path) {
76
+ return (0, node_url_1.pathToFileURL)((0, node_path_1.join)((0, node_path_1.dirname)(packageResult.path), !hasScope ? nameOrFirstPath : '', ...pathPart));
77
77
  }
78
78
  }
79
79
  return result.path ? (0, node_url_1.pathToFileURL)(result.path) : null;
@@ -37,9 +37,13 @@ const path = __importStar(require("path"));
37
37
  const rxjs_1 = require("rxjs");
38
38
  const operators_1 = require("rxjs/operators");
39
39
  const utils_1 = require("../../utils");
40
+ const color_1 = require("../../utils/color");
41
+ const copy_assets_1 = require("../../utils/copy-assets");
42
+ const error_1 = require("../../utils/error");
40
43
  const i18n_inlining_1 = require("../../utils/i18n-inlining");
41
44
  const output_paths_1 = require("../../utils/output-paths");
42
45
  const purge_cache_1 = require("../../utils/purge-cache");
46
+ const spinner_1 = require("../../utils/spinner");
43
47
  const version_1 = require("../../utils/version");
44
48
  const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
45
49
  const configs_1 = require("../../webpack/configs");
@@ -54,7 +58,7 @@ function execute(options, context, transforms = {}) {
54
58
  (0, version_1.assertCompatibleAngularVersion)(root);
55
59
  const baseOutputPath = path.resolve(root, options.outputPath);
56
60
  let outputPaths;
57
- return (0, rxjs_1.from)(initialize(options, context, transforms.webpackConfiguration)).pipe((0, operators_1.concatMap)(({ config, i18n }) => {
61
+ return (0, rxjs_1.from)(initialize(options, context, transforms.webpackConfiguration)).pipe((0, operators_1.concatMap)(({ config, i18n, projectRoot, projectSourceRoot }) => {
58
62
  return (0, build_webpack_1.runWebpack)(config, context, {
59
63
  webpackFactory: require('webpack'),
60
64
  logging: (stats, config) => {
@@ -63,19 +67,47 @@ function execute(options, context, transforms = {}) {
63
67
  }
64
68
  },
65
69
  }).pipe((0, operators_1.concatMap)(async (output) => {
70
+ var _a;
66
71
  const { emittedFiles = [], outputPath, webpackStats } = output;
67
72
  if (!webpackStats) {
68
73
  throw new Error('Webpack stats build result is required.');
69
74
  }
70
- let success = output.success;
71
- if (success && i18n.shouldInline) {
72
- outputPaths = (0, output_paths_1.ensureOutputPaths)(baseOutputPath, i18n);
73
- success = await (0, i18n_inlining_1.i18nInlineEmittedFiles)(context, emittedFiles, i18n, baseOutputPath, Array.from(outputPaths.values()), [], outputPath, options.i18nMissingTranslation);
75
+ if (!output.success) {
76
+ return output;
77
+ }
78
+ const spinner = new spinner_1.Spinner();
79
+ spinner.enabled = options.progress !== false;
80
+ outputPaths = (0, output_paths_1.ensureOutputPaths)(baseOutputPath, i18n);
81
+ // Copy assets
82
+ if (!options.watch && ((_a = options.assets) === null || _a === void 0 ? void 0 : _a.length)) {
83
+ spinner.start('Copying assets...');
84
+ try {
85
+ await (0, copy_assets_1.copyAssets)((0, utils_1.normalizeAssetPatterns)(options.assets, context.workspaceRoot, projectRoot, projectSourceRoot), Array.from(outputPaths.values()), context.workspaceRoot);
86
+ spinner.succeed('Copying assets complete.');
87
+ }
88
+ catch (err) {
89
+ spinner.fail(color_1.colors.redBright('Copying of assets failed.'));
90
+ (0, error_1.assertIsError)(err);
91
+ return {
92
+ ...output,
93
+ success: false,
94
+ error: 'Unable to copy assets: ' + err.message,
95
+ };
96
+ }
97
+ }
98
+ if (i18n.shouldInline) {
99
+ const success = await (0, i18n_inlining_1.i18nInlineEmittedFiles)(context, emittedFiles, i18n, baseOutputPath, Array.from(outputPaths.values()), [], outputPath, options.i18nMissingTranslation);
100
+ if (!success) {
101
+ return {
102
+ ...output,
103
+ success: false,
104
+ };
105
+ }
74
106
  }
75
107
  (0, stats_1.webpackStatsLogger)(context.logger, webpackStats, config);
76
- return { ...output, success };
108
+ return output;
77
109
  }));
78
- }), (0, operators_1.map)((output) => {
110
+ }), (0, operators_1.concatMap)(async (output) => {
79
111
  if (!output.success) {
80
112
  return output;
81
113
  }
@@ -102,8 +134,10 @@ async function initialize(options, context, webpackConfigurationTransform) {
102
134
  await (0, purge_cache_1.purgeStaleBuildCache)(context);
103
135
  const browserslist = (await Promise.resolve().then(() => __importStar(require('browserslist')))).default;
104
136
  const originalOutputPath = options.outputPath;
105
- const { config, i18n } = await (0, webpack_browser_config_1.generateI18nBrowserWebpackConfigFromContext)({
106
- ...options,
137
+ // Assets are processed directly by the builder except when watching
138
+ const adjustedOptions = options.watch ? options : { ...options, assets: [] };
139
+ const { config, projectRoot, projectSourceRoot, i18n } = await (0, webpack_browser_config_1.generateI18nBrowserWebpackConfigFromContext)({
140
+ ...adjustedOptions,
107
141
  buildOptimizer: false,
108
142
  aot: true,
109
143
  platform: 'server',
@@ -119,7 +153,7 @@ async function initialize(options, context, webpackConfigurationTransform) {
119
153
  (0, utils_1.deleteOutputDir)(context.workspaceRoot, originalOutputPath);
120
154
  }
121
155
  const transformedConfig = (_a = (await (webpackConfigurationTransform === null || webpackConfigurationTransform === void 0 ? void 0 : webpackConfigurationTransform(config)))) !== null && _a !== void 0 ? _a : config;
122
- return { config: transformedConfig, i18n };
156
+ return { config: transformedConfig, i18n, projectRoot, projectSourceRoot };
123
157
  }
124
158
  /**
125
159
  * Add `@angular/platform-server` exports.
@@ -1,4 +1,8 @@
1
1
  export interface Schema {
2
+ /**
3
+ * List of static application assets.
4
+ */
5
+ assets?: AssetPattern[];
2
6
  /**
3
7
  * Delete the output path before building.
4
8
  */
@@ -110,6 +114,30 @@ export interface Schema {
110
114
  */
111
115
  watch?: boolean;
112
116
  }
117
+ export declare type AssetPattern = AssetPatternClass | string;
118
+ export interface AssetPatternClass {
119
+ /**
120
+ * Allow glob patterns to follow symlink directories. This allows subdirectories of the
121
+ * symlink to be searched.
122
+ */
123
+ followSymlinks?: boolean;
124
+ /**
125
+ * The pattern to match.
126
+ */
127
+ glob: string;
128
+ /**
129
+ * An array of globs to ignore.
130
+ */
131
+ ignore?: string[];
132
+ /**
133
+ * The input directory path in which to apply 'glob'. Defaults to the project root.
134
+ */
135
+ input: string;
136
+ /**
137
+ * Absolute path within the output.
138
+ */
139
+ output: string;
140
+ }
113
141
  export interface FileReplacement {
114
142
  replace?: string;
115
143
  replaceWith?: string;
@@ -4,6 +4,14 @@
4
4
  "title": "Universal Target",
5
5
  "type": "object",
6
6
  "properties": {
7
+ "assets": {
8
+ "type": "array",
9
+ "description": "List of static application assets.",
10
+ "default": [],
11
+ "items": {
12
+ "$ref": "#/definitions/assetPattern"
13
+ }
14
+ },
7
15
  "main": {
8
16
  "type": "string",
9
17
  "description": "The name of the main entry-point file."
@@ -212,6 +220,44 @@
212
220
  "additionalProperties": false,
213
221
  "required": ["outputPath", "main", "tsConfig"],
214
222
  "definitions": {
223
+ "assetPattern": {
224
+ "oneOf": [
225
+ {
226
+ "type": "object",
227
+ "properties": {
228
+ "followSymlinks": {
229
+ "type": "boolean",
230
+ "default": false,
231
+ "description": "Allow glob patterns to follow symlink directories. This allows subdirectories of the symlink to be searched."
232
+ },
233
+ "glob": {
234
+ "type": "string",
235
+ "description": "The pattern to match."
236
+ },
237
+ "input": {
238
+ "type": "string",
239
+ "description": "The input directory path in which to apply 'glob'. Defaults to the project root."
240
+ },
241
+ "ignore": {
242
+ "description": "An array of globs to ignore.",
243
+ "type": "array",
244
+ "items": {
245
+ "type": "string"
246
+ }
247
+ },
248
+ "output": {
249
+ "type": "string",
250
+ "description": "Absolute path within the output."
251
+ }
252
+ },
253
+ "additionalProperties": false,
254
+ "required": ["glob", "input", "output"]
255
+ },
256
+ {
257
+ "type": "string"
258
+ }
259
+ ]
260
+ },
215
261
  "fileReplacement": {
216
262
  "oneOf": [
217
263
  {
@@ -6,10 +6,16 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  /// <reference types="node" />
9
- /// <reference types="node" />
10
9
  import { RawSourceMap } from '@ampproject/remapping';
11
- import { Dirent } from 'node:fs';
12
10
  import type { FileImporter, Importer, ImporterResult } from 'sass';
11
+ /**
12
+ * A preprocessed cache entry for the files and directories within a previously searched
13
+ * directory when performing Sass import resolution.
14
+ */
15
+ export interface DirectoryEntry {
16
+ files: Set<string>;
17
+ directories: Set<string>;
18
+ }
13
19
  /**
14
20
  * A Sass Importer base class that provides the load logic to rebase all `url()` functions
15
21
  * within a stylesheet. The rebasing will ensure that the URLs in the output of the Sass compiler
@@ -42,7 +48,7 @@ declare abstract class UrlRebasingImporter implements Importer<'sync'> {
42
48
  */
43
49
  export declare class RelativeUrlRebasingImporter extends UrlRebasingImporter {
44
50
  private directoryCache;
45
- constructor(entryDirectory: string, directoryCache?: Map<string, Dirent[]>, rebaseSourceMaps?: Map<string, RawSourceMap>);
51
+ constructor(entryDirectory: string, directoryCache?: Map<string, DirectoryEntry>, rebaseSourceMaps?: Map<string, RawSourceMap>);
46
52
  canonicalize(url: string, options: {
47
53
  fromImport: boolean;
48
54
  }): URL | null;
@@ -59,7 +65,7 @@ export declare class RelativeUrlRebasingImporter extends UrlRebasingImporter {
59
65
  * Checks an array of potential stylesheet files to determine if there is a valid
60
66
  * stylesheet file. More than one discovered file may indicate an error.
61
67
  * @param found An array of discovered stylesheet files.
62
- * @returns A fully resolved URL for a stylesheet file or `null` if not found.
68
+ * @returns A fully resolved path for a stylesheet file or `null` if not found.
63
69
  * @throws If there are ambiguous files discovered.
64
70
  */
65
71
  private checkFound;
@@ -71,7 +77,7 @@ export declare class RelativeUrlRebasingImporter extends UrlRebasingImporter {
71
77
  */
72
78
  export declare class ModuleUrlRebasingImporter extends RelativeUrlRebasingImporter {
73
79
  private finder;
74
- constructor(entryDirectory: string, directoryCache: Map<string, Dirent[]>, rebaseSourceMaps: Map<string, RawSourceMap> | undefined, finder: FileImporter<'sync'>['findFileUrl']);
80
+ constructor(entryDirectory: string, directoryCache: Map<string, DirectoryEntry>, rebaseSourceMaps: Map<string, RawSourceMap> | undefined, finder: FileImporter<'sync'>['findFileUrl']);
75
81
  canonicalize(url: string, options: {
76
82
  fromImport: boolean;
77
83
  }): URL | null;
@@ -83,7 +89,7 @@ export declare class ModuleUrlRebasingImporter extends RelativeUrlRebasingImport
83
89
  */
84
90
  export declare class LoadPathsUrlRebasingImporter extends RelativeUrlRebasingImporter {
85
91
  private loadPaths;
86
- constructor(entryDirectory: string, directoryCache: Map<string, Dirent[]>, rebaseSourceMaps: Map<string, RawSourceMap> | undefined, loadPaths: Iterable<string>);
92
+ constructor(entryDirectory: string, directoryCache: Map<string, DirectoryEntry>, rebaseSourceMaps: Map<string, RawSourceMap> | undefined, loadPaths: Iterable<string>);
87
93
  canonicalize(url: string, options: {
88
94
  fromImport: boolean;
89
95
  }): URL | null;