@openpkg-ts/cli 0.9.2 → 0.10.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.
Files changed (2) hide show
  1. package/dist/index.js +74 -8
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5,9 +5,13 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { parseArgs } from "node:util";
7
7
  import {
8
+ calculateNextVersion,
9
+ categorizeBreakingChanges,
8
10
  createDocs,
9
11
  diffSpecs,
10
12
  extractSpec,
13
+ getAvailableVersions,
14
+ getValidationErrors,
11
15
  listExports,
12
16
  recommendSemverBump
13
17
  } from "@openpkg-ts/sdk";
@@ -17,18 +21,20 @@ Usage:
17
21
  openpkg spec <entry.ts> [-o spec.json]
18
22
  openpkg docs <entry.ts | spec.json> [-f md|html|json] [-o out]
19
23
  openpkg list <entry.ts> [--json]
20
- openpkg diff <old.json> <new.json>
24
+ openpkg validate <spec.json>
25
+ openpkg diff <old.json> <new.json> [--json]
21
26
 
22
27
  Commands:
23
- spec Extract an OpenPkg spec from a TypeScript entry point
24
- docs Generate docs from an entry point or an existing spec file
25
- list List exports (name, kind, location)
26
- diff Compare two spec files and recommend a semver bump
28
+ spec Extract an OpenPkg spec from a TypeScript entry point
29
+ docs Generate docs from an entry point or an existing spec file
30
+ list List exports (name, kind, location)
31
+ validate Validate a spec file against the OpenPkg meta-schema
32
+ diff Compare two spec files and recommend a semver bump
27
33
 
28
34
  Options:
29
35
  -o, --output Write to file instead of stdout
30
36
  -f, --format docs output format: md (default), html, json
31
- --json list output as JSON
37
+ --json list/diff output as JSON
32
38
  -h, --help Show this help
33
39
  -v, --version Show version
34
40
  `;
@@ -130,12 +136,67 @@ function readSpecFile(file) {
130
136
  fail(`failed to read spec file ${file}: ${err instanceof Error ? err.message : String(err)}`);
131
137
  }
132
138
  }
139
+ function pickVersion(spec) {
140
+ const declared = spec?.openpkg;
141
+ if (typeof declared === "string" && getAvailableVersions().includes(declared)) {
142
+ return declared;
143
+ }
144
+ return "latest";
145
+ }
146
+ function readValidSpecFile(file) {
147
+ const parsed = readSpecFile(file);
148
+ const errors = getValidationErrors(parsed, pickVersion(parsed));
149
+ if (errors.length > 0) {
150
+ const details = errors.map((e) => ` ${e.instancePath || "/"} ${e.message}`).join(`
151
+ `);
152
+ fail(`invalid spec ${file}:
153
+ ${details}`);
154
+ }
155
+ return parsed;
156
+ }
157
+ function validateCommand(args) {
158
+ const [file] = args;
159
+ if (!file)
160
+ fail("validate requires a spec file (openpkg validate spec.json)");
161
+ const spec = readSpecFile(file);
162
+ const errors = getValidationErrors(spec, pickVersion(spec));
163
+ if (errors.length === 0) {
164
+ console.log(`${file}: valid`);
165
+ return;
166
+ }
167
+ for (const e of errors) {
168
+ console.error(`${e.instancePath || "/"} ${e.message}`);
169
+ }
170
+ process.exit(1);
171
+ }
133
172
  function diffCommand(args) {
134
- const [oldFile, newFile] = args;
173
+ const { values, positionals } = parseArgs({
174
+ args,
175
+ options: { json: { type: "boolean" } },
176
+ allowPositionals: true
177
+ });
178
+ const [oldFile, newFile] = positionals;
135
179
  if (!oldFile || !newFile)
136
180
  fail("diff requires two spec files (openpkg diff old.json new.json)");
137
- const diff = diffSpecs(readSpecFile(oldFile), readSpecFile(newFile));
181
+ const oldSpec = readValidSpecFile(oldFile);
182
+ const newSpec = readValidSpecFile(newFile);
183
+ const diff = diffSpecs(oldSpec, newSpec);
138
184
  const recommendation = recommendSemverBump(diff);
185
+ const oldVersion = newSpec?.meta?.version;
186
+ const nextVersion = oldVersion ? calculateNextVersion(oldVersion, recommendation.bump) : undefined;
187
+ if (values.json) {
188
+ console.log(JSON.stringify({
189
+ breaking: diff.breaking,
190
+ nonBreaking: diff.nonBreaking,
191
+ docsOnly: diff.docsOnly,
192
+ categorized: categorizeBreakingChanges(diff.breaking, oldSpec, newSpec),
193
+ recommendation,
194
+ ...nextVersion ? { nextVersion } : {}
195
+ }, null, 2));
196
+ if (diff.breaking.length)
197
+ process.exitCode = 2;
198
+ return;
199
+ }
139
200
  const section = (title, items) => {
140
201
  if (!items.length)
141
202
  return;
@@ -151,6 +212,8 @@ function diffCommand(args) {
151
212
  }
152
213
  console.log(`
153
214
  Recommended bump: ${recommendation.bump} (${recommendation.reason})`);
215
+ if (nextVersion)
216
+ console.log(`Next version: ${nextVersion}`);
154
217
  if (diff.breaking.length)
155
218
  process.exitCode = 2;
156
219
  }
@@ -166,6 +229,9 @@ async function main() {
166
229
  case "list":
167
230
  await listCommand(rest);
168
231
  break;
232
+ case "validate":
233
+ validateCommand(rest);
234
+ break;
169
235
  case "diff":
170
236
  diffCommand(rest);
171
237
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/cli",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
4
  "description": "CLI for OpenPkg - extract TypeScript API specs and generate docs",
5
5
  "keywords": [
6
6
  "openpkg",
@@ -35,7 +35,7 @@
35
35
  "test": "bun test"
36
36
  },
37
37
  "dependencies": {
38
- "@openpkg-ts/sdk": "^0.45.0"
38
+ "@openpkg-ts/sdk": "^0.47.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/bun": "latest",