mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 02:54:21 +08:00
Adds a new "builderMode" setting for Karma that can be used to switch between webpack ("browser") and esbuild ("application"). It supports a third value "detect" that will use the same bundler that's also used for development builds. The detect mode is modelled after the logic used for the dev-server builder. This initial implementation doesn't properly support `--watch` mode or code coverage.
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
/**
|
|
* @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 glob, { isDynamicPattern } from 'fast-glob';
|
|
import { PathLike, constants, promises as fs } from 'fs';
|
|
import { basename, dirname, extname, join, relative } from 'path';
|
|
|
|
/* Go through all patterns and find unique list of files */
|
|
export async function findTests(
|
|
include: string[],
|
|
exclude: string[],
|
|
workspaceRoot: string,
|
|
projectSourceRoot: string,
|
|
): Promise<string[]> {
|
|
const matchingTestsPromises = include.map((pattern) =>
|
|
findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot),
|
|
);
|
|
const files = await Promise.all(matchingTestsPromises);
|
|
|
|
// Unique file names
|
|
return [...new Set(files.flat())];
|
|
}
|
|
|
|
const normalizePath = (path: string): string => path.replace(/\\/g, '/');
|
|
|
|
const removeLeadingSlash = (pattern: string): string => {
|
|
if (pattern.charAt(0) === '/') {
|
|
return pattern.substring(1);
|
|
}
|
|
|
|
return pattern;
|
|
};
|
|
|
|
const removeRelativeRoot = (path: string, root: string): string => {
|
|
if (path.startsWith(root)) {
|
|
return path.substring(root.length);
|
|
}
|
|
|
|
return path;
|
|
};
|
|
|
|
async function findMatchingTests(
|
|
pattern: string,
|
|
ignore: string[],
|
|
workspaceRoot: string,
|
|
projectSourceRoot: string,
|
|
): Promise<string[]> {
|
|
// normalize pattern, glob lib only accepts forward slashes
|
|
let normalizedPattern = normalizePath(pattern);
|
|
normalizedPattern = removeLeadingSlash(normalizedPattern);
|
|
|
|
const relativeProjectRoot = normalizePath(relative(workspaceRoot, projectSourceRoot) + '/');
|
|
|
|
// remove relativeProjectRoot to support relative paths from root
|
|
// such paths are easy to get when running scripts via IDEs
|
|
normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot);
|
|
|
|
// special logic when pattern does not look like a glob
|
|
if (!isDynamicPattern(normalizedPattern)) {
|
|
if (await isDirectory(join(projectSourceRoot, normalizedPattern))) {
|
|
normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
|
|
} else {
|
|
// see if matching spec file exists
|
|
const fileExt = extname(normalizedPattern);
|
|
// Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
|
|
const potentialSpec = join(
|
|
projectSourceRoot,
|
|
dirname(normalizedPattern),
|
|
`${basename(normalizedPattern, fileExt)}.spec${fileExt}`,
|
|
);
|
|
|
|
if (await exists(potentialSpec)) {
|
|
return [potentialSpec];
|
|
}
|
|
}
|
|
}
|
|
|
|
// normalize the patterns in the ignore list
|
|
const normalizedIgnorePatternList = ignore.map((pattern: string) =>
|
|
removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot),
|
|
);
|
|
|
|
return glob(normalizedPattern, {
|
|
cwd: projectSourceRoot,
|
|
absolute: true,
|
|
ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
|
|
});
|
|
}
|
|
|
|
async function isDirectory(path: PathLike): Promise<boolean> {
|
|
try {
|
|
const stats = await fs.stat(path);
|
|
|
|
return stats.isDirectory();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function exists(path: PathLike): Promise<boolean> {
|
|
try {
|
|
await fs.access(path, constants.F_OK);
|
|
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|