@angular-devkit/build-angular 18.0.0-next.1 → 18.0.0-next.3
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 +20 -20
- package/src/builders/application/execute-build.js +2 -4
- package/src/builders/application/execute-post-bundle.js +9 -12
- package/src/builders/application/options.js +50 -32
- package/src/builders/browser/index.js +1 -1
- package/src/builders/dev-server/vite-server.js +3 -3
- package/src/builders/dev-server/webpack-server.js +2 -2
- package/src/builders/ssr-dev-server/index.js +29 -29
- package/src/tools/esbuild/angular/compiler-plugin.js +12 -4
- package/src/tools/esbuild/index-html-generator.d.ts +2 -2
- package/src/tools/esbuild/index-html-generator.js +5 -53
- package/src/tools/esbuild/javascript-transformer.js +5 -1
- package/src/tools/esbuild/stylesheets/css-resource-plugin.js +15 -8
- package/src/tools/esbuild/utils.js +2 -2
- package/src/tools/sass/rebasing-importer.js +3 -6
- package/src/tools/sass/sass-service.d.ts +24 -1
- package/src/tools/sass/worker.js +32 -4
- package/src/tools/webpack/plugins/any-component-style-budget-checker.d.ts +2 -2
- package/src/tools/webpack/plugins/any-component-style-budget-checker.js +13 -16
- package/src/tools/webpack/plugins/index-html-webpack-plugin.js +1 -1
- package/src/tools/webpack/utils/stats.d.ts +0 -13
- package/src/tools/webpack/utils/stats.js +3 -207
- package/src/utils/action-executor.d.ts +1 -1
- package/src/utils/bundle-calculator.d.ts +5 -5
- package/src/utils/bundle-calculator.js +8 -7
- package/src/utils/format-bytes.d.ts +8 -0
- package/src/utils/format-bytes.js +22 -0
- package/src/utils/i18n-inlining.d.ts +1 -1
- package/src/utils/i18n-options.d.ts +2 -9
- package/src/utils/i18n-options.js +4 -97
- package/src/utils/i18n-webpack.d.ts +16 -0
- package/src/utils/i18n-webpack.js +108 -0
- package/src/utils/index-file/add-event-dispatch-contract.d.ts +8 -0
- package/src/utils/index-file/add-event-dispatch-contract.js +28 -0
- package/src/utils/index-file/augment-index-html.js +1 -0
- package/src/utils/index-file/index-html-generator.d.ts +12 -2
- package/src/utils/index-file/index-html-generator.js +38 -22
- package/src/utils/index-file/inline-fonts.js +1 -1
- package/src/utils/index-file/{style-nonce.d.ts → nonce.d.ts} +2 -2
- package/src/utils/index-file/{style-nonce.js → nonce.js} +7 -5
- package/src/utils/load-proxy-config.d.ts +1 -1
- package/src/utils/load-proxy-config.js +2 -5
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/output-paths.d.ts +1 -1
- package/src/utils/postcss-configuration.d.ts +7 -1
- package/src/utils/postcss-configuration.js +13 -4
- package/src/utils/resolve-assets.d.ts +18 -0
- package/src/utils/resolve-assets.js +35 -0
- package/src/utils/stats-table.d.ts +20 -0
- package/src/utils/stats-table.js +205 -0
- package/src/utils/webpack-browser-config.d.ts +1 -1
- package/src/utils/webpack-browser-config.js +2 -2
|
@@ -10,18 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.loadTranslations = exports.
|
|
14
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
|
-
const node_module_1 = require("node:module");
|
|
16
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
13
|
+
exports.loadTranslations = exports.createI18nOptions = void 0;
|
|
17
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
-
const schema_1 = require("../builders/browser/schema");
|
|
19
|
-
const read_tsconfig_1 = require("../utils/read-tsconfig");
|
|
20
|
-
const load_translations_1 = require("./load-translations");
|
|
21
|
-
/**
|
|
22
|
-
* The base module location used to search for locale specific data.
|
|
23
|
-
*/
|
|
24
|
-
const LOCALE_DATA_BASE_MODULE = '@angular/common/locales/global';
|
|
25
15
|
function normalizeTranslationFileOption(option, locale, expectObjectInError) {
|
|
26
16
|
if (typeof option === 'string') {
|
|
27
17
|
return [option];
|
|
@@ -123,89 +113,6 @@ function createI18nOptions(projectMetadata, inline) {
|
|
|
123
113
|
return i18n;
|
|
124
114
|
}
|
|
125
115
|
exports.createI18nOptions = createI18nOptions;
|
|
126
|
-
async function configureI18nBuild(context, options) {
|
|
127
|
-
if (!context.target) {
|
|
128
|
-
throw new Error('The builder requires a target.');
|
|
129
|
-
}
|
|
130
|
-
const buildOptions = { ...options };
|
|
131
|
-
const tsConfig = await (0, read_tsconfig_1.readTsconfig)(buildOptions.tsConfig, context.workspaceRoot);
|
|
132
|
-
const metadata = await context.getProjectMetadata(context.target);
|
|
133
|
-
const i18n = createI18nOptions(metadata, buildOptions.localize);
|
|
134
|
-
// No additional processing needed if no inlining requested and no source locale defined.
|
|
135
|
-
if (!i18n.shouldInline && !i18n.hasDefinedSourceLocale) {
|
|
136
|
-
return { buildOptions, i18n };
|
|
137
|
-
}
|
|
138
|
-
const projectRoot = node_path_1.default.join(context.workspaceRoot, metadata.root || '');
|
|
139
|
-
// The trailing slash is required to signal that the path is a directory and not a file.
|
|
140
|
-
const projectRequire = (0, node_module_1.createRequire)(projectRoot + '/');
|
|
141
|
-
const localeResolver = (locale) => projectRequire.resolve(node_path_1.default.join(LOCALE_DATA_BASE_MODULE, locale));
|
|
142
|
-
// Load locale data and translations (if present)
|
|
143
|
-
let loader;
|
|
144
|
-
const usedFormats = new Set();
|
|
145
|
-
for (const [locale, desc] of Object.entries(i18n.locales)) {
|
|
146
|
-
if (!i18n.inlineLocales.has(locale) && locale !== i18n.sourceLocale) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
let localeDataPath = findLocaleDataPath(locale, localeResolver);
|
|
150
|
-
if (!localeDataPath) {
|
|
151
|
-
const [first] = locale.split('-');
|
|
152
|
-
if (first) {
|
|
153
|
-
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeResolver);
|
|
154
|
-
if (localeDataPath) {
|
|
155
|
-
context.logger.warn(`Locale data for '${locale}' cannot be found. Using locale data for '${first}'.`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (!localeDataPath) {
|
|
160
|
-
context.logger.warn(`Locale data for '${locale}' cannot be found. No locale data will be included for this locale.`);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
desc.dataPath = localeDataPath;
|
|
164
|
-
}
|
|
165
|
-
if (!desc.files.length) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
loader ??= await (0, load_translations_1.createTranslationLoader)();
|
|
169
|
-
loadTranslations(locale, desc, context.workspaceRoot, loader, {
|
|
170
|
-
warn(message) {
|
|
171
|
-
context.logger.warn(message);
|
|
172
|
-
},
|
|
173
|
-
error(message) {
|
|
174
|
-
throw new Error(message);
|
|
175
|
-
},
|
|
176
|
-
}, usedFormats, buildOptions.i18nDuplicateTranslation);
|
|
177
|
-
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
|
|
178
|
-
// This limitation is only for legacy message id support (defaults to true as of 9.0)
|
|
179
|
-
throw new Error('Localization currently only supports using one type of translation file format for the entire application.');
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
// If inlining store the output in a temporary location to facilitate post-processing
|
|
183
|
-
if (i18n.shouldInline) {
|
|
184
|
-
// TODO: we should likely save these in the .angular directory in the next major version.
|
|
185
|
-
// We'd need to do a migration to add the temp directory to gitignore.
|
|
186
|
-
const tempPath = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_fs_1.default.realpathSync(node_os_1.default.tmpdir()), 'angular-cli-i18n-'));
|
|
187
|
-
buildOptions.outputPath = tempPath;
|
|
188
|
-
process.on('exit', () => {
|
|
189
|
-
try {
|
|
190
|
-
node_fs_1.default.rmSync(tempPath, { force: true, recursive: true, maxRetries: 3 });
|
|
191
|
-
}
|
|
192
|
-
catch { }
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
return { buildOptions, i18n };
|
|
196
|
-
}
|
|
197
|
-
exports.configureI18nBuild = configureI18nBuild;
|
|
198
|
-
function findLocaleDataPath(locale, resolver) {
|
|
199
|
-
// Remove private use subtags
|
|
200
|
-
const scrubbedLocale = locale.replace(/-x(-[a-zA-Z0-9]{1,8})+$/, '');
|
|
201
|
-
try {
|
|
202
|
-
return resolver(scrubbedLocale);
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
// fallback to known existing en-US locale data as of 14.0
|
|
206
|
-
return scrubbedLocale === 'en-US' ? findLocaleDataPath('en', resolver) : null;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
116
|
function loadTranslations(locale, desc, workspaceRoot, loader, logger, usedFormats, duplicateTranslation) {
|
|
210
117
|
let translations = undefined;
|
|
211
118
|
for (const file of desc.files) {
|
|
@@ -230,12 +137,12 @@ function loadTranslations(locale, desc, workspaceRoot, loader, logger, usedForma
|
|
|
230
137
|
if (translations[id] !== undefined) {
|
|
231
138
|
const duplicateTranslationMessage = `[${file.path}]: Duplicate translations for message '${id}' when merging.`;
|
|
232
139
|
switch (duplicateTranslation) {
|
|
233
|
-
case
|
|
140
|
+
case 'ignore':
|
|
234
141
|
break;
|
|
235
|
-
case
|
|
142
|
+
case 'error':
|
|
236
143
|
logger.error(`ERROR ${duplicateTranslationMessage}`);
|
|
237
144
|
break;
|
|
238
|
-
case
|
|
145
|
+
case 'warning':
|
|
239
146
|
default:
|
|
240
147
|
logger.warn(`WARNING ${duplicateTranslationMessage}`);
|
|
241
148
|
break;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.io/license
|
|
7
|
+
*/
|
|
8
|
+
import { BuilderContext } from '@angular-devkit/architect';
|
|
9
|
+
import { Schema as BrowserBuilderSchema } from '../builders/browser/schema';
|
|
10
|
+
import { Schema as ServerBuilderSchema } from '../builders/server/schema';
|
|
11
|
+
import { I18nOptions, loadTranslations } from './i18n-options';
|
|
12
|
+
export { I18nOptions, loadTranslations };
|
|
13
|
+
export declare function configureI18nBuild<T extends BrowserBuilderSchema | ServerBuilderSchema>(context: BuilderContext, options: T): Promise<{
|
|
14
|
+
buildOptions: T;
|
|
15
|
+
i18n: I18nOptions;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.io/license
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.configureI18nBuild = exports.loadTranslations = void 0;
|
|
14
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
|
+
const node_module_1 = require("node:module");
|
|
16
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const read_tsconfig_1 = require("../utils/read-tsconfig");
|
|
19
|
+
const i18n_options_1 = require("./i18n-options");
|
|
20
|
+
Object.defineProperty(exports, "loadTranslations", { enumerable: true, get: function () { return i18n_options_1.loadTranslations; } });
|
|
21
|
+
const load_translations_1 = require("./load-translations");
|
|
22
|
+
/**
|
|
23
|
+
* The base module location used to search for locale specific data.
|
|
24
|
+
*/
|
|
25
|
+
const LOCALE_DATA_BASE_MODULE = '@angular/common/locales/global';
|
|
26
|
+
async function configureI18nBuild(context, options) {
|
|
27
|
+
if (!context.target) {
|
|
28
|
+
throw new Error('The builder requires a target.');
|
|
29
|
+
}
|
|
30
|
+
const buildOptions = { ...options };
|
|
31
|
+
const tsConfig = await (0, read_tsconfig_1.readTsconfig)(buildOptions.tsConfig, context.workspaceRoot);
|
|
32
|
+
const metadata = await context.getProjectMetadata(context.target);
|
|
33
|
+
const i18n = (0, i18n_options_1.createI18nOptions)(metadata, buildOptions.localize);
|
|
34
|
+
// No additional processing needed if no inlining requested and no source locale defined.
|
|
35
|
+
if (!i18n.shouldInline && !i18n.hasDefinedSourceLocale) {
|
|
36
|
+
return { buildOptions, i18n };
|
|
37
|
+
}
|
|
38
|
+
const projectRoot = node_path_1.default.join(context.workspaceRoot, metadata.root || '');
|
|
39
|
+
// The trailing slash is required to signal that the path is a directory and not a file.
|
|
40
|
+
const projectRequire = (0, node_module_1.createRequire)(projectRoot + '/');
|
|
41
|
+
const localeResolver = (locale) => projectRequire.resolve(node_path_1.default.join(LOCALE_DATA_BASE_MODULE, locale));
|
|
42
|
+
// Load locale data and translations (if present)
|
|
43
|
+
let loader;
|
|
44
|
+
const usedFormats = new Set();
|
|
45
|
+
for (const [locale, desc] of Object.entries(i18n.locales)) {
|
|
46
|
+
if (!i18n.inlineLocales.has(locale) && locale !== i18n.sourceLocale) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
let localeDataPath = findLocaleDataPath(locale, localeResolver);
|
|
50
|
+
if (!localeDataPath) {
|
|
51
|
+
const [first] = locale.split('-');
|
|
52
|
+
if (first) {
|
|
53
|
+
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeResolver);
|
|
54
|
+
if (localeDataPath) {
|
|
55
|
+
context.logger.warn(`Locale data for '${locale}' cannot be found. Using locale data for '${first}'.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!localeDataPath) {
|
|
60
|
+
context.logger.warn(`Locale data for '${locale}' cannot be found. No locale data will be included for this locale.`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
desc.dataPath = localeDataPath;
|
|
64
|
+
}
|
|
65
|
+
if (!desc.files.length) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
loader ??= await (0, load_translations_1.createTranslationLoader)();
|
|
69
|
+
(0, i18n_options_1.loadTranslations)(locale, desc, context.workspaceRoot, loader, {
|
|
70
|
+
warn(message) {
|
|
71
|
+
context.logger.warn(message);
|
|
72
|
+
},
|
|
73
|
+
error(message) {
|
|
74
|
+
throw new Error(message);
|
|
75
|
+
},
|
|
76
|
+
}, usedFormats, buildOptions.i18nDuplicateTranslation);
|
|
77
|
+
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
|
|
78
|
+
// This limitation is only for legacy message id support (defaults to true as of 9.0)
|
|
79
|
+
throw new Error('Localization currently only supports using one type of translation file format for the entire application.');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// If inlining store the output in a temporary location to facilitate post-processing
|
|
83
|
+
if (i18n.shouldInline) {
|
|
84
|
+
// TODO: we should likely save these in the .angular directory in the next major version.
|
|
85
|
+
// We'd need to do a migration to add the temp directory to gitignore.
|
|
86
|
+
const tempPath = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_fs_1.default.realpathSync(node_os_1.default.tmpdir()), 'angular-cli-i18n-'));
|
|
87
|
+
buildOptions.outputPath = tempPath;
|
|
88
|
+
process.on('exit', () => {
|
|
89
|
+
try {
|
|
90
|
+
node_fs_1.default.rmSync(tempPath, { force: true, recursive: true, maxRetries: 3 });
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return { buildOptions, i18n };
|
|
96
|
+
}
|
|
97
|
+
exports.configureI18nBuild = configureI18nBuild;
|
|
98
|
+
function findLocaleDataPath(locale, resolver) {
|
|
99
|
+
// Remove private use subtags
|
|
100
|
+
const scrubbedLocale = locale.replace(/-x(-[a-zA-Z0-9]{1,8})+$/, '');
|
|
101
|
+
try {
|
|
102
|
+
return resolver(scrubbedLocale);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// fallback to known existing en-US locale data as of 14.0
|
|
106
|
+
return scrubbedLocale === 'en-US' ? findLocaleDataPath('en', resolver) : null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.io/license
|
|
7
|
+
*/
|
|
8
|
+
export declare function addEventDispatchContract(html: string): Promise<string>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.io/license
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.addEventDispatchContract = void 0;
|
|
11
|
+
const promises_1 = require("node:fs/promises");
|
|
12
|
+
const html_rewriting_stream_1 = require("./html-rewriting-stream");
|
|
13
|
+
let jsActionContractScript;
|
|
14
|
+
async function addEventDispatchContract(html) {
|
|
15
|
+
const { rewriter, transformedContent } = await (0, html_rewriting_stream_1.htmlRewritingStream)(html);
|
|
16
|
+
jsActionContractScript ??=
|
|
17
|
+
'<script type="text/javascript" id="ng-event-dispatch-contract">' +
|
|
18
|
+
(await (0, promises_1.readFile)(require.resolve('@angular/core/event-dispatch-contract.min.js'), 'utf-8')) +
|
|
19
|
+
'</script>';
|
|
20
|
+
rewriter.on('startTag', (tag) => {
|
|
21
|
+
rewriter.emitStartTag(tag);
|
|
22
|
+
if (tag.tagName === 'body') {
|
|
23
|
+
rewriter.emitRaw(jsActionContractScript);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return transformedContent();
|
|
27
|
+
}
|
|
28
|
+
exports.addEventDispatchContract = addEventDispatchContract;
|
|
@@ -30,18 +30,28 @@ export interface IndexHtmlGeneratorOptions {
|
|
|
30
30
|
optimization?: NormalizedOptimizationOptions;
|
|
31
31
|
cache?: NormalizedCachedOptions;
|
|
32
32
|
imageDomains?: string[];
|
|
33
|
+
generateDedicatedSSRContent?: boolean;
|
|
33
34
|
}
|
|
34
35
|
export type IndexHtmlTransform = (content: string) => Promise<string>;
|
|
35
|
-
export interface
|
|
36
|
+
export interface IndexHtmlPluginTransformResult {
|
|
36
37
|
content: string;
|
|
37
38
|
warnings: string[];
|
|
38
39
|
errors: string[];
|
|
39
40
|
}
|
|
41
|
+
export interface IndexHtmlProcessResult {
|
|
42
|
+
csrContent: string;
|
|
43
|
+
ssrContent?: string;
|
|
44
|
+
warnings: string[];
|
|
45
|
+
errors: string[];
|
|
46
|
+
}
|
|
40
47
|
export declare class IndexHtmlGenerator {
|
|
41
48
|
readonly options: IndexHtmlGeneratorOptions;
|
|
42
49
|
private readonly plugins;
|
|
50
|
+
private readonly csrPlugins;
|
|
51
|
+
private readonly ssrPlugins;
|
|
43
52
|
constructor(options: IndexHtmlGeneratorOptions);
|
|
44
|
-
process(options: IndexHtmlGeneratorProcessOptions): Promise<
|
|
53
|
+
process(options: IndexHtmlGeneratorProcessOptions): Promise<IndexHtmlProcessResult>;
|
|
54
|
+
private runPlugins;
|
|
45
55
|
readAsset(path: string): Promise<string>;
|
|
46
56
|
protected readIndex(path: string): Promise<string>;
|
|
47
57
|
}
|
|
@@ -10,36 +10,53 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.IndexHtmlGenerator = void 0;
|
|
11
11
|
const promises_1 = require("node:fs/promises");
|
|
12
12
|
const node_path_1 = require("node:path");
|
|
13
|
+
const add_event_dispatch_contract_1 = require("./add-event-dispatch-contract");
|
|
13
14
|
const augment_index_html_1 = require("./augment-index-html");
|
|
14
15
|
const inline_critical_css_1 = require("./inline-critical-css");
|
|
15
16
|
const inline_fonts_1 = require("./inline-fonts");
|
|
16
|
-
const
|
|
17
|
+
const nonce_1 = require("./nonce");
|
|
17
18
|
class IndexHtmlGenerator {
|
|
18
19
|
options;
|
|
19
20
|
plugins;
|
|
21
|
+
csrPlugins = [];
|
|
22
|
+
ssrPlugins = [];
|
|
20
23
|
constructor(options) {
|
|
21
24
|
this.options = options;
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
+
const extraCommonPlugins = [];
|
|
26
|
+
if (options?.optimization?.fonts.inline) {
|
|
27
|
+
extraCommonPlugins.push(inlineFontsPlugin(this), nonce_1.addNonce);
|
|
25
28
|
}
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
// Common plugins
|
|
30
|
+
this.plugins = [augmentIndexHtmlPlugin(this), ...extraCommonPlugins, postTransformPlugin(this)];
|
|
31
|
+
// CSR plugins
|
|
32
|
+
if (options?.optimization?.styles?.inlineCritical) {
|
|
33
|
+
this.csrPlugins.push(inlineCriticalCssPlugin(this));
|
|
34
|
+
}
|
|
35
|
+
// SSR plugins
|
|
36
|
+
if (options.generateDedicatedSSRContent) {
|
|
37
|
+
this.ssrPlugins.push(addEventDispatchContractPlugin(), addNoncePlugin());
|
|
28
38
|
}
|
|
29
|
-
this.plugins = [
|
|
30
|
-
augmentIndexHtmlPlugin(this),
|
|
31
|
-
...extraPlugins,
|
|
32
|
-
// Runs after the `extraPlugins` to capture any nonce or
|
|
33
|
-
// `style` tags that might've been added by them.
|
|
34
|
-
addStyleNoncePlugin(),
|
|
35
|
-
postTransformPlugin(this),
|
|
36
|
-
];
|
|
37
39
|
}
|
|
38
40
|
async process(options) {
|
|
39
41
|
let content = await this.readIndex(this.options.indexPath);
|
|
40
42
|
const warnings = [];
|
|
41
43
|
const errors = [];
|
|
42
|
-
|
|
44
|
+
content = await this.runPlugins(content, this.plugins, options, warnings, errors);
|
|
45
|
+
const [csrContent, ssrContent] = await Promise.all([
|
|
46
|
+
this.runPlugins(content, this.csrPlugins, options, warnings, errors),
|
|
47
|
+
this.ssrPlugins.length
|
|
48
|
+
? this.runPlugins(content, this.ssrPlugins, options, warnings, errors)
|
|
49
|
+
: undefined,
|
|
50
|
+
]);
|
|
51
|
+
return {
|
|
52
|
+
ssrContent,
|
|
53
|
+
csrContent,
|
|
54
|
+
warnings,
|
|
55
|
+
errors,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async runPlugins(content, plugins, options, warnings, errors) {
|
|
59
|
+
for (const plugin of plugins) {
|
|
43
60
|
const result = await plugin(content, options);
|
|
44
61
|
if (typeof result === 'string') {
|
|
45
62
|
content = result;
|
|
@@ -54,11 +71,7 @@ class IndexHtmlGenerator {
|
|
|
54
71
|
}
|
|
55
72
|
}
|
|
56
73
|
}
|
|
57
|
-
return
|
|
58
|
-
content,
|
|
59
|
-
warnings,
|
|
60
|
-
errors,
|
|
61
|
-
};
|
|
74
|
+
return content;
|
|
62
75
|
}
|
|
63
76
|
async readAsset(path) {
|
|
64
77
|
try {
|
|
@@ -111,9 +124,12 @@ function inlineCriticalCssPlugin(generator) {
|
|
|
111
124
|
});
|
|
112
125
|
return async (html, options) => inlineCriticalCssProcessor.process(html, { outputPath: options.outputPath });
|
|
113
126
|
}
|
|
114
|
-
function
|
|
115
|
-
return (html) => (0,
|
|
127
|
+
function addNoncePlugin() {
|
|
128
|
+
return (html) => (0, nonce_1.addNonce)(html);
|
|
116
129
|
}
|
|
117
130
|
function postTransformPlugin({ options }) {
|
|
118
131
|
return async (html) => (options.postTransform ? options.postTransform(html) : html);
|
|
119
132
|
}
|
|
133
|
+
function addEventDispatchContractPlugin() {
|
|
134
|
+
return (html) => (0, add_event_dispatch_contract_1.addEventDispatchContract)(html);
|
|
135
|
+
}
|
|
@@ -143,7 +143,7 @@ class InlineFontsProcessor {
|
|
|
143
143
|
if (hrefAttr) {
|
|
144
144
|
const href = hrefAttr.value;
|
|
145
145
|
const cssContent = hrefsContent.get(href);
|
|
146
|
-
rewriter.emitRaw(`<style
|
|
146
|
+
rewriter.emitRaw(`<style>${cssContent}</style>`);
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
149
149
|
rewriter.emitStartTag(tag);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* Finds the `ngCspNonce` value and copies it to all inline `<style>` tags.
|
|
9
|
+
* Finds the `ngCspNonce` value and copies it to all inline `<style>` and `<script> `tags.
|
|
10
10
|
* @param html Markup that should be processed.
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function addNonce(html: string): Promise<string>;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.
|
|
10
|
+
exports.addNonce = void 0;
|
|
11
11
|
const html_rewriting_stream_1 = require("./html-rewriting-stream");
|
|
12
12
|
/**
|
|
13
13
|
* Pattern matching the name of the Angular nonce attribute. Note that this is
|
|
@@ -15,24 +15,26 @@ const html_rewriting_stream_1 = require("./html-rewriting-stream");
|
|
|
15
15
|
*/
|
|
16
16
|
const NONCE_ATTR_PATTERN = /ngCspNonce/i;
|
|
17
17
|
/**
|
|
18
|
-
* Finds the `ngCspNonce` value and copies it to all inline `<style>` tags.
|
|
18
|
+
* Finds the `ngCspNonce` value and copies it to all inline `<style>` and `<script> `tags.
|
|
19
19
|
* @param html Markup that should be processed.
|
|
20
20
|
*/
|
|
21
|
-
async function
|
|
21
|
+
async function addNonce(html) {
|
|
22
22
|
const nonce = await findNonce(html);
|
|
23
23
|
if (!nonce) {
|
|
24
24
|
return html;
|
|
25
25
|
}
|
|
26
26
|
const { rewriter, transformedContent } = await (0, html_rewriting_stream_1.htmlRewritingStream)(html);
|
|
27
27
|
rewriter.on('startTag', (tag) => {
|
|
28
|
-
if (tag.tagName === 'style'
|
|
28
|
+
if ((tag.tagName === 'style' ||
|
|
29
|
+
(tag.tagName === 'script' && !tag.attrs.some((attr) => attr.name === 'src'))) &&
|
|
30
|
+
!tag.attrs.some((attr) => attr.name === 'nonce')) {
|
|
29
31
|
tag.attrs.push({ name: 'nonce', value: nonce });
|
|
30
32
|
}
|
|
31
33
|
rewriter.emitStartTag(tag);
|
|
32
34
|
});
|
|
33
35
|
return transformedContent();
|
|
34
36
|
}
|
|
35
|
-
exports.
|
|
37
|
+
exports.addNonce = addNonce;
|
|
36
38
|
/** Finds the Angular nonce in an HTML string. */
|
|
37
39
|
async function findNonce(html) {
|
|
38
40
|
// Inexpensive check to avoid parsing the HTML when we're sure there's no nonce.
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
|
-
export declare function loadProxyConfiguration(root: string, proxyConfig: string | undefined
|
|
8
|
+
export declare function loadProxyConfiguration(root: string, proxyConfig: string | undefined): Promise<Record<string, object> | undefined>;
|
|
@@ -39,7 +39,7 @@ const node_url_1 = require("node:url");
|
|
|
39
39
|
const picomatch_1 = require("picomatch");
|
|
40
40
|
const error_1 = require("./error");
|
|
41
41
|
const load_esm_1 = require("./load-esm");
|
|
42
|
-
async function loadProxyConfiguration(root, proxyConfig
|
|
42
|
+
async function loadProxyConfiguration(root, proxyConfig) {
|
|
43
43
|
if (!proxyConfig) {
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
@@ -94,10 +94,7 @@ async function loadProxyConfiguration(root, proxyConfig, normalize = false) {
|
|
|
94
94
|
throw e;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
proxyConfiguration = normalizeProxyConfiguration(proxyConfiguration);
|
|
99
|
-
}
|
|
100
|
-
return proxyConfiguration;
|
|
97
|
+
return normalizeProxyConfiguration(proxyConfiguration);
|
|
101
98
|
}
|
|
102
99
|
exports.loadProxyConfiguration = loadProxyConfiguration;
|
|
103
100
|
/**
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.normalizeCacheOptions = void 0;
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
/** Version placeholder is replaced during the build process with actual package version */
|
|
13
|
-
const VERSION = '18.0.0-next.
|
|
13
|
+
const VERSION = '18.0.0-next.3';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
|
-
import { I18nOptions } from './i18n-
|
|
8
|
+
import { I18nOptions } from './i18n-webpack';
|
|
9
9
|
export declare function ensureOutputPaths(baseOutputPath: string, i18n: I18nOptions): Map<string, string>;
|
|
@@ -8,4 +8,10 @@
|
|
|
8
8
|
export interface PostcssConfiguration {
|
|
9
9
|
plugins: [name: string, options?: object | string][];
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export interface SearchDirectory {
|
|
12
|
+
root: string;
|
|
13
|
+
files: Set<string>;
|
|
14
|
+
}
|
|
15
|
+
export declare function generateSearchDirectories(roots: string[]): Promise<SearchDirectory[]>;
|
|
16
|
+
export declare function findTailwindConfiguration(searchDirectories: SearchDirectory[]): string | undefined;
|
|
17
|
+
export declare function loadPostcssConfiguration(searchDirectories: SearchDirectory[]): Promise<PostcssConfiguration | undefined>;
|
|
@@ -7,16 +7,23 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.loadPostcssConfiguration = void 0;
|
|
10
|
+
exports.loadPostcssConfiguration = exports.findTailwindConfiguration = exports.generateSearchDirectories = void 0;
|
|
11
11
|
const promises_1 = require("node:fs/promises");
|
|
12
12
|
const node_path_1 = require("node:path");
|
|
13
13
|
const postcssConfigurationFiles = ['postcss.config.json', '.postcssrc.json'];
|
|
14
|
+
const tailwindConfigFiles = [
|
|
15
|
+
'tailwind.config.js',
|
|
16
|
+
'tailwind.config.cjs',
|
|
17
|
+
'tailwind.config.mjs',
|
|
18
|
+
'tailwind.config.ts',
|
|
19
|
+
];
|
|
14
20
|
async function generateSearchDirectories(roots) {
|
|
15
21
|
return await Promise.all(roots.map((root) => (0, promises_1.readdir)(root, { withFileTypes: true }).then((entries) => ({
|
|
16
22
|
root,
|
|
17
23
|
files: new Set(entries.filter((entry) => entry.isFile()).map((entry) => entry.name)),
|
|
18
24
|
}))));
|
|
19
25
|
}
|
|
26
|
+
exports.generateSearchDirectories = generateSearchDirectories;
|
|
20
27
|
function findFile(searchDirectories, potentialFiles) {
|
|
21
28
|
for (const { root, files } of searchDirectories) {
|
|
22
29
|
for (const potential of potentialFiles) {
|
|
@@ -27,14 +34,16 @@ function findFile(searchDirectories, potentialFiles) {
|
|
|
27
34
|
}
|
|
28
35
|
return undefined;
|
|
29
36
|
}
|
|
37
|
+
function findTailwindConfiguration(searchDirectories) {
|
|
38
|
+
return findFile(searchDirectories, tailwindConfigFiles);
|
|
39
|
+
}
|
|
40
|
+
exports.findTailwindConfiguration = findTailwindConfiguration;
|
|
30
41
|
async function readPostcssConfiguration(configurationFile) {
|
|
31
42
|
const data = await (0, promises_1.readFile)(configurationFile, 'utf-8');
|
|
32
43
|
const config = JSON.parse(data);
|
|
33
44
|
return config;
|
|
34
45
|
}
|
|
35
|
-
async function loadPostcssConfiguration(
|
|
36
|
-
// A configuration file can exist in the project or workspace root
|
|
37
|
-
const searchDirectories = await generateSearchDirectories([projectRoot, workspaceRoot]);
|
|
46
|
+
async function loadPostcssConfiguration(searchDirectories) {
|
|
38
47
|
const configPath = findFile(searchDirectories, postcssConfigurationFiles);
|
|
39
48
|
if (!configPath) {
|
|
40
49
|
return undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.io/license
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveAssets(entries: {
|
|
9
|
+
glob: string;
|
|
10
|
+
ignore?: string[];
|
|
11
|
+
input: string;
|
|
12
|
+
output: string;
|
|
13
|
+
flatten?: boolean;
|
|
14
|
+
followSymlinks?: boolean;
|
|
15
|
+
}[], root: string): Promise<{
|
|
16
|
+
source: string;
|
|
17
|
+
destination: string;
|
|
18
|
+
}[]>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.io/license
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.resolveAssets = void 0;
|
|
14
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
15
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
16
|
+
async function resolveAssets(entries, root) {
|
|
17
|
+
const defaultIgnore = ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'];
|
|
18
|
+
const outputFiles = [];
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const cwd = node_path_1.default.resolve(root, entry.input);
|
|
21
|
+
const files = await (0, fast_glob_1.default)(entry.glob, {
|
|
22
|
+
cwd,
|
|
23
|
+
dot: true,
|
|
24
|
+
ignore: entry.ignore ? defaultIgnore.concat(entry.ignore) : defaultIgnore,
|
|
25
|
+
followSymbolicLinks: entry.followSymlinks,
|
|
26
|
+
});
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const src = node_path_1.default.join(cwd, file);
|
|
29
|
+
const filePath = entry.flatten ? node_path_1.default.basename(file) : file;
|
|
30
|
+
outputFiles.push({ source: src, destination: node_path_1.default.join(entry.output, filePath) });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return outputFiles;
|
|
34
|
+
}
|
|
35
|
+
exports.resolveAssets = resolveAssets;
|