~james/core

core/web/scripts/bones.js -rwxr-xr-x 3.4 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 14 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/env node
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { spawn, spawnSync } from "node:child_process";
import os from "node:os";

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
const configPath = resolve(root, "boneyard.config.json");
const config = JSON.parse(readFileSync(configPath, "utf-8"));

const port = process.env.STORYBOOK_PORT || 6006;
const base = `http://127.0.0.1:${port}`;
const rawArgs = process.argv.slice(2);
const force = rawArgs.includes("--force");
const args = rawArgs.filter((a) => a !== "--force");

let urls = [];
if (args.length > 0) {
	const patterns = args.map((a) => a.toLowerCase());
	const matched = [];
	for (const [name, entry] of Object.entries(config.skeletons || {})) {
		if (patterns.some((p) => name.toLowerCase().includes(p))) {
			matched.push(name);
			urls.push(`${base}${entry.route}`);
		}
	}
	if (urls.length === 0) {
		console.error(`boneyard: no skeletons matched '${args.join(", ")}'`);
		process.exit(1);
	}
	console.log(`boneyard: scoping to ${urls.length} skeleton(s): ${matched.join(", ")}\n`);
} else {
	// For all skeletons, use explicit URLs so we can parallelize across workers
	urls = Object.values(config.skeletons || {}).map((entry) => `${base}${entry.route}`);
}

const flags = force ? ["--force"] : [];
const defaultConcurrency = Math.min(6, Math.max(1, os.cpus().length - 2));
const concurrency = Math.min(
	urls.length,
	parseInt(process.env.BONES_CONCURRENCY || String(defaultConcurrency), 10)
);

async function run() {
	if (concurrency <= 1 || urls.length <= 1) {
		const res = spawnSync("pnpm", ["exec", "boneyard-js", "build", ...urls, ...flags], {
			cwd: root,
			stdio: "inherit"
		});
		if (res.status !== 0) process.exit(res.status ?? 1);
	} else {
		// Partition URLs across workers
		const buckets = Array.from({ length: concurrency }, () => []);
		urls.forEach((url, i) => {
			buckets[i % concurrency].push(url);
		});

		console.log(
			`boneyard: parallelizing ${urls.length} skeletons across ${concurrency} workers...\n`
		);

		const startTime = Date.now();
		await Promise.all(
			buckets.map((bucket, i) => {
				return new Promise((resolveWorker, rejectWorker) => {
					const child = spawn("pnpm", ["exec", "boneyard-js", "build", ...bucket, ...flags], {
						cwd: root,
						stdio: ["ignore", "pipe", "pipe"]
					});

					child.stdout.on("data", (d) => {
						// Stream lines prefixed by worker id for clarity
						const text = d.toString();
						for (const line of text.split("\n")) {
							if (line.trim()) console.log(`[w${i + 1}] ${line}`);
						}
					});

					child.stderr.on("data", (d) => {
						const text = d.toString();
						for (const line of text.split("\n")) {
							if (line.trim()) console.error(`[w${i + 1}] ${line}`);
						}
					});

					child.on("close", (code) => {
						if (code === 0) resolveWorker();
						else rejectWorker(new Error(`Worker ${i + 1} exited with code ${code}`));
					});
				});
			})
		);

		const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
		console.log(`\nšŸ’€ all workers completed in ${elapsed}s!`);
	}

	// Clean up unused registry and format
	spawnSync("rm", ["-f", "src/lib/bones/registry.ts"], { cwd: root });
	spawnSync("pnpm", ["exec", "prettier", "--write", "src/lib/bones"], {
		cwd: root,
		stdio: "inherit"
	});
}

run().catch((err) => {
	console.error(err);
	process.exit(1);
});