|
6 | 6 | * found in the LICENSE file at https://angular.io/license
|
7 | 7 | */
|
8 | 8 |
|
9 |
| -import { BuilderContext } from '@angular-devkit/architect'; |
| 9 | +import type { BuilderContext } from '@angular-devkit/architect'; |
10 | 10 | import type { Plugin } from 'esbuild';
|
11 | 11 | import { realpathSync } from 'node:fs';
|
12 | 12 | import { access, constants } from 'node:fs/promises';
|
13 | 13 | import { createRequire } from 'node:module';
|
14 | 14 | import path from 'node:path';
|
15 |
| -import { |
16 |
| - globalScriptsByBundleName, |
17 |
| - normalizeGlobalStyles, |
18 |
| -} from '../../tools/webpack/utils/helpers'; |
19 | 15 | import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
|
20 | 16 | import { colors } from '../../utils/color';
|
21 | 17 | import { useJSONBuildLogs } from '../../utils/environment-options';
|
22 | 18 | import { I18nOptions, createI18nOptions } from '../../utils/i18n-options';
|
23 | 19 | import { IndexHtmlTransform } from '../../utils/index-file/index-html-generator';
|
24 | 20 | import { normalizeCacheOptions } from '../../utils/normalize-cache';
|
25 |
| -import { generateEntryPoints } from '../../utils/package-chunk-sort'; |
26 | 21 | import { loadPostcssConfiguration } from '../../utils/postcss-configuration';
|
27 | 22 | import { findTailwindConfigurationFile } from '../../utils/tailwind';
|
28 |
| -import { getIndexInputFile, getIndexOutputFile } from '../../utils/webpack-browser-config'; |
29 | 23 | import {
|
30 | 24 | Schema as ApplicationBuilderOptions,
|
31 | 25 | I18NTranslation,
|
@@ -194,35 +188,29 @@ export async function normalizeOptions(
|
194 | 188 | ? undefined
|
195 | 189 | : await getTailwindConfig(workspaceRoot, projectRoot, context);
|
196 | 190 |
|
197 |
| - const globalStyles: { name: string; files: string[]; initial: boolean }[] = []; |
198 |
| - if (options.styles?.length) { |
199 |
| - const { entryPoints: stylesheetEntrypoints, noInjectNames } = normalizeGlobalStyles( |
200 |
| - options.styles || [], |
201 |
| - ); |
202 |
| - for (const [name, files] of Object.entries(stylesheetEntrypoints)) { |
203 |
| - globalStyles.push({ name, files, initial: !noInjectNames.includes(name) }); |
204 |
| - } |
205 |
| - } |
206 |
| - |
207 |
| - const globalScripts: { name: string; files: string[]; initial: boolean }[] = []; |
208 |
| - if (options.scripts?.length) { |
209 |
| - for (const { bundleName, paths, inject } of globalScriptsByBundleName(options.scripts)) { |
210 |
| - globalScripts.push({ name: bundleName, files: paths, initial: inject }); |
211 |
| - } |
212 |
| - } |
| 191 | + const globalStyles = normalizeGlobalEntries(options.styles, 'styles'); |
| 192 | + const globalScripts = normalizeGlobalEntries(options.scripts, 'scripts'); |
213 | 193 |
|
214 | 194 | let indexHtmlOptions;
|
215 | 195 | // index can never have a value of `true` but in the schema it's of type `boolean`.
|
216 | 196 | if (typeof options.index !== 'boolean') {
|
217 | 197 | indexHtmlOptions = {
|
218 |
| - input: path.join(workspaceRoot, getIndexInputFile(options.index)), |
| 198 | + input: path.join( |
| 199 | + workspaceRoot, |
| 200 | + typeof options.index === 'string' ? options.index : options.index.input, |
| 201 | + ), |
219 | 202 | // The output file will be created within the configured output path
|
220 |
| - output: getIndexOutputFile(options.index), |
221 |
| - // TODO: Use existing information from above to create the insertion order |
222 |
| - insertionOrder: generateEntryPoints({ |
223 |
| - scripts: options.scripts ?? [], |
224 |
| - styles: options.styles ?? [], |
225 |
| - }), |
| 203 | + output: |
| 204 | + typeof options.index === 'string' |
| 205 | + ? path.basename(options.index) |
| 206 | + : options.index.output || 'index.html', |
| 207 | + insertionOrder: [ |
| 208 | + ['polyfills', true], |
| 209 | + ...globalStyles.filter((s) => s.initial).map((s) => [s.name, false]), |
| 210 | + ...globalScripts.filter((s) => s.initial).map((s) => [s.name, false]), |
| 211 | + ['main', true], |
| 212 | + // [name, esm] |
| 213 | + ] as [string, boolean][], |
226 | 214 | transformer: extensions?.indexHtmlTransformer,
|
227 | 215 | // Preload initial defaults to true
|
228 | 216 | preloadInitial: typeof options.index !== 'object' || (options.index.preloadInitial ?? true),
|
@@ -462,3 +450,46 @@ function normalizeDirectoryPath(path: string): string {
|
462 | 450 |
|
463 | 451 | return path;
|
464 | 452 | }
|
| 453 | + |
| 454 | +function normalizeGlobalEntries( |
| 455 | + rawEntries: ({ bundleName?: string; input: string; inject?: boolean } | string)[] | undefined, |
| 456 | + defaultName: string, |
| 457 | +): { name: string; files: string[]; initial: boolean }[] { |
| 458 | + if (!rawEntries?.length) { |
| 459 | + return []; |
| 460 | + } |
| 461 | + |
| 462 | + const bundles = new Map<string, { name: string; files: string[]; initial: boolean }>(); |
| 463 | + |
| 464 | + for (const rawEntry of rawEntries) { |
| 465 | + let entry; |
| 466 | + if (typeof rawEntry === 'string') { |
| 467 | + // string entries use default bundle name and inject values |
| 468 | + entry = { input: rawEntry }; |
| 469 | + } else { |
| 470 | + entry = rawEntry; |
| 471 | + } |
| 472 | + |
| 473 | + const { bundleName, input, inject = true } = entry; |
| 474 | + |
| 475 | + // Non-injected entries default to the file name |
| 476 | + const name = bundleName || (inject ? defaultName : path.basename(input, path.extname(input))); |
| 477 | + |
| 478 | + const existing = bundles.get(name); |
| 479 | + if (!existing) { |
| 480 | + bundles.set(name, { name, files: [input], initial: inject }); |
| 481 | + continue; |
| 482 | + } |
| 483 | + |
| 484 | + if (existing.initial !== inject) { |
| 485 | + throw new Error( |
| 486 | + `The "${name}" bundle is mixing injected and non-injected entries. ` + |
| 487 | + 'Verify that the project options are correct.', |
| 488 | + ); |
| 489 | + } |
| 490 | + |
| 491 | + existing.files.push(input); |
| 492 | + } |
| 493 | + |
| 494 | + return [...bundles.values()]; |
| 495 | +} |
0 commit comments