@forwardimpact/libcodegen 0.1.58 → 0.1.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -110,6 +110,7 @@ function parseFlags() {
110
110
  const { values } = parsed;
111
111
  const doAll = values.all;
112
112
  return {
113
+ doAll,
113
114
  doTypes: doAll || values.type,
114
115
  doServices: doAll || values.service,
115
116
  doClients: doAll || values.client,
@@ -327,6 +328,17 @@ async function runCodegen(protoDirs, projectRoot, finder) {
327
328
 
328
329
  await generatedStorage.ensureBucket();
329
330
 
331
+ // Full regeneration (--all) clears the content directories first so that a
332
+ // renamed or removed proto leaves no orphaned per-proto artifacts. The
333
+ // services exports step scans the services/ directory, so a stale service
334
+ // dir would otherwise be re-exported and import types that no longer exist.
335
+ // Partial flags intentionally preserve sibling artifacts and are not cleaned.
336
+ if (parsedFlags.doAll) {
337
+ for (const dir of ["types", "services", "definitions", "proto"]) {
338
+ fs.rmSync(path.join(sourcePath, dir), { recursive: true, force: true });
339
+ }
340
+ }
341
+
330
342
  // Write package.json with "type": "module" so Node.js treats generated
331
343
  // ES module files correctly and avoids MODULE_TYPELESS_PACKAGE_JSON warnings.
332
344
  const generatedPkgPath = path.join(sourcePath, "package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/libcodegen",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "Protobuf code generation — keep types in sync with proto definitions without hand-writing.",
5
5
  "keywords": [
6
6
  "codegen",
@@ -56,7 +56,7 @@
56
56
  "protobufjs-cli": "1.2.2"
57
57
  },
58
58
  "devDependencies": {
59
- "@forwardimpact/libharness": "^0.1.5"
59
+ "@forwardimpact/libmock": "^0.1.0"
60
60
  },
61
61
  "engines": {
62
62
  "bun": ">=1.2.0",
package/src/base.js CHANGED
@@ -1,6 +1,6 @@
1
- import { execFile } from "node:child_process";
2
1
  import { fileURLToPath } from "node:url";
3
2
  import protobuf from "protobufjs";
3
+ import { createDefaultRuntime } from "@forwardimpact/libutil/runtime";
4
4
 
5
5
  /** Convert camelCase to snake_case (protobufjs normalizes field names) */
6
6
  function camelToSnake(str) {
@@ -61,6 +61,7 @@ export class CodegenBase {
61
61
  #mustache;
62
62
  #protoLoader;
63
63
  #fs;
64
+ #subprocess;
64
65
 
65
66
  /**
66
67
  * Creates a new codegen base instance with dependency injection
@@ -70,8 +71,17 @@ export class CodegenBase {
70
71
  * @param {object} mustache - Mustache template rendering module
71
72
  * @param {object} protoLoader - Protocol buffer loader module
72
73
  * @param {object} fs - File system module (sync operations only)
74
+ * @param {object} [runtime] - Optional runtime bag; falls back to createDefaultRuntime()
73
75
  */
74
- constructor(protoDirs, projectRoot, path, mustache, protoLoader, fs) {
76
+ constructor(
77
+ protoDirs,
78
+ projectRoot,
79
+ path,
80
+ mustache,
81
+ protoLoader,
82
+ fs,
83
+ runtime,
84
+ ) {
75
85
  if (!protoDirs || !Array.isArray(protoDirs) || protoDirs.length === 0) {
76
86
  throw new Error("protoDirs must be a non-empty array");
77
87
  }
@@ -87,6 +97,7 @@ export class CodegenBase {
87
97
  this.#mustache = mustache;
88
98
  this.#protoLoader = protoLoader;
89
99
  this.#fs = fs;
100
+ this.#subprocess = (runtime ?? createDefaultRuntime()).subprocess;
90
101
  }
91
102
 
92
103
  /**
@@ -182,22 +193,28 @@ export class CodegenBase {
182
193
  * Run a command with arguments and options
183
194
  * @param {string} cmd - Command to execute
184
195
  * @param {string[]} args - Command-line arguments
185
- * @param {object} [opts] - Child process options
196
+ * @param {object} [opts] - Subprocess options (e.g. cwd)
186
197
  * @returns {Promise<void>} Resolves when the command completes successfully
187
198
  */
188
- run(cmd, args, opts = {}) {
189
- return new Promise((resolvePromise, reject) => {
190
- const child = execFile(
191
- cmd,
192
- args,
193
- { stdio: "inherit", ...opts },
194
- (err) => {
195
- if (err) reject(err);
196
- else resolvePromise();
197
- },
198
- );
199
- child.on("error", reject);
199
+ async run(cmd, args, opts = {}) {
200
+ // `stdio: "inherit"` forwards to the underlying execFile/spawn so the
201
+ // child's stdout/stderr go straight to the parent's fds — preserving the
202
+ // exact pre-1370 behavior (origin/main also ran execFile with
203
+ // stdio:"inherit"), so `just codegen` shows protoc/pbjs progress live.
204
+ // With inherited stdio the buffered result is empty, so the error path
205
+ // below falls back to the exit code. Capture-mode callers override via
206
+ // `opts.stdio`.
207
+ const result = await this.#subprocess.run(cmd, args, {
208
+ stdio: "inherit",
209
+ ...opts,
200
210
  });
211
+ if (result.exitCode !== 0) {
212
+ const msg =
213
+ result.stderr?.trim() ||
214
+ result.stdout?.trim() ||
215
+ `exited with code ${result.exitCode}`;
216
+ throw new Error(`Command failed: ${cmd} ${args.join(" ")}\n${msg}`);
217
+ }
201
218
  }
202
219
 
203
220
  /**