@openpkg-ts/cli 0.6.3 → 0.7.0

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.
@@ -1,990 +0,0 @@
1
- #!/usr/bin/env bun
2
- import {
3
- __require
4
- } from "../shared/chunk-3s189drz.js";
5
-
6
- // bin/openpkg.ts
7
- import { Command as Command12 } from "commander";
8
- // package.json
9
- var package_default = {
10
- name: "@openpkg-ts/cli",
11
- version: "0.6.3",
12
- description: "CLI for OpenPkg TypeScript API extraction and documentation generation",
13
- homepage: "https://github.com/ryanwaits/openpkg-ts#readme",
14
- repository: {
15
- type: "git",
16
- url: "git+https://github.com/ryanwaits/openpkg-ts.git",
17
- directory: "packages/cli"
18
- },
19
- type: "module",
20
- main: "./dist/src/index.js",
21
- types: "./dist/src/index.d.ts",
22
- bin: {
23
- openpkg: "./dist/bin/openpkg.js"
24
- },
25
- files: [
26
- "dist"
27
- ],
28
- scripts: {
29
- build: "bunup",
30
- dev: "bunup --watch",
31
- test: "bun test"
32
- },
33
- dependencies: {
34
- "@openpkg-ts/adapters": "^0.3.14",
35
- "@openpkg-ts/sdk": "^0.35.1",
36
- commander: "^14.0.0"
37
- },
38
- devDependencies: {
39
- "@types/bun": "latest",
40
- "@types/node": "^20.0.0",
41
- bunup: "^0.16.20"
42
- },
43
- publishConfig: {
44
- access: "public"
45
- }
46
- };
47
-
48
- // src/commands/breaking.ts
49
- import { categorizeBreakingChanges, diffSpec } from "@openpkg-ts/spec";
50
- import { Command } from "commander";
51
-
52
- // src/commands/utils.ts
53
- import * as fs from "node:fs";
54
- import * as path from "node:path";
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
- }
86
- function loadSpec(filePath) {
87
- const resolved = path.resolve(filePath);
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;
106
- }
107
-
108
- // src/commands/breaking.ts
109
- function createBreakingCommand() {
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) => {
111
- try {
112
- const oldSpec = loadSpec(oldPath);
113
- const newSpec = loadSpec(newPath);
114
- const diff = diffSpec(oldSpec, newSpec);
115
- const categorized = categorizeBreakingChanges(diff.breaking, oldSpec, newSpec);
116
- const result = {
117
- breaking: categorized,
118
- count: categorized.length
119
- };
120
- console.log(JSON.stringify(result, null, 2));
121
- if (categorized.length > 0) {
122
- process.exit(1);
123
- }
124
- } catch (err) {
125
- handleCommandError(err);
126
- }
127
- });
128
- }
129
-
130
- // src/commands/changelog.ts
131
- import { Command as Command3 } from "commander";
132
-
133
- // src/commands/diff.ts
134
- import {
135
- categorizeBreakingChanges as categorizeBreakingChanges2,
136
- diffSpec as diffSpec2,
137
- recommendSemverBump
138
- } from "@openpkg-ts/spec";
139
- import { Command as Command2 } from "commander";
140
- function toExportMap(spec) {
141
- const map = new Map;
142
- for (const exp of spec.exports) {
143
- map.set(exp.id, { name: exp.name, kind: exp.kind });
144
- }
145
- if (spec.types) {
146
- for (const t of spec.types) {
147
- map.set(t.id, { name: t.name, kind: t.kind });
148
- }
149
- }
150
- return map;
151
- }
152
- function enrichDiff(oldSpec, newSpec) {
153
- const rawDiff = diffSpec2(oldSpec, newSpec);
154
- const categorized = categorizeBreakingChanges2(rawDiff.breaking, oldSpec, newSpec);
155
- const semver = recommendSemverBump(rawDiff);
156
- const oldExports = toExportMap(oldSpec);
157
- const removed = [];
158
- const changed = [];
159
- const breaking = [];
160
- for (const cat of categorized) {
161
- if (cat.reason === "removed") {
162
- const info = oldExports.get(cat.id);
163
- removed.push({
164
- id: cat.id,
165
- name: cat.name,
166
- kind: info?.kind ?? cat.kind
167
- });
168
- } else {
169
- changed.push({
170
- id: cat.id,
171
- name: cat.name,
172
- kind: cat.kind,
173
- description: describeChange(cat)
174
- });
175
- breaking.push(cat);
176
- }
177
- }
178
- const added = rawDiff.nonBreaking;
179
- return {
180
- breaking,
181
- added,
182
- removed,
183
- changed,
184
- docsOnly: rawDiff.docsOnly,
185
- summary: {
186
- breakingCount: breaking.length,
187
- addedCount: added.length,
188
- removedCount: removed.length,
189
- changedCount: changed.length,
190
- docsOnlyCount: rawDiff.docsOnly.length,
191
- semverBump: semver.bump,
192
- semverReason: semver.reason
193
- }
194
- };
195
- }
196
- function describeChange(cat) {
197
- switch (cat.reason) {
198
- case "signature changed":
199
- return `Function signature changed`;
200
- case "type definition changed":
201
- return `Type definition changed`;
202
- case "constructor changed":
203
- return `Class constructor signature changed`;
204
- case "methods removed":
205
- return `Class methods removed`;
206
- case "methods changed":
207
- return `Class methods changed`;
208
- case "changed":
209
- return `${cat.kind} changed`;
210
- default:
211
- return cat.reason;
212
- }
213
- }
214
- function createDiffCommand() {
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) => {
216
- try {
217
- const oldSpec = loadSpec(oldPath);
218
- const newSpec = loadSpec(newPath);
219
- const result = enrichDiff(oldSpec, newSpec);
220
- if (options.summary) {
221
- console.log(JSON.stringify(result.summary, null, 2));
222
- } else {
223
- console.log(JSON.stringify(result, null, 2));
224
- }
225
- } catch (err) {
226
- handleCommandError(err);
227
- }
228
- });
229
- }
230
-
231
- // src/commands/changelog.ts
232
- function formatMarkdown(diff) {
233
- const lines = [];
234
- if (diff.removed.length > 0 || diff.changed.length > 0) {
235
- lines.push("## Breaking Changes");
236
- lines.push("");
237
- for (const r of diff.removed) {
238
- lines.push(`- **Removed** \`${r.name}\` (${r.kind})`);
239
- }
240
- for (const c of diff.changed) {
241
- lines.push(`- **${c.name}** (${c.kind}): ${c.description}`);
242
- }
243
- lines.push("");
244
- }
245
- if (diff.added.length > 0) {
246
- lines.push("## Added");
247
- lines.push("");
248
- for (const id of diff.added) {
249
- lines.push(`- \`${id}\``);
250
- }
251
- lines.push("");
252
- }
253
- if (diff.docsOnly.length > 0) {
254
- lines.push("## Changed");
255
- lines.push("");
256
- for (const id of diff.docsOnly) {
257
- lines.push(`- \`${id}\` (docs)`);
258
- }
259
- lines.push("");
260
- }
261
- return lines.join(`
262
- `).trim() || "No changes detected.";
263
- }
264
- function createChangelogCommand() {
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) => {
266
- try {
267
- const oldSpec = loadSpec(oldPath);
268
- const newSpec = loadSpec(newPath);
269
- const diff = enrichDiff(oldSpec, newSpec);
270
- if (options.format === "json") {
271
- console.log(JSON.stringify(diff, null, 2));
272
- } else {
273
- console.log(formatMarkdown(diff));
274
- }
275
- } catch (err) {
276
- handleCommandError(err);
277
- }
278
- });
279
- }
280
-
281
- // src/commands/docs/index.ts
282
- import { Command as Command9 } from "commander";
283
-
284
- // src/commands/docs/add.ts
285
- import { spawn } from "node:child_process";
286
- import { Command as Command4 } from "commander";
287
-
288
- // src/commands/docs/utils.ts
289
- import * as fs2 from "node:fs";
290
- import * as path2 from "node:path";
291
- function detectPackageManager(cwd = process.cwd()) {
292
- const pkgJsonPath = path2.join(cwd, "package.json");
293
- if (fs2.existsSync(pkgJsonPath)) {
294
- try {
295
- const pkg = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
296
- if (pkg.packageManager) {
297
- if (pkg.packageManager.startsWith("bun"))
298
- return "bun";
299
- if (pkg.packageManager.startsWith("pnpm"))
300
- return "pnpm";
301
- if (pkg.packageManager.startsWith("yarn"))
302
- return "yarn";
303
- if (pkg.packageManager.startsWith("npm"))
304
- return "npm";
305
- }
306
- } catch {}
307
- }
308
- if (fs2.existsSync(path2.join(cwd, "bun.lockb")) || fs2.existsSync(path2.join(cwd, "bun.lock"))) {
309
- return "bun";
310
- }
311
- if (fs2.existsSync(path2.join(cwd, "pnpm-lock.yaml"))) {
312
- return "pnpm";
313
- }
314
- if (fs2.existsSync(path2.join(cwd, "yarn.lock"))) {
315
- return "yarn";
316
- }
317
- return "npm";
318
- }
319
- function getDlxCommand(pkg, pm) {
320
- const packageManager = pm || detectPackageManager();
321
- switch (packageManager) {
322
- case "bun":
323
- return { cmd: "bunx", args: [pkg] };
324
- case "pnpm":
325
- return { cmd: "pnpm", args: ["dlx", pkg] };
326
- case "yarn":
327
- return { cmd: "yarn", args: ["dlx", pkg] };
328
- default:
329
- return { cmd: "npx", args: [pkg] };
330
- }
331
- }
332
- function getShadcnCommand(subcommand, args = []) {
333
- const dlx = getDlxCommand("shadcn@latest");
334
- return {
335
- cmd: dlx.cmd,
336
- args: [...dlx.args, subcommand, ...args]
337
- };
338
- }
339
-
340
- // src/commands/docs/add.ts
341
- function createAddCommand() {
342
- return new Command4("add").description("Install @openpkg components (wrapper for shadcn add)").argument("<components...>", "Components to install").option("-o, --overwrite", "Overwrite existing files").option("-c, --cwd <path>", "Working directory").option("-y, --yes", "Skip confirmation prompt").option("-s, --silent", "Mute output").action(async (components, options) => {
343
- const prefixedComponents = components.map((c) => c.startsWith("@") ? c : `@openpkg/${c}`);
344
- const extraArgs = [];
345
- if (options.overwrite)
346
- extraArgs.push("--overwrite");
347
- if (options.cwd)
348
- extraArgs.push("--cwd", options.cwd);
349
- if (options.yes)
350
- extraArgs.push("--yes");
351
- if (options.silent)
352
- extraArgs.push("--silent");
353
- const { cmd, args } = getShadcnCommand("add", [...prefixedComponents, ...extraArgs]);
354
- if (!options.silent) {
355
- console.log(`Running: ${cmd} ${args.join(" ")}`);
356
- console.log("");
357
- }
358
- const child = spawn(cmd, args, {
359
- stdio: "inherit",
360
- shell: true
361
- });
362
- child.on("close", (code) => {
363
- process.exit(code || 0);
364
- });
365
- });
366
- }
367
-
368
- // src/commands/docs/generate.ts
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";
372
- import { Command as Command5 } from "commander";
373
- function getExtension(format) {
374
- switch (format) {
375
- case "json":
376
- return ".json";
377
- case "html":
378
- return ".html";
379
- case "react":
380
- return ".tsx";
381
- default:
382
- return ".md";
383
- }
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
- ];
397
- function applyFilters(spec, options) {
398
- let qb = query(spec);
399
- if (options.kind) {
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
- }
405
- qb = qb.byKind(...kinds);
406
- }
407
- if (options.tag) {
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
- }
412
- qb = qb.byTag(...tags);
413
- }
414
- if (options.search) {
415
- qb = qb.search(options.search);
416
- }
417
- if (options.deprecated === true) {
418
- qb = qb.deprecated(true);
419
- } else if (options.deprecated === false) {
420
- qb = qb.deprecated(false);
421
- }
422
- return qb.toSpec();
423
- }
424
- function renderExport(docs, exportId, format, collapseUnionThreshold) {
425
- const exp = docs.getExport(exportId);
426
- if (!exp)
427
- throw new Error(`Export not found: ${exportId}`);
428
- switch (format) {
429
- case "json":
430
- return JSON.stringify(docs.toJSON({ export: exportId }), null, 2);
431
- case "html":
432
- return docs.toHTML({ export: exportId });
433
- default:
434
- return docs.toMarkdown({
435
- export: exportId,
436
- frontmatter: true,
437
- codeSignatures: true,
438
- collapseUnionThreshold
439
- });
440
- }
441
- }
442
- function renderFull(docs, format, collapseUnionThreshold) {
443
- switch (format) {
444
- case "json":
445
- return JSON.stringify(docs.toJSON(), null, 2);
446
- case "html":
447
- return docs.toHTML();
448
- default:
449
- return docs.toMarkdown({ frontmatter: true, codeSignatures: true, collapseUnionThreshold });
450
- }
451
- }
452
- function createGenerateCommand() {
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) => {
454
- const format = options.format || "md";
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
- }
463
- if (options.adapter && options.adapter !== "raw") {
464
- let getAdapter;
465
- try {
466
- const _adapterModule = await import(`@openpkg-ts/adapters/${options.adapter}`);
467
- const registryModule = await import("@openpkg-ts/adapters");
468
- getAdapter = registryModule.getAdapter;
469
- } catch {
470
- console.error(JSON.stringify({ error: `Failed to load adapter: ${options.adapter}` }));
471
- process.exit(1);
472
- }
473
- const adapter = getAdapter(options.adapter);
474
- if (!adapter) {
475
- console.error(JSON.stringify({ error: `Unknown adapter: ${options.adapter}` }));
476
- process.exit(1);
477
- }
478
- if (!options.output) {
479
- console.error(JSON.stringify({ error: "--adapter requires --output <directory>" }));
480
- process.exit(1);
481
- }
482
- let spec2 = await loadSpecInput(specPath);
483
- spec2 = applyFilters(spec2, options);
484
- await adapter.generate(spec2, path3.resolve(options.output));
485
- console.error(`Generated docs with ${options.adapter} adapter to ${options.output}`);
486
- return;
487
- }
488
- let spec = await loadSpecInput(specPath);
489
- spec = applyFilters(spec, options);
490
- const docs = loadSpec2(spec);
491
- const collapseUnionThreshold = options.collapseUnions ? parseInt(options.collapseUnions, 10) : undefined;
492
- if (format === "react") {
493
- if (!options.output) {
494
- console.error(JSON.stringify({ error: "--format react requires --output <directory>" }));
495
- process.exit(1);
496
- }
497
- const variant = options.variant === "index" ? "index" : "full";
498
- await toReact(spec, {
499
- outDir: path3.resolve(options.output),
500
- variant,
501
- componentsPath: options.componentsPath ?? "@/components/api"
502
- });
503
- console.error(`Generated React layout to ${options.output}`);
504
- console.error(` - page.tsx: Layout file`);
505
- console.error(` - openpkg.json: Spec data`);
506
- console.error(`
507
- Next: Add components with 'openpkg docs add function-section'`);
508
- return;
509
- }
510
- if (options.export) {
511
- const exports = docs.getAllExports();
512
- const exp = exports.find((e) => e.name === options.export);
513
- if (!exp) {
514
- console.error(JSON.stringify({ error: `Export not found: ${options.export}` }));
515
- process.exit(1);
516
- }
517
- const output2 = renderExport(docs, exp.id, format, collapseUnionThreshold);
518
- if (options.output && options.output !== "-") {
519
- const outputPath = path3.resolve(options.output);
520
- fs3.writeFileSync(outputPath, output2);
521
- console.error(`Wrote ${outputPath}`);
522
- } else {
523
- console.log(output2);
524
- }
525
- return;
526
- }
527
- if (options.split) {
528
- if (!options.output) {
529
- console.error(JSON.stringify({ error: "--split requires --output <directory>" }));
530
- process.exit(1);
531
- }
532
- const outDir = path3.resolve(options.output);
533
- if (!fs3.existsSync(outDir)) {
534
- fs3.mkdirSync(outDir, { recursive: true });
535
- }
536
- const exports = docs.getAllExports();
537
- for (const exp of exports) {
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)) {
543
- console.error(JSON.stringify({ error: `Path traversal detected: ${exp.name}` }));
544
- process.exit(1);
545
- }
546
- const content = renderExport(docs, exp.id, format, collapseUnionThreshold);
547
- fs3.writeFileSync(filePath, content);
548
- }
549
- console.error(`Wrote ${exports.length} files to ${outDir}`);
550
- return;
551
- }
552
- const output = renderFull(docs, format, collapseUnionThreshold);
553
- if (options.output && options.output !== "-") {
554
- const outputPath = path3.resolve(options.output);
555
- fs3.writeFileSync(outputPath, output);
556
- console.error(`Wrote ${outputPath}`);
557
- } else {
558
- console.log(output);
559
- }
560
- } catch (err) {
561
- handleCommandError(err);
562
- }
563
- });
564
- }
565
-
566
- // src/commands/docs/init.ts
567
- import * as fs4 from "node:fs";
568
- import * as path4 from "node:path";
569
- import { Command as Command6 } from "commander";
570
- var COMPONENTS_JSON = "components.json";
571
- var REGISTRY_URL = "https://raw.githubusercontent.com/anthropics/openpkg-ts/main/registry/r/{name}.json";
572
- function loadComponentsJson() {
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 {
579
- return null;
580
- }
581
- }
582
- function createInitCommand() {
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) => {
584
- const configPath = path4.resolve(COMPONENTS_JSON);
585
- const registryUrl = options.registry || REGISTRY_URL;
586
- if (!fs4.existsSync(configPath)) {
587
- console.error(`${COMPONENTS_JSON} not found.`);
588
- console.error('Run "npx shadcn@latest init" first to initialize shadcn.');
589
- process.exit(1);
590
- }
591
- const config = loadComponentsJson();
592
- if (!config) {
593
- console.error(`Failed to parse ${COMPONENTS_JSON}`);
594
- process.exit(1);
595
- }
596
- config.registries = config.registries || {};
597
- config.registries["@openpkg"] = registryUrl;
598
- fs4.writeFileSync(configPath, JSON.stringify(config, null, 2));
599
- console.log(`Added @openpkg registry to ${COMPONENTS_JSON}`);
600
- console.log("");
601
- console.log("Usage:");
602
- console.log(" npx shadcn@latest add @openpkg/function-section");
603
- console.log(" npx shadcn@latest add @openpkg/export-card");
604
- console.log("");
605
- console.log("List components:");
606
- console.log(" openpkg docs list");
607
- });
608
- }
609
-
610
- // src/commands/docs/list.ts
611
- import { spawn as spawn2 } from "node:child_process";
612
- import { Command as Command7 } from "commander";
613
- function createListCommand() {
614
- return new Command7("list").description("List @openpkg components (wrapper for shadcn list)").option("-q, --query <query>", "Search query").option("-l, --limit <number>", "Max items to display").option("-c, --cwd <cwd>", "Working directory").action(async (options) => {
615
- const extraArgs = ["@openpkg"];
616
- if (options.query)
617
- extraArgs.push("-q", options.query);
618
- if (options.limit)
619
- extraArgs.push("-l", options.limit);
620
- if (options.cwd)
621
- extraArgs.push("-c", options.cwd);
622
- const { cmd, args } = getShadcnCommand("list", extraArgs);
623
- const child = spawn2(cmd, args, {
624
- stdio: "inherit",
625
- shell: true
626
- });
627
- child.on("close", (code) => {
628
- process.exit(code || 0);
629
- });
630
- });
631
- }
632
-
633
- // src/commands/docs/view.ts
634
- import { spawn as spawn3 } from "node:child_process";
635
- import { Command as Command8 } from "commander";
636
- function createViewCommand() {
637
- return new Command8("view").description("View @openpkg component before installing (wrapper for shadcn view)").argument("<components...>", "Components to view").option("-c, --cwd <cwd>", "Working directory").action(async (components, options) => {
638
- const prefixedComponents = components.map((c) => c.startsWith("@") ? c : `@openpkg/${c}`);
639
- const extraArgs = [...prefixedComponents];
640
- if (options.cwd)
641
- extraArgs.push("-c", options.cwd);
642
- const { cmd, args } = getShadcnCommand("view", extraArgs);
643
- const child = spawn3(cmd, args, {
644
- stdio: "inherit",
645
- shell: true
646
- });
647
- child.on("close", (code) => {
648
- process.exit(code || 0);
649
- });
650
- });
651
- }
652
-
653
- // src/commands/docs/index.ts
654
- function createDocsCommand() {
655
- const docs = new Command9("docs").description("Documentation generation and component registry");
656
- docs.addCommand(createGenerateCommand());
657
- docs.addCommand(createInitCommand());
658
- docs.addCommand(createListCommand());
659
- docs.addCommand(createViewCommand());
660
- docs.addCommand(createAddCommand());
661
- return docs;
662
- }
663
-
664
- // src/commands/semver.ts
665
- import { diffSpec as diffSpec3, recommendSemverBump as recommendSemverBump2 } from "@openpkg-ts/spec";
666
- import { Command as Command10 } from "commander";
667
- function createSemverCommand() {
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) => {
669
- try {
670
- const oldSpec = loadSpec(oldPath);
671
- const newSpec = loadSpec(newPath);
672
- const diff = diffSpec3(oldSpec, newSpec);
673
- const recommendation = recommendSemverBump2(diff);
674
- const result = {
675
- bump: recommendation.bump,
676
- reason: recommendation.reason
677
- };
678
- console.log(JSON.stringify(result, null, 2));
679
- } catch (err) {
680
- handleCommandError(err);
681
- }
682
- });
683
- }
684
-
685
- // src/commands/spec.ts
686
- import * as fs5 from "node:fs";
687
- import * as path5 from "node:path";
688
- import {
689
- analyzeSpec,
690
- extractSpec,
691
- filterSpec,
692
- getExport,
693
- listExports,
694
- loadConfig,
695
- mergeConfig
696
- } from "@openpkg-ts/sdk";
697
- import { getValidationErrors as getValidationErrors2 } from "@openpkg-ts/spec";
698
- import { Command as Command11 } from "commander";
699
- var VALID_KINDS2 = [
700
- "function",
701
- "class",
702
- "variable",
703
- "interface",
704
- "type",
705
- "enum",
706
- "module",
707
- "namespace",
708
- "reference",
709
- "external"
710
- ];
711
- function parseList(val) {
712
- if (!val)
713
- return;
714
- return val.split(",").map((s) => s.trim()).filter(Boolean);
715
- }
716
- function validateKinds(kinds) {
717
- const invalid = kinds.filter((k) => !VALID_KINDS2.includes(k));
718
- if (invalid.length > 0) {
719
- throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${VALID_KINDS2.join(", ")}`);
720
- }
721
- return kinds;
722
- }
723
- function formatDiagnostics(diagnostics) {
724
- return diagnostics.map((d) => ({
725
- message: d.message,
726
- severity: d.severity,
727
- ...d.code && { code: d.code },
728
- ...d.suggestion && { suggestion: d.suggestion },
729
- ...d.location && { location: d.location }
730
- }));
731
- }
732
- function createSnapshotSubcommand() {
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);
736
- const fileConfig = loadConfig(entryDir);
737
- const cliConfig = options.externalInclude ? {
738
- externals: {
739
- include: options.externalInclude,
740
- exclude: options.externalExclude,
741
- depth: parseInt(options.externalDepth ?? "1", 10)
742
- }
743
- } : {};
744
- const mergedConfig = mergeConfig(fileConfig, cliConfig);
745
- const extractOptions = {
746
- entryFile,
747
- maxTypeDepth: parseInt(options.maxDepth ?? "4", 10),
748
- resolveExternalTypes: !options.skipResolve,
749
- schemaExtraction: options.runtime ? "hybrid" : "static",
750
- only: parseList(options.only),
751
- ignore: parseList(options.ignore),
752
- includePrivate: options.includePrivate,
753
- ...mergedConfig.externals && { externals: mergedConfig.externals }
754
- };
755
- try {
756
- const result = await extractSpec(extractOptions);
757
- const externalExports = result.spec.exports.filter((e) => e.kind === "external");
758
- const summary = {
759
- exports: result.spec.exports.length,
760
- types: result.spec.types?.length ?? 0,
761
- diagnostics: result.diagnostics.length,
762
- ...result.verification && {
763
- verification: {
764
- discovered: result.verification.discovered,
765
- extracted: result.verification.extracted,
766
- skipped: result.verification.skipped,
767
- failed: result.verification.failed,
768
- ...options.verbose && result.verification.details.skipped.length > 0 && {
769
- skippedDetails: result.verification.details.skipped
770
- }
771
- }
772
- },
773
- ...externalExports.length > 0 && { external: { count: externalExports.length } }
774
- };
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
- }
791
- if (options.verify && result.verification && result.verification.failed > 0) {
792
- console.error(JSON.stringify({
793
- error: "Export verification failed",
794
- failed: result.verification.details.failed,
795
- diagnostics: formatDiagnostics(result.diagnostics)
796
- }, null, 2));
797
- process.exit(1);
798
- }
799
- const specJson = JSON.stringify(result.spec, null, 2);
800
- if (options.output === "-") {
801
- console.log(specJson);
802
- } else {
803
- const outputPath = path5.resolve(options.output ?? "openpkg.json");
804
- fs5.writeFileSync(outputPath, specJson);
805
- console.error(`Wrote ${outputPath}`);
806
- }
807
- } catch (err) {
808
- handleCommandError(err);
809
- }
810
- });
811
- }
812
- function createValidateSubcommand() {
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) => {
814
- try {
815
- const spec = loadSpec(specPath);
816
- const version = options.version ?? "latest";
817
- const errors = getValidationErrors2(spec, version);
818
- console.log(JSON.stringify({ valid: errors.length === 0, errors }, null, 2));
819
- if (errors.length > 0)
820
- process.exit(1);
821
- } catch (err) {
822
- handleCommandError(err);
823
- }
824
- });
825
- }
826
- function createFilterSubcommand() {
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) => {
828
- try {
829
- const spec = loadSpec(specPath);
830
- const criteria = {};
831
- if (options.kind) {
832
- const kinds = parseList(options.kind);
833
- if (kinds)
834
- criteria.kinds = validateKinds(kinds);
835
- }
836
- if (options.name)
837
- criteria.names = parseList(options.name);
838
- if (options.id)
839
- criteria.ids = parseList(options.id);
840
- if (options.tag)
841
- criteria.tags = parseList(options.tag);
842
- if (options.deprecated !== undefined)
843
- criteria.deprecated = options.deprecated;
844
- if (options.hasDescription)
845
- criteria.hasDescription = true;
846
- if (options.missingDescription)
847
- criteria.hasDescription = false;
848
- if (options.search)
849
- criteria.search = options.search;
850
- if (options.searchMembers)
851
- criteria.searchMembers = true;
852
- if (options.searchDocs)
853
- criteria.searchDocs = true;
854
- if (options.module)
855
- criteria.module = options.module;
856
- const result = filterSpec(spec, criteria);
857
- let output;
858
- if (options.summary) {
859
- output = { matched: result.matched, total: result.total };
860
- } else if (options.quiet) {
861
- output = result.spec;
862
- } else {
863
- output = { spec: result.spec, matched: result.matched, total: result.total };
864
- }
865
- const json = JSON.stringify(output, null, 2);
866
- if (options.output) {
867
- fs5.writeFileSync(path5.resolve(options.output), json);
868
- } else {
869
- console.log(json);
870
- }
871
- } catch (err) {
872
- handleCommandError(err);
873
- }
874
- });
875
- }
876
- function createLintSubcommand() {
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) => {
878
- try {
879
- const spec = loadSpec(specPath);
880
- const diagnostics = analyzeSpec(spec);
881
- const generation = spec.generation;
882
- const skipped = generation?.skipped ?? [];
883
- const externalExports = spec.exports.filter((e) => e.kind === "external");
884
- const byReason = {};
885
- for (const skip of skipped) {
886
- byReason[skip.reason] = (byReason[skip.reason] ?? 0) + 1;
887
- }
888
- const result = {
889
- summary: {
890
- total: diagnostics.missingDescriptions.length + diagnostics.deprecatedNoReason.length + diagnostics.missingParamDocs.length,
891
- missingDescriptions: diagnostics.missingDescriptions.length,
892
- deprecatedNoReason: diagnostics.deprecatedNoReason.length,
893
- missingParamDocs: diagnostics.missingParamDocs.length,
894
- ...skipped.length > 0 && { skippedExports: skipped.length },
895
- ...externalExports.length > 0 && { externalExports: externalExports.length }
896
- },
897
- diagnostics,
898
- ...skipped.length > 0 && {
899
- skippedExports: {
900
- total: skipped.length,
901
- byReason,
902
- ...options.verbose && { details: skipped }
903
- }
904
- },
905
- ...externalExports.length > 0 && {
906
- externalExports: {
907
- count: externalExports.length,
908
- ...options.verbose && {
909
- details: externalExports.map((e) => ({ name: e.name, package: e.source?.package }))
910
- }
911
- }
912
- }
913
- };
914
- console.log(JSON.stringify(result, null, 2));
915
- } catch (err) {
916
- handleCommandError(err);
917
- }
918
- });
919
- }
920
- function createListSubcommand() {
921
- return new Command11("list").description("List exports from a TypeScript entry point").argument("<entry>", "Entry point file path").action(async (entry) => {
922
- const entryFile = path5.resolve(entry);
923
- const result = await listExports({ entryFile });
924
- if (result.errors.length > 0) {
925
- console.error(JSON.stringify({ errors: result.errors }, null, 2));
926
- process.exit(1);
927
- }
928
- console.log(JSON.stringify(result.exports, null, 2));
929
- });
930
- }
931
- function createGetSubcommand() {
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) => {
933
- const entryFile = path5.resolve(entry);
934
- const result = await getExport({ entryFile, exportName: name });
935
- if (!result.export) {
936
- const errorMsg = result.errors.length > 0 ? result.errors.join("; ") : `Export '${name}' not found`;
937
- console.error(JSON.stringify({ error: errorMsg }, null, 2));
938
- process.exit(1);
939
- }
940
- const output = { export: result.export };
941
- if (result.types.length > 0) {
942
- output.types = result.types;
943
- }
944
- console.log(JSON.stringify(output, null, 2));
945
- });
946
- }
947
- function createSpecCommand() {
948
- const spec = new Command11("spec").description("Spec extraction and manipulation commands");
949
- spec.addCommand(createSnapshotSubcommand());
950
- spec.addCommand(createValidateSubcommand());
951
- spec.addCommand(createFilterSubcommand());
952
- spec.addCommand(createLintSubcommand());
953
- spec.addCommand(createListSubcommand());
954
- spec.addCommand(createGetSubcommand());
955
- return spec;
956
- }
957
-
958
- // bin/openpkg.ts
959
- var program = new Command12;
960
- program.name("openpkg").description("OpenPkg CLI - TypeScript API extraction primitives").version(package_default.version);
961
- program.addCommand(createSpecCommand());
962
- program.addCommand(createDocsCommand());
963
- program.addCommand(createDiffCommand());
964
- program.addCommand(createBreakingCommand());
965
- program.addCommand(createChangelogCommand());
966
- program.addCommand(createSemverCommand());
967
- var specCmd = program.commands.find((c) => c.name() === "spec");
968
- if (!specCmd) {
969
- throw new Error("Internal error: spec command not found");
970
- }
971
- function getSubcommand(parent, name) {
972
- const cmd = parent.commands.find((c) => c.name() === name);
973
- if (!cmd) {
974
- throw new Error(`Internal error: ${name} subcommand not found`);
975
- }
976
- return cmd;
977
- }
978
- function createAlias(aliasName, targetName, description) {
979
- return new Command12(aliasName).description(description).allowUnknownOption().allowExcessArguments().action(async () => {
980
- const args = process.argv.slice(3);
981
- await getSubcommand(specCmd, targetName).parseAsync(args, { from: "user" });
982
- });
983
- }
984
- program.addCommand(createAlias("snapshot", "snapshot", "(alias) → openpkg spec snapshot"));
985
- program.addCommand(createAlias("list", "list", "(alias) → openpkg spec list"));
986
- program.addCommand(createAlias("get", "get", "(alias) → openpkg spec get"));
987
- program.addCommand(createAlias("validate", "validate", "(alias) → openpkg spec validate"));
988
- program.addCommand(createAlias("filter", "filter", "(alias) → openpkg spec filter"));
989
- program.addCommand(createAlias("diagnostics", "lint", "(alias) → openpkg spec lint"));
990
- program.parse();