@openpkg-ts/cli 0.3.0 → 0.3.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/dist/bin/openpkg.js +701 -0
- package/dist/shared/chunk-1dqs11h6.js +20 -0
- package/dist/{index.js → src/index.js} +2 -0
- package/package.json +7 -2
- package/CHANGELOG.md +0 -44
- package/bin/openpkg.ts +0 -73
- package/src/commands/breaking.ts +0 -46
- package/src/commands/changelog.test.ts +0 -78
- package/src/commands/changelog.ts +0 -79
- package/src/commands/diagnostics.test.ts +0 -55
- package/src/commands/diagnostics.ts +0 -55
- package/src/commands/diff.ts +0 -174
- package/src/commands/docs.test.ts +0 -63
- package/src/commands/docs.ts +0 -191
- package/src/commands/filter.test.ts +0 -210
- package/src/commands/filter.ts +0 -131
- package/src/commands/semver.ts +0 -43
- package/src/commands/snapshot.ts +0 -122
- package/src/commands/validate.ts +0 -45
- package/src/index.ts +0 -3
- package/test/diff.test.ts +0 -336
- package/test/docs.test.ts +0 -599
- package/test/e2e/release-workflow.test.ts +0 -189
- package/test/get.test.ts +0 -413
- package/test/spec.test.ts +0 -469
- package/tsconfig.json +0 -15
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
export { __toESM, __require };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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": {
|
|
@@ -9,9 +9,14 @@
|
|
|
9
9
|
"directory": "packages/cli"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
|
+
"main": "./dist/src/index.js",
|
|
13
|
+
"types": "./dist/src/index.d.ts",
|
|
12
14
|
"bin": {
|
|
13
|
-
"openpkg": "./bin/openpkg.
|
|
15
|
+
"openpkg": "./dist/bin/openpkg.js"
|
|
14
16
|
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
15
20
|
"scripts": {
|
|
16
21
|
"build": "bunup",
|
|
17
22
|
"dev": "bunup --watch",
|
package/CHANGELOG.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# @openpkg-ts/cli
|
|
2
|
-
|
|
3
|
-
## 0.3.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- bd70dc7: add diagnostics, filter primitives, new CLI commands (breaking, changelog, diagnostics, filter, semver, validate), registry system, render enhancements
|
|
8
|
-
|
|
9
|
-
### Patch Changes
|
|
10
|
-
|
|
11
|
-
- Updated dependencies [bd70dc7]
|
|
12
|
-
- @openpkg-ts/sdk@0.31.0
|
|
13
|
-
- @openpkg-ts/adapters@0.3.0
|
|
14
|
-
|
|
15
|
-
## 0.2.3
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- update @openpkg-ts/sdk dependency to ^0.30.2
|
|
20
|
-
|
|
21
|
-
## 0.2.2
|
|
22
|
-
|
|
23
|
-
### Patch Changes
|
|
24
|
-
|
|
25
|
-
- pull version from package.json instead of hardcoding
|
|
26
|
-
|
|
27
|
-
## 0.2.1
|
|
28
|
-
|
|
29
|
-
### Patch Changes
|
|
30
|
-
|
|
31
|
-
- fix workspace:\* deps to use published versions for npm compatibility
|
|
32
|
-
- Updated dependencies
|
|
33
|
-
- @openpkg-ts/sdk@0.30.1
|
|
34
|
-
|
|
35
|
-
## 0.2.0
|
|
36
|
-
|
|
37
|
-
### Minor Changes
|
|
38
|
-
|
|
39
|
-
- Major monorepo restructure: extract → sdk, doc-generator split into sdk/react/adapters, fumadocs-adapter deleted (use @openpkg-ts/adapters/fumadocs)
|
|
40
|
-
|
|
41
|
-
### Patch Changes
|
|
42
|
-
|
|
43
|
-
- Updated dependencies
|
|
44
|
-
- @openpkg-ts/sdk@0.30.0
|
package/bin/openpkg.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { getExport, listExports } from '@openpkg-ts/sdk';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
import pkg from '../package.json';
|
|
6
|
-
import { createBreakingCommand } from '../src/commands/breaking';
|
|
7
|
-
import { createChangelogCommand } from '../src/commands/changelog';
|
|
8
|
-
import { createDiagnosticsCommand } from '../src/commands/diagnostics';
|
|
9
|
-
import { createDiffCommand } from '../src/commands/diff';
|
|
10
|
-
import { createFilterCommand } from '../src/commands/filter';
|
|
11
|
-
import { createDocsCommand } from '../src/commands/docs';
|
|
12
|
-
import { createSemverCommand } from '../src/commands/semver';
|
|
13
|
-
import { createSnapshotCommand } from '../src/commands/snapshot';
|
|
14
|
-
import { createValidateCommand } from '../src/commands/validate';
|
|
15
|
-
|
|
16
|
-
const program = new Command();
|
|
17
|
-
|
|
18
|
-
program
|
|
19
|
-
.name('openpkg')
|
|
20
|
-
.description('OpenPkg CLI - TypeScript API extraction primitives')
|
|
21
|
-
.version(pkg.version);
|
|
22
|
-
|
|
23
|
-
program
|
|
24
|
-
.command('list')
|
|
25
|
-
.description('List exports from a TypeScript entry point')
|
|
26
|
-
.argument('<entry>', 'Entry point file path')
|
|
27
|
-
.action(async (entry: string) => {
|
|
28
|
-
const entryFile = path.resolve(entry);
|
|
29
|
-
const result = await listExports({ entryFile });
|
|
30
|
-
|
|
31
|
-
if (result.errors.length > 0) {
|
|
32
|
-
console.error(JSON.stringify({ errors: result.errors }, null, 2));
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log(JSON.stringify(result.exports, null, 2));
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
program
|
|
40
|
-
.command('get')
|
|
41
|
-
.description('Get detailed spec for a single export')
|
|
42
|
-
.argument('<entry>', 'Entry point file path')
|
|
43
|
-
.argument('<name>', 'Export name')
|
|
44
|
-
.action(async (entry: string, name: string) => {
|
|
45
|
-
const entryFile = path.resolve(entry);
|
|
46
|
-
const result = await getExport({ entryFile, exportName: name });
|
|
47
|
-
|
|
48
|
-
if (!result.export) {
|
|
49
|
-
const errorMsg =
|
|
50
|
-
result.errors.length > 0 ? result.errors.join('; ') : `Export '${name}' not found`;
|
|
51
|
-
console.error(JSON.stringify({ error: errorMsg }, null, 2));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Output export with related types if any
|
|
56
|
-
const output: Record<string, unknown> = { export: result.export };
|
|
57
|
-
if (result.types.length > 0) {
|
|
58
|
-
output.types = result.types;
|
|
59
|
-
}
|
|
60
|
-
console.log(JSON.stringify(output, null, 2));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
program.addCommand(createSnapshotCommand());
|
|
64
|
-
program.addCommand(createDiffCommand());
|
|
65
|
-
program.addCommand(createDocsCommand());
|
|
66
|
-
program.addCommand(createBreakingCommand());
|
|
67
|
-
program.addCommand(createChangelogCommand());
|
|
68
|
-
program.addCommand(createSemverCommand());
|
|
69
|
-
program.addCommand(createValidateCommand());
|
|
70
|
-
program.addCommand(createDiagnosticsCommand());
|
|
71
|
-
program.addCommand(createFilterCommand());
|
|
72
|
-
|
|
73
|
-
program.parse();
|
package/src/commands/breaking.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { type CategorizedBreaking, categorizeBreakingChanges, diffSpec, type OpenPkg } from '@openpkg-ts/spec';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
|
|
6
|
-
export type BreakingResult = {
|
|
7
|
-
breaking: CategorizedBreaking[];
|
|
8
|
-
count: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function loadSpec(filePath: string): OpenPkg {
|
|
12
|
-
const resolved = path.resolve(filePath);
|
|
13
|
-
const content = fs.readFileSync(resolved, 'utf-8');
|
|
14
|
-
return JSON.parse(content) as OpenPkg;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function createBreakingCommand(): Command {
|
|
18
|
-
return new Command('breaking')
|
|
19
|
-
.description('Check for breaking changes between two specs')
|
|
20
|
-
.argument('<old>', 'Path to old spec file (JSON)')
|
|
21
|
-
.argument('<new>', 'Path to new spec file (JSON)')
|
|
22
|
-
.action(async (oldPath: string, newPath: string) => {
|
|
23
|
-
try {
|
|
24
|
-
const oldSpec = loadSpec(oldPath);
|
|
25
|
-
const newSpec = loadSpec(newPath);
|
|
26
|
-
|
|
27
|
-
const diff = diffSpec(oldSpec, newSpec);
|
|
28
|
-
const categorized = categorizeBreakingChanges(diff.breaking, oldSpec, newSpec);
|
|
29
|
-
|
|
30
|
-
const result: BreakingResult = {
|
|
31
|
-
breaking: categorized,
|
|
32
|
-
count: categorized.length,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
console.log(JSON.stringify(result, null, 2));
|
|
36
|
-
|
|
37
|
-
if (categorized.length > 0) {
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
} catch (err) {
|
|
41
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
42
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as os from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import type { OpenPkg } from '@openpkg-ts/spec';
|
|
6
|
-
import { $ } from 'bun';
|
|
7
|
-
|
|
8
|
-
const oldSpec: OpenPkg = {
|
|
9
|
-
meta: { name: 'test-pkg', version: '1.0.0' },
|
|
10
|
-
exports: [
|
|
11
|
-
{ id: 'fn-greet', name: 'greet', kind: 'function', signatures: [] },
|
|
12
|
-
{ id: 'fn-removed', name: 'removed', kind: 'function', signatures: [] },
|
|
13
|
-
],
|
|
14
|
-
types: [],
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const newSpec: OpenPkg = {
|
|
18
|
-
meta: { name: 'test-pkg', version: '2.0.0' },
|
|
19
|
-
exports: [
|
|
20
|
-
{ id: 'fn-greet', name: 'greet', kind: 'function', signatures: [] },
|
|
21
|
-
{ id: 'fn-added', name: 'added', kind: 'function', signatures: [] },
|
|
22
|
-
],
|
|
23
|
-
types: [],
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
describe('changelog command', () => {
|
|
27
|
-
test('outputs JSON format', async () => {
|
|
28
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'changelog-test-'));
|
|
29
|
-
const oldPath = path.join(tmpDir, 'old.json');
|
|
30
|
-
const newPath = path.join(tmpDir, 'new.json');
|
|
31
|
-
|
|
32
|
-
fs.writeFileSync(oldPath, JSON.stringify(oldSpec));
|
|
33
|
-
fs.writeFileSync(newPath, JSON.stringify(newSpec));
|
|
34
|
-
|
|
35
|
-
const result = await $`bun packages/cli/bin/openpkg.ts changelog ${oldPath} ${newPath} --format json`.text();
|
|
36
|
-
const parsed = JSON.parse(result);
|
|
37
|
-
|
|
38
|
-
expect(parsed).toHaveProperty('breaking');
|
|
39
|
-
expect(parsed).toHaveProperty('added');
|
|
40
|
-
expect(parsed).toHaveProperty('removed');
|
|
41
|
-
expect(parsed).toHaveProperty('summary');
|
|
42
|
-
|
|
43
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('outputs markdown format', async () => {
|
|
47
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'changelog-test-'));
|
|
48
|
-
const oldPath = path.join(tmpDir, 'old.json');
|
|
49
|
-
const newPath = path.join(tmpDir, 'new.json');
|
|
50
|
-
|
|
51
|
-
fs.writeFileSync(oldPath, JSON.stringify(oldSpec));
|
|
52
|
-
fs.writeFileSync(newPath, JSON.stringify(newSpec));
|
|
53
|
-
|
|
54
|
-
const result = await $`bun packages/cli/bin/openpkg.ts changelog ${oldPath} ${newPath}`.text();
|
|
55
|
-
|
|
56
|
-
expect(result).toContain('## Breaking Changes');
|
|
57
|
-
expect(result).toContain('removed');
|
|
58
|
-
expect(result).toContain('## Added');
|
|
59
|
-
expect(result).toContain('added');
|
|
60
|
-
|
|
61
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('empty diff shows no changes', async () => {
|
|
65
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'changelog-test-'));
|
|
66
|
-
const oldPath = path.join(tmpDir, 'old.json');
|
|
67
|
-
const newPath = path.join(tmpDir, 'new.json');
|
|
68
|
-
|
|
69
|
-
fs.writeFileSync(oldPath, JSON.stringify(oldSpec));
|
|
70
|
-
fs.writeFileSync(newPath, JSON.stringify(oldSpec));
|
|
71
|
-
|
|
72
|
-
const result = await $`bun packages/cli/bin/openpkg.ts changelog ${oldPath} ${newPath}`.text();
|
|
73
|
-
|
|
74
|
-
expect(result.trim()).toBe('No changes detected.');
|
|
75
|
-
|
|
76
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import type { OpenPkg } from '@openpkg-ts/spec';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
import { type DiffResult, enrichDiff } from './diff.js';
|
|
6
|
-
|
|
7
|
-
function loadSpec(filePath: string): OpenPkg {
|
|
8
|
-
const resolved = path.resolve(filePath);
|
|
9
|
-
const content = fs.readFileSync(resolved, 'utf-8');
|
|
10
|
-
return JSON.parse(content) as OpenPkg;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Format diff result as markdown changelog
|
|
15
|
-
*/
|
|
16
|
-
function formatMarkdown(diff: DiffResult): string {
|
|
17
|
-
const lines: string[] = [];
|
|
18
|
-
|
|
19
|
-
// Breaking Changes
|
|
20
|
-
if (diff.removed.length > 0 || diff.changed.length > 0) {
|
|
21
|
-
lines.push('## Breaking Changes');
|
|
22
|
-
lines.push('');
|
|
23
|
-
for (const r of diff.removed) {
|
|
24
|
-
lines.push(`- **Removed** \`${r.name}\` (${r.kind})`);
|
|
25
|
-
}
|
|
26
|
-
for (const c of diff.changed) {
|
|
27
|
-
lines.push(`- **${c.name}** (${c.kind}): ${c.description}`);
|
|
28
|
-
}
|
|
29
|
-
lines.push('');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Added
|
|
33
|
-
if (diff.added.length > 0) {
|
|
34
|
-
lines.push('## Added');
|
|
35
|
-
lines.push('');
|
|
36
|
-
for (const id of diff.added) {
|
|
37
|
-
lines.push(`- \`${id}\``);
|
|
38
|
-
}
|
|
39
|
-
lines.push('');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Changed (docs only)
|
|
43
|
-
if (diff.docsOnly.length > 0) {
|
|
44
|
-
lines.push('## Changed');
|
|
45
|
-
lines.push('');
|
|
46
|
-
for (const id of diff.docsOnly) {
|
|
47
|
-
lines.push(`- \`${id}\` (docs)`);
|
|
48
|
-
}
|
|
49
|
-
lines.push('');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return lines.join('\n').trim() || 'No changes detected.';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function createChangelogCommand(): Command {
|
|
56
|
-
return new Command('changelog')
|
|
57
|
-
.description('Generate changelog from diff between two specs')
|
|
58
|
-
.argument('<old>', 'Path to old spec file (JSON)')
|
|
59
|
-
.argument('<new>', 'Path to new spec file (JSON)')
|
|
60
|
-
.option('--format <format>', 'Output format: md or json', 'md')
|
|
61
|
-
.action(async (oldPath: string, newPath: string, options: { format?: string }) => {
|
|
62
|
-
try {
|
|
63
|
-
const oldSpec = loadSpec(oldPath);
|
|
64
|
-
const newSpec = loadSpec(newPath);
|
|
65
|
-
|
|
66
|
-
const diff = enrichDiff(oldSpec, newSpec);
|
|
67
|
-
|
|
68
|
-
if (options.format === 'json') {
|
|
69
|
-
console.log(JSON.stringify(diff, null, 2));
|
|
70
|
-
} else {
|
|
71
|
-
console.log(formatMarkdown(diff));
|
|
72
|
-
}
|
|
73
|
-
} catch (err) {
|
|
74
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
75
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as os from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { $ } from 'bun';
|
|
6
|
-
|
|
7
|
-
describe('openpkg diagnostics', () => {
|
|
8
|
-
let tmpDir: string;
|
|
9
|
-
|
|
10
|
-
beforeAll(() => {
|
|
11
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'diagnostics-test-'));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterAll(() => {
|
|
15
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('outputs JSON with diagnostics', async () => {
|
|
19
|
-
const specPath = path.join(tmpDir, 'spec.json');
|
|
20
|
-
const spec = {
|
|
21
|
-
openpkg: '0.4.0',
|
|
22
|
-
meta: { name: 'test-pkg' },
|
|
23
|
-
exports: [
|
|
24
|
-
{ id: 'a', name: 'noDesc', kind: 'function' },
|
|
25
|
-
{ id: 'b', name: 'withDesc', kind: 'function', description: 'Has desc' },
|
|
26
|
-
{
|
|
27
|
-
id: 'c',
|
|
28
|
-
name: 'depNoReason',
|
|
29
|
-
kind: 'function',
|
|
30
|
-
description: 'Something',
|
|
31
|
-
deprecated: true,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
};
|
|
35
|
-
fs.writeFileSync(specPath, JSON.stringify(spec));
|
|
36
|
-
|
|
37
|
-
const output = await $`bun packages/cli/bin/openpkg.ts diagnostics ${specPath}`.text();
|
|
38
|
-
const result = JSON.parse(output);
|
|
39
|
-
|
|
40
|
-
expect(result.summary.total).toBe(2);
|
|
41
|
-
expect(result.summary.missingDescriptions).toBe(1);
|
|
42
|
-
expect(result.summary.deprecatedNoReason).toBe(1);
|
|
43
|
-
expect(result.diagnostics.missingDescriptions[0].exportName).toBe('noDesc');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('handles malformed spec gracefully', async () => {
|
|
47
|
-
const specPath = path.join(tmpDir, 'bad.json');
|
|
48
|
-
fs.writeFileSync(specPath, '{ invalid json');
|
|
49
|
-
|
|
50
|
-
const output = await $`bun packages/cli/bin/openpkg.ts diagnostics ${specPath}`.text();
|
|
51
|
-
const result = JSON.parse(output);
|
|
52
|
-
|
|
53
|
-
expect(result.error).toBeDefined();
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { analyzeSpec, type SpecDiagnostics } from '@openpkg-ts/sdk';
|
|
4
|
-
import type { OpenPkg } from '@openpkg-ts/spec';
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
|
|
7
|
-
export interface DiagnosticsResult {
|
|
8
|
-
summary: {
|
|
9
|
-
total: number;
|
|
10
|
-
missingDescriptions: number;
|
|
11
|
-
deprecatedNoReason: number;
|
|
12
|
-
missingParamDocs: number;
|
|
13
|
-
};
|
|
14
|
-
diagnostics: SpecDiagnostics;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function loadJSON(filePath: string): unknown {
|
|
18
|
-
const resolved = path.resolve(filePath);
|
|
19
|
-
const content = fs.readFileSync(resolved, 'utf-8');
|
|
20
|
-
return JSON.parse(content);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function createDiagnosticsCommand(): Command {
|
|
24
|
-
return new Command('diagnostics')
|
|
25
|
-
.description('Analyze spec for quality issues (missing docs, deprecated without reason)')
|
|
26
|
-
.argument('<spec>', 'Path to spec file (JSON)')
|
|
27
|
-
.action(async (specPath: string) => {
|
|
28
|
-
try {
|
|
29
|
-
const spec = loadJSON(specPath) as OpenPkg;
|
|
30
|
-
|
|
31
|
-
const diagnostics = analyzeSpec(spec);
|
|
32
|
-
|
|
33
|
-
const result: DiagnosticsResult = {
|
|
34
|
-
summary: {
|
|
35
|
-
total:
|
|
36
|
-
diagnostics.missingDescriptions.length +
|
|
37
|
-
diagnostics.deprecatedNoReason.length +
|
|
38
|
-
diagnostics.missingParamDocs.length,
|
|
39
|
-
missingDescriptions: diagnostics.missingDescriptions.length,
|
|
40
|
-
deprecatedNoReason: diagnostics.deprecatedNoReason.length,
|
|
41
|
-
missingParamDocs: diagnostics.missingParamDocs.length,
|
|
42
|
-
},
|
|
43
|
-
diagnostics,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
console.log(JSON.stringify(result, null, 2));
|
|
47
|
-
// Always exit 0 - informational only
|
|
48
|
-
process.exit(0);
|
|
49
|
-
} catch (err) {
|
|
50
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
51
|
-
console.log(JSON.stringify({ error: error.message }, null, 2));
|
|
52
|
-
process.exit(0);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
package/src/commands/diff.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
type CategorizedBreaking,
|
|
5
|
-
categorizeBreakingChanges,
|
|
6
|
-
diffSpec,
|
|
7
|
-
type OpenPkg,
|
|
8
|
-
recommendSemverBump,
|
|
9
|
-
type SemverBump,
|
|
10
|
-
type SpecExportKind,
|
|
11
|
-
} from '@openpkg-ts/spec';
|
|
12
|
-
import { Command } from 'commander';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A changed export with details about what changed
|
|
16
|
-
*/
|
|
17
|
-
export interface ChangedExport {
|
|
18
|
-
id: string;
|
|
19
|
-
name: string;
|
|
20
|
-
kind: SpecExportKind;
|
|
21
|
-
description: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Enriched diff result with categorized changes
|
|
26
|
-
*/
|
|
27
|
-
export interface DiffResult {
|
|
28
|
-
breaking: CategorizedBreaking[];
|
|
29
|
-
added: string[];
|
|
30
|
-
removed: RemovedExport[];
|
|
31
|
-
changed: ChangedExport[];
|
|
32
|
-
docsOnly: string[];
|
|
33
|
-
summary: {
|
|
34
|
-
breakingCount: number;
|
|
35
|
-
addedCount: number;
|
|
36
|
-
removedCount: number;
|
|
37
|
-
changedCount: number;
|
|
38
|
-
docsOnlyCount: number;
|
|
39
|
-
semverBump: SemverBump;
|
|
40
|
-
semverReason: string;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface RemovedExport {
|
|
45
|
-
id: string;
|
|
46
|
-
name: string;
|
|
47
|
-
kind: SpecExportKind;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function loadSpec(filePath: string): OpenPkg {
|
|
51
|
-
const resolved = path.resolve(filePath);
|
|
52
|
-
const content = fs.readFileSync(resolved, 'utf-8');
|
|
53
|
-
return JSON.parse(content) as OpenPkg;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function toExportMap(spec: OpenPkg): Map<string, { name: string; kind: SpecExportKind }> {
|
|
57
|
-
const map = new Map<string, { name: string; kind: SpecExportKind }>();
|
|
58
|
-
for (const exp of spec.exports) {
|
|
59
|
-
map.set(exp.id, { name: exp.name, kind: exp.kind });
|
|
60
|
-
}
|
|
61
|
-
if (spec.types) {
|
|
62
|
-
for (const t of spec.types) {
|
|
63
|
-
map.set(t.id, { name: t.name, kind: t.kind as SpecExportKind });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return map;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Enriches basic diffSpec output with categorization
|
|
71
|
-
*/
|
|
72
|
-
export function enrichDiff(oldSpec: OpenPkg, newSpec: OpenPkg): DiffResult {
|
|
73
|
-
const rawDiff = diffSpec(oldSpec, newSpec);
|
|
74
|
-
const categorized = categorizeBreakingChanges(rawDiff.breaking, oldSpec, newSpec);
|
|
75
|
-
const semver = recommendSemverBump(rawDiff);
|
|
76
|
-
|
|
77
|
-
const oldExports = toExportMap(oldSpec);
|
|
78
|
-
|
|
79
|
-
// Separate removed from changed
|
|
80
|
-
const removed: RemovedExport[] = [];
|
|
81
|
-
const changed: ChangedExport[] = [];
|
|
82
|
-
const breaking: CategorizedBreaking[] = [];
|
|
83
|
-
|
|
84
|
-
for (const cat of categorized) {
|
|
85
|
-
if (cat.reason === 'removed') {
|
|
86
|
-
const info = oldExports.get(cat.id);
|
|
87
|
-
removed.push({
|
|
88
|
-
id: cat.id,
|
|
89
|
-
name: cat.name,
|
|
90
|
-
kind: info?.kind ?? cat.kind,
|
|
91
|
-
});
|
|
92
|
-
} else {
|
|
93
|
-
// It's a change, not a removal
|
|
94
|
-
changed.push({
|
|
95
|
-
id: cat.id,
|
|
96
|
-
name: cat.name,
|
|
97
|
-
kind: cat.kind,
|
|
98
|
-
description: describeChange(cat),
|
|
99
|
-
});
|
|
100
|
-
breaking.push(cat);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Added exports
|
|
105
|
-
const added = rawDiff.nonBreaking;
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
breaking,
|
|
109
|
-
added,
|
|
110
|
-
removed,
|
|
111
|
-
changed,
|
|
112
|
-
docsOnly: rawDiff.docsOnly,
|
|
113
|
-
summary: {
|
|
114
|
-
breakingCount: breaking.length,
|
|
115
|
-
addedCount: added.length,
|
|
116
|
-
removedCount: removed.length,
|
|
117
|
-
changedCount: changed.length,
|
|
118
|
-
docsOnlyCount: rawDiff.docsOnly.length,
|
|
119
|
-
semverBump: semver.bump,
|
|
120
|
-
semverReason: semver.reason,
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Generate human-readable description for a change
|
|
127
|
-
*/
|
|
128
|
-
function describeChange(cat: CategorizedBreaking): string {
|
|
129
|
-
switch (cat.reason) {
|
|
130
|
-
case 'signature changed':
|
|
131
|
-
return `Function signature changed`;
|
|
132
|
-
case 'type definition changed':
|
|
133
|
-
return `Type definition changed`;
|
|
134
|
-
case 'constructor changed':
|
|
135
|
-
return `Class constructor signature changed`;
|
|
136
|
-
case 'methods removed':
|
|
137
|
-
return `Class methods removed`;
|
|
138
|
-
case 'methods changed':
|
|
139
|
-
return `Class methods changed`;
|
|
140
|
-
case 'changed':
|
|
141
|
-
return `${cat.kind} changed`;
|
|
142
|
-
default:
|
|
143
|
-
return cat.reason;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function createDiffCommand(): Command {
|
|
148
|
-
return new Command('diff')
|
|
149
|
-
.description('Compare two OpenPkg specs and show differences')
|
|
150
|
-
.argument('<old>', 'Path to old spec file (JSON)')
|
|
151
|
-
.argument('<new>', 'Path to new spec file (JSON)')
|
|
152
|
-
.option('--json', 'Output as JSON (default)')
|
|
153
|
-
.option('--summary', 'Only show summary')
|
|
154
|
-
.action(
|
|
155
|
-
async (oldPath: string, newPath: string, options: { json?: boolean; summary?: boolean }) => {
|
|
156
|
-
try {
|
|
157
|
-
const oldSpec = loadSpec(oldPath);
|
|
158
|
-
const newSpec = loadSpec(newPath);
|
|
159
|
-
|
|
160
|
-
const result = enrichDiff(oldSpec, newSpec);
|
|
161
|
-
|
|
162
|
-
if (options.summary) {
|
|
163
|
-
console.log(JSON.stringify(result.summary, null, 2));
|
|
164
|
-
} else {
|
|
165
|
-
console.log(JSON.stringify(result, null, 2));
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
169
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
);
|
|
174
|
-
}
|