diff --git a/packages/angular_devkit/build_angular/src/builders/application/options.ts b/packages/angular_devkit/build_angular/src/builders/application/options.ts index e04da118f23e..1d1fb9311233 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/options.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/options.ts @@ -100,12 +100,12 @@ export async function normalizeOptions( const outputNames = { bundles: options.outputHashing === OutputHashing.All || options.outputHashing === OutputHashing.Bundles - ? '[name].[hash]' + ? '[name]-[hash]' : '[name]', media: 'media/' + (options.outputHashing === OutputHashing.All || options.outputHashing === OutputHashing.Media - ? '[name].[hash]' + ? '[name]-[hash]' : '[name]'), }; diff --git a/packages/angular_devkit/build_angular/src/builders/application/tests/options/output-hashing_spec.ts b/packages/angular_devkit/build_angular/src/builders/application/tests/options/output-hashing_spec.ts index c095496625d3..41033a7a559a 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/tests/options/output-hashing_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/tests/options/output-hashing_spec.ts @@ -31,10 +31,10 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /main\.[0-9A-Z]{8}\.js$/)).toBeTrue(); - expect(harness.hasFileMatch('dist', /polyfills\.[0-9A-Z]{8}\.js$/)).toBeTrue(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeTrue(); - expect(harness.hasFileMatch('dist/media', /spectrum\.[0-9A-Z]{8}\.png$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /main-[0-9A-Z]{8}\.js$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /polyfills-[0-9A-Z]{8}\.js$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeTrue(); + expect(harness.hasFileMatch('dist/media', /spectrum-[0-9A-Z]{8}\.png$/)).toBeTrue(); }); it(`doesn't hash any filenames when not set`, async () => { @@ -49,10 +49,10 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /main\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /polyfills\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeFalse(); - expect(harness.hasFileMatch('dist/media', /spectrum\.[0-9A-Z]{8}\.png$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /main-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /polyfills-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeFalse(); + expect(harness.hasFileMatch('dist/media', /spectrum-[0-9A-Z]{8}\.png$/)).toBeFalse(); }); it(`doesn't hash any filenames when set to "none"`, async () => { @@ -68,10 +68,10 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /main\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /polyfills\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeFalse(); - expect(harness.hasFileMatch('dist/media', /spectrum\.[0-9A-Z]{8}\.png$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /main-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /polyfills-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeFalse(); + expect(harness.hasFileMatch('dist/media', /spectrum-[0-9A-Z]{8}\.png$/)).toBeFalse(); }); it(`hashes CSS resources filenames only when set to "media"`, async () => { @@ -87,10 +87,10 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /main\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /polyfills\.[0-9A-Z]{8}\.js$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeFalse(); - expect(harness.hasFileMatch('dist/media', /spectrum\.[0-9A-Z]{8}\.png$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /main-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /polyfills-[0-9A-Z]{8}\.js$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeFalse(); + expect(harness.hasFileMatch('dist/media', /spectrum-[0-9A-Z]{8}\.png$/)).toBeTrue(); }); it(`hashes bundles filenames only when set to "bundles"`, async () => { @@ -106,10 +106,10 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /main\.[0-9A-Z]{8}\.js$/)).toBeTrue(); - expect(harness.hasFileMatch('dist', /polyfills\.[0-9A-Z]{8}\.js$/)).toBeTrue(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeTrue(); - expect(harness.hasFileMatch('dist/media', /spectrum\.[0-9A-Z]{8}\.png$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /main-[0-9A-Z]{8}\.js$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /polyfills-[0-9A-Z]{8}\.js$/)).toBeTrue(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeTrue(); + expect(harness.hasFileMatch('dist/media', /spectrum-[0-9A-Z]{8}\.png$/)).toBeFalse(); }); it('does not hash non injected styles', async () => { @@ -128,8 +128,8 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBe(true); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css$/)).toBeFalse(); - expect(harness.hasFileMatch('dist', /styles\.[0-9A-Z]{8}\.css.map$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css$/)).toBeFalse(); + expect(harness.hasFileMatch('dist', /styles-[0-9A-Z]{8}\.css.map$/)).toBeFalse(); harness.expectFile('dist/styles.css').toExist(); harness.expectFile('dist/styles.css.map').toExist(); }); diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts index bf06b5c7a5d2..c920aec39ca5 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/bundler-context.ts @@ -169,8 +169,8 @@ export class BundlerContext { outputFile.path = relativeFilePath; if (entryPoint) { - // The first part of the filename is the name of file (e.g., "polyfills" for "polyfills.7S5G3MDY.js") - const name = basename(relativeFilePath).split('.', 1)[0]; + // The first part of the filename is the name of file (e.g., "polyfills" for "polyfills-7S5G3MDY.js") + const name = basename(relativeFilePath).replace(/(?:-[\dA-Z]{8})?\.[a-z]{2,3}$/, ''); // Entry points are only styles or scripts const type = extname(relativeFilePath) === '.css' ? 'style' : 'script'; diff --git a/tests/legacy-cli/e2e/tests/basic/build.ts b/tests/legacy-cli/e2e/tests/basic/build.ts index 04d3af02c1b2..cd912e78e17d 100644 --- a/tests/legacy-cli/e2e/tests/basic/build.ts +++ b/tests/legacy-cli/e2e/tests/basic/build.ts @@ -16,8 +16,8 @@ export default async function () { // Production build const { stdout: stdout2 } = await ng('build'); if (getGlobalVariable('argv')['esbuild']) { - // esbuild uses an 8 character hash - await expectFileToMatch('dist/test-project/index.html', /main\.[a-zA-Z0-9]{8}\.js/); + // esbuild uses an 8 character hash and a dash as separator + await expectFileToMatch('dist/test-project/index.html', /main-[a-zA-Z0-9]{8}\.js/); } else { await expectFileToMatch('dist/test-project/index.html', /main\.[a-zA-Z0-9]{16}\.js/); } diff --git a/tests/legacy-cli/e2e/tests/basic/run.ts b/tests/legacy-cli/e2e/tests/basic/run.ts index a3b3dd1f21fb..f54301f48d26 100644 --- a/tests/legacy-cli/e2e/tests/basic/run.ts +++ b/tests/legacy-cli/e2e/tests/basic/run.ts @@ -10,8 +10,8 @@ export default async function () { // Production build await silentNg('run', 'test-project:build'); if (getGlobalVariable('argv')['esbuild']) { - // esbuild uses an 8 character hash - await expectFileToMatch('dist/test-project/index.html', /main\.[a-zA-Z0-9]{8}\.js/); + // esbuild uses an 8 character hash and a dash as separator + await expectFileToMatch('dist/test-project/index.html', /main-[a-zA-Z0-9]{8}\.js/); } else { await expectFileToMatch('dist/test-project/index.html', /main\.[a-zA-Z0-9]{16}\.js/); } diff --git a/tests/legacy-cli/e2e/tests/build/prod-build.ts b/tests/legacy-cli/e2e/tests/build/prod-build.ts index 4653788a267f..de709e205433 100644 --- a/tests/legacy-cli/e2e/tests/build/prod-build.ts +++ b/tests/legacy-cli/e2e/tests/build/prod-build.ts @@ -1,5 +1,6 @@ import { statSync } from 'fs'; import { join } from 'path'; +import { getGlobalVariable } from '../../utils/env'; import { expectFileToExist, expectFileToMatch, readFile } from '../../utils/fs'; import { noSilentNg } from '../../utils/process'; @@ -30,12 +31,21 @@ export default async function () { await noSilentNg('build'); await expectFileToExist(join(process.cwd(), 'dist')); // Check for cache busting hash script src - await expectFileToMatch('dist/test-project/index.html', /main\.[0-9a-zA-Z]{8,16}\.js/); - await expectFileToMatch('dist/test-project/index.html', /styles\.[0-9a-zA-Z]{8,16}\.css/); + if (getGlobalVariable('argv')['esbuild']) { + // esbuild uses an 8 character hash and a dash as separator + await expectFileToMatch('dist/test-project/index.html', /main-[0-9a-zA-Z]{8}\.js/); + await expectFileToMatch('dist/test-project/index.html', /styles-[0-9a-zA-Z]{8}\.css/); + } else { + await expectFileToMatch('dist/test-project/index.html', /main\.[0-9a-zA-Z]{16}\.js/); + await expectFileToMatch('dist/test-project/index.html', /styles\.[0-9a-zA-Z]{16}\.css/); + } await expectFileToMatch('dist/test-project/3rdpartylicenses.txt', /MIT/); const indexContent = await readFile('dist/test-project/index.html'); - const mainPath = indexContent.match(/src="(main\.[0-9a-zA-Z]{0,32}\.js)"/)![1]; + const mainSrcRegExp = getGlobalVariable('argv')['esbuild'] + ? /src="(main-[0-9a-zA-Z]{8}\.js)"/ + : /src="(main\.[0-9a-zA-Z]{16}\.js)"/; + const mainPath = indexContent.match(mainSrcRegExp)![1]; // Content checks await expectFileToMatch(`dist/test-project/${mainPath}`, bootstrapRegExp);