snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
This commit is contained in:
397
scripts/build-analysis.mts
Normal file
397
scripts/build-analysis.mts
Normal file
@@ -0,0 +1,397 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { brotliCompressSync, gzipSync } from 'node:zlib';
|
||||
|
||||
type OutputBucketKey = 'cjs' | 'es' | 'types' | 'umd';
|
||||
|
||||
interface IPackageJson {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IFileSizeSummary {
|
||||
brotliBytes: number;
|
||||
bucket: OutputBucketKey;
|
||||
gzipBytes: number;
|
||||
path: string;
|
||||
rawBytes: number;
|
||||
}
|
||||
|
||||
interface IDirectorySizeSummary {
|
||||
brotliBytes: number;
|
||||
fileCount: number;
|
||||
gzipBytes: number;
|
||||
largestFile: IFileSizeSummary | null;
|
||||
rawBytes: number;
|
||||
}
|
||||
|
||||
interface IWorkspacePackage {
|
||||
dir: string;
|
||||
packageJson: IPackageJson;
|
||||
}
|
||||
|
||||
interface IPackageSizeEntry {
|
||||
built: boolean;
|
||||
name: string;
|
||||
outputs: Record<OutputBucketKey, IDirectorySizeSummary | null>;
|
||||
path: string;
|
||||
totals: IDirectorySizeSummary;
|
||||
}
|
||||
|
||||
const ROOT_DIR = process.cwd();
|
||||
const ARTIFACTS_DIR = path.join(ROOT_DIR, 'artifacts');
|
||||
const PACKAGES_DIR = path.join(ROOT_DIR, 'packages');
|
||||
const OUTPUT_DIRECTORIES: Record<OutputBucketKey, string> = {
|
||||
cjs: path.join('lib', 'cjs'),
|
||||
es: path.join('lib', 'es'),
|
||||
types: path.join('lib', 'types'),
|
||||
umd: path.join('lib', 'umd'),
|
||||
};
|
||||
|
||||
function createEmptySummary(): IDirectorySizeSummary {
|
||||
return {
|
||||
brotliBytes: 0,
|
||||
fileCount: 0,
|
||||
gzipBytes: 0,
|
||||
largestFile: null,
|
||||
rawBytes: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function measureFile(filePath: string, bucket: OutputBucketKey): IFileSizeSummary {
|
||||
const fileBuffer = fs.readFileSync(filePath);
|
||||
|
||||
return {
|
||||
brotliBytes: brotliCompressSync(fileBuffer).byteLength,
|
||||
bucket,
|
||||
gzipBytes: gzipSync(fileBuffer).byteLength,
|
||||
path: path.relative(ROOT_DIR, filePath),
|
||||
rawBytes: fileBuffer.byteLength,
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeFiles(files: IFileSizeSummary[]): IDirectorySizeSummary {
|
||||
const summary = createEmptySummary();
|
||||
|
||||
for (const file of files) {
|
||||
summary.fileCount += 1;
|
||||
summary.rawBytes += file.rawBytes;
|
||||
summary.gzipBytes += file.gzipBytes;
|
||||
summary.brotliBytes += file.brotliBytes;
|
||||
|
||||
if (!summary.largestFile || file.rawBytes > summary.largestFile.rawBytes) {
|
||||
summary.largestFile = file;
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
function summarizeDirectoryTotals(summaries: IDirectorySizeSummary[]): IDirectorySizeSummary {
|
||||
const merged = createEmptySummary();
|
||||
|
||||
for (const summary of summaries) {
|
||||
merged.fileCount += summary.fileCount;
|
||||
merged.rawBytes += summary.rawBytes;
|
||||
merged.gzipBytes += summary.gzipBytes;
|
||||
merged.brotliBytes += summary.brotliBytes;
|
||||
|
||||
if (!merged.largestFile || (summary.largestFile && summary.largestFile.rawBytes > merged.largestFile.rawBytes)) {
|
||||
merged.largestFile = summary.largestFile;
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function listFiles(targetDir: string, bucket: OutputBucketKey): IFileSizeSummary[] {
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files: IFileSizeSummary[] = [];
|
||||
|
||||
function walk(currentDir: string) {
|
||||
for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
|
||||
const entryPath = path.join(currentDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
walk(entryPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(measureFile(entryPath, bucket));
|
||||
}
|
||||
}
|
||||
|
||||
walk(targetDir);
|
||||
return files;
|
||||
}
|
||||
|
||||
function readWorkspacePackages(): IWorkspacePackage[] {
|
||||
if (!fs.existsSync(PACKAGES_DIR)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fs.readdirSync(PACKAGES_DIR, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => path.join(PACKAGES_DIR, entry.name))
|
||||
.map((dir) => {
|
||||
const packageJsonPath = path.join(dir, 'package.json');
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
dir,
|
||||
packageJson: JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as IPackageJson,
|
||||
};
|
||||
})
|
||||
.filter((pkg): pkg is IWorkspacePackage => Boolean(pkg))
|
||||
.sort((left, right) => left.packageJson.name.localeCompare(right.packageJson.name));
|
||||
}
|
||||
|
||||
function analyzePackage(pkg: IWorkspacePackage): IPackageSizeEntry {
|
||||
const outputFiles = Object.fromEntries(
|
||||
Object.entries(OUTPUT_DIRECTORIES).map(([bucket, relativeDir]) => {
|
||||
return [bucket, listFiles(path.join(pkg.dir, relativeDir), bucket as OutputBucketKey)];
|
||||
})
|
||||
) as Record<OutputBucketKey, IFileSizeSummary[]>;
|
||||
const outputs = Object.fromEntries(
|
||||
Object.entries(outputFiles).map(([bucket, files]) => {
|
||||
return [bucket, files.length > 0 ? summarizeFiles(files) : null];
|
||||
})
|
||||
) as Record<OutputBucketKey, IDirectorySizeSummary | null>;
|
||||
const files = Object.values(outputFiles).flat();
|
||||
|
||||
return {
|
||||
built: files.length > 0,
|
||||
name: pkg.packageJson.name,
|
||||
outputs,
|
||||
path: path.relative(ROOT_DIR, pkg.dir),
|
||||
totals: summarizeFiles(files),
|
||||
};
|
||||
}
|
||||
|
||||
function buildPackageSizesReport(packages: IWorkspacePackage[]) {
|
||||
const entries = packages.map(analyzePackage);
|
||||
|
||||
return {
|
||||
generatedAt: new Date().toISOString(),
|
||||
packages: entries,
|
||||
scope: 'packages/*',
|
||||
summary: {
|
||||
builtPackageCount: entries.filter((entry) => entry.built).length,
|
||||
totalPackageCount: entries.length,
|
||||
totals: summarizeDirectoryTotals(entries.map((entry) => entry.totals)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function formatBytes(bytes: number) {
|
||||
if (bytes < 1024) {
|
||||
return `${bytes} B`;
|
||||
}
|
||||
|
||||
const units = ['KB', 'MB', 'GB'];
|
||||
let value = bytes / 1024;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (value >= 1024 && unitIndex < units.length - 1) {
|
||||
value /= 1024;
|
||||
unitIndex += 1;
|
||||
}
|
||||
|
||||
return `${value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)} ${units[unitIndex]}`;
|
||||
}
|
||||
|
||||
function buildVisualizationHtml(report: ReturnType<typeof buildPackageSizesReport>) {
|
||||
const rows = [...report.packages].sort((left, right) => right.totals.rawBytes - left.totals.rawBytes);
|
||||
const maxBytes = Math.max(...rows.map((row) => row.totals.rawBytes), 1);
|
||||
|
||||
const cards = rows.map((row) => {
|
||||
const width = `${(row.totals.rawBytes / maxBytes) * 100}%`;
|
||||
const outputs = (Object.entries(row.outputs) as Array<[OutputBucketKey, IDirectorySizeSummary | null]>)
|
||||
.filter(([, summary]) => Boolean(summary))
|
||||
.map(([bucket, summary]) => {
|
||||
return `<span class="chip">${bucket}: ${formatBytes(summary?.rawBytes ?? 0)}</span>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return `
|
||||
<article class="card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h2>${row.name}</h2>
|
||||
<p>${row.path}</p>
|
||||
</div>
|
||||
<strong>${formatBytes(row.totals.rawBytes)}</strong>
|
||||
</div>
|
||||
<div class="bar"><span style="width:${width}"></span></div>
|
||||
<div class="meta">
|
||||
<span>${row.totals.fileCount} files</span>
|
||||
<span>gzip ${formatBytes(row.totals.gzipBytes)}</span>
|
||||
<span>brotli ${formatBytes(row.totals.brotliBytes)}</span>
|
||||
</div>
|
||||
<div class="chips">${outputs}</div>
|
||||
</article>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Package Sizes</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4efe7;
|
||||
--panel: #fffdf8;
|
||||
--ink: #1d1a16;
|
||||
--muted: #6f665c;
|
||||
--line: #d9d0c4;
|
||||
--bar: #0b7a75;
|
||||
--bar-soft: #d6ecea;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "IBM Plex Sans", "Helvetica Neue", sans-serif;
|
||||
background:
|
||||
radial-gradient(circle at top left, #fff8ec 0, transparent 28%),
|
||||
linear-gradient(180deg, #efe6d7 0%, var(--bg) 100%);
|
||||
color: var(--ink);
|
||||
}
|
||||
main {
|
||||
width: min(1120px, calc(100vw - 32px));
|
||||
margin: 0 auto;
|
||||
padding: 40px 0 64px;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(32px, 5vw, 56px);
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
.intro {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 12px;
|
||||
margin: 24px 0 32px;
|
||||
}
|
||||
.stat {
|
||||
padding: 16px 18px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 253, 248, 0.9);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.stat strong {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.stat span {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.card {
|
||||
padding: 18px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
background: var(--panel);
|
||||
box-shadow: 0 12px 30px rgba(29, 26, 22, 0.06);
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
align-items: baseline;
|
||||
}
|
||||
.card-head h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
.card-head p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
.bar {
|
||||
height: 12px;
|
||||
margin: 14px 0 12px;
|
||||
background: var(--bar-soft);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bar span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--bar), #16a394);
|
||||
border-radius: inherit;
|
||||
}
|
||||
.meta, .chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 10px;
|
||||
}
|
||||
.meta {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.chip {
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f2ebe0;
|
||||
color: #463d35;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Package Size Report</h1>
|
||||
<section class="intro">
|
||||
<div class="stat">
|
||||
<strong>${report.summary.totalPackageCount}</strong>
|
||||
<span>packages in scope</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong>${report.summary.builtPackageCount}</strong>
|
||||
<span>packages with build output</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<strong>${report.generatedAt}</strong>
|
||||
<span>generated at</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="grid">
|
||||
${cards}
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const report = buildPackageSizesReport(readWorkspacePackages());
|
||||
|
||||
fs.mkdirSync(ARTIFACTS_DIR, { recursive: true });
|
||||
fs.writeFileSync(path.join(ARTIFACTS_DIR, 'package-sizes.json'), `${JSON.stringify(report, null, 4)}\n`);
|
||||
fs.writeFileSync(
|
||||
path.join(ARTIFACTS_DIR, 'package-sizes.html'),
|
||||
buildVisualizationHtml(report),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
console.log(`Wrote ${path.relative(ROOT_DIR, path.join(ARTIFACTS_DIR, 'package-sizes.json'))}`);
|
||||
console.log(`Wrote ${path.relative(ROOT_DIR, path.join(ARTIFACTS_DIR, 'package-sizes.html'))}`);
|
||||
}
|
||||
|
||||
main();
|
||||
229
scripts/coverage-shard.mts
Normal file
229
scripts/coverage-shard.mts
Normal file
@@ -0,0 +1,229 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
interface IWorkspacePackage {
|
||||
dir: string;
|
||||
name: string;
|
||||
testFiles: number;
|
||||
}
|
||||
|
||||
const workspaceRoots = [
|
||||
'common',
|
||||
'examples',
|
||||
'presets',
|
||||
'presets/packages',
|
||||
'packages',
|
||||
'tests',
|
||||
];
|
||||
|
||||
function parseArgs() {
|
||||
const args = new Map<string, string | boolean>();
|
||||
|
||||
for (const arg of process.argv.slice(2)) {
|
||||
if (arg.startsWith('--') && arg.includes('=')) {
|
||||
const [key, value] = arg.slice(2).split(/=(.*)/s);
|
||||
args.set(key, value);
|
||||
} else if (arg.startsWith('--')) {
|
||||
args.set(arg.slice(2), true);
|
||||
}
|
||||
}
|
||||
|
||||
const shardIndex = Number(args.get('shard-index') ?? process.env.COVERAGE_SHARD_INDEX ?? 1);
|
||||
const shardTotal = Number(args.get('shard-total') ?? process.env.COVERAGE_SHARD_TOTAL ?? 1);
|
||||
const concurrency = String(args.get('concurrency') ?? process.env.COVERAGE_TURBO_CONCURRENCY ?? 2);
|
||||
|
||||
if (!Number.isInteger(shardIndex) || !Number.isInteger(shardTotal) || shardIndex < 1 || shardTotal < 1 || shardIndex > shardTotal) {
|
||||
throw new Error(`Invalid shard arguments: shard-index=${shardIndex}, shard-total=${shardTotal}`);
|
||||
}
|
||||
|
||||
return {
|
||||
shardIndex,
|
||||
shardTotal,
|
||||
concurrency,
|
||||
dryRun: args.has('dry-run'),
|
||||
listOnly: args.has('list-only'),
|
||||
};
|
||||
}
|
||||
|
||||
function readPackageJson(dir: string) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function listPackageDirs(root: string) {
|
||||
const packageDirs: string[] = [];
|
||||
|
||||
for (const workspaceRoot of workspaceRoots) {
|
||||
let rootStat;
|
||||
try {
|
||||
rootStat = statSync(workspaceRoot);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rootStat.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rootPackageJson = readPackageJson(workspaceRoot);
|
||||
if (rootPackageJson) {
|
||||
packageDirs.push(workspaceRoot);
|
||||
}
|
||||
|
||||
for (const entry of readdirSync(workspaceRoot)) {
|
||||
const dir = join(workspaceRoot, entry);
|
||||
try {
|
||||
if (statSync(dir).isDirectory() && readPackageJson(dir)) {
|
||||
packageDirs.push(dir);
|
||||
}
|
||||
} catch {
|
||||
// Ignore entries that disappear or cannot be read.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(packageDirs)].map((dir) => relative(root, dir) || '.').sort();
|
||||
}
|
||||
|
||||
function countTestFiles(dir: string) {
|
||||
let count = 0;
|
||||
|
||||
function walk(currentDir: string) {
|
||||
for (const entry of readdirSync(currentDir)) {
|
||||
if (entry === 'node_modules' || entry === 'coverage' || entry === 'dist' || entry === 'lib') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullPath = join(currentDir, entry);
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
walk(fullPath);
|
||||
} else if (/[.-](spec|test)\.[cm]?[jt]sx?$/.test(entry)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(dir);
|
||||
return count;
|
||||
}
|
||||
|
||||
function getCoveragePackages() {
|
||||
const root = process.cwd();
|
||||
|
||||
return listPackageDirs(root)
|
||||
.map((dir): IWorkspacePackage | null => {
|
||||
const packageJson = readPackageJson(dir);
|
||||
if (!packageJson?.scripts?.coverage || !packageJson.name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
dir,
|
||||
name: packageJson.name,
|
||||
testFiles: countTestFiles(dir),
|
||||
};
|
||||
})
|
||||
.filter((pkg): pkg is IWorkspacePackage => pkg !== null)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
function assignShards(packages: IWorkspacePackage[], shardTotal: number) {
|
||||
const shards = Array.from({ length: shardTotal }, () => ({
|
||||
weight: 0,
|
||||
packages: [] as IWorkspacePackage[],
|
||||
}));
|
||||
|
||||
for (const pkg of [...packages].sort((a, b) => b.testFiles - a.testFiles || a.name.localeCompare(b.name))) {
|
||||
const target = shards.reduce((lowest, shard, index) => {
|
||||
if (shard.weight < shards[lowest].weight) {
|
||||
return index;
|
||||
}
|
||||
|
||||
return lowest;
|
||||
}, 0);
|
||||
|
||||
shards[target].packages.push(pkg);
|
||||
shards[target].weight += Math.max(pkg.testFiles, 1);
|
||||
}
|
||||
|
||||
return shards.map((shard) => ({
|
||||
...shard,
|
||||
packages: shard.packages.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
}));
|
||||
}
|
||||
|
||||
const { shardIndex, shardTotal, concurrency, dryRun, listOnly } = parseArgs();
|
||||
const packages = getCoveragePackages();
|
||||
const totalTestFiles = packages.reduce((total, pkg) => total + pkg.testFiles, 0);
|
||||
const heavyPackageThreshold = Math.ceil(totalTestFiles / shardTotal);
|
||||
const heavyPackages = packages.filter((pkg) => pkg.testFiles > heavyPackageThreshold);
|
||||
const regularPackages = packages.filter((pkg) => !heavyPackages.includes(pkg));
|
||||
const shards = assignShards(regularPackages, shardTotal);
|
||||
const selected = shards[shardIndex - 1].packages;
|
||||
|
||||
console.log(`Coverage shard ${shardIndex}/${shardTotal}: ${selected.length}/${regularPackages.length} regular packages, ${shards[shardIndex - 1].weight} regular test files`);
|
||||
for (const pkg of heavyPackages) {
|
||||
console.log(`- ${pkg.name} (${pkg.dir}, ${pkg.testFiles} test files, vitest shard ${shardIndex}/${shardTotal})`);
|
||||
}
|
||||
for (const pkg of selected) {
|
||||
console.log(`- ${pkg.name} (${pkg.dir}, ${pkg.testFiles} test files)`);
|
||||
}
|
||||
|
||||
if (heavyPackages.length === 0 && selected.length === 0) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (listOnly) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function runPnpm(args: string[]) {
|
||||
const result = spawnSync('pnpm', args, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32',
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (const pkg of heavyPackages) {
|
||||
const args = [
|
||||
'--dir',
|
||||
pkg.dir,
|
||||
'exec',
|
||||
'vitest',
|
||||
'run',
|
||||
'--coverage',
|
||||
`--shard=${shardIndex}/${shardTotal}`,
|
||||
'--passWithNoTests',
|
||||
];
|
||||
|
||||
if (dryRun) {
|
||||
console.log(`[dry-run] pnpm ${args.join(' ')}`);
|
||||
} else {
|
||||
runPnpm(args);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.length > 0) {
|
||||
runPnpm([
|
||||
'turbo',
|
||||
`--concurrency=${concurrency}`,
|
||||
'coverage',
|
||||
...selected.map((pkg) => `--filter=${pkg.name}`),
|
||||
...(dryRun ? ['--dry=json'] : []),
|
||||
'--',
|
||||
'--passWithNoTests',
|
||||
]);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
Reference in New Issue
Block a user