[READ-ONLY] Mirror of https://github.com/bombshell-dev/tools. Internal CLI to standardize tooling across all Bombshell projects
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184import { readFile, writeFile, access } from "node:fs/promises";import { dirname, join } from "node:path";import { confirm } from "@clack/prompts";import type { CommandContext } from "../../context.ts";import { getPackageJSONPath } from "../../utils.ts";import { devEngines } from "./_env.ts";
interface ConfigChange { location: string; current: string; new: string;}
export async function sync(ctx: CommandContext) { try { const changes: ConfigChange[] = [];
// Get the package.json file path and directory const pkgPath = await getPackageJSONPath(); const projectDir = dirname(pkgPath);
// Read the raw file content to preserve formatting const content = await readFile(pkgPath, { encoding: "utf8" }); const pkg = JSON.parse(content);
// Check devEngines const currentDevEngines = pkg.devEngines; const devEnginesMatch = currentDevEngines && JSON.stringify(currentDevEngines) === JSON.stringify(devEngines);
if (!devEnginesMatch) { changes.push({ location: "package.json → devEngines", current: currentDevEngines ? JSON.stringify(currentDevEngines, null, 2) : "(not set)", new: JSON.stringify(devEngines, null, 2), }); }
// Check packageManager field const expectedPackageManager = `${devEngines.packageManager.name}@${devEngines.packageManager.version}`; const currentPackageManager = pkg.packageManager;
if (currentPackageManager !== expectedPackageManager) { changes.push({ location: "package.json → packageManager", current: currentPackageManager || "(not set)", new: expectedPackageManager, }); }
// Check volta.node field const currentVolta = pkg.volta?.node; const expectedVolta = devEngines.runtime.version;
if (currentVolta !== expectedVolta) { changes.push({ location: "package.json → volta.node", current: currentVolta || "(not set)", new: expectedVolta, }); }
// Check .npmrc for node-version const npmrcPath = join(projectDir, ".npmrc"); let npmrcContent: string | null = null; let currentNpmrcNodeVersion: string | null = null;
try { await access(npmrcPath); npmrcContent = await readFile(npmrcPath, { encoding: "utf8" });
// Parse node-version from .npmrc const nodeVersionMatch = npmrcContent.match( /^node-version\s*=\s*(.+)$/m, ); if (nodeVersionMatch?.[1]) { currentNpmrcNodeVersion = nodeVersionMatch[1].trim(); }
if (currentNpmrcNodeVersion !== devEngines.runtime.version) { changes.push({ location: ".npmrc → node-version", current: currentNpmrcNodeVersion || "(not set)", new: devEngines.runtime.version, }); } } catch { // .npmrc doesn't exist, we could optionally create it // For now, skip if it doesn't exist }
// If everything is in sync, exit early if (changes.length === 0) { console.log("✓ All configuration is already in sync"); return; }
// Display all changes console.log("\nThe following changes will be made:\n"); for (const change of changes) { console.log(`📝 ${change.location}`); console.log(` Current: ${change.current.replace(/\n/g, "\n ")}`); console.log(` New: ${change.new.replace(/\n/g, "\n ")}`); console.log(); }
// Prompt user for confirmation const shouldUpdate = await confirm({ message: `Update ${changes.length} configuration location${changes.length > 1 ? "s" : ""}?`, initialValue: true, });
if (!shouldUpdate) { console.log("Sync cancelled"); return; }
// Apply all changes let updatedCount = 0;
// Update package.json fields let pkgModified = false;
if (!devEnginesMatch) { pkg.devEngines = devEngines; pkgModified = true; updatedCount++; }
if (currentPackageManager !== expectedPackageManager) { pkg.packageManager = expectedPackageManager; pkgModified = true; updatedCount++; }
if (currentVolta !== expectedVolta) { if (!pkg.volta) { pkg.volta = {}; } pkg.volta.node = expectedVolta; pkgModified = true; updatedCount++; }
if (pkgModified) { await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, { encoding: "utf8", }); }
// Update .npmrc if needed if (npmrcContent !== null && currentNpmrcNodeVersion !== devEngines.runtime.version) { let newNpmrcContent = npmrcContent;
if (currentNpmrcNodeVersion) { // Replace existing node-version newNpmrcContent = newNpmrcContent.replace( /^node-version\s*=\s*.+$/m, `node-version=${devEngines.runtime.version}`, ); } else { // Add node-version newNpmrcContent = newNpmrcContent.trim(); newNpmrcContent += `\nnode-version=${devEngines.runtime.version}\n`; }
await writeFile(npmrcPath, newNpmrcContent, { encoding: "utf8" }); updatedCount++; }
console.log(`✓ Successfully updated ${updatedCount} configuration location${updatedCount > 1 ? "s" : ""}`); } catch (error) { if (error instanceof Error) { console.error(`Error: ${error.message}`); } else { console.error("An unexpected error occurred"); } process.exit(1); }}