@openpkg-ts/cli 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/openpkg.js +135 -52
- package/package.json +4 -4
package/dist/bin/openpkg.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Command as Command12 } from "commander";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "@openpkg-ts/cli",
|
|
12
|
-
version: "0.6.
|
|
12
|
+
version: "0.6.2",
|
|
13
13
|
description: "CLI for OpenPkg TypeScript API extraction and documentation generation",
|
|
14
14
|
homepage: "https://github.com/ryanwaits/openpkg-ts#readme",
|
|
15
15
|
repository: {
|
|
@@ -32,14 +32,14 @@ var package_default = {
|
|
|
32
32
|
test: "bun test"
|
|
33
33
|
},
|
|
34
34
|
dependencies: {
|
|
35
|
-
"@openpkg-ts/adapters": "^0.3.
|
|
36
|
-
"@openpkg-ts/sdk": "^0.
|
|
35
|
+
"@openpkg-ts/adapters": "^0.3.5",
|
|
36
|
+
"@openpkg-ts/sdk": "^0.35.0",
|
|
37
37
|
commander: "^14.0.0"
|
|
38
38
|
},
|
|
39
39
|
devDependencies: {
|
|
40
40
|
"@types/bun": "latest",
|
|
41
41
|
"@types/node": "^20.0.0",
|
|
42
|
-
bunup: "
|
|
42
|
+
bunup: "^0.16.20"
|
|
43
43
|
},
|
|
44
44
|
publishConfig: {
|
|
45
45
|
access: "public"
|
|
@@ -344,11 +344,15 @@ import * as path5 from "node:path";
|
|
|
344
344
|
import { loadSpec as loadSpec4, query, toReact } from "@openpkg-ts/sdk";
|
|
345
345
|
import { Command as Command5 } from "commander";
|
|
346
346
|
async function readStdin() {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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)}`);
|
|
350
355
|
}
|
|
351
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
352
356
|
}
|
|
353
357
|
function getExtension(format) {
|
|
354
358
|
switch (format) {
|
|
@@ -362,14 +366,33 @@ function getExtension(format) {
|
|
|
362
366
|
return ".md";
|
|
363
367
|
}
|
|
364
368
|
}
|
|
369
|
+
var VALID_KINDS = [
|
|
370
|
+
"function",
|
|
371
|
+
"class",
|
|
372
|
+
"variable",
|
|
373
|
+
"interface",
|
|
374
|
+
"type",
|
|
375
|
+
"enum",
|
|
376
|
+
"module",
|
|
377
|
+
"namespace",
|
|
378
|
+
"reference",
|
|
379
|
+
"external"
|
|
380
|
+
];
|
|
365
381
|
function applyFilters(spec, options) {
|
|
366
382
|
let qb = query(spec);
|
|
367
383
|
if (options.kind) {
|
|
368
|
-
const kinds = options.kind.split(",").map((k) => k.trim());
|
|
384
|
+
const kinds = options.kind.split(",").map((k) => k.trim()).filter(Boolean);
|
|
385
|
+
const invalid = kinds.filter((k) => !VALID_KINDS.includes(k));
|
|
386
|
+
if (invalid.length) {
|
|
387
|
+
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid: ${VALID_KINDS.join(", ")}`);
|
|
388
|
+
}
|
|
369
389
|
qb = qb.byKind(...kinds);
|
|
370
390
|
}
|
|
371
391
|
if (options.tag) {
|
|
372
|
-
const tags = options.tag.split(",").map((t) => t.trim());
|
|
392
|
+
const tags = options.tag.split(",").map((t) => t.trim()).filter(Boolean);
|
|
393
|
+
if (tags.length === 0) {
|
|
394
|
+
throw new Error("--tag requires at least one non-empty tag");
|
|
395
|
+
}
|
|
373
396
|
qb = qb.byTag(...tags);
|
|
374
397
|
}
|
|
375
398
|
if (options.search) {
|
|
@@ -414,6 +437,13 @@ function createGenerateCommand() {
|
|
|
414
437
|
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) => {
|
|
415
438
|
const format = options.format || "md";
|
|
416
439
|
try {
|
|
440
|
+
if (options.collapseUnions) {
|
|
441
|
+
const n = parseInt(options.collapseUnions, 10);
|
|
442
|
+
if (Number.isNaN(n) || n < 1) {
|
|
443
|
+
console.error(JSON.stringify({ error: "--collapse-unions must be a positive integer" }));
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
417
447
|
if (options.adapter && options.adapter !== "raw") {
|
|
418
448
|
let getAdapter;
|
|
419
449
|
try {
|
|
@@ -436,14 +466,24 @@ function createGenerateCommand() {
|
|
|
436
466
|
let spec2;
|
|
437
467
|
if (specPath === "-") {
|
|
438
468
|
const input = await readStdin();
|
|
439
|
-
|
|
469
|
+
try {
|
|
470
|
+
spec2 = JSON.parse(input);
|
|
471
|
+
} catch (err) {
|
|
472
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
473
|
+
throw new Error(`Invalid JSON in stdin: ${msg}`);
|
|
474
|
+
}
|
|
440
475
|
} else {
|
|
441
476
|
const specFile = path5.resolve(specPath);
|
|
442
477
|
if (!fs5.existsSync(specFile)) {
|
|
443
478
|
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
444
479
|
process.exit(1);
|
|
445
480
|
}
|
|
446
|
-
|
|
481
|
+
try {
|
|
482
|
+
spec2 = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
483
|
+
} catch (err) {
|
|
484
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
485
|
+
throw new Error(`Invalid JSON in ${specPath}: ${msg}`);
|
|
486
|
+
}
|
|
447
487
|
}
|
|
448
488
|
spec2 = applyFilters(spec2, options);
|
|
449
489
|
await adapter.generate(spec2, path5.resolve(options.output));
|
|
@@ -453,14 +493,24 @@ function createGenerateCommand() {
|
|
|
453
493
|
let spec;
|
|
454
494
|
if (specPath === "-") {
|
|
455
495
|
const input = await readStdin();
|
|
456
|
-
|
|
496
|
+
try {
|
|
497
|
+
spec = JSON.parse(input);
|
|
498
|
+
} catch (err) {
|
|
499
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
500
|
+
throw new Error(`Invalid JSON in stdin: ${msg}`);
|
|
501
|
+
}
|
|
457
502
|
} else {
|
|
458
503
|
const specFile = path5.resolve(specPath);
|
|
459
504
|
if (!fs5.existsSync(specFile)) {
|
|
460
505
|
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
461
506
|
process.exit(1);
|
|
462
507
|
}
|
|
463
|
-
|
|
508
|
+
try {
|
|
509
|
+
spec = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
510
|
+
} catch (err) {
|
|
511
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
512
|
+
throw new Error(`Invalid JSON in ${specPath}: ${msg}`);
|
|
513
|
+
}
|
|
464
514
|
}
|
|
465
515
|
spec = applyFilters(spec, options);
|
|
466
516
|
const docs = loadSpec4(spec);
|
|
@@ -511,8 +561,14 @@ Next: Add components with 'openpkg docs add function-section'`);
|
|
|
511
561
|
}
|
|
512
562
|
const exports = docs.getAllExports();
|
|
513
563
|
for (const exp of exports) {
|
|
514
|
-
const filename = `${exp.name}${getExtension(format)}
|
|
564
|
+
const filename = path5.basename(`${exp.name}${getExtension(format)}`);
|
|
515
565
|
const filePath = path5.join(outDir, filename);
|
|
566
|
+
const resolvedPath = path5.resolve(filePath);
|
|
567
|
+
const resolvedOutDir = path5.resolve(outDir);
|
|
568
|
+
if (!resolvedPath.startsWith(resolvedOutDir + path5.sep)) {
|
|
569
|
+
console.error(JSON.stringify({ error: `Path traversal detected: ${exp.name}` }));
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
516
572
|
const content = renderExport(docs, exp.id, format, collapseUnionThreshold);
|
|
517
573
|
fs5.writeFileSync(filePath, content);
|
|
518
574
|
}
|
|
@@ -545,7 +601,11 @@ function loadComponentsJson() {
|
|
|
545
601
|
const configPath = path6.resolve(COMPONENTS_JSON);
|
|
546
602
|
if (!fs6.existsSync(configPath))
|
|
547
603
|
return null;
|
|
548
|
-
|
|
604
|
+
try {
|
|
605
|
+
return JSON.parse(fs6.readFileSync(configPath, "utf-8"));
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
549
609
|
}
|
|
550
610
|
function createInitCommand() {
|
|
551
611
|
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) => {
|
|
@@ -673,7 +733,7 @@ import {
|
|
|
673
733
|
} from "@openpkg-ts/sdk";
|
|
674
734
|
import { getValidationErrors } from "@openpkg-ts/spec";
|
|
675
735
|
import { Command as Command11 } from "commander";
|
|
676
|
-
var
|
|
736
|
+
var VALID_KINDS2 = [
|
|
677
737
|
"function",
|
|
678
738
|
"class",
|
|
679
739
|
"variable",
|
|
@@ -687,8 +747,24 @@ var VALID_KINDS = [
|
|
|
687
747
|
];
|
|
688
748
|
function loadSpec6(filePath) {
|
|
689
749
|
const resolved = path8.resolve(filePath);
|
|
690
|
-
|
|
691
|
-
|
|
750
|
+
let content;
|
|
751
|
+
let spec;
|
|
752
|
+
try {
|
|
753
|
+
content = fs8.readFileSync(resolved, "utf-8");
|
|
754
|
+
} catch (err) {
|
|
755
|
+
throw new Error(`Failed to read spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
spec = JSON.parse(content);
|
|
759
|
+
} catch (err) {
|
|
760
|
+
throw new Error(`Invalid JSON in spec file: ${err instanceof Error ? err.message : String(err)}`);
|
|
761
|
+
}
|
|
762
|
+
const errors = getValidationErrors(spec);
|
|
763
|
+
if (errors.length > 0) {
|
|
764
|
+
const details = errors.slice(0, 5).map((e) => `${e.instancePath || "/"}: ${e.message}`).join("; ");
|
|
765
|
+
throw new Error(`Invalid OpenPkg spec: ${details}`);
|
|
766
|
+
}
|
|
767
|
+
return spec;
|
|
692
768
|
}
|
|
693
769
|
function parseList(val) {
|
|
694
770
|
if (!val)
|
|
@@ -696,9 +772,9 @@ function parseList(val) {
|
|
|
696
772
|
return val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
697
773
|
}
|
|
698
774
|
function validateKinds(kinds) {
|
|
699
|
-
const invalid = kinds.filter((k) => !
|
|
775
|
+
const invalid = kinds.filter((k) => !VALID_KINDS2.includes(k));
|
|
700
776
|
if (invalid.length > 0) {
|
|
701
|
-
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${
|
|
777
|
+
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${VALID_KINDS2.join(", ")}`);
|
|
702
778
|
}
|
|
703
779
|
return kinds;
|
|
704
780
|
}
|
|
@@ -712,7 +788,7 @@ function formatDiagnostics(diagnostics) {
|
|
|
712
788
|
}));
|
|
713
789
|
}
|
|
714
790
|
function createSnapshotSubcommand() {
|
|
715
|
-
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) => {
|
|
791
|
+
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) => {
|
|
716
792
|
const entryFile = path8.resolve(entry);
|
|
717
793
|
const entryDir = path8.dirname(entryFile);
|
|
718
794
|
const fileConfig = loadConfig(entryDir);
|
|
@@ -755,6 +831,21 @@ function createSnapshotSubcommand() {
|
|
|
755
831
|
...externalExports.length > 0 && { external: { count: externalExports.length } }
|
|
756
832
|
};
|
|
757
833
|
console.error(JSON.stringify(summary, null, 2));
|
|
834
|
+
const extractionWarnings = result.runtimeSchemas?.warnings ?? [];
|
|
835
|
+
if (extractionWarnings.length > 0 && !options.quiet) {
|
|
836
|
+
console.error(`
|
|
837
|
+
Skipped ${extractionWarnings.length} schema(s) with extraction errors:`);
|
|
838
|
+
for (const w of extractionWarnings) {
|
|
839
|
+
console.error(` - ${w.exportName ?? "unknown"}: ${w.code} - ${w.message}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (options.strict && extractionWarnings.length > 0) {
|
|
843
|
+
console.error(JSON.stringify({
|
|
844
|
+
error: "Extraction warnings present (--strict mode)",
|
|
845
|
+
warnings: extractionWarnings
|
|
846
|
+
}, null, 2));
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
758
849
|
if (options.verify && result.verification && result.verification.failed > 0) {
|
|
759
850
|
console.error(JSON.stringify({
|
|
760
851
|
error: "Export verification failed",
|
|
@@ -940,34 +1031,26 @@ program.addCommand(createBreakingCommand());
|
|
|
940
1031
|
program.addCommand(createChangelogCommand());
|
|
941
1032
|
program.addCommand(createSemverCommand());
|
|
942
1033
|
var specCmd = program.commands.find((c) => c.name() === "spec");
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
});
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
program.addCommand(
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
await specCmd.commands.find((c) => c.name() === "filter").parseAsync(args, { from: "user" });
|
|
966
|
-
});
|
|
967
|
-
program.addCommand(filterAlias);
|
|
968
|
-
var diagnosticsAlias = new Command12("diagnostics").description("(alias) → openpkg spec lint").allowUnknownOption().allowExcessArguments().action(async () => {
|
|
969
|
-
const args = process.argv.slice(3);
|
|
970
|
-
await specCmd.commands.find((c) => c.name() === "lint").parseAsync(args, { from: "user" });
|
|
971
|
-
});
|
|
972
|
-
program.addCommand(diagnosticsAlias);
|
|
1034
|
+
if (!specCmd) {
|
|
1035
|
+
throw new Error("Internal error: spec command not found");
|
|
1036
|
+
}
|
|
1037
|
+
function getSubcommand(parent, name) {
|
|
1038
|
+
const cmd = parent.commands.find((c) => c.name() === name);
|
|
1039
|
+
if (!cmd) {
|
|
1040
|
+
throw new Error(`Internal error: ${name} subcommand not found`);
|
|
1041
|
+
}
|
|
1042
|
+
return cmd;
|
|
1043
|
+
}
|
|
1044
|
+
function createAlias(aliasName, targetName, description) {
|
|
1045
|
+
return new Command12(aliasName).description(description).allowUnknownOption().allowExcessArguments().action(async () => {
|
|
1046
|
+
const args = process.argv.slice(3);
|
|
1047
|
+
await getSubcommand(specCmd, targetName).parseAsync(args, { from: "user" });
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
program.addCommand(createAlias("snapshot", "snapshot", "(alias) → openpkg spec snapshot"));
|
|
1051
|
+
program.addCommand(createAlias("list", "list", "(alias) → openpkg spec list"));
|
|
1052
|
+
program.addCommand(createAlias("get", "get", "(alias) → openpkg spec get"));
|
|
1053
|
+
program.addCommand(createAlias("validate", "validate", "(alias) → openpkg spec validate"));
|
|
1054
|
+
program.addCommand(createAlias("filter", "filter", "(alias) → openpkg spec filter"));
|
|
1055
|
+
program.addCommand(createAlias("diagnostics", "lint", "(alias) → openpkg spec lint"));
|
|
973
1056
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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,14 +23,14 @@
|
|
|
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.5",
|
|
27
|
+
"@openpkg-ts/sdk": "^0.35.0",
|
|
28
28
|
"commander": "^14.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/bun": "latest",
|
|
32
32
|
"@types/node": "^20.0.0",
|
|
33
|
-
"bunup": "
|
|
33
|
+
"bunup": "^0.16.20"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|