@openpkg-ts/cli 0.3.0 → 0.4.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.
- package/dist/bin/openpkg.js +715 -0
- package/dist/shared/chunk-1dqs11h6.js +20 -0
- package/dist/{index.js → src/index.js} +2 -0
- package/package.json +9 -4
- 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
package/src/commands/snapshot.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { type Diagnostic, type ExtractOptions, extractSpec } from '@openpkg-ts/sdk';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
|
|
6
|
-
interface SnapshotCommandOptions {
|
|
7
|
-
output?: string;
|
|
8
|
-
maxDepth?: string;
|
|
9
|
-
skipResolve?: boolean;
|
|
10
|
-
runtime?: boolean;
|
|
11
|
-
only?: string;
|
|
12
|
-
ignore?: string;
|
|
13
|
-
verify?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function parseFilter(value: string | undefined): string[] | undefined {
|
|
17
|
-
if (!value) return undefined;
|
|
18
|
-
return value
|
|
19
|
-
.split(',')
|
|
20
|
-
.map((s) => s.trim())
|
|
21
|
-
.filter(Boolean);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function formatDiagnostics(diagnostics: Diagnostic[]): object[] {
|
|
25
|
-
return diagnostics.map((d) => ({
|
|
26
|
-
message: d.message,
|
|
27
|
-
severity: d.severity,
|
|
28
|
-
...(d.code && { code: d.code }),
|
|
29
|
-
...(d.suggestion && { suggestion: d.suggestion }),
|
|
30
|
-
...(d.location && { location: d.location }),
|
|
31
|
-
}));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function createSnapshotCommand(): Command {
|
|
35
|
-
return new Command('snapshot')
|
|
36
|
-
.description('Generate full OpenPkg spec from TypeScript entry point')
|
|
37
|
-
.argument('<entry>', 'Entry point file path')
|
|
38
|
-
.option(
|
|
39
|
-
'-o, --output <file>',
|
|
40
|
-
'Output file (default: openpkg.json, use - for stdout)',
|
|
41
|
-
'openpkg.json',
|
|
42
|
-
)
|
|
43
|
-
.option('--max-depth <n>', 'Max type depth (default: 4)', '4')
|
|
44
|
-
.option('--skip-resolve', 'Skip external type resolution')
|
|
45
|
-
.option('--runtime', 'Enable Standard Schema runtime extraction')
|
|
46
|
-
.option('--only <exports>', 'Filter exports (comma-separated, wildcards supported)')
|
|
47
|
-
.option('--ignore <exports>', 'Ignore exports (comma-separated, wildcards supported)')
|
|
48
|
-
.option('--verify', 'Exit 1 if any exports fail')
|
|
49
|
-
.action(async (entry: string, options: SnapshotCommandOptions) => {
|
|
50
|
-
const entryFile = path.resolve(entry);
|
|
51
|
-
|
|
52
|
-
const extractOptions: ExtractOptions = {
|
|
53
|
-
entryFile,
|
|
54
|
-
maxTypeDepth: parseInt(options.maxDepth ?? '4', 10),
|
|
55
|
-
resolveExternalTypes: !options.skipResolve,
|
|
56
|
-
schemaExtraction: options.runtime ? 'hybrid' : 'static',
|
|
57
|
-
only: parseFilter(options.only),
|
|
58
|
-
ignore: parseFilter(options.ignore),
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const result = await extractSpec(extractOptions);
|
|
63
|
-
|
|
64
|
-
// Build summary for stderr
|
|
65
|
-
const summary = {
|
|
66
|
-
exports: result.spec.exports.length,
|
|
67
|
-
types: result.spec.types?.length ?? 0,
|
|
68
|
-
diagnostics: result.diagnostics.length,
|
|
69
|
-
...(result.verification && {
|
|
70
|
-
verification: {
|
|
71
|
-
discovered: result.verification.discovered,
|
|
72
|
-
extracted: result.verification.extracted,
|
|
73
|
-
skipped: result.verification.skipped,
|
|
74
|
-
failed: result.verification.failed,
|
|
75
|
-
},
|
|
76
|
-
}),
|
|
77
|
-
...(result.runtimeSchemas && {
|
|
78
|
-
runtime: {
|
|
79
|
-
extracted: result.runtimeSchemas.extracted,
|
|
80
|
-
merged: result.runtimeSchemas.merged,
|
|
81
|
-
vendors: result.runtimeSchemas.vendors,
|
|
82
|
-
},
|
|
83
|
-
}),
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Write summary to stderr
|
|
87
|
-
console.error(JSON.stringify(summary, null, 2));
|
|
88
|
-
|
|
89
|
-
// Check for failures if --verify
|
|
90
|
-
if (options.verify && result.verification && result.verification.failed > 0) {
|
|
91
|
-
const errorOutput = {
|
|
92
|
-
error: 'Export verification failed',
|
|
93
|
-
failed: result.verification.details.failed,
|
|
94
|
-
diagnostics: formatDiagnostics(result.diagnostics),
|
|
95
|
-
};
|
|
96
|
-
console.error(JSON.stringify(errorOutput, null, 2));
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Output spec
|
|
101
|
-
const specJson = JSON.stringify(result.spec, null, 2);
|
|
102
|
-
|
|
103
|
-
if (options.output === '-') {
|
|
104
|
-
// Stdout mode
|
|
105
|
-
console.log(specJson);
|
|
106
|
-
} else {
|
|
107
|
-
// File mode
|
|
108
|
-
const outputPath = path.resolve(options.output ?? 'openpkg.json');
|
|
109
|
-
fs.writeFileSync(outputPath, specJson);
|
|
110
|
-
console.error(`Wrote ${outputPath}`);
|
|
111
|
-
}
|
|
112
|
-
} catch (err) {
|
|
113
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
114
|
-
const errorOutput = {
|
|
115
|
-
error: error.message,
|
|
116
|
-
...(error.stack && { stack: error.stack }),
|
|
117
|
-
};
|
|
118
|
-
console.error(JSON.stringify(errorOutput, null, 2));
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
}
|
package/src/commands/validate.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { getValidationErrors, type SchemaVersion, type SpecError } from '@openpkg-ts/spec';
|
|
4
|
-
import { Command } from 'commander';
|
|
5
|
-
|
|
6
|
-
export type ValidateResult = {
|
|
7
|
-
valid: boolean;
|
|
8
|
-
errors: SpecError[];
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function loadJSON(filePath: string): unknown {
|
|
12
|
-
const resolved = path.resolve(filePath);
|
|
13
|
-
const content = fs.readFileSync(resolved, 'utf-8');
|
|
14
|
-
return JSON.parse(content);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function createValidateCommand(): Command {
|
|
18
|
-
return new Command('validate')
|
|
19
|
-
.description('Validate an OpenPkg spec against the schema')
|
|
20
|
-
.argument('<spec>', 'Path to spec file (JSON)')
|
|
21
|
-
.option('--version <version>', 'Schema version to validate against (default: latest)')
|
|
22
|
-
.action(async (specPath: string, options: { version?: string }) => {
|
|
23
|
-
try {
|
|
24
|
-
const spec = loadJSON(specPath);
|
|
25
|
-
const version = (options.version ?? 'latest') as SchemaVersion;
|
|
26
|
-
|
|
27
|
-
const errors = getValidationErrors(spec, version);
|
|
28
|
-
|
|
29
|
-
const result: ValidateResult = {
|
|
30
|
-
valid: errors.length === 0,
|
|
31
|
-
errors,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
console.log(JSON.stringify(result, null, 2));
|
|
35
|
-
|
|
36
|
-
if (errors.length > 0) {
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
} catch (err) {
|
|
40
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
41
|
-
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
package/src/index.ts
DELETED
package/test/diff.test.ts
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import type { OpenPkg, SpecExport } from '@openpkg-ts/spec';
|
|
3
|
-
import { enrichDiff } from '../src/commands/diff';
|
|
4
|
-
|
|
5
|
-
// Helper to create minimal spec
|
|
6
|
-
function makeSpec(exports: SpecExport[], types: OpenPkg['types'] = []): OpenPkg {
|
|
7
|
-
return {
|
|
8
|
-
openpkg: '0.4.0',
|
|
9
|
-
meta: { name: 'test' },
|
|
10
|
-
exports,
|
|
11
|
-
types,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe('enrichDiff', () => {
|
|
16
|
-
describe('removed export detection', () => {
|
|
17
|
-
test('detect removed export → major bump', () => {
|
|
18
|
-
const old = makeSpec([{ id: 'foo', name: 'foo', kind: 'function' }]);
|
|
19
|
-
const new_ = makeSpec([]);
|
|
20
|
-
const result = enrichDiff(old, new_);
|
|
21
|
-
|
|
22
|
-
expect(result.removed.map((r) => r.id)).toContain('foo');
|
|
23
|
-
expect(result.summary.removedCount).toBe(1);
|
|
24
|
-
expect(result.summary.semverBump).toBe('major');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('removed function has high severity', () => {
|
|
28
|
-
const old = makeSpec([{ id: 'fn', name: 'fn', kind: 'function' }]);
|
|
29
|
-
const new_ = makeSpec([]);
|
|
30
|
-
const result = enrichDiff(old, new_);
|
|
31
|
-
|
|
32
|
-
expect(result.removed[0].kind).toBe('function');
|
|
33
|
-
expect(result.summary.semverBump).toBe('major');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('removed class has high severity', () => {
|
|
37
|
-
const old = makeSpec([{ id: 'MyClass', name: 'MyClass', kind: 'class' }]);
|
|
38
|
-
const new_ = makeSpec([]);
|
|
39
|
-
const result = enrichDiff(old, new_);
|
|
40
|
-
|
|
41
|
-
expect(result.removed[0].kind).toBe('class');
|
|
42
|
-
expect(result.summary.semverBump).toBe('major');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('removed variable tracked separately from changed', () => {
|
|
46
|
-
const old = makeSpec([
|
|
47
|
-
{ id: 'v1', name: 'v1', kind: 'variable' },
|
|
48
|
-
{ id: 'v2', name: 'v2', kind: 'variable' },
|
|
49
|
-
]);
|
|
50
|
-
const new_ = makeSpec([{ id: 'v1', name: 'v1', kind: 'variable' }]);
|
|
51
|
-
const result = enrichDiff(old, new_);
|
|
52
|
-
|
|
53
|
-
expect(result.removed.length).toBe(1);
|
|
54
|
-
expect(result.removed[0].id).toBe('v2');
|
|
55
|
-
expect(result.changed.length).toBe(0);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('added export detection', () => {
|
|
60
|
-
test('detect added export → minor bump', () => {
|
|
61
|
-
const old = makeSpec([]);
|
|
62
|
-
const new_ = makeSpec([{ id: 'bar', name: 'bar', kind: 'function' }]);
|
|
63
|
-
const result = enrichDiff(old, new_);
|
|
64
|
-
|
|
65
|
-
expect(result.added).toContain('bar');
|
|
66
|
-
expect(result.summary.addedCount).toBe(1);
|
|
67
|
-
expect(result.summary.semverBump).toBe('minor');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('multiple additions all counted', () => {
|
|
71
|
-
const old = makeSpec([]);
|
|
72
|
-
const new_ = makeSpec([
|
|
73
|
-
{ id: 'a', name: 'a', kind: 'function' },
|
|
74
|
-
{ id: 'b', name: 'b', kind: 'variable' },
|
|
75
|
-
{ id: 'c', name: 'c', kind: 'class' },
|
|
76
|
-
]);
|
|
77
|
-
const result = enrichDiff(old, new_);
|
|
78
|
-
|
|
79
|
-
expect(result.added.length).toBe(3);
|
|
80
|
-
expect(result.summary.addedCount).toBe(3);
|
|
81
|
-
expect(result.summary.semverBump).toBe('minor');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('added type detected', () => {
|
|
85
|
-
const old = makeSpec([], []);
|
|
86
|
-
const new_ = makeSpec([], [{ id: 'NewType', name: 'NewType', kind: 'type' }]);
|
|
87
|
-
const result = enrichDiff(old, new_);
|
|
88
|
-
|
|
89
|
-
expect(result.added).toContain('NewType');
|
|
90
|
-
expect(result.summary.semverBump).toBe('minor');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('docs-only change detection', () => {
|
|
95
|
-
test('detect docs-only change → patch bump', () => {
|
|
96
|
-
const old = makeSpec([{ id: 'x', name: 'x', kind: 'function', description: 'old' }]);
|
|
97
|
-
const new_ = makeSpec([{ id: 'x', name: 'x', kind: 'function', description: 'new' }]);
|
|
98
|
-
const result = enrichDiff(old, new_);
|
|
99
|
-
|
|
100
|
-
expect(result.docsOnly).toContain('x');
|
|
101
|
-
expect(result.summary.docsOnlyCount).toBe(1);
|
|
102
|
-
expect(result.summary.semverBump).toBe('patch');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('examples change is docs-only', () => {
|
|
106
|
-
const old = makeSpec([
|
|
107
|
-
{ id: 'fn', name: 'fn', kind: 'function', examples: [{ code: 'fn()' }] },
|
|
108
|
-
]);
|
|
109
|
-
const new_ = makeSpec([
|
|
110
|
-
{ id: 'fn', name: 'fn', kind: 'function', examples: [{ code: 'fn("updated")' }] },
|
|
111
|
-
]);
|
|
112
|
-
const result = enrichDiff(old, new_);
|
|
113
|
-
|
|
114
|
-
expect(result.docsOnly).toContain('fn');
|
|
115
|
-
expect(result.summary.semverBump).toBe('patch');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('tags change is docs-only', () => {
|
|
119
|
-
const old = makeSpec([
|
|
120
|
-
{ id: 'fn', name: 'fn', kind: 'function', tags: [{ name: 'alpha', text: '' }] },
|
|
121
|
-
]);
|
|
122
|
-
const new_ = makeSpec([
|
|
123
|
-
{ id: 'fn', name: 'fn', kind: 'function', tags: [{ name: 'beta', text: '' }] },
|
|
124
|
-
]);
|
|
125
|
-
const result = enrichDiff(old, new_);
|
|
126
|
-
|
|
127
|
-
expect(result.docsOnly).toContain('fn');
|
|
128
|
-
expect(result.summary.semverBump).toBe('patch');
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe('changed export detection', () => {
|
|
133
|
-
test('function signature change detected', () => {
|
|
134
|
-
const old = makeSpec([
|
|
135
|
-
{ id: 'fn', name: 'fn', kind: 'function', signatures: [{ parameters: [] }] },
|
|
136
|
-
]);
|
|
137
|
-
const new_ = makeSpec([
|
|
138
|
-
{
|
|
139
|
-
id: 'fn',
|
|
140
|
-
name: 'fn',
|
|
141
|
-
kind: 'function',
|
|
142
|
-
signatures: [{ parameters: [{ name: 'x', schema: { type: 'string' } }] }],
|
|
143
|
-
},
|
|
144
|
-
]);
|
|
145
|
-
const result = enrichDiff(old, new_);
|
|
146
|
-
|
|
147
|
-
expect(result.changed.length).toBe(1);
|
|
148
|
-
expect(result.changed[0].id).toBe('fn');
|
|
149
|
-
expect(result.changed[0].description).toBe('Function signature changed');
|
|
150
|
-
expect(result.summary.semverBump).toBe('major');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('interface change detected', () => {
|
|
154
|
-
const old = makeSpec([{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [] }]);
|
|
155
|
-
const new_ = makeSpec([
|
|
156
|
-
{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [{ name: 'x', kind: 'property' }] },
|
|
157
|
-
]);
|
|
158
|
-
const result = enrichDiff(old, new_);
|
|
159
|
-
|
|
160
|
-
expect(result.changed.length).toBe(1);
|
|
161
|
-
expect(result.changed[0].id).toBe('IFoo');
|
|
162
|
-
expect(result.changed[0].description).toBe('Type definition changed');
|
|
163
|
-
expect(result.summary.semverBump).toBe('major');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test('type alias change detected', () => {
|
|
167
|
-
const old = makeSpec([{ id: 'MyType', name: 'MyType', kind: 'type', type: 'string' }]);
|
|
168
|
-
const new_ = makeSpec([{ id: 'MyType', name: 'MyType', kind: 'type', type: 'number' }]);
|
|
169
|
-
const result = enrichDiff(old, new_);
|
|
170
|
-
|
|
171
|
-
expect(result.changed.length).toBe(1);
|
|
172
|
-
expect(result.changed[0].description).toBe('Type definition changed');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('variable schema change detected', () => {
|
|
176
|
-
const old = makeSpec([
|
|
177
|
-
{ id: 'config', name: 'config', kind: 'variable', schema: { type: 'string' } },
|
|
178
|
-
]);
|
|
179
|
-
const new_ = makeSpec([
|
|
180
|
-
{ id: 'config', name: 'config', kind: 'variable', schema: { type: 'number' } },
|
|
181
|
-
]);
|
|
182
|
-
const result = enrichDiff(old, new_);
|
|
183
|
-
|
|
184
|
-
expect(result.changed.length).toBe(1);
|
|
185
|
-
expect(result.summary.semverBump).toBe('major');
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe('breaking change severity', () => {
|
|
190
|
-
test('function signature change is high severity', () => {
|
|
191
|
-
const old = makeSpec([
|
|
192
|
-
{ id: 'fn', name: 'fn', kind: 'function', signatures: [{ parameters: [] }] },
|
|
193
|
-
]);
|
|
194
|
-
const new_ = makeSpec([
|
|
195
|
-
{
|
|
196
|
-
id: 'fn',
|
|
197
|
-
name: 'fn',
|
|
198
|
-
kind: 'function',
|
|
199
|
-
signatures: [{ parameters: [{ name: 'x', schema: { type: 'string' } }] }],
|
|
200
|
-
},
|
|
201
|
-
]);
|
|
202
|
-
const result = enrichDiff(old, new_);
|
|
203
|
-
|
|
204
|
-
expect(result.breaking.length).toBe(1);
|
|
205
|
-
expect(result.breaking[0].severity).toBe('high');
|
|
206
|
-
expect(result.breaking[0].reason).toBe('signature changed');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('interface change is medium severity', () => {
|
|
210
|
-
const old = makeSpec([{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [] }]);
|
|
211
|
-
const new_ = makeSpec([
|
|
212
|
-
{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [{ name: 'x', kind: 'property' }] },
|
|
213
|
-
]);
|
|
214
|
-
const result = enrichDiff(old, new_);
|
|
215
|
-
|
|
216
|
-
expect(result.breaking.length).toBe(1);
|
|
217
|
-
expect(result.breaking[0].severity).toBe('medium');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test('breaking changes sorted by severity', () => {
|
|
221
|
-
const old = makeSpec([
|
|
222
|
-
{ id: 'fn', name: 'fn', kind: 'function', signatures: [{ parameters: [] }] },
|
|
223
|
-
{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [] },
|
|
224
|
-
]);
|
|
225
|
-
const new_ = makeSpec([
|
|
226
|
-
{
|
|
227
|
-
id: 'fn',
|
|
228
|
-
name: 'fn',
|
|
229
|
-
kind: 'function',
|
|
230
|
-
signatures: [{ parameters: [{ name: 'x', schema: { type: 'string' } }] }],
|
|
231
|
-
},
|
|
232
|
-
{ id: 'IFoo', name: 'IFoo', kind: 'interface', members: [{ name: 'x', kind: 'property' }] },
|
|
233
|
-
]);
|
|
234
|
-
const result = enrichDiff(old, new_);
|
|
235
|
-
|
|
236
|
-
expect(result.breaking.length).toBe(2);
|
|
237
|
-
expect(result.breaking[0].severity).toBe('high');
|
|
238
|
-
expect(result.breaking[1].severity).toBe('medium');
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
describe('summary counts', () => {
|
|
243
|
-
test('counts all categories correctly', () => {
|
|
244
|
-
const old = makeSpec([
|
|
245
|
-
{ id: 'removed', name: 'removed', kind: 'function' },
|
|
246
|
-
{ id: 'changed', name: 'changed', kind: 'function', signatures: [{ parameters: [] }] },
|
|
247
|
-
{ id: 'docsOnly', name: 'docsOnly', kind: 'variable', description: 'old' },
|
|
248
|
-
]);
|
|
249
|
-
const new_ = makeSpec([
|
|
250
|
-
{
|
|
251
|
-
id: 'changed',
|
|
252
|
-
name: 'changed',
|
|
253
|
-
kind: 'function',
|
|
254
|
-
signatures: [{ parameters: [{ name: 'x', schema: { type: 'string' } }] }],
|
|
255
|
-
},
|
|
256
|
-
{ id: 'docsOnly', name: 'docsOnly', kind: 'variable', description: 'new' },
|
|
257
|
-
{ id: 'added', name: 'added', kind: 'class' },
|
|
258
|
-
]);
|
|
259
|
-
const result = enrichDiff(old, new_);
|
|
260
|
-
|
|
261
|
-
expect(result.summary.removedCount).toBe(1);
|
|
262
|
-
expect(result.summary.changedCount).toBe(1);
|
|
263
|
-
expect(result.summary.docsOnlyCount).toBe(1);
|
|
264
|
-
expect(result.summary.addedCount).toBe(1);
|
|
265
|
-
// Breaking = removed (counted separately) + changed
|
|
266
|
-
expect(result.summary.breakingCount).toBe(1);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('empty specs produce zero counts', () => {
|
|
270
|
-
const empty = makeSpec([]);
|
|
271
|
-
const result = enrichDiff(empty, empty);
|
|
272
|
-
|
|
273
|
-
expect(result.summary.removedCount).toBe(0);
|
|
274
|
-
expect(result.summary.changedCount).toBe(0);
|
|
275
|
-
expect(result.summary.docsOnlyCount).toBe(0);
|
|
276
|
-
expect(result.summary.addedCount).toBe(0);
|
|
277
|
-
expect(result.summary.breakingCount).toBe(0);
|
|
278
|
-
expect(result.summary.semverBump).toBe('none');
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test('semverReason included in summary', () => {
|
|
282
|
-
const old = makeSpec([{ id: 'fn', name: 'fn', kind: 'function' }]);
|
|
283
|
-
const new_ = makeSpec([]);
|
|
284
|
-
const result = enrichDiff(old, new_);
|
|
285
|
-
|
|
286
|
-
expect(result.summary.semverReason).toContain('breaking');
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
describe('semver bump priority', () => {
|
|
291
|
-
test('major takes priority over minor', () => {
|
|
292
|
-
const old = makeSpec([{ id: 'removed', name: 'removed', kind: 'function' }]);
|
|
293
|
-
const new_ = makeSpec([{ id: 'added', name: 'added', kind: 'function' }]);
|
|
294
|
-
const result = enrichDiff(old, new_);
|
|
295
|
-
|
|
296
|
-
expect(result.summary.semverBump).toBe('major');
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test('minor takes priority over patch', () => {
|
|
300
|
-
const old = makeSpec([{ id: 'doc', name: 'doc', kind: 'function', description: 'old' }]);
|
|
301
|
-
const new_ = makeSpec([
|
|
302
|
-
{ id: 'doc', name: 'doc', kind: 'function', description: 'new' },
|
|
303
|
-
{ id: 'added', name: 'added', kind: 'function' },
|
|
304
|
-
]);
|
|
305
|
-
const result = enrichDiff(old, new_);
|
|
306
|
-
|
|
307
|
-
expect(result.summary.semverBump).toBe('minor');
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test('no changes produces none bump', () => {
|
|
311
|
-
const spec = makeSpec([{ id: 'fn', name: 'fn', kind: 'function' }]);
|
|
312
|
-
const result = enrichDiff(spec, spec);
|
|
313
|
-
|
|
314
|
-
expect(result.summary.semverBump).toBe('none');
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
describe('types handling', () => {
|
|
319
|
-
test('removed type is breaking', () => {
|
|
320
|
-
const old = makeSpec([], [{ id: 'MyType', name: 'MyType', kind: 'type' }]);
|
|
321
|
-
const new_ = makeSpec([], []);
|
|
322
|
-
const result = enrichDiff(old, new_);
|
|
323
|
-
|
|
324
|
-
// Removed types go into breaking array via diff logic
|
|
325
|
-
expect(result.summary.semverBump).toBe('major');
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
test('handles undefined types arrays', () => {
|
|
329
|
-
const old: OpenPkg = { openpkg: '0.4.0', meta: { name: 'test' }, exports: [] };
|
|
330
|
-
const new_: OpenPkg = { openpkg: '0.4.0', meta: { name: 'test' }, exports: [] };
|
|
331
|
-
const result = enrichDiff(old, new_);
|
|
332
|
-
|
|
333
|
-
expect(result.summary.semverBump).toBe('none');
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
});
|