@openpkg-ts/cli 0.11.3 → 0.12.1
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/README.md +26 -5
- package/dist/index.js +280 -69
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,9 +5,15 @@ Extract [OpenPkg](https://openpkg.dev) documents and generate docs from the comm
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
# Extract an
|
|
8
|
+
# Extract from an entry file
|
|
9
9
|
bunx @openpkg-ts/cli spec src/index.ts -o openpkg.json
|
|
10
10
|
|
|
11
|
+
# Or resolve the package/entry from a dir, cwd, intent, or git URL
|
|
12
|
+
bunx @openpkg-ts/cli spec
|
|
13
|
+
bunx @openpkg-ts/cli spec .
|
|
14
|
+
bunx @openpkg-ts/cli spec . sdk
|
|
15
|
+
bunx @openpkg-ts/cli spec https://github.com/org/repo
|
|
16
|
+
|
|
11
17
|
# Generate markdown docs (from source or an existing spec)
|
|
12
18
|
bunx @openpkg-ts/cli docs src/index.ts -o docs/api.md
|
|
13
19
|
bunx @openpkg-ts/cli docs openpkg.json -f html -o docs/api.html
|
|
@@ -19,16 +25,31 @@ bunx @openpkg-ts/cli list src/index.ts
|
|
|
19
25
|
bunx @openpkg-ts/cli diff old.json new.json
|
|
20
26
|
```
|
|
21
27
|
|
|
28
|
+
Prefers TypeScript source (`src/index.ts`) over `dist/*.d.ts`. Several packages and no intent → prompt (or a list if not a TTY).
|
|
29
|
+
|
|
30
|
+
Opt-in Jev routing (needs `AI_GATEWAY_API_KEY` and the `ai` package). Sends package.json + file heads to Vercel AI Gateway with zero data retention:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bunx @openpkg-ts/cli spec . --jev
|
|
34
|
+
bunx @openpkg-ts/cli spec . --jev --follow-external auto
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`followExternal: "auto"` (config or flag) requires `--jev`. Config: `openpkg.config.json` or `package.json#openpkg`.
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{ "followExternal": "auto", "decisions": "jev" }
|
|
41
|
+
```
|
|
42
|
+
|
|
22
43
|
## Commands
|
|
23
44
|
|
|
24
45
|
| Command | Description |
|
|
25
46
|
|---------|-------------|
|
|
26
|
-
| `spec
|
|
27
|
-
| `docs
|
|
28
|
-
| `list
|
|
47
|
+
| `spec [path \| entry.ts] [intent...]` | Extract a spec from a file, package dir, cwd, or git URL |
|
|
48
|
+
| `docs [path \| entry.ts \| spec.json] [intent...]` | Generate docs (`-f md\|html\|json`) |
|
|
49
|
+
| `list [path \| entry.ts] [intent...]` | List exports with kind and location (`--json`) |
|
|
29
50
|
| `diff <old.json> <new.json>` | Compare specs; exits 2 if breaking changes |
|
|
30
51
|
|
|
31
|
-
`-o, --output` writes to a file instead of stdout.
|
|
52
|
+
`-o, --output` writes to a file instead of stdout. `--jev` routes package/entry with Jev. `--follow-external auto` expands load-bearing externals (requires `--jev`).
|
|
32
53
|
|
|
33
54
|
For programmatic use, richer options, and framework integrations (search indexes, nav trees), use `@openpkg-ts/sdk` directly.
|
|
34
55
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs2 from "node:fs";
|
|
5
|
+
import path2 from "node:path";
|
|
6
|
+
import readline from "node:readline/promises";
|
|
6
7
|
import { parseArgs } from "node:util";
|
|
7
8
|
import {
|
|
8
9
|
calculateNextVersion,
|
|
@@ -12,23 +13,99 @@ import {
|
|
|
12
13
|
extractSpec,
|
|
13
14
|
getAvailableVersions,
|
|
14
15
|
getValidationErrors,
|
|
16
|
+
isPathLikeInput,
|
|
17
|
+
isRemoteInput,
|
|
15
18
|
listExports,
|
|
16
19
|
loadConfig,
|
|
17
20
|
mergeConfig,
|
|
18
|
-
|
|
21
|
+
pickEntry,
|
|
22
|
+
recommendSemverBump,
|
|
23
|
+
resolveTarget
|
|
19
24
|
} from "@openpkg-ts/sdk";
|
|
25
|
+
|
|
26
|
+
// src/env.ts
|
|
27
|
+
import fs from "node:fs";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
function parseEnvText(content) {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
32
|
+
let line = rawLine.trim();
|
|
33
|
+
if (!line || line.startsWith("#"))
|
|
34
|
+
continue;
|
|
35
|
+
if (line.startsWith("export "))
|
|
36
|
+
line = line.slice(7).trimStart();
|
|
37
|
+
const eq = line.indexOf("=");
|
|
38
|
+
if (eq <= 0)
|
|
39
|
+
continue;
|
|
40
|
+
const key = line.slice(0, eq).trim();
|
|
41
|
+
if (!key || /\s/.test(key))
|
|
42
|
+
continue;
|
|
43
|
+
let value = line.slice(eq + 1);
|
|
44
|
+
if (value.startsWith(" ") || value.startsWith("\t"))
|
|
45
|
+
value = value.trimStart();
|
|
46
|
+
if (value.startsWith('"')) {
|
|
47
|
+
let i = 1;
|
|
48
|
+
let parsed = "";
|
|
49
|
+
while (i < value.length) {
|
|
50
|
+
const ch = value[i];
|
|
51
|
+
if (ch === "\\" && i + 1 < value.length) {
|
|
52
|
+
const next = value[i + 1];
|
|
53
|
+
parsed += next === "n" ? `
|
|
54
|
+
` : next === "r" ? "\r" : next === "t" ? "\t" : next;
|
|
55
|
+
i += 2;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (ch === '"')
|
|
59
|
+
break;
|
|
60
|
+
parsed += ch;
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
out[key] = parsed;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (value.startsWith("'")) {
|
|
67
|
+
const end = value.indexOf("'", 1);
|
|
68
|
+
out[key] = end === -1 ? value.slice(1) : value.slice(1, end);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const hash = value.search(/\s+#/);
|
|
72
|
+
out[key] = (hash === -1 ? value : value.slice(0, hash)).trim();
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
function parseIfExists(file) {
|
|
77
|
+
try {
|
|
78
|
+
if (!fs.existsSync(file))
|
|
79
|
+
return {};
|
|
80
|
+
return parseEnvText(fs.readFileSync(file, "utf8"));
|
|
81
|
+
} catch {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function loadCwdEnv(cwd = process.cwd()) {
|
|
86
|
+
const parsed = {
|
|
87
|
+
...parseIfExists(path.join(cwd, ".env")),
|
|
88
|
+
...parseIfExists(path.join(cwd, ".env.local"))
|
|
89
|
+
};
|
|
90
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
91
|
+
if (process.env[k] === undefined)
|
|
92
|
+
process.env[k] = v;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/index.ts
|
|
20
97
|
var HELP = `openpkg - extract TypeScript API specs and generate docs
|
|
21
98
|
|
|
22
99
|
Usage:
|
|
23
|
-
openpkg spec
|
|
24
|
-
openpkg docs
|
|
25
|
-
openpkg list
|
|
100
|
+
openpkg spec [path | entry.ts] [intent...] [-o spec.json] [--follow-external <pkg,...>]
|
|
101
|
+
openpkg docs [path | entry.ts | spec.json] [intent...] [-f md|html|json] [-o out]
|
|
102
|
+
openpkg list [path | entry.ts] [intent...] [--json]
|
|
26
103
|
openpkg validate <spec.json>
|
|
27
104
|
openpkg diff <old.json> <new.json> [--json]
|
|
28
105
|
|
|
29
106
|
Commands:
|
|
30
|
-
spec Extract an OpenPkg spec
|
|
31
|
-
docs Generate docs from
|
|
107
|
+
spec Extract an OpenPkg spec (dir, cwd, or entry file)
|
|
108
|
+
docs Generate docs from a package, entry point, or spec file
|
|
32
109
|
list List exports (name, kind, location)
|
|
33
110
|
validate Validate a spec file against the OpenPkg meta-schema
|
|
34
111
|
diff Compare two spec files and recommend a semver bump
|
|
@@ -38,32 +115,45 @@ Options:
|
|
|
38
115
|
-f, --format docs output format: md (default), html, json
|
|
39
116
|
--json list/diff output as JSON
|
|
40
117
|
--follow-external Expand types from these packages (comma-separated,
|
|
41
|
-
globs ok: "@ai-sdk/*"
|
|
118
|
+
globs ok: "@ai-sdk/*", or "auto" with --jev).
|
|
119
|
+
Default: stub externals.
|
|
42
120
|
--follow-external-all Expand every external package (use with care)
|
|
43
121
|
--only Only extract these exports (comma-separated, * ok)
|
|
44
122
|
--ignore Ignore these exports (comma-separated, * ok)
|
|
123
|
+
--jev Route package/entry with Jev (needs AI_GATEWAY_API_KEY)
|
|
45
124
|
-h, --help Show this help
|
|
46
125
|
-v, --version Show version
|
|
47
126
|
|
|
48
127
|
Config: reads openpkg.config.json (or package.json "openpkg" field) from the
|
|
49
|
-
cwd. Flags override the file. Example
|
|
50
|
-
{ "followExternal": ["@
|
|
128
|
+
cwd. Flags override the file. Example:
|
|
129
|
+
{ "followExternal": ["@ai-sdk/*"] }
|
|
130
|
+
{ "followExternal": "auto", "decisions": "jev" }
|
|
51
131
|
`;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
132
|
+
|
|
133
|
+
class CliError extends Error {
|
|
134
|
+
exitCode;
|
|
135
|
+
printed;
|
|
136
|
+
constructor(message, exitCode = 1, printed = false) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.exitCode = exitCode;
|
|
139
|
+
this.printed = printed;
|
|
140
|
+
this.name = "CliError";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function fail(message, exitCode = 1) {
|
|
144
|
+
throw new CliError(message, exitCode);
|
|
55
145
|
}
|
|
56
146
|
function write(content, output) {
|
|
57
147
|
if (output) {
|
|
58
|
-
|
|
59
|
-
|
|
148
|
+
fs2.mkdirSync(path2.dirname(path2.resolve(output)), { recursive: true });
|
|
149
|
+
fs2.writeFileSync(output, content);
|
|
60
150
|
console.error(`wrote ${output}`);
|
|
61
151
|
} else {
|
|
62
152
|
console.log(content);
|
|
63
153
|
}
|
|
64
154
|
}
|
|
65
155
|
function version() {
|
|
66
|
-
const pkg = JSON.parse(
|
|
156
|
+
const pkg = JSON.parse(fs2.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
67
157
|
return pkg.version;
|
|
68
158
|
}
|
|
69
159
|
function reportDiagnostics(diagnostics) {
|
|
@@ -73,7 +163,87 @@ function reportDiagnostics(diagnostics) {
|
|
|
73
163
|
}
|
|
74
164
|
}
|
|
75
165
|
if (diagnostics.some((d) => d.severity === "error")) {
|
|
76
|
-
|
|
166
|
+
throw new CliError("", 1, true);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function parseTargetArgs(positionals, cwd) {
|
|
170
|
+
if (!positionals.length)
|
|
171
|
+
return { input: cwd };
|
|
172
|
+
const first = positionals[0];
|
|
173
|
+
const rest = positionals.slice(1).join(" ").trim();
|
|
174
|
+
if (isRemoteInput(first)) {
|
|
175
|
+
return { input: first, ...rest ? { intent: rest } : {} };
|
|
176
|
+
}
|
|
177
|
+
const abs = path2.resolve(cwd, first);
|
|
178
|
+
if (fs2.existsSync(abs) || isPathLikeInput(first)) {
|
|
179
|
+
return { input: abs, ...rest ? { intent: rest } : {} };
|
|
180
|
+
}
|
|
181
|
+
return { input: cwd, intent: positionals.join(" ") };
|
|
182
|
+
}
|
|
183
|
+
function formatPackages(candidates, cwd) {
|
|
184
|
+
return candidates.map((c, i) => {
|
|
185
|
+
const rel = path2.relative(cwd, c.dir) || ".";
|
|
186
|
+
return ` ${i + 1}. ${c.name} ${rel}`;
|
|
187
|
+
}).join(`
|
|
188
|
+
`);
|
|
189
|
+
}
|
|
190
|
+
async function choosePackage(candidates, cwd) {
|
|
191
|
+
const body = `multiple packages — pick one:
|
|
192
|
+
${formatPackages(candidates, cwd)}`;
|
|
193
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
194
|
+
const hint = candidates[0]?.name.split("/").pop() ?? "sdk";
|
|
195
|
+
fail(`${body}
|
|
196
|
+
re-run with a path or intent, e.g. openpkg spec . ${hint}`);
|
|
197
|
+
}
|
|
198
|
+
console.error(body);
|
|
199
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
200
|
+
try {
|
|
201
|
+
const answer = await rl.question(`Package [1-${candidates.length}]: `);
|
|
202
|
+
const i = Number(answer.trim());
|
|
203
|
+
if (!Number.isInteger(i) || i < 1 || i > candidates.length)
|
|
204
|
+
fail("invalid selection");
|
|
205
|
+
return candidates[i - 1];
|
|
206
|
+
} finally {
|
|
207
|
+
rl.close();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function resolveCliTarget(positionals, decisions) {
|
|
211
|
+
loadCwdEnv();
|
|
212
|
+
const cwd = process.cwd();
|
|
213
|
+
const { input, intent } = parseTargetArgs(positionals, cwd);
|
|
214
|
+
const resolved = await resolveTarget({ input, intent, cwd, decisions });
|
|
215
|
+
const cleanup = resolved.cleanup;
|
|
216
|
+
try {
|
|
217
|
+
if (resolved.kind === "unavailable")
|
|
218
|
+
fail(resolved.reason);
|
|
219
|
+
if (resolved.kind === "empty")
|
|
220
|
+
fail(resolved.reason);
|
|
221
|
+
if (resolved.kind === "needs-build") {
|
|
222
|
+
fail(resolved.command ? `${resolved.reason}
|
|
223
|
+
→ ${resolved.command}` : resolved.reason, 2);
|
|
224
|
+
}
|
|
225
|
+
if (resolved.kind === "explicit") {
|
|
226
|
+
return {
|
|
227
|
+
entryFile: resolved.entryFile,
|
|
228
|
+
entryPointSource: resolved.entryPointSource,
|
|
229
|
+
cleanup
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (resolved.kind === "ok") {
|
|
233
|
+
return {
|
|
234
|
+
entryFile: resolved.entryFile,
|
|
235
|
+
entryPointSource: resolved.entryPointSource,
|
|
236
|
+
cleanup
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const chosen = await choosePackage(resolved.candidates, cwd);
|
|
240
|
+
const picked = pickEntry(chosen.dir);
|
|
241
|
+
if (!picked)
|
|
242
|
+
fail(`no TypeScript entry found in ${chosen.name}`);
|
|
243
|
+
return { ...picked, cleanup };
|
|
244
|
+
} catch (err) {
|
|
245
|
+
cleanup?.();
|
|
246
|
+
throw err;
|
|
77
247
|
}
|
|
78
248
|
}
|
|
79
249
|
function toList(value) {
|
|
@@ -82,6 +252,15 @@ function toList(value) {
|
|
|
82
252
|
const items = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
83
253
|
return items.length > 0 ? items : undefined;
|
|
84
254
|
}
|
|
255
|
+
function parseFollowExternal(value, all) {
|
|
256
|
+
if (all)
|
|
257
|
+
return true;
|
|
258
|
+
if (!value)
|
|
259
|
+
return;
|
|
260
|
+
if (value.trim() === "auto")
|
|
261
|
+
return "auto";
|
|
262
|
+
return toList(value);
|
|
263
|
+
}
|
|
85
264
|
function reportStubbedExternals(spec) {
|
|
86
265
|
const counts = new Map;
|
|
87
266
|
for (const t of spec.types ?? []) {
|
|
@@ -105,86 +284,112 @@ async function specCommand(args) {
|
|
|
105
284
|
"follow-external": { type: "string" },
|
|
106
285
|
"follow-external-all": { type: "boolean" },
|
|
107
286
|
only: { type: "string" },
|
|
108
|
-
ignore: { type: "string" }
|
|
287
|
+
ignore: { type: "string" },
|
|
288
|
+
jev: { type: "boolean" }
|
|
109
289
|
},
|
|
110
290
|
allowPositionals: true
|
|
111
291
|
});
|
|
112
|
-
const entryFile = positionals[0];
|
|
113
|
-
if (!entryFile)
|
|
114
|
-
fail("spec requires an entry file (openpkg spec src/index.ts)");
|
|
115
292
|
const fileConfig = loadConfig(process.cwd());
|
|
116
293
|
const cliConfig = {
|
|
117
|
-
followExternal: values["follow-external
|
|
294
|
+
followExternal: parseFollowExternal(values["follow-external"], values["follow-external-all"]),
|
|
118
295
|
only: toList(values.only),
|
|
119
|
-
ignore: toList(values.ignore)
|
|
296
|
+
ignore: toList(values.ignore),
|
|
297
|
+
...values.jev ? { decisions: "jev" } : {}
|
|
120
298
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
299
|
+
let cleanup;
|
|
300
|
+
try {
|
|
301
|
+
const resolved = await resolveCliTarget(positionals, cliConfig.decisions ?? fileConfig?.decisions);
|
|
302
|
+
cleanup = resolved.cleanup;
|
|
303
|
+
const { entryFile, entryPointSource } = resolved;
|
|
304
|
+
const config = mergeConfig(fileConfig, cliConfig);
|
|
305
|
+
if (config.followExternal === "auto" && config.decisions !== "jev") {
|
|
306
|
+
fail("followExternal auto requires --jev");
|
|
307
|
+
}
|
|
308
|
+
const { spec, diagnostics } = await extractSpec({
|
|
309
|
+
entryFile,
|
|
310
|
+
entryPointSource,
|
|
311
|
+
followExternal: config.followExternal,
|
|
312
|
+
only: config.only,
|
|
313
|
+
ignore: config.ignore,
|
|
314
|
+
externals: config.externals,
|
|
315
|
+
decisions: config.decisions
|
|
316
|
+
});
|
|
317
|
+
reportDiagnostics(diagnostics);
|
|
318
|
+
if (!config.followExternal)
|
|
319
|
+
reportStubbedExternals(spec);
|
|
320
|
+
write(JSON.stringify(spec, null, 2), values.output);
|
|
321
|
+
} finally {
|
|
322
|
+
cleanup?.();
|
|
323
|
+
}
|
|
133
324
|
}
|
|
134
325
|
async function docsCommand(args) {
|
|
135
326
|
const { values, positionals } = parseArgs({
|
|
136
327
|
args,
|
|
137
328
|
options: {
|
|
138
329
|
output: { type: "string", short: "o" },
|
|
139
|
-
format: { type: "string", short: "f" }
|
|
330
|
+
format: { type: "string", short: "f" },
|
|
331
|
+
jev: { type: "boolean" }
|
|
140
332
|
},
|
|
141
333
|
allowPositionals: true
|
|
142
334
|
});
|
|
143
|
-
const input = positionals[0];
|
|
144
|
-
if (!input)
|
|
145
|
-
fail("docs requires an entry file or spec file (openpkg docs src/index.ts)");
|
|
146
335
|
const format = values.format ?? "md";
|
|
147
336
|
if (!["md", "html", "json"].includes(format))
|
|
148
337
|
fail(`unknown format "${format}" (md|html|json)`);
|
|
149
338
|
let docs;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
339
|
+
let cleanup;
|
|
340
|
+
try {
|
|
341
|
+
if (positionals[0]?.endsWith(".json")) {
|
|
342
|
+
docs = createDocs(positionals[0]);
|
|
343
|
+
} else {
|
|
344
|
+
const decisions = values.jev ? "jev" : loadConfig(process.cwd())?.decisions;
|
|
345
|
+
const resolved = await resolveCliTarget(positionals, decisions);
|
|
346
|
+
cleanup = resolved.cleanup;
|
|
347
|
+
const { spec, diagnostics } = await extractSpec({
|
|
348
|
+
entryFile: resolved.entryFile,
|
|
349
|
+
entryPointSource: resolved.entryPointSource
|
|
350
|
+
});
|
|
351
|
+
reportDiagnostics(diagnostics);
|
|
352
|
+
docs = createDocs(spec);
|
|
353
|
+
}
|
|
354
|
+
const content = format === "md" ? docs.toMarkdown() : format === "html" ? docs.toHTML() : JSON.stringify(docs.toJSON(), null, 2);
|
|
355
|
+
write(content, values.output);
|
|
356
|
+
} finally {
|
|
357
|
+
cleanup?.();
|
|
156
358
|
}
|
|
157
|
-
const content = format === "md" ? docs.toMarkdown() : format === "html" ? docs.toHTML() : JSON.stringify(docs.toJSON(), null, 2);
|
|
158
|
-
write(content, values.output);
|
|
159
359
|
}
|
|
160
360
|
async function listCommand(args) {
|
|
161
361
|
const { values, positionals } = parseArgs({
|
|
162
362
|
args,
|
|
163
|
-
options: { json: { type: "boolean" } },
|
|
363
|
+
options: { json: { type: "boolean" }, jev: { type: "boolean" } },
|
|
164
364
|
allowPositionals: true
|
|
165
365
|
});
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
366
|
+
const decisions = values.jev ? "jev" : loadConfig(process.cwd())?.decisions;
|
|
367
|
+
let cleanup;
|
|
368
|
+
try {
|
|
369
|
+
const resolved = await resolveCliTarget(positionals, decisions);
|
|
370
|
+
cleanup = resolved.cleanup;
|
|
371
|
+
const { exports, errors } = await listExports({ entryFile: resolved.entryFile });
|
|
372
|
+
for (const err of errors) {
|
|
373
|
+
console.error(`error: ${err}`);
|
|
374
|
+
}
|
|
375
|
+
if (errors.length > 0 && exports.length === 0) {
|
|
376
|
+
throw new CliError("", 1, true);
|
|
377
|
+
}
|
|
378
|
+
if (values.json) {
|
|
379
|
+
console.log(JSON.stringify(exports, null, 2));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
for (const exp of exports) {
|
|
383
|
+
const location = exp.file ? ` (${exp.file}:${exp.line})` : "";
|
|
384
|
+
console.log(`${exp.kind.padEnd(10)}${exp.name}${location}`);
|
|
385
|
+
}
|
|
386
|
+
} finally {
|
|
387
|
+
cleanup?.();
|
|
183
388
|
}
|
|
184
389
|
}
|
|
185
390
|
function readSpecFile(file) {
|
|
186
391
|
try {
|
|
187
|
-
return JSON.parse(
|
|
392
|
+
return JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
188
393
|
} catch (err) {
|
|
189
394
|
fail(`failed to read spec file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
190
395
|
}
|
|
@@ -220,7 +425,7 @@ function validateCommand(args) {
|
|
|
220
425
|
for (const e of errors) {
|
|
221
426
|
console.error(`${e.instancePath || "/"} ${e.message}`);
|
|
222
427
|
}
|
|
223
|
-
|
|
428
|
+
throw new CliError("", 1, true);
|
|
224
429
|
}
|
|
225
430
|
function diffCommand(args) {
|
|
226
431
|
const { values, positionals } = parseArgs({
|
|
@@ -302,5 +507,11 @@ async function main() {
|
|
|
302
507
|
}
|
|
303
508
|
}
|
|
304
509
|
main().catch((err) => {
|
|
305
|
-
|
|
510
|
+
if (err instanceof CliError) {
|
|
511
|
+
if (!err.printed && err.message)
|
|
512
|
+
console.error(`error: ${err.message}`);
|
|
513
|
+
process.exit(err.exitCode);
|
|
514
|
+
}
|
|
515
|
+
console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
516
|
+
process.exit(1);
|
|
306
517
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "CLI for OpenPkg - extract TypeScript API specs and generate docs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openpkg",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"lint": "biome check src/",
|
|
32
32
|
"lint:fix": "biome check --write src/",
|
|
33
33
|
"format": "biome format --write src/",
|
|
34
|
-
"typecheck": "tsc --noEmit -p .",
|
|
34
|
+
"typecheck": "tsc --noEmit --types bun -p .",
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@openpkg-ts/sdk": "^0.
|
|
38
|
+
"@openpkg-ts/sdk": "^0.52.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/bun": "latest",
|