js-to-cli 0.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Million Software, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,422 @@
1
+ import { Command } from "commander";
2
+ import { basename, extname, resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ //#region src/utils/camel-to-kebab.ts
5
+ const camelToKebab = (input) => input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
6
+ //#endregion
7
+ //#region src/load-module.ts
8
+ const SUPPORTED_EXTENSIONS = new Set([
9
+ ".js",
10
+ ".mjs",
11
+ ".cjs",
12
+ ".ts",
13
+ ".mts",
14
+ ".cts"
15
+ ]);
16
+ const RESERVED_COMMAND_NAMES = new Set(["help", "version"]);
17
+ const isCallableValue = (value) => typeof value === "function";
18
+ const isClassConstructor = (value) => /^\s*class\b/.test(Function.prototype.toString.call(value));
19
+ const loadModule = async (modulePath) => {
20
+ const absolutePath = resolve(process.cwd(), modulePath);
21
+ const extension = extname(absolutePath).toLowerCase();
22
+ if (!SUPPORTED_EXTENSIONS.has(extension)) throw new Error(`unsupported module extension: "${extension}"`);
23
+ const importedNamespace = await import(pathToFileURL(absolutePath).href);
24
+ const functionExports = [];
25
+ for (const [exportName, exportedValue] of Object.entries(importedNamespace)) {
26
+ if (exportName === "__esModule") continue;
27
+ if (!isCallableValue(exportedValue)) continue;
28
+ if (isClassConstructor(exportedValue)) continue;
29
+ const commandName = exportName === "default" ? "default" : camelToKebab(exportName);
30
+ if (RESERVED_COMMAND_NAMES.has(commandName)) {
31
+ process.stderr.write(`js-to-cli: skipping export "${exportName}" — collides with reserved command name\n`);
32
+ continue;
33
+ }
34
+ functionExports.push({
35
+ exportName,
36
+ commandName,
37
+ fn: exportedValue
38
+ });
39
+ }
40
+ if (functionExports.length === 0) throw new Error(`no exported functions found in ${modulePath}`);
41
+ return {
42
+ modulePath,
43
+ absolutePath,
44
+ namespace: importedNamespace,
45
+ functionExports
46
+ };
47
+ };
48
+ //#endregion
49
+ //#region src/utils/split-top-level.ts
50
+ const splitTopLevel = (source, separator) => {
51
+ const segments = [];
52
+ let depth = 0;
53
+ let stringQuote = null;
54
+ let segmentStart = 0;
55
+ for (let index = 0; index < source.length; index++) {
56
+ const character = source[index];
57
+ if (stringQuote !== null) {
58
+ if (character === "\\") {
59
+ index++;
60
+ continue;
61
+ }
62
+ if (character === stringQuote) stringQuote = null;
63
+ continue;
64
+ }
65
+ if (character === "\"" || character === "'" || character === "`") {
66
+ stringQuote = character;
67
+ continue;
68
+ }
69
+ if (character === "(" || character === "[" || character === "{") {
70
+ depth++;
71
+ continue;
72
+ }
73
+ if (character === ")" || character === "]" || character === "}") {
74
+ depth--;
75
+ continue;
76
+ }
77
+ if (depth === 0 && source.startsWith(separator, index)) {
78
+ segments.push(source.slice(segmentStart, index));
79
+ segmentStart = index + separator.length;
80
+ index += separator.length - 1;
81
+ }
82
+ }
83
+ segments.push(source.slice(segmentStart));
84
+ return segments;
85
+ };
86
+ //#endregion
87
+ //#region src/utils/strip-comments.ts
88
+ const stripComments = (source) => {
89
+ let output = "";
90
+ let stringQuote = null;
91
+ let index = 0;
92
+ while (index < source.length) {
93
+ const character = source[index];
94
+ if (stringQuote !== null) {
95
+ output += character;
96
+ if (character === "\\" && index + 1 < source.length) {
97
+ output += source[index + 1];
98
+ index += 2;
99
+ continue;
100
+ }
101
+ if (character === stringQuote) stringQuote = null;
102
+ index++;
103
+ continue;
104
+ }
105
+ if (character === "\"" || character === "'" || character === "`") {
106
+ stringQuote = character;
107
+ output += character;
108
+ index++;
109
+ continue;
110
+ }
111
+ if (character === "/" && source[index + 1] === "/") {
112
+ const newlineIndex = source.indexOf("\n", index + 2);
113
+ if (newlineIndex === -1) index = source.length;
114
+ else index = newlineIndex;
115
+ continue;
116
+ }
117
+ if (character === "/" && source[index + 1] === "*") {
118
+ const closingIndex = source.indexOf("*/", index + 2);
119
+ if (closingIndex === -1) index = source.length;
120
+ else index = closingIndex + 2;
121
+ continue;
122
+ }
123
+ output += character;
124
+ index++;
125
+ }
126
+ return output;
127
+ };
128
+ //#endregion
129
+ //#region src/parse-function.ts
130
+ const IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/;
131
+ const findMatchingClose = (source, openIndex) => {
132
+ const openChar = source[openIndex];
133
+ const closeChar = openChar === "(" ? ")" : openChar === "{" ? "}" : "]";
134
+ let depth = 0;
135
+ let stringQuote = null;
136
+ for (let index = openIndex; index < source.length; index++) {
137
+ const character = source[index];
138
+ if (stringQuote !== null) {
139
+ if (character === "\\") {
140
+ index++;
141
+ continue;
142
+ }
143
+ if (character === stringQuote) stringQuote = null;
144
+ continue;
145
+ }
146
+ if (character === "\"" || character === "'" || character === "`") {
147
+ stringQuote = character;
148
+ continue;
149
+ }
150
+ if (character === openChar) depth++;
151
+ else if (character === closeChar) {
152
+ depth--;
153
+ if (depth === 0) return index;
154
+ }
155
+ }
156
+ return -1;
157
+ };
158
+ const findTopLevelDefaultEquals = (source) => {
159
+ let depth = 0;
160
+ let stringQuote = null;
161
+ for (let index = 0; index < source.length; index++) {
162
+ const character = source[index];
163
+ if (stringQuote !== null) {
164
+ if (character === "\\") {
165
+ index++;
166
+ continue;
167
+ }
168
+ if (character === stringQuote) stringQuote = null;
169
+ continue;
170
+ }
171
+ if (character === "\"" || character === "'" || character === "`") {
172
+ stringQuote = character;
173
+ continue;
174
+ }
175
+ if (character === "(" || character === "[" || character === "{") {
176
+ depth++;
177
+ continue;
178
+ }
179
+ if (character === ")" || character === "]" || character === "}") {
180
+ depth--;
181
+ continue;
182
+ }
183
+ if (depth === 0 && character === "=") {
184
+ const nextCharacter = source[index + 1];
185
+ const previousCharacter = source[index - 1];
186
+ if (nextCharacter === "=" || nextCharacter === ">") continue;
187
+ if (previousCharacter === "=" || previousCharacter === "<" || previousCharacter === ">" || previousCharacter === "!") continue;
188
+ return index;
189
+ }
190
+ }
191
+ return -1;
192
+ };
193
+ const splitNameAndDefault = (source) => {
194
+ const equalsIndex = findTopLevelDefaultEquals(source);
195
+ if (equalsIndex === -1) return {
196
+ beforeEquals: source.trim(),
197
+ defaultLiteral: null
198
+ };
199
+ return {
200
+ beforeEquals: source.slice(0, equalsIndex).trim(),
201
+ defaultLiteral: source.slice(equalsIndex + 1).trim()
202
+ };
203
+ };
204
+ const parseOptionFields = (innerSource) => {
205
+ const fields = [];
206
+ for (const rawSlice of splitTopLevel(innerSource, ",")) {
207
+ const slice = rawSlice.trim();
208
+ if (slice === "") continue;
209
+ const { beforeEquals, defaultLiteral } = splitNameAndDefault(slice);
210
+ let propertyName = beforeEquals;
211
+ const colonIndex = beforeEquals.indexOf(":");
212
+ if (colonIndex !== -1) propertyName = beforeEquals.slice(0, colonIndex).trim();
213
+ if (!IDENTIFIER_PATTERN.test(propertyName)) throw new Error(`unsupported destructured option field: "${slice}"`);
214
+ fields.push({
215
+ name: propertyName,
216
+ hasDefault: defaultLiteral !== null,
217
+ defaultLiteral
218
+ });
219
+ }
220
+ return fields;
221
+ };
222
+ const parseParameter = (rawSlice, exportedName) => {
223
+ const slice = rawSlice.trim();
224
+ if (slice.startsWith("...")) {
225
+ const restName = slice.slice(3).trim();
226
+ if (!IDENTIFIER_PATTERN.test(restName)) throw new Error(`unsupported rest parameter "${slice}" in "${exportedName}"`);
227
+ return {
228
+ name: restName,
229
+ kind: "rest",
230
+ hasDefault: false,
231
+ defaultLiteral: null,
232
+ optionFields: null
233
+ };
234
+ }
235
+ if (slice.startsWith("{")) {
236
+ const closingBraceIndex = findMatchingClose(slice, 0);
237
+ if (closingBraceIndex === -1) throw new Error(`could not parse destructured options parameter in "${exportedName}"`);
238
+ const innerSource = slice.slice(1, closingBraceIndex);
239
+ const afterBrace = slice.slice(closingBraceIndex + 1).trim();
240
+ const hasDefault = afterBrace.startsWith("=");
241
+ return {
242
+ name: "options",
243
+ kind: "options",
244
+ hasDefault,
245
+ defaultLiteral: hasDefault ? afterBrace.slice(1).trim() : null,
246
+ optionFields: parseOptionFields(innerSource)
247
+ };
248
+ }
249
+ if (slice.startsWith("[")) throw new Error(`unsupported parameter pattern "${slice}" in "${exportedName}" (array destructuring not supported)`);
250
+ const { beforeEquals, defaultLiteral } = splitNameAndDefault(slice);
251
+ if (!IDENTIFIER_PATTERN.test(beforeEquals)) throw new Error(`unsupported parameter pattern "${slice}" in "${exportedName}"`);
252
+ return {
253
+ name: beforeEquals,
254
+ kind: "primitive",
255
+ hasDefault: defaultLiteral !== null,
256
+ defaultLiteral,
257
+ optionFields: null
258
+ };
259
+ };
260
+ const extractParameterListSource = (source) => {
261
+ let cursor = 0;
262
+ while (cursor < source.length && /\s/.test(source[cursor])) cursor++;
263
+ if (source.startsWith("function", cursor)) {
264
+ cursor += 8;
265
+ while (cursor < source.length && source[cursor] !== "(") cursor++;
266
+ }
267
+ while (cursor < source.length && /\s/.test(source[cursor])) cursor++;
268
+ if (source[cursor] !== "(") {
269
+ const arrowIndex = source.indexOf("=>", cursor);
270
+ if (arrowIndex === -1) throw new Error(`could not parse function source: ${source}`);
271
+ return source.slice(cursor, arrowIndex).trim();
272
+ }
273
+ const closingIndex = findMatchingClose(source, cursor);
274
+ if (closingIndex === -1) throw new Error(`could not find matching paren in function source: ${source}`);
275
+ return source.slice(cursor + 1, closingIndex);
276
+ };
277
+ const parseFunctionSignature = (fn, exportedName) => {
278
+ const strippedSource = stripComments(fn.toString()).trim();
279
+ const isAsync = /^async\b/.test(strippedSource);
280
+ const parameterListSource = extractParameterListSource(isAsync ? strippedSource.slice(5).trimStart() : strippedSource);
281
+ if (parameterListSource.trim() === "") return {
282
+ name: exportedName,
283
+ isAsync,
284
+ parameters: []
285
+ };
286
+ const parameters = splitTopLevel(parameterListSource, ",").map((slice) => slice.trim()).filter((slice) => slice !== "").map((slice) => parseParameter(slice, exportedName));
287
+ for (let index = 0; index < parameters.length; index++) {
288
+ const parameter = parameters[index];
289
+ const isLast = index === parameters.length - 1;
290
+ if ((parameter.kind === "options" || parameter.kind === "rest") && !isLast) throw new Error(`${parameter.kind} parameter "${parameter.name}" must be last in "${exportedName}"`);
291
+ }
292
+ return {
293
+ name: exportedName,
294
+ isAsync,
295
+ parameters
296
+ };
297
+ };
298
+ //#endregion
299
+ //#region src/utils/format-result.ts
300
+ const formatResult = (value) => {
301
+ if (value === void 0) return null;
302
+ if (value === null) return "null";
303
+ if (typeof value === "string") return value;
304
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
305
+ return JSON.stringify(value, null, 2);
306
+ };
307
+ //#endregion
308
+ //#region src/utils/infer-option-type.ts
309
+ const NUMBER_LITERAL_PATTERN = /^-?\d+(?:\.\d+)?$/;
310
+ const inferOptionType = (defaultLiteral) => {
311
+ if (defaultLiteral === null) return { commanderType: "required-string" };
312
+ const trimmedLiteral = defaultLiteral.trim();
313
+ if (trimmedLiteral === "false") return { commanderType: "boolean" };
314
+ if (trimmedLiteral === "true") return { commanderType: "negated-boolean" };
315
+ if (NUMBER_LITERAL_PATTERN.test(trimmedLiteral)) return {
316
+ commanderType: "number",
317
+ defaultValue: parseFloat(trimmedLiteral)
318
+ };
319
+ if (trimmedLiteral.startsWith("[")) return { commanderType: "array" };
320
+ const firstCharacter = trimmedLiteral[0];
321
+ if (firstCharacter === "\"" || firstCharacter === "'" || firstCharacter === "`") return {
322
+ commanderType: "string",
323
+ defaultValue: trimmedLiteral[trimmedLiteral.length - 1] === firstCharacter ? trimmedLiteral.slice(1, -1) : trimmedLiteral.slice(1)
324
+ };
325
+ return {
326
+ commanderType: "string",
327
+ defaultValue: void 0
328
+ };
329
+ };
330
+ //#endregion
331
+ //#region src/build-cli.ts
332
+ const collectArrayValue = (value, previous) => [...previous, value];
333
+ const applyOptionField = (subcommand, field) => {
334
+ const kebabName = camelToKebab(field.name);
335
+ const inferred = inferOptionType(field.defaultLiteral);
336
+ switch (inferred.commanderType) {
337
+ case "boolean":
338
+ subcommand.option(`--${kebabName}`, "");
339
+ return;
340
+ case "negated-boolean":
341
+ subcommand.option(`--no-${kebabName}`, "");
342
+ return;
343
+ case "number":
344
+ subcommand.option(`--${kebabName} <number>`, "", parseFloat, inferred.defaultValue);
345
+ return;
346
+ case "array":
347
+ subcommand.option(`--${kebabName} <value>`, "", collectArrayValue, []);
348
+ return;
349
+ case "required-string":
350
+ subcommand.requiredOption(`--${kebabName} <value>`, "");
351
+ return;
352
+ case "string":
353
+ if (inferred.defaultValue !== void 0) {
354
+ subcommand.option(`--${kebabName} <value>`, "", inferred.defaultValue);
355
+ return;
356
+ }
357
+ subcommand.option(`--${kebabName} <value>`, "");
358
+ return;
359
+ }
360
+ };
361
+ const applyParameter = (subcommand, parameter) => {
362
+ if (parameter.kind === "primitive") {
363
+ const argumentSpec = parameter.hasDefault ? `[${camelToKebab(parameter.name)}]` : `<${camelToKebab(parameter.name)}>`;
364
+ subcommand.argument(argumentSpec);
365
+ return;
366
+ }
367
+ if (parameter.kind === "rest") {
368
+ subcommand.argument(`[${camelToKebab(parameter.name)}...]`);
369
+ return;
370
+ }
371
+ for (const field of parameter.optionFields ?? []) applyOptionField(subcommand, field);
372
+ };
373
+ const isPlainObject = (value) => typeof value === "object" && value !== null;
374
+ const assembleCallArgs = (signature, commanderArgs) => {
375
+ const positionalParameters = signature.parameters.filter((parameter) => parameter.kind === "primitive" || parameter.kind === "rest");
376
+ const positionalValues = commanderArgs.slice(0, positionalParameters.length);
377
+ const optionsSlot = commanderArgs[positionalParameters.length];
378
+ const rawOptions = isPlainObject(optionsSlot) ? optionsSlot : {};
379
+ const callArgs = [];
380
+ let positionalCursor = 0;
381
+ for (const parameter of signature.parameters) {
382
+ if (parameter.kind === "primitive") {
383
+ callArgs.push(positionalValues[positionalCursor]);
384
+ positionalCursor++;
385
+ continue;
386
+ }
387
+ if (parameter.kind === "rest") {
388
+ const restValue = positionalValues[positionalCursor];
389
+ positionalCursor++;
390
+ if (Array.isArray(restValue)) callArgs.push(...restValue);
391
+ else if (restValue !== void 0) callArgs.push(restValue);
392
+ continue;
393
+ }
394
+ const optionsObject = {};
395
+ for (const field of parameter.optionFields ?? []) if (field.name in rawOptions) optionsObject[field.name] = rawOptions[field.name];
396
+ callArgs.push(optionsObject);
397
+ }
398
+ return callArgs;
399
+ };
400
+ const buildActionHandler = (fn, signature) => async (...commanderArgs) => {
401
+ try {
402
+ const formatted = formatResult(await fn(...assembleCallArgs(signature, commanderArgs)));
403
+ if (formatted !== null) process.stdout.write(formatted + "\n");
404
+ } catch (error) {
405
+ const message = error instanceof Error ? error.message : String(error);
406
+ process.stderr.write(message + "\n");
407
+ process.exit(1);
408
+ }
409
+ };
410
+ const convertJsToCli = async (modulePath, options = {}) => {
411
+ const loaded = await loadModule(modulePath);
412
+ const program = new Command().name(options.programName ?? basename(loaded.absolutePath)).description(`CLI generated from ${loaded.modulePath}`);
413
+ for (const functionExport of loaded.functionExports) {
414
+ const signature = parseFunctionSignature(functionExport.fn, functionExport.exportName);
415
+ const subcommand = program.command(functionExport.commandName);
416
+ for (const parameter of signature.parameters) applyParameter(subcommand, parameter);
417
+ subcommand.action(buildActionHandler(functionExport.fn, signature));
418
+ }
419
+ return program;
420
+ };
421
+ //#endregion
422
+ export { loadModule as i, inferOptionType as n, parseFunctionSignature as r, convertJsToCli as t };
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { t as convertJsToCli } from "./build-cli-Bl-vRrRB.mjs";
3
+ //#region src/cli.ts
4
+ const HELP_TEXT = "Usage: js-to-cli <module-path> <subcommand> [args...]\n\nLoads the given JS/TS module and exposes its exported functions as subcommands.\nEach function becomes a subcommand. Primitive parameters become positional args;\na trailing destructured options object becomes --flags.\n";
5
+ const main = async () => {
6
+ const argv = process.argv.slice(2);
7
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
8
+ process.stdout.write(HELP_TEXT);
9
+ process.exit(0);
10
+ }
11
+ const modulePath = argv[0];
12
+ const remainingArgv = argv.slice(1);
13
+ try {
14
+ await (await convertJsToCli(modulePath)).parseAsync(remainingArgv, { from: "user" });
15
+ } catch (error) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ process.stderr.write(`js-to-cli: ${message}\n`);
18
+ process.exit(1);
19
+ }
20
+ };
21
+ main();
22
+ //#endregion
23
+ export {};
@@ -0,0 +1,67 @@
1
+ import { Command } from "commander";
2
+
3
+ //#region src/build-cli.d.ts
4
+ interface BuildCliOptions {
5
+ programName?: string;
6
+ }
7
+ declare const convertJsToCli: (modulePath: string, options?: BuildCliOptions) => Promise<Command>;
8
+ //#endregion
9
+ //#region src/load-module.d.ts
10
+ interface LoadedFunctionExport {
11
+ exportName: string;
12
+ commandName: string;
13
+ fn: (...args: unknown[]) => unknown;
14
+ }
15
+ interface LoadedModule {
16
+ modulePath: string;
17
+ absolutePath: string;
18
+ namespace: Record<string, unknown>;
19
+ functionExports: LoadedFunctionExport[];
20
+ }
21
+ declare const loadModule: (modulePath: string) => Promise<LoadedModule>;
22
+ //#endregion
23
+ //#region src/parse-function.d.ts
24
+ interface ParsedOptionField {
25
+ name: string;
26
+ hasDefault: boolean;
27
+ defaultLiteral: string | null;
28
+ }
29
+ interface ParsedParameter {
30
+ name: string;
31
+ kind: "primitive" | "options" | "rest";
32
+ hasDefault: boolean;
33
+ defaultLiteral: string | null;
34
+ optionFields: ParsedOptionField[] | null;
35
+ }
36
+ interface ParsedFunctionSignature {
37
+ name: string;
38
+ isAsync: boolean;
39
+ parameters: ParsedParameter[];
40
+ }
41
+ declare const parseFunctionSignature: (fn: Function, exportedName: string) => ParsedFunctionSignature;
42
+ //#endregion
43
+ //#region src/utils/infer-option-type.d.ts
44
+ interface BooleanOption {
45
+ commanderType: "boolean";
46
+ }
47
+ interface NegatedBooleanOption {
48
+ commanderType: "negated-boolean";
49
+ }
50
+ interface NumberOption {
51
+ commanderType: "number";
52
+ defaultValue: number;
53
+ }
54
+ interface ArrayOption {
55
+ commanderType: "array";
56
+ }
57
+ interface StringOption {
58
+ commanderType: "string";
59
+ defaultValue: string | undefined;
60
+ }
61
+ interface RequiredStringOption {
62
+ commanderType: "required-string";
63
+ }
64
+ type InferredOptionType = BooleanOption | NegatedBooleanOption | NumberOption | ArrayOption | StringOption | RequiredStringOption;
65
+ declare const inferOptionType: (defaultLiteral: string | null) => InferredOptionType;
66
+ //#endregion
67
+ export { type BuildCliOptions, type LoadedFunctionExport, type LoadedModule, type ParsedFunctionSignature, type ParsedOptionField, type ParsedParameter, convertJsToCli, inferOptionType, loadModule, parseFunctionSignature };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { i as loadModule, n as inferOptionType, r as parseFunctionSignature, t as convertJsToCli } from "./build-cli-Bl-vRrRB.mjs";
2
+ export { convertJsToCli, inferOptionType, loadModule, parseFunctionSignature };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "js-to-cli",
3
+ "version": "0.0.2",
4
+ "description": "Turn any Node.js module into a Commander CLI. Inverse of cli-to-js.",
5
+ "keywords": [
6
+ "cli",
7
+ "commander",
8
+ "module",
9
+ "node",
10
+ "runtime",
11
+ "wrapper"
12
+ ],
13
+ "homepage": "https://github.com/aidenybai/cli-to-js",
14
+ "bugs": {
15
+ "url": "https://github.com/aidenybai/cli-to-js/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": {
19
+ "name": "Aiden Bai",
20
+ "email": "aiden@million.dev"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/aidenybai/cli-to-js.git"
25
+ },
26
+ "bin": {
27
+ "js-to-cli": "./dist/cli.mjs"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "type": "module",
34
+ "main": "./dist/index.mjs",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.mts",
38
+ "import": "./dist/index.mjs"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "dependencies": {
46
+ "commander": "^14.0.3"
47
+ },
48
+ "engines": {
49
+ "node": ">=22"
50
+ },
51
+ "scripts": {
52
+ "build": "vp pack",
53
+ "dev": "vp pack --watch",
54
+ "test": "vp test run",
55
+ "typecheck": "tsc --noEmit"
56
+ }
57
+ }