@forwardimpact/libcodegen 0.1.37 → 0.1.41
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.
- package/bin/fit-codegen.js +127 -48
- package/package.json +3 -2
package/bin/fit-codegen.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs, { readFileSync } from "node:fs";
|
|
4
4
|
import fsAsync from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
|
-
import { parseArgs } from "node:util";
|
|
8
7
|
|
|
9
8
|
import protoLoader from "@grpc/proto-loader";
|
|
10
9
|
import mustache from "mustache";
|
|
11
10
|
|
|
11
|
+
import { createCli, SummaryRenderer } from "@forwardimpact/libcli";
|
|
12
12
|
import { Finder } from "@forwardimpact/libutil";
|
|
13
13
|
import { Logger } from "@forwardimpact/libtelemetry";
|
|
14
14
|
import {
|
|
@@ -19,6 +19,39 @@ import {
|
|
|
19
19
|
} from "@forwardimpact/libcodegen";
|
|
20
20
|
import { createStorage } from "@forwardimpact/libstorage";
|
|
21
21
|
|
|
22
|
+
const { version: VERSION } = JSON.parse(
|
|
23
|
+
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const definition = {
|
|
27
|
+
name: "fit-codegen",
|
|
28
|
+
version: VERSION,
|
|
29
|
+
description: "Generate protobuf types, service clients, and definitions",
|
|
30
|
+
options: {
|
|
31
|
+
all: { type: "boolean", description: "Generate all code" },
|
|
32
|
+
type: { type: "boolean", description: "Generate protobuf types only" },
|
|
33
|
+
service: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
description: "Generate service bases only",
|
|
36
|
+
},
|
|
37
|
+
client: { type: "boolean", description: "Generate clients only" },
|
|
38
|
+
definition: {
|
|
39
|
+
type: "boolean",
|
|
40
|
+
description: "Generate service definitions only",
|
|
41
|
+
},
|
|
42
|
+
help: { type: "boolean", short: "h", description: "Show this help" },
|
|
43
|
+
version: { type: "boolean", description: "Show version" },
|
|
44
|
+
json: { type: "boolean", description: "Output help as JSON" },
|
|
45
|
+
},
|
|
46
|
+
examples: [
|
|
47
|
+
"npx fit-codegen --all",
|
|
48
|
+
"npx fit-codegen --type",
|
|
49
|
+
"npx fit-codegen --service",
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const cli = createCli(definition);
|
|
54
|
+
|
|
22
55
|
/**
|
|
23
56
|
* Create tar.gz bundle of all directories inside sourcePath
|
|
24
57
|
* @param {string} sourcePath - Path containing directories to bundle
|
|
@@ -53,51 +86,16 @@ async function createBundle(sourcePath) {
|
|
|
53
86
|
}
|
|
54
87
|
|
|
55
88
|
/**
|
|
56
|
-
*
|
|
57
|
-
*/
|
|
58
|
-
function printUsage() {
|
|
59
|
-
process.stdout.write(
|
|
60
|
-
[
|
|
61
|
-
"Usage:",
|
|
62
|
-
` npx fit-codegen --all # Generate all code`,
|
|
63
|
-
` npx fit-codegen --type # Generate protobuf types only`,
|
|
64
|
-
` npx fit-codegen --service # Generate service bases only`,
|
|
65
|
-
` npx fit-codegen --client # Generate clients only`,
|
|
66
|
-
` npx fit-codegen --definition # Generate service definitions only`,
|
|
67
|
-
].join("\n") + "\n",
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Parse command line flags
|
|
89
|
+
* Parse command line flags using libcli
|
|
73
90
|
* @returns {object} Parsed flags with convenience methods
|
|
74
91
|
*/
|
|
75
92
|
function parseFlags() {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
default: false,
|
|
81
|
-
},
|
|
82
|
-
type: {
|
|
83
|
-
type: "boolean",
|
|
84
|
-
default: false,
|
|
85
|
-
},
|
|
86
|
-
service: {
|
|
87
|
-
type: "boolean",
|
|
88
|
-
default: false,
|
|
89
|
-
},
|
|
90
|
-
client: {
|
|
91
|
-
type: "boolean",
|
|
92
|
-
default: false,
|
|
93
|
-
},
|
|
94
|
-
definition: {
|
|
95
|
-
type: "boolean",
|
|
96
|
-
default: false,
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
});
|
|
93
|
+
const parsed = cli.parse(process.argv.slice(2));
|
|
94
|
+
if (!parsed) {
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
100
97
|
|
|
98
|
+
const { values } = parsed;
|
|
101
99
|
const doAll = values.all;
|
|
102
100
|
return {
|
|
103
101
|
doTypes: doAll || values.type,
|
|
@@ -179,6 +177,72 @@ function createCodegen(
|
|
|
179
177
|
};
|
|
180
178
|
}
|
|
181
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Count files recursively in a directory
|
|
182
|
+
* @param {string} dirPath - Directory to count files in
|
|
183
|
+
* @returns {number} Total file count
|
|
184
|
+
*/
|
|
185
|
+
function countFiles(dirPath) {
|
|
186
|
+
let count = 0;
|
|
187
|
+
if (!fs.existsSync(dirPath)) return count;
|
|
188
|
+
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
189
|
+
if (entry.isDirectory()) {
|
|
190
|
+
count += countFiles(path.join(dirPath, entry.name));
|
|
191
|
+
} else if (!entry.name.endsWith(".tar.gz")) {
|
|
192
|
+
count++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return count;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Print a summary of generated code
|
|
200
|
+
* @param {string} sourcePath - Path to generated directory
|
|
201
|
+
* @param {object} flags - Parsed generation flags
|
|
202
|
+
*/
|
|
203
|
+
function printSummary(sourcePath, flags) {
|
|
204
|
+
const totalFiles = countFiles(sourcePath);
|
|
205
|
+
const relPath = path.relative(process.cwd(), sourcePath);
|
|
206
|
+
|
|
207
|
+
const dirLabels = {
|
|
208
|
+
types: "Protocol Buffer types",
|
|
209
|
+
proto: "Proto source files",
|
|
210
|
+
services: "Service bases and clients",
|
|
211
|
+
definitions: "Service definitions",
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const items = [];
|
|
215
|
+
if (fs.existsSync(sourcePath)) {
|
|
216
|
+
const dirs = fs
|
|
217
|
+
.readdirSync(sourcePath, { withFileTypes: true })
|
|
218
|
+
.filter((e) => e.isDirectory());
|
|
219
|
+
|
|
220
|
+
for (const dir of dirs) {
|
|
221
|
+
const label = dirLabels[dir.name];
|
|
222
|
+
if (label) items.push({ label: `${dir.name}/`, description: label });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const summary = new SummaryRenderer({ process });
|
|
227
|
+
summary.render(
|
|
228
|
+
{
|
|
229
|
+
title: `Generated ${totalFiles} files in ./${relPath}/`,
|
|
230
|
+
items,
|
|
231
|
+
},
|
|
232
|
+
process.stdout,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const generated = [
|
|
236
|
+
flags.doTypes && "types",
|
|
237
|
+
flags.doServices && "services",
|
|
238
|
+
flags.doClients && "clients",
|
|
239
|
+
flags.doDefinitions && "definitions",
|
|
240
|
+
].filter(Boolean);
|
|
241
|
+
process.stdout.write(
|
|
242
|
+
`\nCode generation complete (${generated.join(", ")}).\n`,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
182
246
|
/**
|
|
183
247
|
* Execute code generation tasks
|
|
184
248
|
* @param {object} codegens - Codegen instances
|
|
@@ -229,9 +293,10 @@ async function runCodegen(protoDirs, projectRoot, finder) {
|
|
|
229
293
|
const parsedFlags = parseFlags();
|
|
230
294
|
|
|
231
295
|
if (!parsedFlags.hasGenerationFlags()) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
296
|
+
cli.usageError(
|
|
297
|
+
"no generation flags specified (use --all, --type, --service, --client, or --definition)",
|
|
298
|
+
);
|
|
299
|
+
process.exit(2);
|
|
235
300
|
}
|
|
236
301
|
|
|
237
302
|
const generatedStorage = createStorage("generated", "local");
|
|
@@ -239,6 +304,16 @@ async function runCodegen(protoDirs, projectRoot, finder) {
|
|
|
239
304
|
|
|
240
305
|
await generatedStorage.ensureBucket();
|
|
241
306
|
|
|
307
|
+
// Write package.json with "type": "module" so Node.js treats generated
|
|
308
|
+
// ES module files correctly and avoids MODULE_TYPELESS_PACKAGE_JSON warnings.
|
|
309
|
+
const generatedPkgPath = path.join(sourcePath, "package.json");
|
|
310
|
+
if (!fs.existsSync(generatedPkgPath)) {
|
|
311
|
+
fs.writeFileSync(
|
|
312
|
+
generatedPkgPath,
|
|
313
|
+
JSON.stringify({ type: "module" }, null, 2) + "\n",
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
242
317
|
const codegens = createCodegen(
|
|
243
318
|
protoDirs,
|
|
244
319
|
projectRoot,
|
|
@@ -251,6 +326,8 @@ async function runCodegen(protoDirs, projectRoot, finder) {
|
|
|
251
326
|
|
|
252
327
|
await finder.createPackageSymlinks(sourcePath);
|
|
253
328
|
await createBundle(sourcePath);
|
|
329
|
+
|
|
330
|
+
printSummary(sourcePath, parsedFlags);
|
|
254
331
|
}
|
|
255
332
|
|
|
256
333
|
/**
|
|
@@ -273,12 +350,14 @@ async function main() {
|
|
|
273
350
|
|
|
274
351
|
await runCodegen(protoDirs, projectRoot, finder);
|
|
275
352
|
} catch (err) {
|
|
276
|
-
|
|
353
|
+
const logger = new Logger("codegen");
|
|
354
|
+
logger.exception("main", err);
|
|
355
|
+
cli.error(err.message);
|
|
277
356
|
process.exit(1);
|
|
278
357
|
}
|
|
279
358
|
}
|
|
280
359
|
|
|
281
360
|
main().catch((err) => {
|
|
282
|
-
|
|
361
|
+
cli.error(err.message);
|
|
283
362
|
process.exit(1);
|
|
284
363
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/libcodegen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"description": "Protocol Buffer code generation utilities for Guide",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "D. Olsson <hi@senzilla.io>",
|
|
@@ -17,13 +17,14 @@
|
|
|
17
17
|
"test": "bun run node --test test/*.test.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@forwardimpact/libcli": "^0.1.0",
|
|
20
21
|
"@forwardimpact/libstorage": "^0.1.53",
|
|
21
22
|
"@forwardimpact/libtelemetry": "^0.1.22",
|
|
22
23
|
"@forwardimpact/libutil": "^0.1.64",
|
|
23
24
|
"@grpc/proto-loader": "^0.8.0",
|
|
24
25
|
"mustache": "^4.2.0",
|
|
25
26
|
"protobufjs": "^7.5.4",
|
|
26
|
-
"protobufjs-cli": "^2.0.
|
|
27
|
+
"protobufjs-cli": "^2.0.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@forwardimpact/libharness": "^0.1.5"
|