@forwardimpact/libcodegen
Advanced tools
+12
-0
@@ -113,2 +113,3 @@ #!/usr/bin/env node | ||
| return { | ||
| doAll, | ||
| doTypes: doAll || values.type, | ||
@@ -331,2 +332,13 @@ doServices: doAll || values.service, | ||
| // Full regeneration (--all) clears the content directories first so that a | ||
| // renamed or removed proto leaves no orphaned per-proto artifacts. The | ||
| // services exports step scans the services/ directory, so a stale service | ||
| // dir would otherwise be re-exported and import types that no longer exist. | ||
| // Partial flags intentionally preserve sibling artifacts and are not cleaned. | ||
| if (parsedFlags.doAll) { | ||
| for (const dir of ["types", "services", "definitions", "proto"]) { | ||
| fs.rmSync(path.join(sourcePath, dir), { recursive: true, force: true }); | ||
| } | ||
| } | ||
| // Write package.json with "type": "module" so Node.js treats generated | ||
@@ -333,0 +345,0 @@ // ES module files correctly and avoids MODULE_TYPELESS_PACKAGE_JSON warnings. |
+1
-1
| { | ||
| "name": "@forwardimpact/libcodegen", | ||
| "version": "0.1.59", | ||
| "version": "0.1.60", | ||
| "description": "Protobuf code generation — keep types in sync with proto definitions without hand-writing.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+32
-15
@@ -1,4 +0,4 @@ | ||
| import { execFile } from "node:child_process"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import protobuf from "protobufjs"; | ||
| import { createDefaultRuntime } from "@forwardimpact/libutil/runtime"; | ||
@@ -64,2 +64,3 @@ /** Convert camelCase to snake_case (protobufjs normalizes field names) */ | ||
| #fs; | ||
| #subprocess; | ||
@@ -74,4 +75,13 @@ /** | ||
| * @param {object} fs - File system module (sync operations only) | ||
| * @param {object} [runtime] - Optional runtime bag; falls back to createDefaultRuntime() | ||
| */ | ||
| constructor(protoDirs, projectRoot, path, mustache, protoLoader, fs) { | ||
| constructor( | ||
| protoDirs, | ||
| projectRoot, | ||
| path, | ||
| mustache, | ||
| protoLoader, | ||
| fs, | ||
| runtime, | ||
| ) { | ||
| if (!protoDirs || !Array.isArray(protoDirs) || protoDirs.length === 0) { | ||
@@ -92,2 +102,3 @@ throw new Error("protoDirs must be a non-empty array"); | ||
| this.#fs = fs; | ||
| this.#subprocess = (runtime ?? createDefaultRuntime()).subprocess; | ||
| } | ||
@@ -188,18 +199,24 @@ | ||
| * @param {string[]} args - Command-line arguments | ||
| * @param {object} [opts] - Child process options | ||
| * @param {object} [opts] - Subprocess options (e.g. cwd) | ||
| * @returns {Promise<void>} Resolves when the command completes successfully | ||
| */ | ||
| run(cmd, args, opts = {}) { | ||
| return new Promise((resolvePromise, reject) => { | ||
| const child = execFile( | ||
| cmd, | ||
| args, | ||
| { stdio: "inherit", ...opts }, | ||
| (err) => { | ||
| if (err) reject(err); | ||
| else resolvePromise(); | ||
| }, | ||
| ); | ||
| child.on("error", reject); | ||
| async run(cmd, args, opts = {}) { | ||
| // `stdio: "inherit"` forwards to the underlying execFile/spawn so the | ||
| // child's stdout/stderr go straight to the parent's fds — preserving the | ||
| // exact pre-1370 behavior (origin/main also ran execFile with | ||
| // stdio:"inherit"), so `just codegen` shows protoc/pbjs progress live. | ||
| // With inherited stdio the buffered result is empty, so the error path | ||
| // below falls back to the exit code. Capture-mode callers override via | ||
| // `opts.stdio`. | ||
| const result = await this.#subprocess.run(cmd, args, { | ||
| stdio: "inherit", | ||
| ...opts, | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| const msg = | ||
| result.stderr?.trim() || | ||
| result.stdout?.trim() || | ||
| `exited with code ${result.exitCode}`; | ||
| throw new Error(`Command failed: ${cmd} ${args.join(" ")}\n${msg}`); | ||
| } | ||
| } | ||
@@ -206,0 +223,0 @@ |
59977
2.34%1074
2.68%