@openpkg-ts/cli 0.6.1 → 0.6.3
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/dist/bin/openpkg.js +177 -178
- package/dist/shared/chunk-3s189drz.js +4 -0
- package/dist/src/index.d.ts +40 -1
- package/dist/src/index.js +1 -1
- package/package.json +3 -3
- package/dist/shared/chunk-1dqs11h6.js +0 -20
package/dist/bin/openpkg.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import {
|
|
3
|
-
__require
|
|
4
|
-
|
|
5
|
-
} from "../shared/chunk-1dqs11h6.js";
|
|
3
|
+
__require
|
|
4
|
+
} from "../shared/chunk-3s189drz.js";
|
|
6
5
|
|
|
7
6
|
// bin/openpkg.ts
|
|
8
7
|
import { Command as Command12 } from "commander";
|
|
9
8
|
// package.json
|
|
10
9
|
var package_default = {
|
|
11
10
|
name: "@openpkg-ts/cli",
|
|
12
|
-
version: "0.6.
|
|
11
|
+
version: "0.6.3",
|
|
13
12
|
description: "CLI for OpenPkg TypeScript API extraction and documentation generation",
|
|
14
13
|
homepage: "https://github.com/ryanwaits/openpkg-ts#readme",
|
|
15
14
|
repository: {
|
|
@@ -32,8 +31,8 @@ var package_default = {
|
|
|
32
31
|
test: "bun test"
|
|
33
32
|
},
|
|
34
33
|
dependencies: {
|
|
35
|
-
"@openpkg-ts/adapters": "^0.3.
|
|
36
|
-
"@openpkg-ts/sdk": "^0.
|
|
34
|
+
"@openpkg-ts/adapters": "^0.3.14",
|
|
35
|
+
"@openpkg-ts/sdk": "^0.35.1",
|
|
37
36
|
commander: "^14.0.0"
|
|
38
37
|
},
|
|
39
38
|
devDependencies: {
|
|
@@ -47,18 +46,66 @@ var package_default = {
|
|
|
47
46
|
};
|
|
48
47
|
|
|
49
48
|
// src/commands/breaking.ts
|
|
49
|
+
import { categorizeBreakingChanges, diffSpec } from "@openpkg-ts/spec";
|
|
50
|
+
import { Command } from "commander";
|
|
51
|
+
|
|
52
|
+
// src/commands/utils.ts
|
|
50
53
|
import * as fs from "node:fs";
|
|
51
54
|
import * as path from "node:path";
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
55
|
+
import { getValidationErrors } from "@openpkg-ts/spec";
|
|
56
|
+
function handleCommandError(err) {
|
|
57
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
58
|
+
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
async function loadSpecInput(specPath) {
|
|
62
|
+
if (specPath === "-") {
|
|
63
|
+
const chunks = [];
|
|
64
|
+
for await (const chunk of process.stdin) {
|
|
65
|
+
chunks.push(chunk);
|
|
66
|
+
}
|
|
67
|
+
const input = Buffer.concat(chunks).toString("utf-8");
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(input);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
72
|
+
throw new Error(`Invalid JSON in stdin: ${msg}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const resolved = path.resolve(specPath);
|
|
76
|
+
if (!fs.existsSync(resolved)) {
|
|
77
|
+
throw new Error(`Spec file not found: ${resolved}`);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(fs.readFileSync(resolved, "utf-8"));
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
83
|
+
throw new Error(`Invalid JSON in ${specPath}: ${msg}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
57
86
|
function loadSpec(filePath) {
|
|
58
87
|
const resolved = path.resolve(filePath);
|
|
59
|
-
|
|
60
|
-
|
|
88
|
+
let content;
|
|
89
|
+
let spec;
|
|
90
|
+
try {
|
|
91
|
+
content = fs.readFileSync(resolved, "utf-8");
|
|
92
|
+
} catch (err) {
|
|
93
|
+
throw new Error(`Failed to read spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
spec = JSON.parse(content);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
throw new Error(`Invalid JSON in spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
}
|
|
100
|
+
const errors = getValidationErrors(spec);
|
|
101
|
+
if (errors.length > 0) {
|
|
102
|
+
const details = errors.slice(0, 5).map((e) => `${e.instancePath || "/"}: ${e.message}`).join("; ");
|
|
103
|
+
throw new Error(`Invalid OpenPkg spec: ${details}`);
|
|
104
|
+
}
|
|
105
|
+
return spec;
|
|
61
106
|
}
|
|
107
|
+
|
|
108
|
+
// src/commands/breaking.ts
|
|
62
109
|
function createBreakingCommand() {
|
|
63
110
|
return new Command("breaking").description("Check for breaking changes between two specs").argument("<old>", "Path to old spec file (JSON)").argument("<new>", "Path to new spec file (JSON)").action(async (oldPath, newPath) => {
|
|
64
111
|
try {
|
|
@@ -75,32 +122,21 @@ function createBreakingCommand() {
|
|
|
75
122
|
process.exit(1);
|
|
76
123
|
}
|
|
77
124
|
} catch (err) {
|
|
78
|
-
|
|
79
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
80
|
-
process.exit(1);
|
|
125
|
+
handleCommandError(err);
|
|
81
126
|
}
|
|
82
127
|
});
|
|
83
128
|
}
|
|
84
129
|
|
|
85
130
|
// src/commands/changelog.ts
|
|
86
|
-
import * as fs3 from "node:fs";
|
|
87
|
-
import * as path3 from "node:path";
|
|
88
131
|
import { Command as Command3 } from "commander";
|
|
89
132
|
|
|
90
133
|
// src/commands/diff.ts
|
|
91
|
-
import * as fs2 from "node:fs";
|
|
92
|
-
import * as path2 from "node:path";
|
|
93
134
|
import {
|
|
94
135
|
categorizeBreakingChanges as categorizeBreakingChanges2,
|
|
95
136
|
diffSpec as diffSpec2,
|
|
96
137
|
recommendSemverBump
|
|
97
138
|
} from "@openpkg-ts/spec";
|
|
98
139
|
import { Command as Command2 } from "commander";
|
|
99
|
-
function loadSpec2(filePath) {
|
|
100
|
-
const resolved = path2.resolve(filePath);
|
|
101
|
-
const content = fs2.readFileSync(resolved, "utf-8");
|
|
102
|
-
return JSON.parse(content);
|
|
103
|
-
}
|
|
104
140
|
function toExportMap(spec) {
|
|
105
141
|
const map = new Map;
|
|
106
142
|
for (const exp of spec.exports) {
|
|
@@ -178,8 +214,8 @@ function describeChange(cat) {
|
|
|
178
214
|
function createDiffCommand() {
|
|
179
215
|
return new Command2("diff").description("Compare two OpenPkg specs and show differences").argument("<old>", "Path to old spec file (JSON)").argument("<new>", "Path to new spec file (JSON)").option("--json", "Output as JSON (default)").option("--summary", "Only show summary").action(async (oldPath, newPath, options) => {
|
|
180
216
|
try {
|
|
181
|
-
const oldSpec =
|
|
182
|
-
const newSpec =
|
|
217
|
+
const oldSpec = loadSpec(oldPath);
|
|
218
|
+
const newSpec = loadSpec(newPath);
|
|
183
219
|
const result = enrichDiff(oldSpec, newSpec);
|
|
184
220
|
if (options.summary) {
|
|
185
221
|
console.log(JSON.stringify(result.summary, null, 2));
|
|
@@ -187,19 +223,12 @@ function createDiffCommand() {
|
|
|
187
223
|
console.log(JSON.stringify(result, null, 2));
|
|
188
224
|
}
|
|
189
225
|
} catch (err) {
|
|
190
|
-
|
|
191
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
192
|
-
process.exit(1);
|
|
226
|
+
handleCommandError(err);
|
|
193
227
|
}
|
|
194
228
|
});
|
|
195
229
|
}
|
|
196
230
|
|
|
197
231
|
// src/commands/changelog.ts
|
|
198
|
-
function loadSpec3(filePath) {
|
|
199
|
-
const resolved = path3.resolve(filePath);
|
|
200
|
-
const content = fs3.readFileSync(resolved, "utf-8");
|
|
201
|
-
return JSON.parse(content);
|
|
202
|
-
}
|
|
203
232
|
function formatMarkdown(diff) {
|
|
204
233
|
const lines = [];
|
|
205
234
|
if (diff.removed.length > 0 || diff.changed.length > 0) {
|
|
@@ -235,8 +264,8 @@ function formatMarkdown(diff) {
|
|
|
235
264
|
function createChangelogCommand() {
|
|
236
265
|
return new Command3("changelog").description("Generate changelog from diff between two specs").argument("<old>", "Path to old spec file (JSON)").argument("<new>", "Path to new spec file (JSON)").option("--format <format>", "Output format: md or json", "md").action(async (oldPath, newPath, options) => {
|
|
237
266
|
try {
|
|
238
|
-
const oldSpec =
|
|
239
|
-
const newSpec =
|
|
267
|
+
const oldSpec = loadSpec(oldPath);
|
|
268
|
+
const newSpec = loadSpec(newPath);
|
|
240
269
|
const diff = enrichDiff(oldSpec, newSpec);
|
|
241
270
|
if (options.format === "json") {
|
|
242
271
|
console.log(JSON.stringify(diff, null, 2));
|
|
@@ -244,9 +273,7 @@ function createChangelogCommand() {
|
|
|
244
273
|
console.log(formatMarkdown(diff));
|
|
245
274
|
}
|
|
246
275
|
} catch (err) {
|
|
247
|
-
|
|
248
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
249
|
-
process.exit(1);
|
|
276
|
+
handleCommandError(err);
|
|
250
277
|
}
|
|
251
278
|
});
|
|
252
279
|
}
|
|
@@ -259,13 +286,13 @@ import { spawn } from "node:child_process";
|
|
|
259
286
|
import { Command as Command4 } from "commander";
|
|
260
287
|
|
|
261
288
|
// src/commands/docs/utils.ts
|
|
262
|
-
import * as
|
|
263
|
-
import * as
|
|
289
|
+
import * as fs2 from "node:fs";
|
|
290
|
+
import * as path2 from "node:path";
|
|
264
291
|
function detectPackageManager(cwd = process.cwd()) {
|
|
265
|
-
const pkgJsonPath =
|
|
266
|
-
if (
|
|
292
|
+
const pkgJsonPath = path2.join(cwd, "package.json");
|
|
293
|
+
if (fs2.existsSync(pkgJsonPath)) {
|
|
267
294
|
try {
|
|
268
|
-
const pkg = JSON.parse(
|
|
295
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
|
|
269
296
|
if (pkg.packageManager) {
|
|
270
297
|
if (pkg.packageManager.startsWith("bun"))
|
|
271
298
|
return "bun";
|
|
@@ -278,13 +305,13 @@ function detectPackageManager(cwd = process.cwd()) {
|
|
|
278
305
|
}
|
|
279
306
|
} catch {}
|
|
280
307
|
}
|
|
281
|
-
if (
|
|
308
|
+
if (fs2.existsSync(path2.join(cwd, "bun.lockb")) || fs2.existsSync(path2.join(cwd, "bun.lock"))) {
|
|
282
309
|
return "bun";
|
|
283
310
|
}
|
|
284
|
-
if (
|
|
311
|
+
if (fs2.existsSync(path2.join(cwd, "pnpm-lock.yaml"))) {
|
|
285
312
|
return "pnpm";
|
|
286
313
|
}
|
|
287
|
-
if (
|
|
314
|
+
if (fs2.existsSync(path2.join(cwd, "yarn.lock"))) {
|
|
288
315
|
return "yarn";
|
|
289
316
|
}
|
|
290
317
|
return "npm";
|
|
@@ -339,21 +366,10 @@ function createAddCommand() {
|
|
|
339
366
|
}
|
|
340
367
|
|
|
341
368
|
// src/commands/docs/generate.ts
|
|
342
|
-
import * as
|
|
343
|
-
import * as
|
|
344
|
-
import { loadSpec as
|
|
369
|
+
import * as fs3 from "node:fs";
|
|
370
|
+
import * as path3 from "node:path";
|
|
371
|
+
import { loadSpec as loadSpec2, query, toReact } from "@openpkg-ts/sdk";
|
|
345
372
|
import { Command as Command5 } from "commander";
|
|
346
|
-
async function readStdin() {
|
|
347
|
-
try {
|
|
348
|
-
const chunks = [];
|
|
349
|
-
for await (const chunk of process.stdin) {
|
|
350
|
-
chunks.push(chunk);
|
|
351
|
-
}
|
|
352
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
353
|
-
} catch (err) {
|
|
354
|
-
throw new Error(`stdin read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
373
|
function getExtension(format) {
|
|
358
374
|
switch (format) {
|
|
359
375
|
case "json":
|
|
@@ -366,14 +382,33 @@ function getExtension(format) {
|
|
|
366
382
|
return ".md";
|
|
367
383
|
}
|
|
368
384
|
}
|
|
385
|
+
var VALID_KINDS = [
|
|
386
|
+
"function",
|
|
387
|
+
"class",
|
|
388
|
+
"variable",
|
|
389
|
+
"interface",
|
|
390
|
+
"type",
|
|
391
|
+
"enum",
|
|
392
|
+
"module",
|
|
393
|
+
"namespace",
|
|
394
|
+
"reference",
|
|
395
|
+
"external"
|
|
396
|
+
];
|
|
369
397
|
function applyFilters(spec, options) {
|
|
370
398
|
let qb = query(spec);
|
|
371
399
|
if (options.kind) {
|
|
372
|
-
const kinds = options.kind.split(",").map((k) => k.trim());
|
|
400
|
+
const kinds = options.kind.split(",").map((k) => k.trim()).filter(Boolean);
|
|
401
|
+
const invalid = kinds.filter((k) => !VALID_KINDS.includes(k));
|
|
402
|
+
if (invalid.length) {
|
|
403
|
+
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid: ${VALID_KINDS.join(", ")}`);
|
|
404
|
+
}
|
|
373
405
|
qb = qb.byKind(...kinds);
|
|
374
406
|
}
|
|
375
407
|
if (options.tag) {
|
|
376
|
-
const tags = options.tag.split(",").map((t) => t.trim());
|
|
408
|
+
const tags = options.tag.split(",").map((t) => t.trim()).filter(Boolean);
|
|
409
|
+
if (tags.length === 0) {
|
|
410
|
+
throw new Error("--tag requires at least one non-empty tag");
|
|
411
|
+
}
|
|
377
412
|
qb = qb.byTag(...tags);
|
|
378
413
|
}
|
|
379
414
|
if (options.search) {
|
|
@@ -418,6 +453,13 @@ function createGenerateCommand() {
|
|
|
418
453
|
return new Command5("generate").description("Generate documentation from OpenPkg spec").argument("<spec>", "Path to openpkg.json spec file (use - for stdin)").option("-o, --output <path>", "Output file or directory (default: stdout)").option("-f, --format <format>", "Output format: md, json, html, react (default: md)", "md").option("--split", "Output one file per export (requires --output as directory)").option("-e, --export <name>", "Generate docs for a single export by name").option("-a, --adapter <name>", "Use adapter for generation (default: raw)").option("--collapse-unions <n>", "Collapse unions with more than N members").option("-k, --kind <kinds>", "Filter by kind(s), comma-separated").option("-t, --tag <tags>", "Filter by tag(s), comma-separated").option("-s, --search <term>", "Search name and description").option("--deprecated", "Only include deprecated exports").option("--no-deprecated", "Exclude deprecated exports").option("--variant <variant>", "React layout variant: full (single page) or index (links)", "full").option("--components-path <path>", "React components import path", "@/components/api").action(async (specPath, options) => {
|
|
419
454
|
const format = options.format || "md";
|
|
420
455
|
try {
|
|
456
|
+
if (options.collapseUnions) {
|
|
457
|
+
const n = parseInt(options.collapseUnions, 10);
|
|
458
|
+
if (Number.isNaN(n) || n < 1) {
|
|
459
|
+
console.error(JSON.stringify({ error: "--collapse-unions must be a positive integer" }));
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
421
463
|
if (options.adapter && options.adapter !== "raw") {
|
|
422
464
|
let getAdapter;
|
|
423
465
|
try {
|
|
@@ -437,37 +479,15 @@ function createGenerateCommand() {
|
|
|
437
479
|
console.error(JSON.stringify({ error: "--adapter requires --output <directory>" }));
|
|
438
480
|
process.exit(1);
|
|
439
481
|
}
|
|
440
|
-
let spec2;
|
|
441
|
-
if (specPath === "-") {
|
|
442
|
-
const input = await readStdin();
|
|
443
|
-
spec2 = JSON.parse(input);
|
|
444
|
-
} else {
|
|
445
|
-
const specFile = path5.resolve(specPath);
|
|
446
|
-
if (!fs5.existsSync(specFile)) {
|
|
447
|
-
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
448
|
-
process.exit(1);
|
|
449
|
-
}
|
|
450
|
-
spec2 = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
451
|
-
}
|
|
482
|
+
let spec2 = await loadSpecInput(specPath);
|
|
452
483
|
spec2 = applyFilters(spec2, options);
|
|
453
|
-
await adapter.generate(spec2,
|
|
484
|
+
await adapter.generate(spec2, path3.resolve(options.output));
|
|
454
485
|
console.error(`Generated docs with ${options.adapter} adapter to ${options.output}`);
|
|
455
486
|
return;
|
|
456
487
|
}
|
|
457
|
-
let spec;
|
|
458
|
-
if (specPath === "-") {
|
|
459
|
-
const input = await readStdin();
|
|
460
|
-
spec = JSON.parse(input);
|
|
461
|
-
} else {
|
|
462
|
-
const specFile = path5.resolve(specPath);
|
|
463
|
-
if (!fs5.existsSync(specFile)) {
|
|
464
|
-
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
465
|
-
process.exit(1);
|
|
466
|
-
}
|
|
467
|
-
spec = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
468
|
-
}
|
|
488
|
+
let spec = await loadSpecInput(specPath);
|
|
469
489
|
spec = applyFilters(spec, options);
|
|
470
|
-
const docs =
|
|
490
|
+
const docs = loadSpec2(spec);
|
|
471
491
|
const collapseUnionThreshold = options.collapseUnions ? parseInt(options.collapseUnions, 10) : undefined;
|
|
472
492
|
if (format === "react") {
|
|
473
493
|
if (!options.output) {
|
|
@@ -476,7 +496,7 @@ function createGenerateCommand() {
|
|
|
476
496
|
}
|
|
477
497
|
const variant = options.variant === "index" ? "index" : "full";
|
|
478
498
|
await toReact(spec, {
|
|
479
|
-
outDir:
|
|
499
|
+
outDir: path3.resolve(options.output),
|
|
480
500
|
variant,
|
|
481
501
|
componentsPath: options.componentsPath ?? "@/components/api"
|
|
482
502
|
});
|
|
@@ -496,8 +516,8 @@ Next: Add components with 'openpkg docs add function-section'`);
|
|
|
496
516
|
}
|
|
497
517
|
const output2 = renderExport(docs, exp.id, format, collapseUnionThreshold);
|
|
498
518
|
if (options.output && options.output !== "-") {
|
|
499
|
-
const outputPath =
|
|
500
|
-
|
|
519
|
+
const outputPath = path3.resolve(options.output);
|
|
520
|
+
fs3.writeFileSync(outputPath, output2);
|
|
501
521
|
console.error(`Wrote ${outputPath}`);
|
|
502
522
|
} else {
|
|
503
523
|
console.log(output2);
|
|
@@ -509,59 +529,61 @@ Next: Add components with 'openpkg docs add function-section'`);
|
|
|
509
529
|
console.error(JSON.stringify({ error: "--split requires --output <directory>" }));
|
|
510
530
|
process.exit(1);
|
|
511
531
|
}
|
|
512
|
-
const outDir =
|
|
513
|
-
if (!
|
|
514
|
-
|
|
532
|
+
const outDir = path3.resolve(options.output);
|
|
533
|
+
if (!fs3.existsSync(outDir)) {
|
|
534
|
+
fs3.mkdirSync(outDir, { recursive: true });
|
|
515
535
|
}
|
|
516
536
|
const exports = docs.getAllExports();
|
|
517
537
|
for (const exp of exports) {
|
|
518
|
-
const filename =
|
|
519
|
-
const filePath =
|
|
520
|
-
const resolvedPath =
|
|
521
|
-
const resolvedOutDir =
|
|
522
|
-
if (!resolvedPath.startsWith(resolvedOutDir +
|
|
538
|
+
const filename = path3.basename(`${exp.name}${getExtension(format)}`);
|
|
539
|
+
const filePath = path3.join(outDir, filename);
|
|
540
|
+
const resolvedPath = path3.resolve(filePath);
|
|
541
|
+
const resolvedOutDir = path3.resolve(outDir);
|
|
542
|
+
if (!resolvedPath.startsWith(resolvedOutDir + path3.sep)) {
|
|
523
543
|
console.error(JSON.stringify({ error: `Path traversal detected: ${exp.name}` }));
|
|
524
544
|
process.exit(1);
|
|
525
545
|
}
|
|
526
546
|
const content = renderExport(docs, exp.id, format, collapseUnionThreshold);
|
|
527
|
-
|
|
547
|
+
fs3.writeFileSync(filePath, content);
|
|
528
548
|
}
|
|
529
549
|
console.error(`Wrote ${exports.length} files to ${outDir}`);
|
|
530
550
|
return;
|
|
531
551
|
}
|
|
532
552
|
const output = renderFull(docs, format, collapseUnionThreshold);
|
|
533
553
|
if (options.output && options.output !== "-") {
|
|
534
|
-
const outputPath =
|
|
535
|
-
|
|
554
|
+
const outputPath = path3.resolve(options.output);
|
|
555
|
+
fs3.writeFileSync(outputPath, output);
|
|
536
556
|
console.error(`Wrote ${outputPath}`);
|
|
537
557
|
} else {
|
|
538
558
|
console.log(output);
|
|
539
559
|
}
|
|
540
560
|
} catch (err) {
|
|
541
|
-
|
|
542
|
-
console.error(JSON.stringify({ error: error.message }));
|
|
543
|
-
process.exit(1);
|
|
561
|
+
handleCommandError(err);
|
|
544
562
|
}
|
|
545
563
|
});
|
|
546
564
|
}
|
|
547
565
|
|
|
548
566
|
// src/commands/docs/init.ts
|
|
549
|
-
import * as
|
|
550
|
-
import * as
|
|
567
|
+
import * as fs4 from "node:fs";
|
|
568
|
+
import * as path4 from "node:path";
|
|
551
569
|
import { Command as Command6 } from "commander";
|
|
552
570
|
var COMPONENTS_JSON = "components.json";
|
|
553
571
|
var REGISTRY_URL = "https://raw.githubusercontent.com/anthropics/openpkg-ts/main/registry/r/{name}.json";
|
|
554
572
|
function loadComponentsJson() {
|
|
555
|
-
const configPath =
|
|
556
|
-
if (!
|
|
573
|
+
const configPath = path4.resolve(COMPONENTS_JSON);
|
|
574
|
+
if (!fs4.existsSync(configPath))
|
|
575
|
+
return null;
|
|
576
|
+
try {
|
|
577
|
+
return JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
578
|
+
} catch {
|
|
557
579
|
return null;
|
|
558
|
-
|
|
580
|
+
}
|
|
559
581
|
}
|
|
560
582
|
function createInitCommand() {
|
|
561
583
|
return new Command6("init").description("Add @openpkg registry to components.json for shadcn CLI").option("--registry <url>", "Custom registry URL", REGISTRY_URL).action(async (options) => {
|
|
562
|
-
const configPath =
|
|
584
|
+
const configPath = path4.resolve(COMPONENTS_JSON);
|
|
563
585
|
const registryUrl = options.registry || REGISTRY_URL;
|
|
564
|
-
if (!
|
|
586
|
+
if (!fs4.existsSync(configPath)) {
|
|
565
587
|
console.error(`${COMPONENTS_JSON} not found.`);
|
|
566
588
|
console.error('Run "npx shadcn@latest init" first to initialize shadcn.');
|
|
567
589
|
process.exit(1);
|
|
@@ -573,7 +595,7 @@ function createInitCommand() {
|
|
|
573
595
|
}
|
|
574
596
|
config.registries = config.registries || {};
|
|
575
597
|
config.registries["@openpkg"] = registryUrl;
|
|
576
|
-
|
|
598
|
+
fs4.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
577
599
|
console.log(`Added @openpkg registry to ${COMPONENTS_JSON}`);
|
|
578
600
|
console.log("");
|
|
579
601
|
console.log("Usage:");
|
|
@@ -640,20 +662,13 @@ function createDocsCommand() {
|
|
|
640
662
|
}
|
|
641
663
|
|
|
642
664
|
// src/commands/semver.ts
|
|
643
|
-
import * as fs7 from "node:fs";
|
|
644
|
-
import * as path7 from "node:path";
|
|
645
665
|
import { diffSpec as diffSpec3, recommendSemverBump as recommendSemverBump2 } from "@openpkg-ts/spec";
|
|
646
666
|
import { Command as Command10 } from "commander";
|
|
647
|
-
function loadSpec5(filePath) {
|
|
648
|
-
const resolved = path7.resolve(filePath);
|
|
649
|
-
const content = fs7.readFileSync(resolved, "utf-8");
|
|
650
|
-
return JSON.parse(content);
|
|
651
|
-
}
|
|
652
667
|
function createSemverCommand() {
|
|
653
668
|
return new Command10("semver").description("Recommend semver bump based on spec changes").argument("<old>", "Path to old spec file (JSON)").argument("<new>", "Path to new spec file (JSON)").action(async (oldPath, newPath) => {
|
|
654
669
|
try {
|
|
655
|
-
const oldSpec =
|
|
656
|
-
const newSpec =
|
|
670
|
+
const oldSpec = loadSpec(oldPath);
|
|
671
|
+
const newSpec = loadSpec(newPath);
|
|
657
672
|
const diff = diffSpec3(oldSpec, newSpec);
|
|
658
673
|
const recommendation = recommendSemverBump2(diff);
|
|
659
674
|
const result = {
|
|
@@ -662,16 +677,14 @@ function createSemverCommand() {
|
|
|
662
677
|
};
|
|
663
678
|
console.log(JSON.stringify(result, null, 2));
|
|
664
679
|
} catch (err) {
|
|
665
|
-
|
|
666
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
667
|
-
process.exit(1);
|
|
680
|
+
handleCommandError(err);
|
|
668
681
|
}
|
|
669
682
|
});
|
|
670
683
|
}
|
|
671
684
|
|
|
672
685
|
// src/commands/spec.ts
|
|
673
|
-
import * as
|
|
674
|
-
import * as
|
|
686
|
+
import * as fs5 from "node:fs";
|
|
687
|
+
import * as path5 from "node:path";
|
|
675
688
|
import {
|
|
676
689
|
analyzeSpec,
|
|
677
690
|
extractSpec,
|
|
@@ -681,9 +694,9 @@ import {
|
|
|
681
694
|
loadConfig,
|
|
682
695
|
mergeConfig
|
|
683
696
|
} from "@openpkg-ts/sdk";
|
|
684
|
-
import { getValidationErrors } from "@openpkg-ts/spec";
|
|
697
|
+
import { getValidationErrors as getValidationErrors2 } from "@openpkg-ts/spec";
|
|
685
698
|
import { Command as Command11 } from "commander";
|
|
686
|
-
var
|
|
699
|
+
var VALID_KINDS2 = [
|
|
687
700
|
"function",
|
|
688
701
|
"class",
|
|
689
702
|
"variable",
|
|
@@ -695,36 +708,15 @@ var VALID_KINDS = [
|
|
|
695
708
|
"reference",
|
|
696
709
|
"external"
|
|
697
710
|
];
|
|
698
|
-
function loadSpec6(filePath) {
|
|
699
|
-
const resolved = path8.resolve(filePath);
|
|
700
|
-
let content;
|
|
701
|
-
let spec;
|
|
702
|
-
try {
|
|
703
|
-
content = fs8.readFileSync(resolved, "utf-8");
|
|
704
|
-
} catch (err) {
|
|
705
|
-
throw new Error(`Failed to read spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
706
|
-
}
|
|
707
|
-
try {
|
|
708
|
-
spec = JSON.parse(content);
|
|
709
|
-
} catch (err) {
|
|
710
|
-
throw new Error(`Invalid JSON in spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
711
|
-
}
|
|
712
|
-
const errors = getValidationErrors(spec);
|
|
713
|
-
if (errors.length > 0) {
|
|
714
|
-
const details = errors.slice(0, 5).map((e) => `${e.instancePath || "/"}: ${e.message}`).join("; ");
|
|
715
|
-
throw new Error(`Invalid OpenPkg spec: ${details}`);
|
|
716
|
-
}
|
|
717
|
-
return spec;
|
|
718
|
-
}
|
|
719
711
|
function parseList(val) {
|
|
720
712
|
if (!val)
|
|
721
713
|
return;
|
|
722
714
|
return val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
723
715
|
}
|
|
724
716
|
function validateKinds(kinds) {
|
|
725
|
-
const invalid = kinds.filter((k) => !
|
|
717
|
+
const invalid = kinds.filter((k) => !VALID_KINDS2.includes(k));
|
|
726
718
|
if (invalid.length > 0) {
|
|
727
|
-
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${
|
|
719
|
+
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${VALID_KINDS2.join(", ")}`);
|
|
728
720
|
}
|
|
729
721
|
return kinds;
|
|
730
722
|
}
|
|
@@ -738,9 +730,9 @@ function formatDiagnostics(diagnostics) {
|
|
|
738
730
|
}));
|
|
739
731
|
}
|
|
740
732
|
function createSnapshotSubcommand() {
|
|
741
|
-
return new Command11("snapshot").description("Generate full OpenPkg spec from TypeScript entry point").argument("<entry>", "Entry point file path").option("-o, --output <file>", "Output file (default: openpkg.json)", "openpkg.json").option("--max-depth <n>", "Max type depth (default: 4)", "4").option("--skip-resolve", "Skip external type resolution").option("--runtime", "Enable Standard Schema runtime extraction").option("--only <exports>", "Filter exports (comma-separated)").option("--ignore <exports>", "Ignore exports (comma-separated)").option("--verify", "Exit 1 if any exports fail").option("--verbose", "Show detailed output").option("--include-private", "Include private/protected class members").option("--external-include <patterns...>", "Resolve re-exports from these packages").option("--external-exclude <patterns...>", "Never resolve from these packages").option("--external-depth <n>", "Max transitive depth for external resolution", "1").action(async (entry, options) => {
|
|
742
|
-
const entryFile =
|
|
743
|
-
const entryDir =
|
|
733
|
+
return new Command11("snapshot").description("Generate full OpenPkg spec from TypeScript entry point").argument("<entry>", "Entry point file path").option("-o, --output <file>", "Output file (default: openpkg.json)", "openpkg.json").option("--max-depth <n>", "Max type depth (default: 4)", "4").option("--skip-resolve", "Skip external type resolution").option("--runtime", "Enable Standard Schema runtime extraction").option("--only <exports>", "Filter exports (comma-separated)").option("--ignore <exports>", "Ignore exports (comma-separated)").option("--verify", "Exit 1 if any exports fail").option("--verbose", "Show detailed output").option("--quiet", "Suppress extraction warnings").option("--strict", "Exit 1 if any extraction warnings").option("--include-private", "Include private/protected class members").option("--external-include <patterns...>", "Resolve re-exports from these packages").option("--external-exclude <patterns...>", "Never resolve from these packages").option("--external-depth <n>", "Max transitive depth for external resolution", "1").action(async (entry, options) => {
|
|
734
|
+
const entryFile = path5.resolve(entry);
|
|
735
|
+
const entryDir = path5.dirname(entryFile);
|
|
744
736
|
const fileConfig = loadConfig(entryDir);
|
|
745
737
|
const cliConfig = options.externalInclude ? {
|
|
746
738
|
externals: {
|
|
@@ -781,6 +773,21 @@ function createSnapshotSubcommand() {
|
|
|
781
773
|
...externalExports.length > 0 && { external: { count: externalExports.length } }
|
|
782
774
|
};
|
|
783
775
|
console.error(JSON.stringify(summary, null, 2));
|
|
776
|
+
const extractionWarnings = result.runtimeSchemas?.warnings ?? [];
|
|
777
|
+
if (extractionWarnings.length > 0 && !options.quiet) {
|
|
778
|
+
console.error(`
|
|
779
|
+
Skipped ${extractionWarnings.length} schema(s) with extraction errors:`);
|
|
780
|
+
for (const w of extractionWarnings) {
|
|
781
|
+
console.error(` - ${w.exportName ?? "unknown"}: ${w.code} - ${w.message}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (options.strict && extractionWarnings.length > 0) {
|
|
785
|
+
console.error(JSON.stringify({
|
|
786
|
+
error: "Extraction warnings present (--strict mode)",
|
|
787
|
+
warnings: extractionWarnings
|
|
788
|
+
}, null, 2));
|
|
789
|
+
process.exit(1);
|
|
790
|
+
}
|
|
784
791
|
if (options.verify && result.verification && result.verification.failed > 0) {
|
|
785
792
|
console.error(JSON.stringify({
|
|
786
793
|
error: "Export verification failed",
|
|
@@ -793,37 +800,33 @@ function createSnapshotSubcommand() {
|
|
|
793
800
|
if (options.output === "-") {
|
|
794
801
|
console.log(specJson);
|
|
795
802
|
} else {
|
|
796
|
-
const outputPath =
|
|
797
|
-
|
|
803
|
+
const outputPath = path5.resolve(options.output ?? "openpkg.json");
|
|
804
|
+
fs5.writeFileSync(outputPath, specJson);
|
|
798
805
|
console.error(`Wrote ${outputPath}`);
|
|
799
806
|
}
|
|
800
807
|
} catch (err) {
|
|
801
|
-
|
|
802
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
803
|
-
process.exit(1);
|
|
808
|
+
handleCommandError(err);
|
|
804
809
|
}
|
|
805
810
|
});
|
|
806
811
|
}
|
|
807
812
|
function createValidateSubcommand() {
|
|
808
813
|
return new Command11("validate").description("Validate an OpenPkg spec against the schema").argument("<spec>", "Path to spec file (JSON)").option("--version <version>", "Schema version to validate against (default: latest)").action(async (specPath, options) => {
|
|
809
814
|
try {
|
|
810
|
-
const spec =
|
|
815
|
+
const spec = loadSpec(specPath);
|
|
811
816
|
const version = options.version ?? "latest";
|
|
812
|
-
const errors =
|
|
817
|
+
const errors = getValidationErrors2(spec, version);
|
|
813
818
|
console.log(JSON.stringify({ valid: errors.length === 0, errors }, null, 2));
|
|
814
819
|
if (errors.length > 0)
|
|
815
820
|
process.exit(1);
|
|
816
821
|
} catch (err) {
|
|
817
|
-
|
|
818
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
819
|
-
process.exit(1);
|
|
822
|
+
handleCommandError(err);
|
|
820
823
|
}
|
|
821
824
|
});
|
|
822
825
|
}
|
|
823
826
|
function createFilterSubcommand() {
|
|
824
827
|
return new Command11("filter").description("Filter an OpenPkg spec by various criteria").argument("<spec>", "Path to spec file (JSON)").option("--kind <kinds>", "Filter by kinds (comma-separated)").option("--name <names>", "Filter by exact names (comma-separated)").option("--id <ids>", "Filter by IDs (comma-separated)").option("--tag <tags>", "Filter by tags (comma-separated)").option("--deprecated", "Only deprecated exports").option("--no-deprecated", "Exclude deprecated exports").option("--has-description", "Only exports with descriptions").option("--missing-description", "Only exports without descriptions").option("--search <term>", "Search name/description").option("--search-members", "Also search member names/descriptions").option("--search-docs", "Also search param/return descriptions").option("--module <path>", "Filter by source file path").option("-o, --output <file>", "Output file (default: stdout)").option("--summary", "Only output matched/total counts").option("--quiet", "Output raw spec only").action(async (specPath, options) => {
|
|
825
828
|
try {
|
|
826
|
-
const spec =
|
|
829
|
+
const spec = loadSpec(specPath);
|
|
827
830
|
const criteria = {};
|
|
828
831
|
if (options.kind) {
|
|
829
832
|
const kinds = parseList(options.kind);
|
|
@@ -861,21 +864,19 @@ function createFilterSubcommand() {
|
|
|
861
864
|
}
|
|
862
865
|
const json = JSON.stringify(output, null, 2);
|
|
863
866
|
if (options.output) {
|
|
864
|
-
|
|
867
|
+
fs5.writeFileSync(path5.resolve(options.output), json);
|
|
865
868
|
} else {
|
|
866
869
|
console.log(json);
|
|
867
870
|
}
|
|
868
871
|
} catch (err) {
|
|
869
|
-
|
|
870
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
871
|
-
process.exit(1);
|
|
872
|
+
handleCommandError(err);
|
|
872
873
|
}
|
|
873
874
|
});
|
|
874
875
|
}
|
|
875
876
|
function createLintSubcommand() {
|
|
876
877
|
return new Command11("lint").description("Analyze spec for quality issues (missing docs, deprecated without reason)").argument("<spec>", "Path to spec file (JSON)").option("--verbose", "Show detailed information").action(async (specPath, options) => {
|
|
877
878
|
try {
|
|
878
|
-
const spec =
|
|
879
|
+
const spec = loadSpec(specPath);
|
|
879
880
|
const diagnostics = analyzeSpec(spec);
|
|
880
881
|
const generation = spec.generation;
|
|
881
882
|
const skipped = generation?.skipped ?? [];
|
|
@@ -912,15 +913,13 @@ function createLintSubcommand() {
|
|
|
912
913
|
};
|
|
913
914
|
console.log(JSON.stringify(result, null, 2));
|
|
914
915
|
} catch (err) {
|
|
915
|
-
|
|
916
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
917
|
-
process.exit(1);
|
|
916
|
+
handleCommandError(err);
|
|
918
917
|
}
|
|
919
918
|
});
|
|
920
919
|
}
|
|
921
920
|
function createListSubcommand() {
|
|
922
921
|
return new Command11("list").description("List exports from a TypeScript entry point").argument("<entry>", "Entry point file path").action(async (entry) => {
|
|
923
|
-
const entryFile =
|
|
922
|
+
const entryFile = path5.resolve(entry);
|
|
924
923
|
const result = await listExports({ entryFile });
|
|
925
924
|
if (result.errors.length > 0) {
|
|
926
925
|
console.error(JSON.stringify({ errors: result.errors }, null, 2));
|
|
@@ -931,7 +930,7 @@ function createListSubcommand() {
|
|
|
931
930
|
}
|
|
932
931
|
function createGetSubcommand() {
|
|
933
932
|
return new Command11("get").description("Get detailed spec for a single export").argument("<entry>", "Entry point file path").argument("<name>", "Export name").action(async (entry, name) => {
|
|
934
|
-
const entryFile =
|
|
933
|
+
const entryFile = path5.resolve(entry);
|
|
935
934
|
const result = await getExport({ entryFile, exportName: name });
|
|
936
935
|
if (!result.export) {
|
|
937
936
|
const errorMsg = result.errors.length > 0 ? result.errors.join("; ") : `Export '${name}' not found`;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
1
|
import { getExport, listExports } from "@openpkg-ts/sdk";
|
|
2
|
+
import { CategorizedBreaking } from "@openpkg-ts/spec";
|
|
3
|
+
type BreakingResult = {
|
|
4
|
+
breaking: CategorizedBreaking[];
|
|
5
|
+
count: number;
|
|
6
|
+
};
|
|
7
|
+
import { CategorizedBreaking as CategorizedBreaking2, SemverBump, SpecExportKind } from "@openpkg-ts/spec";
|
|
8
|
+
/**
|
|
9
|
+
* A changed with details about what changed
|
|
10
|
+
*/
|
|
11
|
+
interface ChangedExport {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
kind: SpecExportKind;
|
|
15
|
+
description: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Enriched diff result with categorized changes
|
|
19
|
+
*/
|
|
20
|
+
interface DiffResult {
|
|
21
|
+
breaking: CategorizedBreaking2[];
|
|
22
|
+
added: string[];
|
|
23
|
+
removed: RemovedExport[];
|
|
24
|
+
changed: ChangedExport[];
|
|
25
|
+
docsOnly: string[];
|
|
26
|
+
summary: {
|
|
27
|
+
breakingCount: number;
|
|
28
|
+
addedCount: number;
|
|
29
|
+
removedCount: number;
|
|
30
|
+
changedCount: number;
|
|
31
|
+
docsOnlyCount: number;
|
|
32
|
+
semverBump: SemverBump;
|
|
33
|
+
semverReason: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface RemovedExport {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
kind: SpecExportKind;
|
|
40
|
+
}
|
|
2
41
|
import { FilterResult, FilterSummaryResult } from "./commands/filter";
|
|
3
|
-
export { listExports, getExport, FilterSummaryResult, FilterResult };
|
|
42
|
+
export { listExports, getExport, RemovedExport, FilterSummaryResult, FilterResult, DiffResult, ChangedExport, BreakingResult };
|
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "CLI for OpenPkg TypeScript API extraction and documentation generation",
|
|
5
5
|
"homepage": "https://github.com/ryanwaits/openpkg-ts#readme",
|
|
6
6
|
"repository": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"test": "bun test"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@openpkg-ts/adapters": "^0.3.
|
|
27
|
-
"@openpkg-ts/sdk": "^0.
|
|
26
|
+
"@openpkg-ts/adapters": "^0.3.14",
|
|
27
|
+
"@openpkg-ts/sdk": "^0.35.1",
|
|
28
28
|
"commander": "^14.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
-
for (let key of __getOwnPropNames(mod))
|
|
11
|
-
if (!__hasOwnProp.call(to, key))
|
|
12
|
-
__defProp(to, key, {
|
|
13
|
-
get: () => mod[key],
|
|
14
|
-
enumerable: true
|
|
15
|
-
});
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
-
|
|
20
|
-
export { __toESM, __require };
|