@openpkg-ts/cli 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -145
- package/dist/bin/openpkg.js +465 -271
- package/dist/src/index.d.ts +1 -10
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,11 +10,24 @@ npm install -g @openpkg-ts/cli
|
|
|
10
10
|
npx @openpkg-ts/cli <command>
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Command Structure
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Commands organized under `openpkg spec` and `openpkg docs`. Legacy commands work as aliases.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
openpkg spec snapshot ./src/index.ts -o spec.json
|
|
19
|
+
openpkg spec validate spec.json
|
|
20
|
+
|
|
21
|
+
openpkg docs init
|
|
22
|
+
openpkg docs generate spec.json -o ./docs
|
|
23
|
+
openpkg docs add function-section
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
## Spec Commands
|
|
29
|
+
|
|
30
|
+
### list
|
|
18
31
|
|
|
19
32
|
```bash
|
|
20
33
|
openpkg list src/index.ts
|
|
@@ -24,31 +37,21 @@ Output: JSON array of `{ name, kind, file, line, description }`
|
|
|
24
37
|
|
|
25
38
|
### get
|
|
26
39
|
|
|
27
|
-
Get detailed spec for single export.
|
|
28
|
-
|
|
29
40
|
```bash
|
|
30
41
|
openpkg get src/index.ts createClient
|
|
31
42
|
```
|
|
32
43
|
|
|
33
44
|
Output: JSON with `{ export, types }` - full spec for the export plus referenced types.
|
|
34
45
|
|
|
35
|
-
### snapshot
|
|
36
|
-
|
|
37
|
-
Generate full OpenPkg spec from TypeScript.
|
|
46
|
+
### spec snapshot
|
|
38
47
|
|
|
39
48
|
```bash
|
|
40
|
-
|
|
41
|
-
openpkg snapshot src/index.ts -o
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
openpkg snapshot src/index.ts -o -
|
|
45
|
-
|
|
46
|
-
# With options
|
|
47
|
-
openpkg snapshot src/index.ts --max-depth 4 --runtime --verify
|
|
48
|
-
openpkg snapshot src/index.ts --only "use*,create*" --ignore "*Internal"
|
|
49
|
+
openpkg spec snapshot src/index.ts -o openpkg.json
|
|
50
|
+
openpkg spec snapshot src/index.ts -o - # stdout
|
|
51
|
+
openpkg spec snapshot src/index.ts --max-depth 4 --runtime --verify
|
|
52
|
+
openpkg spec snapshot src/index.ts --only "use*,create*" --ignore "*Internal"
|
|
49
53
|
```
|
|
50
54
|
|
|
51
|
-
Options:
|
|
52
55
|
| Flag | Description |
|
|
53
56
|
|------|-------------|
|
|
54
57
|
| `-o, --output <file>` | Output file (default: openpkg.json, `-` for stdout) |
|
|
@@ -59,195 +62,161 @@ Options:
|
|
|
59
62
|
| `--ignore <exports>` | Ignore exports (comma-separated, wildcards) |
|
|
60
63
|
| `--verify` | Exit 1 if any exports fail |
|
|
61
64
|
|
|
62
|
-
###
|
|
63
|
-
|
|
64
|
-
Generate documentation from spec.
|
|
65
|
+
### spec validate
|
|
65
66
|
|
|
66
67
|
```bash
|
|
67
|
-
|
|
68
|
-
openpkg
|
|
69
|
-
|
|
70
|
-
# HTML
|
|
71
|
-
openpkg docs openpkg.json -f html -o api.html
|
|
68
|
+
openpkg spec validate openpkg.json
|
|
69
|
+
openpkg spec validate openpkg.json --version 1.0
|
|
70
|
+
```
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
openpkg docs openpkg.json -f json
|
|
72
|
+
### spec diagnostics
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
openpkg
|
|
74
|
+
```bash
|
|
75
|
+
openpkg spec diagnostics openpkg.json
|
|
76
|
+
```
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
openpkg snapshot src/index.ts -o - | openpkg docs - -f md
|
|
78
|
+
### spec filter
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
openpkg
|
|
80
|
+
```bash
|
|
81
|
+
openpkg spec filter openpkg.json --kind function,class
|
|
82
|
+
openpkg spec filter openpkg.json --has-description -o documented.json
|
|
83
|
+
openpkg spec filter openpkg.json --search "user" --summary
|
|
84
|
+
openpkg spec filter openpkg.json --deprecated --quiet | jq '.exports[].name'
|
|
84
85
|
```
|
|
85
86
|
|
|
86
|
-
Options:
|
|
87
87
|
| Flag | Description |
|
|
88
88
|
|------|-------------|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
| `--
|
|
92
|
-
|
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
| `--kind <kinds>` | Filter by kinds (comma-separated) |
|
|
90
|
+
| `--name <names>` | Filter by exact names (comma-separated) |
|
|
91
|
+
| `--id <ids>` | Filter by export IDs (comma-separated) |
|
|
92
|
+
| `--tag <tags>` | Filter by tags (comma-separated) |
|
|
93
|
+
| `--deprecated` | Only deprecated exports |
|
|
94
|
+
| `--no-deprecated` | Exclude deprecated exports |
|
|
95
|
+
| `--has-description` | Only exports with descriptions |
|
|
96
|
+
| `--missing-description` | Only exports without descriptions |
|
|
97
|
+
| `--search <term>` | Search name/description (case-insensitive) |
|
|
98
|
+
| `--module <path>` | Filter by source file path (contains) |
|
|
99
|
+
| `-o, --output <file>` | Output file (default: stdout) |
|
|
100
|
+
| `--summary` | Only output matched/total counts |
|
|
101
|
+
| `--quiet` | Output raw spec only (no wrapper) |
|
|
95
102
|
|
|
96
|
-
|
|
103
|
+
### spec diff
|
|
97
104
|
|
|
98
105
|
```bash
|
|
99
|
-
openpkg diff old.json new.json
|
|
100
|
-
openpkg diff old.json new.json --summary
|
|
106
|
+
openpkg spec diff old.json new.json
|
|
107
|
+
openpkg spec diff old.json new.json --summary
|
|
101
108
|
```
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
- `breaking` - categorized breaking changes
|
|
105
|
-
- `added` - new exports
|
|
106
|
-
- `removed` - removed exports
|
|
107
|
-
- `changed` - modified exports
|
|
108
|
-
- `docsOnly` - documentation-only changes
|
|
109
|
-
- `summary.semverBump` - recommended version bump
|
|
110
|
+
### spec breaking
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Check for breaking changes. Exit 1 if any found.
|
|
112
|
+
Exit 1 if breaking changes found.
|
|
114
113
|
|
|
115
114
|
```bash
|
|
116
|
-
openpkg breaking old.json new.json
|
|
115
|
+
openpkg spec breaking old.json new.json
|
|
117
116
|
```
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
```json
|
|
121
|
-
{
|
|
122
|
-
"breaking": [
|
|
123
|
-
{ "id": "createClient", "name": "createClient", "kind": "function", "severity": "high", "reason": "signature changed" }
|
|
124
|
-
],
|
|
125
|
-
"count": 1
|
|
126
|
-
}
|
|
127
|
-
```
|
|
118
|
+
### spec semver
|
|
128
119
|
|
|
129
|
-
|
|
120
|
+
```bash
|
|
121
|
+
openpkg spec semver old.json new.json
|
|
122
|
+
```
|
|
130
123
|
|
|
131
|
-
|
|
124
|
+
### spec changelog
|
|
132
125
|
|
|
133
126
|
```bash
|
|
134
|
-
openpkg
|
|
127
|
+
openpkg spec changelog old.json new.json
|
|
128
|
+
openpkg spec changelog old.json new.json --format json
|
|
135
129
|
```
|
|
136
130
|
|
|
137
|
-
|
|
138
|
-
```json
|
|
139
|
-
{
|
|
140
|
-
"bump": "major",
|
|
141
|
-
"reason": "1 breaking change detected"
|
|
142
|
-
}
|
|
143
|
-
```
|
|
131
|
+
---
|
|
144
132
|
|
|
145
|
-
|
|
133
|
+
## Docs Commands
|
|
146
134
|
|
|
147
|
-
|
|
135
|
+
### docs init
|
|
136
|
+
|
|
137
|
+
Initialize docs configuration.
|
|
148
138
|
|
|
149
139
|
```bash
|
|
150
|
-
openpkg
|
|
151
|
-
openpkg validate openpkg.json --version 1.0
|
|
140
|
+
openpkg docs init
|
|
152
141
|
```
|
|
153
142
|
|
|
154
|
-
|
|
155
|
-
```json
|
|
156
|
-
{
|
|
157
|
-
"valid": true,
|
|
158
|
-
"errors": []
|
|
159
|
-
}
|
|
160
|
-
```
|
|
143
|
+
Creates `openpkg.config.json` with default settings.
|
|
161
144
|
|
|
162
|
-
###
|
|
145
|
+
### docs generate
|
|
163
146
|
|
|
164
|
-
Generate
|
|
147
|
+
Generate documentation from spec.
|
|
165
148
|
|
|
166
149
|
```bash
|
|
167
|
-
|
|
168
|
-
openpkg
|
|
169
|
-
```
|
|
150
|
+
# Markdown (default)
|
|
151
|
+
openpkg docs generate openpkg.json -o api.md
|
|
170
152
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
## Breaking Changes
|
|
174
|
-
- **Removed** `oldFunction` (function)
|
|
153
|
+
# React layout (single layout + spec JSON, add components via registry)
|
|
154
|
+
openpkg docs generate openpkg.json -f react -o ./app/api
|
|
175
155
|
|
|
176
|
-
|
|
177
|
-
-
|
|
178
|
-
```
|
|
156
|
+
# HTML
|
|
157
|
+
openpkg docs generate openpkg.json -f html -o api.html
|
|
179
158
|
|
|
180
|
-
|
|
159
|
+
# JSON (simplified structure)
|
|
160
|
+
openpkg docs generate openpkg.json -f json
|
|
181
161
|
|
|
182
|
-
|
|
162
|
+
# Split: one file per export
|
|
163
|
+
openpkg docs generate openpkg.json --split -o docs/api/
|
|
183
164
|
|
|
184
|
-
|
|
185
|
-
openpkg
|
|
165
|
+
# With adapter
|
|
166
|
+
openpkg docs generate openpkg.json -a fumadocs -o docs/api/
|
|
167
|
+
|
|
168
|
+
# From stdin
|
|
169
|
+
openpkg spec snapshot src/index.ts -o - | openpkg docs generate - -f md
|
|
186
170
|
```
|
|
187
171
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
172
|
+
| Flag | Description |
|
|
173
|
+
|------|-------------|
|
|
174
|
+
| `-o, --output <path>` | Output file or directory (default: stdout) |
|
|
175
|
+
| `-f, --format <fmt>` | Format: `md`, `json`, `html`, `react` (default: md) |
|
|
176
|
+
| `--split` | One file per export (requires `-o` as directory) |
|
|
177
|
+
| `-a, --adapter <name>` | Use adapter: `fumadocs`, `raw` (default: raw) |
|
|
178
|
+
|
|
179
|
+
### docs add
|
|
180
|
+
|
|
181
|
+
Add components from shadcn-compatible registry.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
openpkg docs add function-section
|
|
185
|
+
openpkg docs add class-section interface-section
|
|
186
|
+
openpkg docs add export-card param-table signature
|
|
199
187
|
```
|
|
200
188
|
|
|
201
|
-
###
|
|
189
|
+
### docs list
|
|
202
190
|
|
|
203
|
-
|
|
191
|
+
List available registry components.
|
|
204
192
|
|
|
205
193
|
```bash
|
|
206
|
-
openpkg
|
|
207
|
-
openpkg filter openpkg.json --has-description -o documented.json
|
|
208
|
-
openpkg filter openpkg.json --search "user" --summary
|
|
209
|
-
openpkg filter openpkg.json --deprecated --quiet | jq '.exports[].name'
|
|
194
|
+
openpkg docs list
|
|
210
195
|
```
|
|
211
196
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
| `--kind <kinds>` | Filter by kinds (comma-separated) |
|
|
216
|
-
| `--name <names>` | Filter by exact names (comma-separated) |
|
|
217
|
-
| `--id <ids>` | Filter by export IDs (comma-separated) |
|
|
218
|
-
| `--tag <tags>` | Filter by tags (comma-separated) |
|
|
219
|
-
| `--deprecated` | Only deprecated exports |
|
|
220
|
-
| `--no-deprecated` | Exclude deprecated exports |
|
|
221
|
-
| `--has-description` | Only exports with descriptions |
|
|
222
|
-
| `--missing-description` | Only exports without descriptions |
|
|
223
|
-
| `--search <term>` | Search name/description (case-insensitive) |
|
|
224
|
-
| `--module <path>` | Filter by source file path (contains) |
|
|
225
|
-
| `-o, --output <file>` | Output file (default: stdout) |
|
|
226
|
-
| `--summary` | Only output matched/total counts |
|
|
227
|
-
| `--quiet` | Output raw spec only (no wrapper) |
|
|
197
|
+
16 components available: layouts, sections, primitives.
|
|
198
|
+
|
|
199
|
+
### docs view
|
|
228
200
|
|
|
229
|
-
|
|
201
|
+
View component details and dependencies.
|
|
230
202
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
{
|
|
234
|
-
"spec": { ... },
|
|
235
|
-
"matched": 12,
|
|
236
|
-
"total": 45
|
|
237
|
-
}
|
|
203
|
+
```bash
|
|
204
|
+
openpkg docs view function-section
|
|
238
205
|
```
|
|
239
206
|
|
|
207
|
+
---
|
|
208
|
+
|
|
240
209
|
## Pipelines
|
|
241
210
|
|
|
242
211
|
Commands are composable via stdin/stdout:
|
|
243
212
|
|
|
244
213
|
```bash
|
|
245
214
|
# Extract and generate docs
|
|
246
|
-
openpkg snapshot src/index.ts -o - | openpkg docs - -f md > api.md
|
|
215
|
+
openpkg spec snapshot src/index.ts -o - | openpkg docs generate - -f md > api.md
|
|
247
216
|
|
|
248
217
|
# Extract, verify, then diff
|
|
249
|
-
openpkg snapshot src/index.ts --verify -o new.json
|
|
250
|
-
openpkg diff baseline.json new.json --summary
|
|
218
|
+
openpkg spec snapshot src/index.ts --verify -o new.json
|
|
219
|
+
openpkg spec diff baseline.json new.json --summary
|
|
251
220
|
```
|
|
252
221
|
|
|
253
222
|
## Programmatic Use
|
package/dist/bin/openpkg.js
CHANGED
|
@@ -5,13 +5,11 @@ import {
|
|
|
5
5
|
} from "../shared/chunk-1dqs11h6.js";
|
|
6
6
|
|
|
7
7
|
// bin/openpkg.ts
|
|
8
|
-
import
|
|
9
|
-
import { getExport, listExports } from "@openpkg-ts/sdk";
|
|
10
|
-
import { Command as Command10 } from "commander";
|
|
8
|
+
import { Command as Command12 } from "commander";
|
|
11
9
|
// package.json
|
|
12
10
|
var package_default = {
|
|
13
11
|
name: "@openpkg-ts/cli",
|
|
14
|
-
version: "0.
|
|
12
|
+
version: "0.6.1",
|
|
15
13
|
description: "CLI for OpenPkg TypeScript API extraction and documentation generation",
|
|
16
14
|
homepage: "https://github.com/ryanwaits/openpkg-ts#readme",
|
|
17
15
|
repository: {
|
|
@@ -34,14 +32,14 @@ var package_default = {
|
|
|
34
32
|
test: "bun test"
|
|
35
33
|
},
|
|
36
34
|
dependencies: {
|
|
37
|
-
"@openpkg-ts/adapters": "^0.3.
|
|
38
|
-
"@openpkg-ts/sdk": "^0.
|
|
35
|
+
"@openpkg-ts/adapters": "^0.3.4",
|
|
36
|
+
"@openpkg-ts/sdk": "^0.34.1",
|
|
39
37
|
commander: "^14.0.0"
|
|
40
38
|
},
|
|
41
39
|
devDependencies: {
|
|
42
40
|
"@types/bun": "latest",
|
|
43
41
|
"@types/node": "^20.0.0",
|
|
44
|
-
bunup: "
|
|
42
|
+
bunup: "^0.16.20"
|
|
45
43
|
},
|
|
46
44
|
publishConfig: {
|
|
47
45
|
access: "public"
|
|
@@ -253,78 +251,108 @@ function createChangelogCommand() {
|
|
|
253
251
|
});
|
|
254
252
|
}
|
|
255
253
|
|
|
256
|
-
// src/commands/
|
|
254
|
+
// src/commands/docs/index.ts
|
|
255
|
+
import { Command as Command9 } from "commander";
|
|
256
|
+
|
|
257
|
+
// src/commands/docs/add.ts
|
|
258
|
+
import { spawn } from "node:child_process";
|
|
259
|
+
import { Command as Command4 } from "commander";
|
|
260
|
+
|
|
261
|
+
// src/commands/docs/utils.ts
|
|
257
262
|
import * as fs4 from "node:fs";
|
|
258
263
|
import * as path4 from "node:path";
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const resolved = path4.resolve(filePath);
|
|
263
|
-
const content = fs4.readFileSync(resolved, "utf-8");
|
|
264
|
-
return JSON.parse(content);
|
|
265
|
-
}
|
|
266
|
-
function createDiagnosticsCommand() {
|
|
267
|
-
return new Command4("diagnostics").description("Analyze spec for quality issues (missing docs, deprecated without reason)").argument("<spec>", "Path to spec file (JSON)").option("--verbose", "Show detailed information including skipped export details").action(async (specPath, options) => {
|
|
264
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
265
|
+
const pkgJsonPath = path4.join(cwd, "package.json");
|
|
266
|
+
if (fs4.existsSync(pkgJsonPath)) {
|
|
268
267
|
try {
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
const pkg = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
|
|
269
|
+
if (pkg.packageManager) {
|
|
270
|
+
if (pkg.packageManager.startsWith("bun"))
|
|
271
|
+
return "bun";
|
|
272
|
+
if (pkg.packageManager.startsWith("pnpm"))
|
|
273
|
+
return "pnpm";
|
|
274
|
+
if (pkg.packageManager.startsWith("yarn"))
|
|
275
|
+
return "yarn";
|
|
276
|
+
if (pkg.packageManager.startsWith("npm"))
|
|
277
|
+
return "npm";
|
|
277
278
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
279
|
+
} catch {}
|
|
280
|
+
}
|
|
281
|
+
if (fs4.existsSync(path4.join(cwd, "bun.lockb")) || fs4.existsSync(path4.join(cwd, "bun.lock"))) {
|
|
282
|
+
return "bun";
|
|
283
|
+
}
|
|
284
|
+
if (fs4.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) {
|
|
285
|
+
return "pnpm";
|
|
286
|
+
}
|
|
287
|
+
if (fs4.existsSync(path4.join(cwd, "yarn.lock"))) {
|
|
288
|
+
return "yarn";
|
|
289
|
+
}
|
|
290
|
+
return "npm";
|
|
291
|
+
}
|
|
292
|
+
function getDlxCommand(pkg, pm) {
|
|
293
|
+
const packageManager = pm || detectPackageManager();
|
|
294
|
+
switch (packageManager) {
|
|
295
|
+
case "bun":
|
|
296
|
+
return { cmd: "bunx", args: [pkg] };
|
|
297
|
+
case "pnpm":
|
|
298
|
+
return { cmd: "pnpm", args: ["dlx", pkg] };
|
|
299
|
+
case "yarn":
|
|
300
|
+
return { cmd: "yarn", args: ["dlx", pkg] };
|
|
301
|
+
default:
|
|
302
|
+
return { cmd: "npx", args: [pkg] };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function getShadcnCommand(subcommand, args = []) {
|
|
306
|
+
const dlx = getDlxCommand("shadcn@latest");
|
|
307
|
+
return {
|
|
308
|
+
cmd: dlx.cmd,
|
|
309
|
+
args: [...dlx.args, subcommand, ...args]
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/commands/docs/add.ts
|
|
314
|
+
function createAddCommand() {
|
|
315
|
+
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) => {
|
|
316
|
+
const prefixedComponents = components.map((c) => c.startsWith("@") ? c : `@openpkg/${c}`);
|
|
317
|
+
const extraArgs = [];
|
|
318
|
+
if (options.overwrite)
|
|
319
|
+
extraArgs.push("--overwrite");
|
|
320
|
+
if (options.cwd)
|
|
321
|
+
extraArgs.push("--cwd", options.cwd);
|
|
322
|
+
if (options.yes)
|
|
323
|
+
extraArgs.push("--yes");
|
|
324
|
+
if (options.silent)
|
|
325
|
+
extraArgs.push("--silent");
|
|
326
|
+
const { cmd, args } = getShadcnCommand("add", [...prefixedComponents, ...extraArgs]);
|
|
327
|
+
if (!options.silent) {
|
|
328
|
+
console.log(`Running: ${cmd} ${args.join(" ")}`);
|
|
329
|
+
console.log("");
|
|
313
330
|
}
|
|
331
|
+
const child = spawn(cmd, args, {
|
|
332
|
+
stdio: "inherit",
|
|
333
|
+
shell: true
|
|
334
|
+
});
|
|
335
|
+
child.on("close", (code) => {
|
|
336
|
+
process.exit(code || 0);
|
|
337
|
+
});
|
|
314
338
|
});
|
|
315
339
|
}
|
|
316
340
|
|
|
317
|
-
// src/commands/docs.ts
|
|
341
|
+
// src/commands/docs/generate.ts
|
|
318
342
|
import * as fs5 from "node:fs";
|
|
319
343
|
import * as path5 from "node:path";
|
|
320
|
-
import {
|
|
344
|
+
import { loadSpec as loadSpec4, query, toReact } from "@openpkg-ts/sdk";
|
|
321
345
|
import { Command as Command5 } from "commander";
|
|
322
346
|
async function readStdin() {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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)}`);
|
|
326
355
|
}
|
|
327
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
328
356
|
}
|
|
329
357
|
function getExtension(format) {
|
|
330
358
|
switch (format) {
|
|
@@ -332,10 +360,32 @@ function getExtension(format) {
|
|
|
332
360
|
return ".json";
|
|
333
361
|
case "html":
|
|
334
362
|
return ".html";
|
|
363
|
+
case "react":
|
|
364
|
+
return ".tsx";
|
|
335
365
|
default:
|
|
336
366
|
return ".md";
|
|
337
367
|
}
|
|
338
368
|
}
|
|
369
|
+
function applyFilters(spec, options) {
|
|
370
|
+
let qb = query(spec);
|
|
371
|
+
if (options.kind) {
|
|
372
|
+
const kinds = options.kind.split(",").map((k) => k.trim());
|
|
373
|
+
qb = qb.byKind(...kinds);
|
|
374
|
+
}
|
|
375
|
+
if (options.tag) {
|
|
376
|
+
const tags = options.tag.split(",").map((t) => t.trim());
|
|
377
|
+
qb = qb.byTag(...tags);
|
|
378
|
+
}
|
|
379
|
+
if (options.search) {
|
|
380
|
+
qb = qb.search(options.search);
|
|
381
|
+
}
|
|
382
|
+
if (options.deprecated === true) {
|
|
383
|
+
qb = qb.deprecated(true);
|
|
384
|
+
} else if (options.deprecated === false) {
|
|
385
|
+
qb = qb.deprecated(false);
|
|
386
|
+
}
|
|
387
|
+
return qb.toSpec();
|
|
388
|
+
}
|
|
339
389
|
function renderExport(docs, exportId, format, collapseUnionThreshold) {
|
|
340
390
|
const exp = docs.getExport(exportId);
|
|
341
391
|
if (!exp)
|
|
@@ -364,8 +414,8 @@ function renderFull(docs, format, collapseUnionThreshold) {
|
|
|
364
414
|
return docs.toMarkdown({ frontmatter: true, codeSignatures: true, collapseUnionThreshold });
|
|
365
415
|
}
|
|
366
416
|
}
|
|
367
|
-
function
|
|
368
|
-
return new Command5("
|
|
417
|
+
function createGenerateCommand() {
|
|
418
|
+
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) => {
|
|
369
419
|
const format = options.format || "md";
|
|
370
420
|
try {
|
|
371
421
|
if (options.adapter && options.adapter !== "raw") {
|
|
@@ -387,36 +437,56 @@ function createDocsCommand() {
|
|
|
387
437
|
console.error(JSON.stringify({ error: "--adapter requires --output <directory>" }));
|
|
388
438
|
process.exit(1);
|
|
389
439
|
}
|
|
390
|
-
let
|
|
440
|
+
let spec2;
|
|
391
441
|
if (specPath === "-") {
|
|
392
442
|
const input = await readStdin();
|
|
393
|
-
|
|
443
|
+
spec2 = JSON.parse(input);
|
|
394
444
|
} else {
|
|
395
445
|
const specFile = path5.resolve(specPath);
|
|
396
446
|
if (!fs5.existsSync(specFile)) {
|
|
397
447
|
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
398
448
|
process.exit(1);
|
|
399
449
|
}
|
|
400
|
-
|
|
450
|
+
spec2 = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
401
451
|
}
|
|
402
|
-
|
|
452
|
+
spec2 = applyFilters(spec2, options);
|
|
453
|
+
await adapter.generate(spec2, path5.resolve(options.output));
|
|
403
454
|
console.error(`Generated docs with ${options.adapter} adapter to ${options.output}`);
|
|
404
455
|
return;
|
|
405
456
|
}
|
|
406
|
-
let
|
|
457
|
+
let spec;
|
|
407
458
|
if (specPath === "-") {
|
|
408
459
|
const input = await readStdin();
|
|
409
|
-
|
|
410
|
-
docs = loadSpec4(spec);
|
|
460
|
+
spec = JSON.parse(input);
|
|
411
461
|
} else {
|
|
412
462
|
const specFile = path5.resolve(specPath);
|
|
413
463
|
if (!fs5.existsSync(specFile)) {
|
|
414
464
|
console.error(JSON.stringify({ error: `Spec file not found: ${specFile}` }));
|
|
415
465
|
process.exit(1);
|
|
416
466
|
}
|
|
417
|
-
|
|
467
|
+
spec = JSON.parse(fs5.readFileSync(specFile, "utf-8"));
|
|
418
468
|
}
|
|
469
|
+
spec = applyFilters(spec, options);
|
|
470
|
+
const docs = loadSpec4(spec);
|
|
419
471
|
const collapseUnionThreshold = options.collapseUnions ? parseInt(options.collapseUnions, 10) : undefined;
|
|
472
|
+
if (format === "react") {
|
|
473
|
+
if (!options.output) {
|
|
474
|
+
console.error(JSON.stringify({ error: "--format react requires --output <directory>" }));
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
const variant = options.variant === "index" ? "index" : "full";
|
|
478
|
+
await toReact(spec, {
|
|
479
|
+
outDir: path5.resolve(options.output),
|
|
480
|
+
variant,
|
|
481
|
+
componentsPath: options.componentsPath ?? "@/components/api"
|
|
482
|
+
});
|
|
483
|
+
console.error(`Generated React layout to ${options.output}`);
|
|
484
|
+
console.error(` - page.tsx: Layout file`);
|
|
485
|
+
console.error(` - openpkg.json: Spec data`);
|
|
486
|
+
console.error(`
|
|
487
|
+
Next: Add components with 'openpkg docs add function-section'`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
420
490
|
if (options.export) {
|
|
421
491
|
const exports = docs.getAllExports();
|
|
422
492
|
const exp = exports.find((e) => e.name === options.export);
|
|
@@ -445,8 +515,14 @@ function createDocsCommand() {
|
|
|
445
515
|
}
|
|
446
516
|
const exports = docs.getAllExports();
|
|
447
517
|
for (const exp of exports) {
|
|
448
|
-
const filename = `${exp.name}${getExtension(format)}
|
|
518
|
+
const filename = path5.basename(`${exp.name}${getExtension(format)}`);
|
|
449
519
|
const filePath = path5.join(outDir, filename);
|
|
520
|
+
const resolvedPath = path5.resolve(filePath);
|
|
521
|
+
const resolvedOutDir = path5.resolve(outDir);
|
|
522
|
+
if (!resolvedPath.startsWith(resolvedOutDir + path5.sep)) {
|
|
523
|
+
console.error(JSON.stringify({ error: `Path traversal detected: ${exp.name}` }));
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
450
526
|
const content = renderExport(docs, exp.id, format, collapseUnionThreshold);
|
|
451
527
|
fs5.writeFileSync(filePath, content);
|
|
452
528
|
}
|
|
@@ -469,108 +545,115 @@ function createDocsCommand() {
|
|
|
469
545
|
});
|
|
470
546
|
}
|
|
471
547
|
|
|
472
|
-
// src/commands/
|
|
548
|
+
// src/commands/docs/init.ts
|
|
473
549
|
import * as fs6 from "node:fs";
|
|
474
550
|
import * as path6 from "node:path";
|
|
475
|
-
import { filterSpec } from "@openpkg-ts/sdk";
|
|
476
551
|
import { Command as Command6 } from "commander";
|
|
477
|
-
var
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"
|
|
484
|
-
"module",
|
|
485
|
-
"namespace",
|
|
486
|
-
"reference",
|
|
487
|
-
"external"
|
|
488
|
-
];
|
|
489
|
-
function loadSpec5(filePath) {
|
|
490
|
-
const resolved = path6.resolve(filePath);
|
|
491
|
-
const content = fs6.readFileSync(resolved, "utf-8");
|
|
492
|
-
return JSON.parse(content);
|
|
493
|
-
}
|
|
494
|
-
function parseList(val) {
|
|
495
|
-
if (!val)
|
|
496
|
-
return;
|
|
497
|
-
return val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
498
|
-
}
|
|
499
|
-
function validateKinds(kinds) {
|
|
500
|
-
const invalid = kinds.filter((k) => !VALID_KINDS.includes(k));
|
|
501
|
-
if (invalid.length > 0) {
|
|
502
|
-
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${VALID_KINDS.join(", ")}`);
|
|
503
|
-
}
|
|
504
|
-
return kinds;
|
|
552
|
+
var COMPONENTS_JSON = "components.json";
|
|
553
|
+
var REGISTRY_URL = "https://raw.githubusercontent.com/anthropics/openpkg-ts/main/registry/r/{name}.json";
|
|
554
|
+
function loadComponentsJson() {
|
|
555
|
+
const configPath = path6.resolve(COMPONENTS_JSON);
|
|
556
|
+
if (!fs6.existsSync(configPath))
|
|
557
|
+
return null;
|
|
558
|
+
return JSON.parse(fs6.readFileSync(configPath, "utf-8"));
|
|
505
559
|
}
|
|
506
|
-
function
|
|
507
|
-
return new Command6("
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (options.id)
|
|
519
|
-
criteria.ids = parseList(options.id);
|
|
520
|
-
if (options.tag)
|
|
521
|
-
criteria.tags = parseList(options.tag);
|
|
522
|
-
if (options.deprecated !== undefined)
|
|
523
|
-
criteria.deprecated = options.deprecated;
|
|
524
|
-
if (options.hasDescription)
|
|
525
|
-
criteria.hasDescription = true;
|
|
526
|
-
if (options.missingDescription)
|
|
527
|
-
criteria.hasDescription = false;
|
|
528
|
-
if (options.search)
|
|
529
|
-
criteria.search = options.search;
|
|
530
|
-
if (options.searchMembers)
|
|
531
|
-
criteria.searchMembers = true;
|
|
532
|
-
if (options.searchDocs)
|
|
533
|
-
criteria.searchDocs = true;
|
|
534
|
-
if (options.module)
|
|
535
|
-
criteria.module = options.module;
|
|
536
|
-
const result = filterSpec(spec, criteria);
|
|
537
|
-
let output;
|
|
538
|
-
if (options.summary) {
|
|
539
|
-
output = { matched: result.matched, total: result.total };
|
|
540
|
-
} else if (options.quiet) {
|
|
541
|
-
output = result.spec;
|
|
542
|
-
} else {
|
|
543
|
-
output = { spec: result.spec, matched: result.matched, total: result.total };
|
|
544
|
-
}
|
|
545
|
-
const json = JSON.stringify(output, null, 2);
|
|
546
|
-
if (options.output) {
|
|
547
|
-
fs6.writeFileSync(path6.resolve(options.output), json);
|
|
548
|
-
} else {
|
|
549
|
-
console.log(json);
|
|
550
|
-
}
|
|
551
|
-
} catch (err) {
|
|
552
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
553
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
560
|
+
function createInitCommand() {
|
|
561
|
+
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 = path6.resolve(COMPONENTS_JSON);
|
|
563
|
+
const registryUrl = options.registry || REGISTRY_URL;
|
|
564
|
+
if (!fs6.existsSync(configPath)) {
|
|
565
|
+
console.error(`${COMPONENTS_JSON} not found.`);
|
|
566
|
+
console.error('Run "npx shadcn@latest init" first to initialize shadcn.');
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
const config = loadComponentsJson();
|
|
570
|
+
if (!config) {
|
|
571
|
+
console.error(`Failed to parse ${COMPONENTS_JSON}`);
|
|
554
572
|
process.exit(1);
|
|
555
573
|
}
|
|
574
|
+
config.registries = config.registries || {};
|
|
575
|
+
config.registries["@openpkg"] = registryUrl;
|
|
576
|
+
fs6.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
577
|
+
console.log(`Added @openpkg registry to ${COMPONENTS_JSON}`);
|
|
578
|
+
console.log("");
|
|
579
|
+
console.log("Usage:");
|
|
580
|
+
console.log(" npx shadcn@latest add @openpkg/function-section");
|
|
581
|
+
console.log(" npx shadcn@latest add @openpkg/export-card");
|
|
582
|
+
console.log("");
|
|
583
|
+
console.log("List components:");
|
|
584
|
+
console.log(" openpkg docs list");
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/commands/docs/list.ts
|
|
589
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
590
|
+
import { Command as Command7 } from "commander";
|
|
591
|
+
function createListCommand() {
|
|
592
|
+
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) => {
|
|
593
|
+
const extraArgs = ["@openpkg"];
|
|
594
|
+
if (options.query)
|
|
595
|
+
extraArgs.push("-q", options.query);
|
|
596
|
+
if (options.limit)
|
|
597
|
+
extraArgs.push("-l", options.limit);
|
|
598
|
+
if (options.cwd)
|
|
599
|
+
extraArgs.push("-c", options.cwd);
|
|
600
|
+
const { cmd, args } = getShadcnCommand("list", extraArgs);
|
|
601
|
+
const child = spawn2(cmd, args, {
|
|
602
|
+
stdio: "inherit",
|
|
603
|
+
shell: true
|
|
604
|
+
});
|
|
605
|
+
child.on("close", (code) => {
|
|
606
|
+
process.exit(code || 0);
|
|
607
|
+
});
|
|
556
608
|
});
|
|
557
609
|
}
|
|
558
610
|
|
|
611
|
+
// src/commands/docs/view.ts
|
|
612
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
613
|
+
import { Command as Command8 } from "commander";
|
|
614
|
+
function createViewCommand() {
|
|
615
|
+
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) => {
|
|
616
|
+
const prefixedComponents = components.map((c) => c.startsWith("@") ? c : `@openpkg/${c}`);
|
|
617
|
+
const extraArgs = [...prefixedComponents];
|
|
618
|
+
if (options.cwd)
|
|
619
|
+
extraArgs.push("-c", options.cwd);
|
|
620
|
+
const { cmd, args } = getShadcnCommand("view", extraArgs);
|
|
621
|
+
const child = spawn3(cmd, args, {
|
|
622
|
+
stdio: "inherit",
|
|
623
|
+
shell: true
|
|
624
|
+
});
|
|
625
|
+
child.on("close", (code) => {
|
|
626
|
+
process.exit(code || 0);
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/commands/docs/index.ts
|
|
632
|
+
function createDocsCommand() {
|
|
633
|
+
const docs = new Command9("docs").description("Documentation generation and component registry");
|
|
634
|
+
docs.addCommand(createGenerateCommand());
|
|
635
|
+
docs.addCommand(createInitCommand());
|
|
636
|
+
docs.addCommand(createListCommand());
|
|
637
|
+
docs.addCommand(createViewCommand());
|
|
638
|
+
docs.addCommand(createAddCommand());
|
|
639
|
+
return docs;
|
|
640
|
+
}
|
|
641
|
+
|
|
559
642
|
// src/commands/semver.ts
|
|
560
643
|
import * as fs7 from "node:fs";
|
|
561
644
|
import * as path7 from "node:path";
|
|
562
645
|
import { diffSpec as diffSpec3, recommendSemverBump as recommendSemverBump2 } from "@openpkg-ts/spec";
|
|
563
|
-
import { Command as
|
|
564
|
-
function
|
|
646
|
+
import { Command as Command10 } from "commander";
|
|
647
|
+
function loadSpec5(filePath) {
|
|
565
648
|
const resolved = path7.resolve(filePath);
|
|
566
649
|
const content = fs7.readFileSync(resolved, "utf-8");
|
|
567
650
|
return JSON.parse(content);
|
|
568
651
|
}
|
|
569
652
|
function createSemverCommand() {
|
|
570
|
-
return new
|
|
653
|
+
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) => {
|
|
571
654
|
try {
|
|
572
|
-
const oldSpec =
|
|
573
|
-
const newSpec =
|
|
655
|
+
const oldSpec = loadSpec5(oldPath);
|
|
656
|
+
const newSpec = loadSpec5(newPath);
|
|
574
657
|
const diff = diffSpec3(oldSpec, newSpec);
|
|
575
658
|
const recommendation = recommendSemverBump2(diff);
|
|
576
659
|
const result = {
|
|
@@ -586,19 +669,64 @@ function createSemverCommand() {
|
|
|
586
669
|
});
|
|
587
670
|
}
|
|
588
671
|
|
|
589
|
-
// src/commands/
|
|
672
|
+
// src/commands/spec.ts
|
|
590
673
|
import * as fs8 from "node:fs";
|
|
591
674
|
import * as path8 from "node:path";
|
|
592
675
|
import {
|
|
676
|
+
analyzeSpec,
|
|
593
677
|
extractSpec,
|
|
678
|
+
filterSpec,
|
|
679
|
+
getExport,
|
|
680
|
+
listExports,
|
|
594
681
|
loadConfig,
|
|
595
682
|
mergeConfig
|
|
596
683
|
} from "@openpkg-ts/sdk";
|
|
597
|
-
import {
|
|
598
|
-
|
|
599
|
-
|
|
684
|
+
import { getValidationErrors } from "@openpkg-ts/spec";
|
|
685
|
+
import { Command as Command11 } from "commander";
|
|
686
|
+
var VALID_KINDS = [
|
|
687
|
+
"function",
|
|
688
|
+
"class",
|
|
689
|
+
"variable",
|
|
690
|
+
"interface",
|
|
691
|
+
"type",
|
|
692
|
+
"enum",
|
|
693
|
+
"module",
|
|
694
|
+
"namespace",
|
|
695
|
+
"reference",
|
|
696
|
+
"external"
|
|
697
|
+
];
|
|
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
|
+
function parseList(val) {
|
|
720
|
+
if (!val)
|
|
600
721
|
return;
|
|
601
|
-
return
|
|
722
|
+
return val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
723
|
+
}
|
|
724
|
+
function validateKinds(kinds) {
|
|
725
|
+
const invalid = kinds.filter((k) => !VALID_KINDS.includes(k));
|
|
726
|
+
if (invalid.length > 0) {
|
|
727
|
+
throw new Error(`Invalid kind(s): ${invalid.join(", ")}. Valid kinds: ${VALID_KINDS.join(", ")}`);
|
|
728
|
+
}
|
|
729
|
+
return kinds;
|
|
602
730
|
}
|
|
603
731
|
function formatDiagnostics(diagnostics) {
|
|
604
732
|
return diagnostics.map((d) => ({
|
|
@@ -609,11 +737,8 @@ function formatDiagnostics(diagnostics) {
|
|
|
609
737
|
...d.location && { location: d.location }
|
|
610
738
|
}));
|
|
611
739
|
}
|
|
612
|
-
function
|
|
613
|
-
return new
|
|
614
|
-
|
|
615
|
-
` + `Config: Reads from openpkg.config.json or package.json "openpkg" field.
|
|
616
|
-
` + "CLI flags override config file settings.").argument("<entry>", "Entry point file path").option("-o, --output <file>", "Output file (default: openpkg.json, use - for stdout)", "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, wildcards supported)").option("--ignore <exports>", "Ignore exports (comma-separated, wildcards supported)").option("--verify", "Exit 1 if any exports fail").option("--verbose", "Show detailed output including skipped exports").option("--include-private", "Include private/protected class members").option("--external-include <patterns...>", "Resolve re-exports from these packages (globs supported)").option("--external-exclude <patterns...>", "Never resolve from these packages").option("--external-depth <n>", "Max transitive depth for external resolution (default: 1)", "1").action(async (entry, options) => {
|
|
740
|
+
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) => {
|
|
617
742
|
const entryFile = path8.resolve(entry);
|
|
618
743
|
const entryDir = path8.dirname(entryFile);
|
|
619
744
|
const fileConfig = loadConfig(entryDir);
|
|
@@ -630,8 +755,8 @@ function createSnapshotCommand() {
|
|
|
630
755
|
maxTypeDepth: parseInt(options.maxDepth ?? "4", 10),
|
|
631
756
|
resolveExternalTypes: !options.skipResolve,
|
|
632
757
|
schemaExtraction: options.runtime ? "hybrid" : "static",
|
|
633
|
-
only:
|
|
634
|
-
ignore:
|
|
758
|
+
only: parseList(options.only),
|
|
759
|
+
ignore: parseList(options.ignore),
|
|
635
760
|
includePrivate: options.includePrivate,
|
|
636
761
|
...mergedConfig.externals && { externals: mergedConfig.externals }
|
|
637
762
|
};
|
|
@@ -653,58 +778,15 @@ function createSnapshotCommand() {
|
|
|
653
778
|
}
|
|
654
779
|
}
|
|
655
780
|
},
|
|
656
|
-
...externalExports.length > 0 && {
|
|
657
|
-
external: {
|
|
658
|
-
count: externalExports.length,
|
|
659
|
-
...options.verbose && {
|
|
660
|
-
exports: externalExports.map((e) => ({
|
|
661
|
-
name: e.name,
|
|
662
|
-
package: e.source?.package
|
|
663
|
-
}))
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
},
|
|
667
|
-
...result.runtimeSchemas && {
|
|
668
|
-
runtime: {
|
|
669
|
-
extracted: result.runtimeSchemas.extracted,
|
|
670
|
-
merged: result.runtimeSchemas.merged,
|
|
671
|
-
vendors: result.runtimeSchemas.vendors
|
|
672
|
-
}
|
|
673
|
-
}
|
|
781
|
+
...externalExports.length > 0 && { external: { count: externalExports.length } }
|
|
674
782
|
};
|
|
675
783
|
console.error(JSON.stringify(summary, null, 2));
|
|
676
|
-
if (externalExports.length > 0 || (result.verification?.skipped ?? 0) > 0) {
|
|
677
|
-
console.error("");
|
|
678
|
-
if (externalExports.length > 0) {
|
|
679
|
-
if (options.verbose) {
|
|
680
|
-
console.error(`⚠ ${externalExports.length} external re-export(s) (install dependencies for full type info):`);
|
|
681
|
-
for (const exp of externalExports) {
|
|
682
|
-
console.error(` - ${exp.name} from "${exp.source?.package}"`);
|
|
683
|
-
}
|
|
684
|
-
} else {
|
|
685
|
-
console.error(`⚠ ${externalExports.length} external re-export(s) (install dependencies for full type info)`);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
const skipped = result.verification?.details.skipped ?? [];
|
|
689
|
-
if (skipped.length > 0) {
|
|
690
|
-
if (options.verbose) {
|
|
691
|
-
console.error(`⚠ ${skipped.length} export(s) skipped:`);
|
|
692
|
-
for (const skip of skipped) {
|
|
693
|
-
const pkgInfo = skip.package ? ` from "${skip.package}"` : "";
|
|
694
|
-
console.error(` - ${skip.name} (${skip.reason})${pkgInfo}`);
|
|
695
|
-
}
|
|
696
|
-
} else {
|
|
697
|
-
console.error(`⚠ ${skipped.length} export(s) skipped (use --verbose for details)`);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
784
|
if (options.verify && result.verification && result.verification.failed > 0) {
|
|
702
|
-
|
|
785
|
+
console.error(JSON.stringify({
|
|
703
786
|
error: "Export verification failed",
|
|
704
787
|
failed: result.verification.details.failed,
|
|
705
788
|
diagnostics: formatDiagnostics(result.diagnostics)
|
|
706
|
-
};
|
|
707
|
-
console.error(JSON.stringify(errorOutput, null, 2));
|
|
789
|
+
}, null, 2));
|
|
708
790
|
process.exit(1);
|
|
709
791
|
}
|
|
710
792
|
const specJson = JSON.stringify(result.spec, null, 2);
|
|
@@ -717,40 +799,118 @@ function createSnapshotCommand() {
|
|
|
717
799
|
}
|
|
718
800
|
} catch (err) {
|
|
719
801
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
720
|
-
|
|
721
|
-
error: error.message,
|
|
722
|
-
...error.stack && { stack: error.stack }
|
|
723
|
-
};
|
|
724
|
-
console.error(JSON.stringify(errorOutput, null, 2));
|
|
802
|
+
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
725
803
|
process.exit(1);
|
|
726
804
|
}
|
|
727
805
|
});
|
|
728
806
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
import * as fs9 from "node:fs";
|
|
732
|
-
import * as path9 from "node:path";
|
|
733
|
-
import { getValidationErrors } from "@openpkg-ts/spec";
|
|
734
|
-
import { Command as Command9 } from "commander";
|
|
735
|
-
function loadJSON2(filePath) {
|
|
736
|
-
const resolved = path9.resolve(filePath);
|
|
737
|
-
const content = fs9.readFileSync(resolved, "utf-8");
|
|
738
|
-
return JSON.parse(content);
|
|
739
|
-
}
|
|
740
|
-
function createValidateCommand() {
|
|
741
|
-
return new Command9("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) => {
|
|
807
|
+
function createValidateSubcommand() {
|
|
808
|
+
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) => {
|
|
742
809
|
try {
|
|
743
|
-
const spec =
|
|
810
|
+
const spec = loadSpec6(specPath);
|
|
744
811
|
const version = options.version ?? "latest";
|
|
745
812
|
const errors = getValidationErrors(spec, version);
|
|
813
|
+
console.log(JSON.stringify({ valid: errors.length === 0, errors }, null, 2));
|
|
814
|
+
if (errors.length > 0)
|
|
815
|
+
process.exit(1);
|
|
816
|
+
} catch (err) {
|
|
817
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
818
|
+
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
function createFilterSubcommand() {
|
|
824
|
+
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
|
+
try {
|
|
826
|
+
const spec = loadSpec6(specPath);
|
|
827
|
+
const criteria = {};
|
|
828
|
+
if (options.kind) {
|
|
829
|
+
const kinds = parseList(options.kind);
|
|
830
|
+
if (kinds)
|
|
831
|
+
criteria.kinds = validateKinds(kinds);
|
|
832
|
+
}
|
|
833
|
+
if (options.name)
|
|
834
|
+
criteria.names = parseList(options.name);
|
|
835
|
+
if (options.id)
|
|
836
|
+
criteria.ids = parseList(options.id);
|
|
837
|
+
if (options.tag)
|
|
838
|
+
criteria.tags = parseList(options.tag);
|
|
839
|
+
if (options.deprecated !== undefined)
|
|
840
|
+
criteria.deprecated = options.deprecated;
|
|
841
|
+
if (options.hasDescription)
|
|
842
|
+
criteria.hasDescription = true;
|
|
843
|
+
if (options.missingDescription)
|
|
844
|
+
criteria.hasDescription = false;
|
|
845
|
+
if (options.search)
|
|
846
|
+
criteria.search = options.search;
|
|
847
|
+
if (options.searchMembers)
|
|
848
|
+
criteria.searchMembers = true;
|
|
849
|
+
if (options.searchDocs)
|
|
850
|
+
criteria.searchDocs = true;
|
|
851
|
+
if (options.module)
|
|
852
|
+
criteria.module = options.module;
|
|
853
|
+
const result = filterSpec(spec, criteria);
|
|
854
|
+
let output;
|
|
855
|
+
if (options.summary) {
|
|
856
|
+
output = { matched: result.matched, total: result.total };
|
|
857
|
+
} else if (options.quiet) {
|
|
858
|
+
output = result.spec;
|
|
859
|
+
} else {
|
|
860
|
+
output = { spec: result.spec, matched: result.matched, total: result.total };
|
|
861
|
+
}
|
|
862
|
+
const json = JSON.stringify(output, null, 2);
|
|
863
|
+
if (options.output) {
|
|
864
|
+
fs8.writeFileSync(path8.resolve(options.output), json);
|
|
865
|
+
} else {
|
|
866
|
+
console.log(json);
|
|
867
|
+
}
|
|
868
|
+
} catch (err) {
|
|
869
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
870
|
+
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
871
|
+
process.exit(1);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
function createLintSubcommand() {
|
|
876
|
+
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
|
+
try {
|
|
878
|
+
const spec = loadSpec6(specPath);
|
|
879
|
+
const diagnostics = analyzeSpec(spec);
|
|
880
|
+
const generation = spec.generation;
|
|
881
|
+
const skipped = generation?.skipped ?? [];
|
|
882
|
+
const externalExports = spec.exports.filter((e) => e.kind === "external");
|
|
883
|
+
const byReason = {};
|
|
884
|
+
for (const skip of skipped) {
|
|
885
|
+
byReason[skip.reason] = (byReason[skip.reason] ?? 0) + 1;
|
|
886
|
+
}
|
|
746
887
|
const result = {
|
|
747
|
-
|
|
748
|
-
|
|
888
|
+
summary: {
|
|
889
|
+
total: diagnostics.missingDescriptions.length + diagnostics.deprecatedNoReason.length + diagnostics.missingParamDocs.length,
|
|
890
|
+
missingDescriptions: diagnostics.missingDescriptions.length,
|
|
891
|
+
deprecatedNoReason: diagnostics.deprecatedNoReason.length,
|
|
892
|
+
missingParamDocs: diagnostics.missingParamDocs.length,
|
|
893
|
+
...skipped.length > 0 && { skippedExports: skipped.length },
|
|
894
|
+
...externalExports.length > 0 && { externalExports: externalExports.length }
|
|
895
|
+
},
|
|
896
|
+
diagnostics,
|
|
897
|
+
...skipped.length > 0 && {
|
|
898
|
+
skippedExports: {
|
|
899
|
+
total: skipped.length,
|
|
900
|
+
byReason,
|
|
901
|
+
...options.verbose && { details: skipped }
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
...externalExports.length > 0 && {
|
|
905
|
+
externalExports: {
|
|
906
|
+
count: externalExports.length,
|
|
907
|
+
...options.verbose && {
|
|
908
|
+
details: externalExports.map((e) => ({ name: e.name, package: e.source?.package }))
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
749
912
|
};
|
|
750
913
|
console.log(JSON.stringify(result, null, 2));
|
|
751
|
-
if (errors.length > 0) {
|
|
752
|
-
process.exit(1);
|
|
753
|
-
}
|
|
754
914
|
} catch (err) {
|
|
755
915
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
756
916
|
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
@@ -758,40 +918,74 @@ function createValidateCommand() {
|
|
|
758
918
|
}
|
|
759
919
|
});
|
|
760
920
|
}
|
|
921
|
+
function createListSubcommand() {
|
|
922
|
+
return new Command11("list").description("List exports from a TypeScript entry point").argument("<entry>", "Entry point file path").action(async (entry) => {
|
|
923
|
+
const entryFile = path8.resolve(entry);
|
|
924
|
+
const result = await listExports({ entryFile });
|
|
925
|
+
if (result.errors.length > 0) {
|
|
926
|
+
console.error(JSON.stringify({ errors: result.errors }, null, 2));
|
|
927
|
+
process.exit(1);
|
|
928
|
+
}
|
|
929
|
+
console.log(JSON.stringify(result.exports, null, 2));
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
function createGetSubcommand() {
|
|
933
|
+
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 = path8.resolve(entry);
|
|
935
|
+
const result = await getExport({ entryFile, exportName: name });
|
|
936
|
+
if (!result.export) {
|
|
937
|
+
const errorMsg = result.errors.length > 0 ? result.errors.join("; ") : `Export '${name}' not found`;
|
|
938
|
+
console.error(JSON.stringify({ error: errorMsg }, null, 2));
|
|
939
|
+
process.exit(1);
|
|
940
|
+
}
|
|
941
|
+
const output = { export: result.export };
|
|
942
|
+
if (result.types.length > 0) {
|
|
943
|
+
output.types = result.types;
|
|
944
|
+
}
|
|
945
|
+
console.log(JSON.stringify(output, null, 2));
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
function createSpecCommand() {
|
|
949
|
+
const spec = new Command11("spec").description("Spec extraction and manipulation commands");
|
|
950
|
+
spec.addCommand(createSnapshotSubcommand());
|
|
951
|
+
spec.addCommand(createValidateSubcommand());
|
|
952
|
+
spec.addCommand(createFilterSubcommand());
|
|
953
|
+
spec.addCommand(createLintSubcommand());
|
|
954
|
+
spec.addCommand(createListSubcommand());
|
|
955
|
+
spec.addCommand(createGetSubcommand());
|
|
956
|
+
return spec;
|
|
957
|
+
}
|
|
761
958
|
|
|
762
959
|
// bin/openpkg.ts
|
|
763
|
-
var program = new
|
|
960
|
+
var program = new Command12;
|
|
764
961
|
program.name("openpkg").description("OpenPkg CLI - TypeScript API extraction primitives").version(package_default.version);
|
|
765
|
-
program.
|
|
766
|
-
const entryFile = path10.resolve(entry);
|
|
767
|
-
const result = await listExports({ entryFile });
|
|
768
|
-
if (result.errors.length > 0) {
|
|
769
|
-
console.error(JSON.stringify({ errors: result.errors }, null, 2));
|
|
770
|
-
process.exit(1);
|
|
771
|
-
}
|
|
772
|
-
console.log(JSON.stringify(result.exports, null, 2));
|
|
773
|
-
});
|
|
774
|
-
program.command("get").description("Get detailed spec for a single export").argument("<entry>", "Entry point file path").argument("<name>", "Export name").action(async (entry, name) => {
|
|
775
|
-
const entryFile = path10.resolve(entry);
|
|
776
|
-
const result = await getExport({ entryFile, exportName: name });
|
|
777
|
-
if (!result.export) {
|
|
778
|
-
const errorMsg = result.errors.length > 0 ? result.errors.join("; ") : `Export '${name}' not found`;
|
|
779
|
-
console.error(JSON.stringify({ error: errorMsg }, null, 2));
|
|
780
|
-
process.exit(1);
|
|
781
|
-
}
|
|
782
|
-
const output = { export: result.export };
|
|
783
|
-
if (result.types.length > 0) {
|
|
784
|
-
output.types = result.types;
|
|
785
|
-
}
|
|
786
|
-
console.log(JSON.stringify(output, null, 2));
|
|
787
|
-
});
|
|
788
|
-
program.addCommand(createSnapshotCommand());
|
|
789
|
-
program.addCommand(createDiffCommand());
|
|
962
|
+
program.addCommand(createSpecCommand());
|
|
790
963
|
program.addCommand(createDocsCommand());
|
|
964
|
+
program.addCommand(createDiffCommand());
|
|
791
965
|
program.addCommand(createBreakingCommand());
|
|
792
966
|
program.addCommand(createChangelogCommand());
|
|
793
967
|
program.addCommand(createSemverCommand());
|
|
794
|
-
program.
|
|
795
|
-
|
|
796
|
-
|
|
968
|
+
var specCmd = program.commands.find((c) => c.name() === "spec");
|
|
969
|
+
if (!specCmd) {
|
|
970
|
+
throw new Error("Internal error: spec command not found");
|
|
971
|
+
}
|
|
972
|
+
function getSubcommand(parent, name) {
|
|
973
|
+
const cmd = parent.commands.find((c) => c.name() === name);
|
|
974
|
+
if (!cmd) {
|
|
975
|
+
throw new Error(`Internal error: ${name} subcommand not found`);
|
|
976
|
+
}
|
|
977
|
+
return cmd;
|
|
978
|
+
}
|
|
979
|
+
function createAlias(aliasName, targetName, description) {
|
|
980
|
+
return new Command12(aliasName).description(description).allowUnknownOption().allowExcessArguments().action(async () => {
|
|
981
|
+
const args = process.argv.slice(3);
|
|
982
|
+
await getSubcommand(specCmd, targetName).parseAsync(args, { from: "user" });
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
program.addCommand(createAlias("snapshot", "snapshot", "(alias) → openpkg spec snapshot"));
|
|
986
|
+
program.addCommand(createAlias("list", "list", "(alias) → openpkg spec list"));
|
|
987
|
+
program.addCommand(createAlias("get", "get", "(alias) → openpkg spec get"));
|
|
988
|
+
program.addCommand(createAlias("validate", "validate", "(alias) → openpkg spec validate"));
|
|
989
|
+
program.addCommand(createAlias("filter", "filter", "(alias) → openpkg spec filter"));
|
|
990
|
+
program.addCommand(createAlias("diagnostics", "lint", "(alias) → openpkg spec lint"));
|
|
797
991
|
program.parse();
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
1
|
import { getExport, listExports } from "@openpkg-ts/sdk";
|
|
2
|
-
import {
|
|
3
|
-
type FilterResult = {
|
|
4
|
-
spec: OpenPkg;
|
|
5
|
-
matched: number;
|
|
6
|
-
total: number;
|
|
7
|
-
};
|
|
8
|
-
type FilterSummaryResult = {
|
|
9
|
-
matched: number;
|
|
10
|
-
total: number;
|
|
11
|
-
};
|
|
2
|
+
import { FilterResult, FilterSummaryResult } from "./commands/filter";
|
|
12
3
|
export { listExports, getExport, FilterSummaryResult, FilterResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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.4",
|
|
27
|
+
"@openpkg-ts/sdk": "^0.34.1",
|
|
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"
|