Skip to content

Commit a6f76df

Browse files
docs(dev-runner): add explanatory comments to basic multi-service dev script
1 parent fcb1e50 commit a6f76df

File tree

1 file changed

+52
-6
lines changed

1 file changed

+52
-6
lines changed

scripts/dev-basic.cjs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,120 @@
11
#!/usr/bin/env node
2-
// Simple multi-service dev runner (no turborepo / nx)
3-
// Starts each workspace's dev script concurrently.
4-
// Basic, no restart on file change; rely on each service's own dev behavior.
5-
// CommonJS to ensure compatibility regardless of root type/module settings.
2+
// ---------------------------------------------------------------------------
3+
// Basic Multi-Service Dev Runner
4+
// ---------------------------------------------------------------------------
5+
// Purpose:
6+
// Lightweight script to start each service's "dev" script in parallel when
7+
// you are NOT using an advanced orchestrator (like Turborepo or Nx).
8+
// Intended for the scaffold's "basic" preset.
9+
//
10+
// High-Level Flow:
11+
// 1. Identify the root directory and the conventional services/ folder.
12+
// 2. Detect which JavaScript package manager to use (npm / pnpm / yarn / bun).
13+
// 3. Enumerate each subfolder in services/.
14+
// 4. For every folder containing a package.json with a dev script, spawn it.
15+
// 5. Stream each child process' output with a readable prefix.
16+
//
17+
// Design Constraints:
18+
// - Zero external dependencies (Node built‑ins only) to stay portable.
19+
// - Does NOT attempt file-change restarts (leave that to each service's own
20+
// tooling, e.g. nodemon, ts-node-dev, next dev, etc.).
21+
// - Ignores non-Node services (e.g., Go, Python, Java) since they don't have
22+
// a package.json dev script; those are expected to run via their own tools.
23+
// - CommonJS for maximum compatibility regardless of root "type: module".
24+
//
25+
// Extending Tips:
26+
// - To include non-Node services, add detection logic (e.g., main.go, pom.xml)
27+
// and spawn the appropriate commands.
28+
// - To implement auto-restart, consider integrating chokidar to watch files
29+
// and restart specific child processes.
30+
// - To add coloring per service, map service names to a color palette and
31+
// wrap the prefix() output (keep it optional for plain CI logs).
32+
// ---------------------------------------------------------------------------
633

734
const { spawn } = require('node:child_process');
835
const fs = require('fs');
936
const path = require('path');
1037

38+
// Root of the repo (assumes script is run from project root via npm script)
1139
const root = process.cwd();
12-
// Updated to new structure: services directory replaces apps
40+
41+
// Convention: all services generated by the scaffold live under services/
42+
// (Older versions used apps/; that was replaced to avoid confusion with Next.js)
1343
const servicesDir = path.join(root, 'services');
1444
if (!fs.existsSync(servicesDir)) {
1545
console.error('No services directory found.');
1646
process.exit(1);
1747
}
1848

49+
// Snapshot the list of entries inside services/ (folders assumed to be services)
1950
const services = fs.readdirSync(servicesDir);
2051

2152
// Determine the root package manager heuristically
2253
function detectPM() {
54+
// Heuristic: look for lock files in order of preference.
55+
// If multiple exist (rare / misconfigured), first match wins.
2356
if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) return 'pnpm';
2457
if (fs.existsSync(path.join(root, 'yarn.lock'))) return 'yarn';
2558
if (fs.existsSync(path.join(root, 'bun.lockb'))) return 'bun';
59+
// Fallback to npm when no known lockfile is detected.
2660
return 'npm';
2761
}
2862
const pm = detectPM();
2963

3064
if (services.length === 0) {
65+
// Nothing inside services/; we exit early (not an error condition).
3166
console.log('No Node-based services with package.json to run.');
3267
process.exit(0);
3368
}
3469

3570
console.log(`Using package manager: ${pm}`);
3671
console.log(`Discovered services: ${services.join(', ')}`);
3772

73+
// Keep references to spawned child processes so we can forward signals.
3874
const procs = [];
3975

4076
function prefix(name, data) {
77+
// Prepend the service name to each line of output for readability.
78+
// NOTE: data may contain multiple lines; we leave as‑is to avoid splitting.
4179
process.stdout.write(`[${name}] ${data}`);
4280
}
4381

4482
services.forEach(svc => {
4583
const svcPath = path.join(servicesDir, svc);
4684
const pkgPath = path.join(svcPath, 'package.json');
4785
if (!fs.existsSync(pkgPath)) {
86+
// Non-Node service (e.g., python, go, java). Skip silently with context.
4887
console.log(`Skipping ${svc} (no package.json; likely non-Node service)`);
4988
return;
5089
}
5190
try {
5291
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
5392
if (!pkg.scripts || !pkg.scripts.dev) {
93+
// Node project present but without a dev script; user may add later.
5494
console.log(`Skipping ${svc} (no dev script)`);
5595
return;
5696
}
57-
const cmd = pm === 'bun' ? 'bun' : pm;
97+
// Determine spawn command + arguments.
98+
// For yarn classic, `yarn dev` would also work, but keeping uniform form.
99+
const cmd = pm === 'bun' ? 'bun' : pm; // direct binary (npm, pnpm, yarn, bun)
58100
const args = pm === 'yarn' ? ['run', 'dev'] : pm === 'bun' ? ['run', 'dev'] : ['run', 'dev'];
101+
// Could special-case: if (pm==='yarn') args=['dev'] but current pattern is fine.
59102
const child = spawn(cmd, args, { cwd: svcPath, shell: true, env: process.env });
60103
procs.push(child);
61104
child.stdout.on('data', d => prefix(svc, d));
62105
child.stderr.on('data', d => prefix(svc, d));
63106
child.on('exit', code => {
107+
// Log exit so the developer sees early failures.
64108
console.log(`[${svc}] exited with code ${code}`);
65109
});
66110
} catch (e) {
111+
// JSON parse errors or spawn failures bubble here.
67112
console.log(`Failed to start ${svc}:`, e.message);
68113
}
69114
});
70115

71116
process.on('SIGINT', () => {
117+
// Graceful shutdown: forward Ctrl+C to each child, then exit.
72118
procs.forEach(p => p.kill('SIGINT'));
73119
process.exit(0);
74120
});

0 commit comments

Comments
 (0)