mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-18 03:23:57 +08:00
fix(@angular-devkit/build-angular): handle basename collisions
This commit is contained in:
parent
8f6ee1ac83
commit
2c9904ec7a
@ -64,8 +64,10 @@ interface InternalOptions {
|
||||
* If given a relative path, it is resolved relative to the current workspace and will generate an output at the same relative location
|
||||
* in the output directory. If given an absolute path, the output will be generated in the root of the output directory with the same base
|
||||
* name.
|
||||
*
|
||||
* If provided a Map, the key is the name of the output bundle and the value is the entry point file.
|
||||
*/
|
||||
entryPoints?: Set<string>;
|
||||
entryPoints?: Set<string> | Map<string, string>;
|
||||
|
||||
/** File extension to use for the generated output files. */
|
||||
outExtension?: 'js' | 'mjs';
|
||||
@ -519,7 +521,7 @@ async function getTailwindConfig(
|
||||
function normalizeEntryPoints(
|
||||
workspaceRoot: string,
|
||||
browser: string | undefined,
|
||||
entryPoints: Set<string> = new Set(),
|
||||
entryPoints: Set<string> | Map<string, string> = new Set(),
|
||||
): Record<string, string> {
|
||||
if (browser === '') {
|
||||
throw new Error('`browser` option cannot be an empty string.');
|
||||
@ -538,6 +540,16 @@ function normalizeEntryPoints(
|
||||
if (browser) {
|
||||
// Use `browser` alone.
|
||||
return { 'main': path.join(workspaceRoot, browser) };
|
||||
} else if (entryPoints instanceof Map) {
|
||||
return Object.fromEntries(
|
||||
Array.from(entryPoints.entries(), ([name, entryPoint]) => {
|
||||
// Get the full file path to a relative entry point input. Leave bare specifiers alone so they are resolved as modules.
|
||||
const isRelativePath = entryPoint.startsWith('.');
|
||||
const entryPointPath = isRelativePath ? path.join(workspaceRoot, entryPoint) : entryPoint;
|
||||
|
||||
return [name, entryPointPath];
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// Use `entryPoints` alone.
|
||||
const entryPointPaths: Record<string, string> = {};
|
||||
|
@ -260,7 +260,7 @@ async function collectEntrypoints(
|
||||
options: KarmaBuilderOptions,
|
||||
context: BuilderContext,
|
||||
projectSourceRoot: string,
|
||||
): Promise<Set<string>> {
|
||||
): Promise<Map<string, string>> {
|
||||
// Glob for files to test.
|
||||
const testFiles = await findTests(
|
||||
options.include ?? [],
|
||||
@ -269,7 +269,28 @@ async function collectEntrypoints(
|
||||
projectSourceRoot,
|
||||
);
|
||||
|
||||
return new Set(testFiles);
|
||||
const seen = new Set<string>();
|
||||
|
||||
return new Map(
|
||||
Array.from(testFiles, (testFile) => {
|
||||
const relativePath = path
|
||||
.relative(
|
||||
testFile.startsWith(projectSourceRoot) ? projectSourceRoot : context.workspaceRoot,
|
||||
testFile,
|
||||
)
|
||||
.replace(/^[./]+/, '_')
|
||||
.replace(/\//g, '-');
|
||||
let uniqueName = `spec-${path.basename(relativePath, path.extname(relativePath))}`;
|
||||
let suffix = 2;
|
||||
while (seen.has(uniqueName)) {
|
||||
uniqueName = `${relativePath}-${suffix}`;
|
||||
++suffix;
|
||||
}
|
||||
seen.add(uniqueName);
|
||||
|
||||
return [uniqueName, testFile];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function initializeApplication(
|
||||
@ -298,12 +319,11 @@ async function initializeApplication(
|
||||
fs.rm(outputPath, { recursive: true, force: true }),
|
||||
]);
|
||||
|
||||
let mainName = 'init_test_bed';
|
||||
const mainName = 'test_main';
|
||||
if (options.main) {
|
||||
entryPoints.add(options.main);
|
||||
mainName = path.basename(options.main, path.extname(options.main));
|
||||
entryPoints.set(mainName, options.main);
|
||||
} else {
|
||||
entryPoints.add('@angular-devkit/build-angular/src/builders/karma/init_test_bed.js');
|
||||
entryPoints.set(mainName, '@angular-devkit/build-angular/src/builders/karma/init_test_bed.js');
|
||||
}
|
||||
|
||||
const instrumentForCoverage = options.codeCoverage
|
||||
@ -358,6 +378,8 @@ async function initializeApplication(
|
||||
{ pattern: `${outputPath}/${mainName}.js`, type: 'module', watched: false },
|
||||
// Serve all source maps.
|
||||
{ pattern: `${outputPath}/*.map`, included: false, watched: false },
|
||||
// These are the test entrypoints.
|
||||
{ pattern: `${outputPath}/spec-*.js`, type: 'module', watched: false },
|
||||
);
|
||||
|
||||
if (hasChunkOrWorkerFiles(buildOutput.files)) {
|
||||
@ -371,10 +393,6 @@ async function initializeApplication(
|
||||
},
|
||||
);
|
||||
}
|
||||
karmaOptions.files.push(
|
||||
// Serve remaining JS on page load, these are the test entrypoints.
|
||||
{ pattern: `${outputPath}/*.js`, type: 'module', watched: false },
|
||||
);
|
||||
|
||||
if (options.styles?.length) {
|
||||
// Serve CSS outputs on page load, these are the global styles.
|
||||
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import { execute } from '../../index';
|
||||
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';
|
||||
|
||||
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApp) => {
|
||||
describe('Behavior: "Specs"', () => {
|
||||
beforeEach(async () => {
|
||||
await setupTarget(harness);
|
||||
});
|
||||
|
||||
it('supports multiple spec files with same basename', async () => {
|
||||
harness.useTarget('test', {
|
||||
...BASE_OPTIONS,
|
||||
});
|
||||
|
||||
const collidingBasename = 'collision.spec.ts';
|
||||
|
||||
// src/app/app.component.spec.ts conflicts with this one:
|
||||
await harness.writeFiles({
|
||||
[`src/app/a/${collidingBasename}`]: `/** Success! */`,
|
||||
[`src/app/b/${collidingBasename}`]: `/** Success! */`,
|
||||
});
|
||||
|
||||
const { result, logs } = await harness.executeOnce();
|
||||
|
||||
expect(result?.success).toBeTrue();
|
||||
|
||||
if (isApp) {
|
||||
const bundleLog = logs.find((log) =>
|
||||
log.message.includes('Application bundle generation complete.'),
|
||||
);
|
||||
expect(bundleLog?.message).toContain('spec-app-a-collision.spec.js');
|
||||
expect(bundleLog?.message).toContain('spec-app-b-collision.spec.js');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user