You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
2.5 KiB
84 lines
2.5 KiB
#!/usr/bin/env -S deno run --allow-read --allow-write |
|
import { dirname, resolve } from "jsr:@std/path"; |
|
|
|
/** Read and parse JSON */ |
|
async function readJson(p: string) { |
|
try { |
|
const txt = await Deno.readTextFile(p); |
|
return JSON.parse(txt); |
|
} catch (e) { |
|
console.error(`Failed to read JSON: ${p}\n${e.message}`); |
|
Deno.exit(1); |
|
} |
|
} |
|
|
|
/** Ensure parent directory exists */ |
|
async function ensureDir(filePath: string) { |
|
await Deno.mkdir(dirname(filePath), { recursive: true }); |
|
} |
|
|
|
/** File exists? */ |
|
async function exists(p: string) { |
|
try { |
|
await Deno.lstat(p); |
|
return true; |
|
} catch (e) { |
|
if (e instanceof Deno.errors.NotFound) return false; |
|
throw e; |
|
} |
|
} |
|
|
|
/** Write file if changed; return action marker */ |
|
async function writeFile(p: string, content: string) { |
|
await ensureDir(p); |
|
const had = await exists(p); |
|
if (had) { |
|
const current = await Deno.readTextFile(p); |
|
if (current === content) return "skip" as const; |
|
} |
|
await Deno.writeTextFile(p, content); |
|
return had ? ("update" as const) : ("create" as const); |
|
} |
|
|
|
/** Shallow-merge package.json fields */ |
|
function mergePackage(pkg: any, merge: any) { |
|
const next = { ...pkg }; |
|
for (const key of ["scripts", "dependencies", "devDependencies"]) { |
|
if (merge?.[key]) next[key] = { ...(pkg[key] || {}), ...merge[key] }; |
|
} |
|
return next; |
|
} |
|
|
|
async function main() { |
|
const manifestPath = Deno.args[0]; |
|
if (!manifestPath) { |
|
console.error("Usage: deno run --allow-read --allow-write scripts/scaffold.ts <manifest.json>"); |
|
Deno.exit(1); |
|
} |
|
|
|
const manifest = await readJson(manifestPath); |
|
|
|
// Optional: merge package.json if present |
|
const pkgPath = resolve(Deno.cwd(), "package.json"); |
|
if (manifest.package?.merge && (await exists(pkgPath))) { |
|
const pkg = await readJson(pkgPath); |
|
const merged = mergePackage(pkg, manifest.package.merge); |
|
await Deno.writeTextFile(pkgPath, JSON.stringify(merged, null, 2) + "\n"); |
|
console.log("✓ package.json merged"); |
|
} else if (manifest.package?.merge) { |
|
console.log("• package.json not found — skipping merge"); |
|
} |
|
|
|
// Write files from manifest |
|
const files = manifest.files ?? []; |
|
for (const f of files) { |
|
const out = resolve(Deno.cwd(), String(f.path)); |
|
const action = await writeFile(out, String(f.content ?? "")); |
|
const marker = action === "skip" ? "•" : action === "update" ? "↻" : "✓"; |
|
console.log(`${marker} ${f.path}`); |
|
} |
|
|
|
console.log("\nDone."); |
|
} |
|
|
|
if (import.meta.main) main();
|
|
|