@angular/build
Version:
Official build system for Angular
112 lines (111 loc) • 4.6 kB
JavaScript
;
/**
* @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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.findTests = findTests;
exports.getTestEntrypoints = getTestEntrypoints;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const tinyglobby_1 = require("tinyglobby");
/* Go through all patterns and find unique list of files */
async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
const matchingTestsPromises = include.map((pattern) => findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot));
const files = await Promise.all(matchingTestsPromises);
// Unique file names
return [...new Set(files.flat())];
}
/** Generate unique bundle names for a set of test files. */
function getTestEntrypoints(testFiles, { projectSourceRoot, workspaceRoot }) {
const seen = new Set();
return new Map(Array.from(testFiles, (testFile) => {
const relativePath = removeRoots(testFile, [projectSourceRoot, workspaceRoot])
// Strip leading dots and path separators.
.replace(/^[./\\]+/, '')
// Replace any path separators with dashes.
.replace(/[/\\]/g, '-');
const baseName = `spec-${(0, node_path_1.basename)(relativePath, (0, node_path_1.extname)(relativePath))}`;
let uniqueName = baseName;
let suffix = 2;
while (seen.has(uniqueName)) {
uniqueName = `${baseName}-${suffix}`.replace(/([^\w](?:spec|test))-([\d]+)$/, '-$2$1');
++suffix;
}
seen.add(uniqueName);
return [uniqueName, testFile];
}));
}
const normalizePath = (path) => path.replace(/\\/g, '/');
const removeLeadingSlash = (pattern) => {
if (pattern.charAt(0) === '/') {
return pattern.substring(1);
}
return pattern;
};
const removeRelativeRoot = (path, root) => {
if (path.startsWith(root)) {
return path.substring(root.length);
}
return path;
};
function removeRoots(path, roots) {
for (const root of roots) {
if (path.startsWith(root)) {
return path.substring(root.length);
}
}
return (0, node_path_1.basename)(path);
}
async function findMatchingTests(pattern, ignore, workspaceRoot, projectSourceRoot) {
// normalize pattern, glob lib only accepts forward slashes
let normalizedPattern = normalizePath(pattern);
normalizedPattern = removeLeadingSlash(normalizedPattern);
const relativeProjectRoot = normalizePath((0, node_path_1.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 (!(0, tinyglobby_1.isDynamicPattern)(normalizedPattern)) {
if (await isDirectory((0, node_path_1.join)(projectSourceRoot, normalizedPattern))) {
normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
}
else {
// see if matching spec file exists
const fileExt = (0, node_path_1.extname)(normalizedPattern);
// Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
const potentialSpec = (0, node_path_1.join)(projectSourceRoot, (0, node_path_1.dirname)(normalizedPattern), `${(0, node_path_1.basename)(normalizedPattern, fileExt)}.spec${fileExt}`);
if (await exists(potentialSpec)) {
return [potentialSpec];
}
}
}
// normalize the patterns in the ignore list
const normalizedIgnorePatternList = ignore.map((pattern) => removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot));
return (0, tinyglobby_1.glob)(normalizedPattern, {
cwd: projectSourceRoot,
absolute: true,
ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
});
}
async function isDirectory(path) {
try {
const stats = await node_fs_1.promises.stat(path);
return stats.isDirectory();
}
catch {
return false;
}
}
async function exists(path) {
try {
await node_fs_1.promises.access(path, node_fs_1.constants.F_OK);
return true;
}
catch {
return false;
}
}