chore: import upstream snapshot with attribution
This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* This scripts looks at all packages and starts their dev command in parallel, if they have one.
|
||||
*/
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { colors, log } from "./logger.mjs";
|
||||
|
||||
const processes = [];
|
||||
|
||||
// Cleanup function
|
||||
const cleanup = () => {
|
||||
console.log(`\n\n${colors.yellow}[stop]${colors.reset} Stopping all dev servers...`);
|
||||
processes.forEach((proc) => {
|
||||
try {
|
||||
proc.kill("SIGINT");
|
||||
} catch (error) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
});
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
// Handle various termination signals
|
||||
process.on("SIGINT", cleanup);
|
||||
process.on("SIGTERM", cleanup);
|
||||
process.on("exit", cleanup);
|
||||
|
||||
// Find all packages with dev scripts
|
||||
const findPackagesWithDevScript = () => {
|
||||
const packagesDir = "packages";
|
||||
const packagesWithDev = [];
|
||||
|
||||
if (!existsSync(packagesDir)) {
|
||||
log.error(`Packages directory not found: ${packagesDir}`);
|
||||
return packagesWithDev;
|
||||
}
|
||||
|
||||
const packages = readdirSync(packagesDir, { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name);
|
||||
|
||||
for (const pkg of packages) {
|
||||
const packageJsonPath = join(packagesDir, pkg, "package.json");
|
||||
|
||||
if (existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
||||
if (packageJson.scripts && packageJson.scripts.dev) {
|
||||
packagesWithDev.push({
|
||||
name: pkg,
|
||||
displayName: packageJson.name || pkg,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Failed to read package.json for ${pkg}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packagesWithDev;
|
||||
};
|
||||
|
||||
log.section("DEV SERVERS");
|
||||
log.info("Discovering packages with dev scripts...\n");
|
||||
|
||||
const packagesWithDev = findPackagesWithDevScript();
|
||||
|
||||
if (packagesWithDev.length === 0) {
|
||||
log.error("No packages found with dev scripts!");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log.info(`Found ${packagesWithDev.length} package(s) with dev scripts:`);
|
||||
packagesWithDev.forEach((pkg) => {
|
||||
log.info(` - ${pkg.displayName} (${pkg.name})`);
|
||||
});
|
||||
log.info("\nStarting development servers...\n");
|
||||
|
||||
// Start dev server for each package
|
||||
packagesWithDev.forEach((pkg) => {
|
||||
const proc = spawn("pnpm", ["--filter", pkg.displayName, "dev"], {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
|
||||
processes.push(proc);
|
||||
|
||||
// Handle process exits
|
||||
proc.on("exit", (code) => {
|
||||
if (code !== 0 && code !== null) {
|
||||
log.error(`${pkg.displayName} dev server exited with code ${code}`);
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Keep process alive
|
||||
process.stdin.resume();
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Shared logging utility with colored output
|
||||
*/
|
||||
|
||||
// ANSI color codes
|
||||
export const colors = {
|
||||
reset: "\x1b[0m",
|
||||
bright: "\x1b[1m",
|
||||
dim: "\x1b[2m",
|
||||
cyan: "\x1b[36m",
|
||||
green: "\x1b[32m",
|
||||
yellow: "\x1b[33m",
|
||||
blue: "\x1b[34m",
|
||||
magenta: "\x1b[35m",
|
||||
red: "\x1b[31m",
|
||||
gray: "\x1b[90m",
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a logger with optional package prefix
|
||||
* @param {string} [prefix] - Optional prefix to prepend to all logs (e.g., "transformers", "react")
|
||||
* @returns {Object} Logger object with various log methods
|
||||
*/
|
||||
export function createLogger(prefix = "") {
|
||||
const formatPrefix = prefix ? `${colors.magenta}[${prefix}]${colors.reset} ` : "";
|
||||
|
||||
return {
|
||||
section: (text) => console.log(`\n${formatPrefix}${colors.bright}${colors.cyan}=== ${text} ===${colors.reset}`),
|
||||
info: (text) => console.log(`${formatPrefix}${colors.blue}[info]${colors.reset} ${text}`),
|
||||
success: (text) => console.log(`${formatPrefix}${colors.green}✓${colors.reset} ${text}`),
|
||||
warning: (text) => console.log(`${formatPrefix}${colors.yellow}[warn]${colors.reset} ${text}`),
|
||||
error: (text) => console.log(`${formatPrefix}${colors.red}[error]${colors.reset} ${text}`),
|
||||
dim: (text) => console.log(`${formatPrefix}${colors.dim}${text}${colors.reset}`),
|
||||
url: (text) => console.log(`${formatPrefix} ${colors.cyan}→${colors.reset} ${colors.bright}${text}${colors.reset}`),
|
||||
file: (text) => console.log(`${formatPrefix} ${colors.gray}-${colors.reset} ${text}`),
|
||||
build: (text) => console.log(`${formatPrefix}${colors.cyan}[build]${colors.reset} ${text}`),
|
||||
done: (text) => console.log(`${formatPrefix}${colors.green}[done]${colors.reset} ${text}`),
|
||||
};
|
||||
}
|
||||
|
||||
// Default logger without prefix for backward compatibility
|
||||
export const log = createLogger();
|
||||
@@ -0,0 +1,9 @@
|
||||
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
||||
|
||||
export default function prepareOutDir(dir) {
|
||||
if (existsSync(dir)) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { colors } from "./logger.mjs";
|
||||
|
||||
/**
|
||||
* Plugin to log rebuild information in watch mode
|
||||
* @param {string} targetName - Name of the build target
|
||||
* @param {Object} log - Logger instance
|
||||
* @param {Object} options - Plugin options
|
||||
* @param {boolean} options.logFirstBuild - Whether to log the first build completion (default: true)
|
||||
*/
|
||||
export function rebuildPlugin(targetName, log, options = {}) {
|
||||
const { logFirstBuild = true } = options;
|
||||
|
||||
return {
|
||||
name: "rebuild-logger",
|
||||
setup(build) {
|
||||
let startTime = 0;
|
||||
let isFirstBuild = true;
|
||||
|
||||
build.onStart(() => {
|
||||
startTime = performance.now();
|
||||
if (!isFirstBuild) {
|
||||
log.build(`${colors.gray}Rebuilding ${targetName}...${colors.reset}`);
|
||||
}
|
||||
});
|
||||
|
||||
build.onEnd((result) => {
|
||||
const endTime = performance.now();
|
||||
const duration = (endTime - startTime).toFixed(2);
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
log.error(`${colors.bright}${targetName}${colors.reset} - Build failed with ${result.errors.length} error(s) in ${duration}ms`);
|
||||
} else if (!isFirstBuild) {
|
||||
log.done(`${colors.bright}${targetName}${colors.reset} - Rebuilt in ${colors.gray}${duration}ms${colors.reset}`);
|
||||
} else if (logFirstBuild) {
|
||||
log.done(`${colors.bright}${targetName}${colors.reset} - Built in ${colors.gray}${duration}ms${colors.reset}`);
|
||||
}
|
||||
|
||||
isFirstBuild = false;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { gzipSync } from "node:zlib";
|
||||
import path from "node:path";
|
||||
import { colors } from "./logger.mjs";
|
||||
|
||||
export const formatSize = (bytes) => {
|
||||
if (bytes < 1024) return `${bytes}b`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}kb`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)}mb`;
|
||||
};
|
||||
|
||||
export const reportSize = (outfile, log) => {
|
||||
const content = readFileSync(outfile);
|
||||
const size = content.length;
|
||||
const gzipSize = gzipSync(content).length;
|
||||
const filename = path.basename(outfile);
|
||||
|
||||
log.done(
|
||||
`${colors.bright}${filename}${colors.reset} ${colors.gray}${formatSize(size)}${colors.reset} ${colors.dim}(gzip: ${formatSize(gzipSize)})${colors.reset}`,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user