cddl2ts 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +43 -15
- package/package.json +2 -2
- package/src/index.ts +57 -15
- package/tests/__snapshots__/extensible_metadata.test.ts.snap +16 -0
- package/tests/__snapshots__/group_choice.test.ts.snap +24 -24
- package/tests/__snapshots__/mod.test.ts.snap +2 -2
- package/tests/__snapshots__/webdriver_remote.test.ts.snap +2 -2
- package/tests/extensible_metadata.test.ts +49 -0
- package/tests/named_group_choice.test.ts +2 -2
- package/tests/transform_edge_cases.test.ts +36 -2
package/build/index.js
CHANGED
|
@@ -57,6 +57,21 @@ export function transform(assignments, options) {
|
|
|
57
57
|
}
|
|
58
58
|
return print(ast).code;
|
|
59
59
|
}
|
|
60
|
+
function getAssignmentComments(assignment) {
|
|
61
|
+
return assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true));
|
|
62
|
+
}
|
|
63
|
+
function exportWithComments(declaration) {
|
|
64
|
+
const expr = b.exportDeclaration(false, declaration);
|
|
65
|
+
expr.comments = declaration.comments;
|
|
66
|
+
declaration.comments = [];
|
|
67
|
+
return expr;
|
|
68
|
+
}
|
|
69
|
+
function isExtensibleRecordProperty(prop) {
|
|
70
|
+
return !isUnNamedProperty(prop) &&
|
|
71
|
+
prop.Occurrence.m === Infinity &&
|
|
72
|
+
!prop.HasCut &&
|
|
73
|
+
RECORD_KEY_TYPES.has(prop.Name);
|
|
74
|
+
}
|
|
60
75
|
function parseAssignment(assignment) {
|
|
61
76
|
if (isVariable(assignment)) {
|
|
62
77
|
const propType = Array.isArray(assignment.PropertyType)
|
|
@@ -72,8 +87,8 @@ function parseAssignment(assignment) {
|
|
|
72
87
|
typeParameters = b.tsUnionType(propType.map(parseUnionType));
|
|
73
88
|
}
|
|
74
89
|
const expr = b.tsTypeAliasDeclaration(id, typeParameters);
|
|
75
|
-
expr.comments = assignment
|
|
76
|
-
return
|
|
90
|
+
expr.comments = getAssignmentComments(assignment);
|
|
91
|
+
return exportWithComments(expr);
|
|
77
92
|
}
|
|
78
93
|
if (isGroup(assignment)) {
|
|
79
94
|
const id = b.identifier(pascalCase(assignment.Name));
|
|
@@ -154,8 +169,8 @@ function parseAssignment(assignment) {
|
|
|
154
169
|
value = b.tsIntersectionType(intersections);
|
|
155
170
|
}
|
|
156
171
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
157
|
-
expr.comments = assignment
|
|
158
|
-
return
|
|
172
|
+
expr.comments = getAssignmentComments(assignment);
|
|
173
|
+
return exportWithComments(expr);
|
|
159
174
|
}
|
|
160
175
|
const props = properties;
|
|
161
176
|
/**
|
|
@@ -167,8 +182,8 @@ function parseAssignment(assignment) {
|
|
|
167
182
|
if (propType.length === 1 && RECORD_KEY_TYPES.has(prop.Name)) {
|
|
168
183
|
const value = parseUnionType(assignment);
|
|
169
184
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
170
|
-
expr.comments = assignment
|
|
171
|
-
return
|
|
185
|
+
expr.comments = getAssignmentComments(assignment);
|
|
186
|
+
return exportWithComments(expr);
|
|
172
187
|
}
|
|
173
188
|
}
|
|
174
189
|
// Check if extended interfaces are likely unions or conflicting types
|
|
@@ -385,14 +400,14 @@ function parseAssignment(assignment) {
|
|
|
385
400
|
value = b.tsIntersectionType(intersections);
|
|
386
401
|
}
|
|
387
402
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
388
|
-
expr.comments = assignment
|
|
389
|
-
return
|
|
403
|
+
expr.comments = getAssignmentComments(assignment);
|
|
404
|
+
return exportWithComments(expr);
|
|
390
405
|
}
|
|
391
406
|
// Fallback to interface if no mixins (pure object)
|
|
392
407
|
const objectType = parseObjectType(props);
|
|
393
408
|
const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType));
|
|
394
|
-
expr.comments = assignment
|
|
395
|
-
return
|
|
409
|
+
expr.comments = getAssignmentComments(assignment);
|
|
410
|
+
return exportWithComments(expr);
|
|
396
411
|
}
|
|
397
412
|
if (isCDDLArray(assignment)) {
|
|
398
413
|
const id = b.identifier(pascalCase(assignment.Name));
|
|
@@ -408,8 +423,8 @@ function parseAssignment(assignment) {
|
|
|
408
423
|
});
|
|
409
424
|
const value = b.tsArrayType(b.tsParenthesizedType(b.tsUnionType(obj)));
|
|
410
425
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
411
|
-
expr.comments = assignment
|
|
412
|
-
return
|
|
426
|
+
expr.comments = getAssignmentComments(assignment);
|
|
427
|
+
return exportWithComments(expr);
|
|
413
428
|
}
|
|
414
429
|
// Standard array
|
|
415
430
|
const firstType = assignmentValues.Type;
|
|
@@ -422,8 +437,8 @@ function parseAssignment(assignment) {
|
|
|
422
437
|
? obj[0]
|
|
423
438
|
: b.tsParenthesizedType(b.tsUnionType(obj)));
|
|
424
439
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
425
|
-
expr.comments = assignment
|
|
426
|
-
return
|
|
440
|
+
expr.comments = getAssignmentComments(assignment);
|
|
441
|
+
return exportWithComments(expr);
|
|
427
442
|
}
|
|
428
443
|
throw new Error(`Unknown assignment type "${assignment.Type}"`);
|
|
429
444
|
}
|
|
@@ -444,9 +459,22 @@ function parseObjectType(props) {
|
|
|
444
459
|
if (isUnNamedProperty(prop)) {
|
|
445
460
|
continue;
|
|
446
461
|
}
|
|
447
|
-
const id = b.identifier(camelcase(prop.Name));
|
|
448
462
|
const cddlType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
|
|
449
463
|
const comments = prop.Comments.map((c) => ` ${c.Content}`);
|
|
464
|
+
if (isExtensibleRecordProperty(prop)) {
|
|
465
|
+
const keyIdentifier = b.identifier('key');
|
|
466
|
+
keyIdentifier.typeAnnotation = b.tsTypeAnnotation(NATIVE_TYPES[prop.Name]);
|
|
467
|
+
const indexSignature = b.tsIndexSignature([keyIdentifier], b.tsTypeAnnotation(b.tsUnionType([
|
|
468
|
+
...cddlType.map((t) => parseUnionType(t)),
|
|
469
|
+
b.tsUndefinedKeyword()
|
|
470
|
+
])));
|
|
471
|
+
indexSignature.comments = comments.length
|
|
472
|
+
? [b.commentBlock(`*\n *${comments.join('\n *')}\n `)]
|
|
473
|
+
: [];
|
|
474
|
+
propItems.push(indexSignature);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const id = b.identifier(camelcase(prop.Name));
|
|
450
478
|
if (prop.Operator && prop.Operator.Type === 'default') {
|
|
451
479
|
const defaultValue = parseDefaultValue(prop.Operator);
|
|
452
480
|
defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cddl2ts",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "A Node.js package that can generate a TypeScript definition based on a CDDL file",
|
|
5
5
|
"author": "Christian Bromann <mail@bromann.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"camelcase": "^9.0.0",
|
|
33
33
|
"recast": "^0.23.11",
|
|
34
34
|
"yargs": "^18.0.0",
|
|
35
|
-
"cddl": "0.19.
|
|
35
|
+
"cddl": "0.19.2"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"release": "release-it --config .release-it.ts --VV",
|
package/src/index.ts
CHANGED
|
@@ -90,6 +90,27 @@ export function transform (assignments: Assignment[], options?: TransformOptions
|
|
|
90
90
|
return print(ast).code
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function getAssignmentComments (assignment: Assignment) {
|
|
94
|
+
return assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function exportWithComments (
|
|
98
|
+
declaration: types.namedTypes.TSTypeAliasDeclaration
|
|
99
|
+
| types.namedTypes.TSInterfaceDeclaration
|
|
100
|
+
) {
|
|
101
|
+
const expr = b.exportDeclaration(false, declaration)
|
|
102
|
+
expr.comments = declaration.comments
|
|
103
|
+
declaration.comments = []
|
|
104
|
+
return expr
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isExtensibleRecordProperty (prop: Property) {
|
|
108
|
+
return !isUnNamedProperty(prop) &&
|
|
109
|
+
prop.Occurrence.m === Infinity &&
|
|
110
|
+
!prop.HasCut &&
|
|
111
|
+
RECORD_KEY_TYPES.has(prop.Name)
|
|
112
|
+
}
|
|
113
|
+
|
|
93
114
|
function parseAssignment (assignment: Assignment) {
|
|
94
115
|
if (isVariable(assignment)) {
|
|
95
116
|
const propType = Array.isArray(assignment.PropertyType)
|
|
@@ -107,8 +128,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
107
128
|
}
|
|
108
129
|
|
|
109
130
|
const expr = b.tsTypeAliasDeclaration(id, typeParameters)
|
|
110
|
-
expr.comments = assignment
|
|
111
|
-
return
|
|
131
|
+
expr.comments = getAssignmentComments(assignment)
|
|
132
|
+
return exportWithComments(expr)
|
|
112
133
|
}
|
|
113
134
|
|
|
114
135
|
if (isGroup(assignment)) {
|
|
@@ -201,8 +222,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
201
222
|
}
|
|
202
223
|
|
|
203
224
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
204
|
-
expr.comments = assignment
|
|
205
|
-
return
|
|
225
|
+
expr.comments = getAssignmentComments(assignment)
|
|
226
|
+
return exportWithComments(expr)
|
|
206
227
|
}
|
|
207
228
|
|
|
208
229
|
const props = properties as Property[]
|
|
@@ -216,8 +237,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
216
237
|
if (propType.length === 1 && RECORD_KEY_TYPES.has(prop.Name)) {
|
|
217
238
|
const value = parseUnionType(assignment)
|
|
218
239
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
219
|
-
expr.comments = assignment
|
|
220
|
-
return
|
|
240
|
+
expr.comments = getAssignmentComments(assignment)
|
|
241
|
+
return exportWithComments(expr)
|
|
221
242
|
}
|
|
222
243
|
}
|
|
223
244
|
|
|
@@ -431,16 +452,16 @@ function parseAssignment (assignment: Assignment) {
|
|
|
431
452
|
}
|
|
432
453
|
|
|
433
454
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
434
|
-
expr.comments = assignment
|
|
435
|
-
return
|
|
455
|
+
expr.comments = getAssignmentComments(assignment)
|
|
456
|
+
return exportWithComments(expr)
|
|
436
457
|
}
|
|
437
458
|
|
|
438
459
|
// Fallback to interface if no mixins (pure object)
|
|
439
460
|
const objectType = parseObjectType(props)
|
|
440
461
|
|
|
441
462
|
const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType))
|
|
442
|
-
expr.comments = assignment
|
|
443
|
-
return
|
|
463
|
+
expr.comments = getAssignmentComments(assignment)
|
|
464
|
+
return exportWithComments(expr)
|
|
444
465
|
}
|
|
445
466
|
|
|
446
467
|
if (isCDDLArray(assignment)) {
|
|
@@ -459,8 +480,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
459
480
|
})
|
|
460
481
|
const value = b.tsArrayType(b.tsParenthesizedType(b.tsUnionType(obj)))
|
|
461
482
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
462
|
-
expr.comments = assignment
|
|
463
|
-
return
|
|
483
|
+
expr.comments = getAssignmentComments(assignment)
|
|
484
|
+
return exportWithComments(expr)
|
|
464
485
|
}
|
|
465
486
|
|
|
466
487
|
// Standard array
|
|
@@ -477,8 +498,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
477
498
|
: b.tsParenthesizedType(b.tsUnionType(obj))
|
|
478
499
|
)
|
|
479
500
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
480
|
-
expr.comments = assignment
|
|
481
|
-
return
|
|
501
|
+
expr.comments = getAssignmentComments(assignment)
|
|
502
|
+
return exportWithComments(expr)
|
|
482
503
|
}
|
|
483
504
|
|
|
484
505
|
throw new Error(`Unknown assignment type "${(assignment as any).Type}"`)
|
|
@@ -502,10 +523,31 @@ function parseObjectType (props: Property[]): ObjectBody {
|
|
|
502
523
|
continue
|
|
503
524
|
}
|
|
504
525
|
|
|
505
|
-
const id = b.identifier(camelcase(prop.Name))
|
|
506
526
|
const cddlType: PropertyType[] = Array.isArray(prop.Type) ? prop.Type : [prop.Type]
|
|
507
527
|
const comments: string[] = prop.Comments.map((c) => ` ${c.Content}`)
|
|
508
528
|
|
|
529
|
+
if (isExtensibleRecordProperty(prop)) {
|
|
530
|
+
const keyIdentifier = b.identifier('key')
|
|
531
|
+
keyIdentifier.typeAnnotation = b.tsTypeAnnotation(NATIVE_TYPES[prop.Name])
|
|
532
|
+
|
|
533
|
+
const indexSignature = b.tsIndexSignature(
|
|
534
|
+
[keyIdentifier],
|
|
535
|
+
b.tsTypeAnnotation(
|
|
536
|
+
b.tsUnionType([
|
|
537
|
+
...cddlType.map((t) => parseUnionType(t)),
|
|
538
|
+
b.tsUndefinedKeyword()
|
|
539
|
+
])
|
|
540
|
+
)
|
|
541
|
+
)
|
|
542
|
+
indexSignature.comments = comments.length
|
|
543
|
+
? [b.commentBlock(`*\n *${comments.join('\n *')}\n `)]
|
|
544
|
+
: []
|
|
545
|
+
propItems.push(indexSignature)
|
|
546
|
+
continue
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const id = b.identifier(camelcase(prop.Name))
|
|
550
|
+
|
|
509
551
|
if (prop.Operator && prop.Operator.Type === 'default') {
|
|
510
552
|
const defaultValue = parseDefaultValue(prop.Operator)
|
|
511
553
|
defaultValue && comments.length && comments.push('') // add empty line if we have previous comments
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`extensible metadata > should render extensible metadata as an interface with an index signature 1`] = `
|
|
4
|
+
"export type MetadataScalar = null | boolean | number | number | string;
|
|
5
|
+
|
|
6
|
+
export interface MessageMetadata {
|
|
7
|
+
provider?: string;
|
|
8
|
+
model?: string;
|
|
9
|
+
modelType?: string;
|
|
10
|
+
runId?: string;
|
|
11
|
+
threadId?: string;
|
|
12
|
+
systemFingerprint?: string;
|
|
13
|
+
serviceTier?: string;
|
|
14
|
+
[key: string]: MetadataScalar | undefined;
|
|
15
|
+
}"
|
|
16
|
+
`;
|
|
@@ -12,52 +12,52 @@ export type DirectProxyConfiguration = Extensible & {
|
|
|
12
12
|
proxyType: "direct";
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
type ManualProxyConfiguration = Extensible & {
|
|
15
|
+
// 1. Simple Group Choice
|
|
16
|
+
export type ManualProxyConfiguration = Extensible & {
|
|
17
17
|
proxyType: "manual";
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
type SimpleGroupChoice = Int | string;
|
|
20
|
+
// 2. Nested Group Choice
|
|
21
|
+
export type SimpleGroupChoice = Int | string;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
type NestedGroupChoice = (Int | Tstr);
|
|
23
|
+
// 3. Group Choice with Multiple Items (Sequence) - interpreted as tuple
|
|
24
|
+
export type NestedGroupChoice = (Int | Tstr);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
type SequenceGroupChoice = [number, string] | number;
|
|
26
|
+
// 4. Map Group Choice - interpreted as map (interface) union
|
|
27
|
+
export type SequenceGroupChoice = [number, string] | number;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
type MapGroupChoice = {
|
|
29
|
+
// 5. Type Choice inside Group - should be value union
|
|
30
|
+
export type MapGroupChoice = {
|
|
31
31
|
a: 1;
|
|
32
32
|
} | {
|
|
33
33
|
b: 2;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
type TypeChoiceInsideGroup = number | string;
|
|
36
|
+
// 6. Array with Group Choice - should be array of union?
|
|
37
|
+
export type TypeChoiceInsideGroup = number | string;
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
type ArrayGroupChoice = Int | string[];
|
|
39
|
+
// 7. Map with nested group (bare and parens) - should be object like
|
|
40
|
+
export type ArrayGroupChoice = Int | string[];
|
|
41
41
|
|
|
42
42
|
export type MapWithBareGroup = number;
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
type MapWithParensGroup = number;
|
|
44
|
+
// 8. Group wrapped in map with multiple properties
|
|
45
|
+
export type MapWithParensGroup = number;
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
type MapGroupWrap = ;
|
|
47
|
+
// 9. Property type choice without operators
|
|
48
|
+
export type MapGroupWrap = ;
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
interface PropChoiceNoOp {
|
|
50
|
+
// 10. Nested choice with group reference
|
|
51
|
+
export interface PropChoiceNoOp {
|
|
52
52
|
a: number | number;
|
|
53
53
|
b: string;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
type NestedChoiceRef = number | string;
|
|
56
|
+
// 11. Complex type (group) followed by property without comma
|
|
57
|
+
export type NestedChoiceRef = number | string;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
interface MapNoComma {
|
|
59
|
+
// 12. Multi-item group choice (verifies isChoice persistence)
|
|
60
|
+
export interface MapNoComma {
|
|
61
61
|
a: number;
|
|
62
62
|
b: string;
|
|
63
63
|
}
|
|
@@ -95,8 +95,8 @@ export interface SomeGroup {
|
|
|
95
95
|
export type ScriptListLocalValue = ScriptLocalValue[];
|
|
96
96
|
export type ScriptMappingLocalValue = (ScriptLocalValue | ScriptLocalValue)[];
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
type Extensible = Record<string, any>;",
|
|
98
|
+
// some comments here
|
|
99
|
+
export type Extensible = Record<string, any>;",
|
|
100
100
|
],
|
|
101
101
|
]
|
|
102
102
|
`;
|
|
@@ -388,9 +388,9 @@ export interface BrowsingContextPrintParameters {
|
|
|
388
388
|
shrinkToFit?: boolean;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
|
|
391
|
+
// Minimum size is 1pt x 1pt. Conversion follows from
|
|
392
392
|
// https://www.w3.org/TR/css3-values/#absolute-lengths
|
|
393
|
-
interface BrowsingContextPrintMarginParameters {
|
|
393
|
+
export interface BrowsingContextPrintMarginParameters {
|
|
394
394
|
/**
|
|
395
395
|
* @default 1
|
|
396
396
|
*/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import url from 'node:url'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import cli from '../src/cli.js'
|
|
6
|
+
|
|
7
|
+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
|
8
|
+
const cddlFile = path.join(__dirname, '..', '..', '..', 'examples', 'commons', 'extensible_metadata.cddl')
|
|
9
|
+
|
|
10
|
+
vi.mock('../src/constants', () => ({
|
|
11
|
+
pkg: {
|
|
12
|
+
name: 'cddl2ts',
|
|
13
|
+
version: '0.0.0'
|
|
14
|
+
}
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
describe('extensible metadata', () => {
|
|
18
|
+
let exitOrig = process.exit
|
|
19
|
+
let logOrig = console.log
|
|
20
|
+
let errorOrig = console.error
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
process.exit = vi.fn() as any
|
|
24
|
+
console.log = vi.fn()
|
|
25
|
+
console.error = vi.fn()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
process.exit = exitOrig
|
|
30
|
+
console.log = logOrig
|
|
31
|
+
console.error = errorOrig
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should render extensible metadata as an interface with an index signature', async () => {
|
|
35
|
+
await cli([cddlFile])
|
|
36
|
+
|
|
37
|
+
expect(process.exit).not.toHaveBeenCalledWith(1)
|
|
38
|
+
expect(console.error).not.toHaveBeenCalled()
|
|
39
|
+
|
|
40
|
+
const output = vi.mocked(console.log).mock.calls.flat().join('\n')
|
|
41
|
+
|
|
42
|
+
expect(output).toContain('export interface MessageMetadata {')
|
|
43
|
+
expect(output).toContain('provider?: string;')
|
|
44
|
+
expect(output).toContain('modelType?: string;')
|
|
45
|
+
expect(output).toContain('[key: string]: MetadataScalar | undefined;')
|
|
46
|
+
expect(output).not.toContain('text?: MetadataScalar;')
|
|
47
|
+
expect(output).toMatchSnapshot()
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -39,8 +39,8 @@ describe('named group choice', () => {
|
|
|
39
39
|
|
|
40
40
|
const output = vi.mocked(console.log).mock.calls.flat().join('\n')
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
expect(output).toMatch(/
|
|
42
|
+
// Leading comments should render before the exported declaration.
|
|
43
|
+
expect(output).toMatch(/(\/\/.*\n)+export type Choice = OptionA \| OptionB/)
|
|
44
44
|
expect(output).toContain('export interface OptionA {')
|
|
45
45
|
expect(output).toContain('export interface OptionB {')
|
|
46
46
|
})
|
|
@@ -14,11 +14,11 @@ import { transform } from '../src/index.js'
|
|
|
14
14
|
|
|
15
15
|
const COMMENTS: Comment[] = []
|
|
16
16
|
|
|
17
|
-
function comment (content: string): Comment {
|
|
17
|
+
function comment (content: string, leading = false): Comment {
|
|
18
18
|
return {
|
|
19
19
|
Type: 'comment',
|
|
20
20
|
Content: content,
|
|
21
|
-
Leading:
|
|
21
|
+
Leading: leading
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -240,6 +240,40 @@ describe('transform edge cases', () => {
|
|
|
240
240
|
expect(output).toContain('enabled?: boolean')
|
|
241
241
|
})
|
|
242
242
|
|
|
243
|
+
it('should place leading comments before exported declarations', () => {
|
|
244
|
+
const output = transform([
|
|
245
|
+
variable('metadata-scalar', ['null', 'bool', 'int', 'float', 'text'], [
|
|
246
|
+
comment('Flat scalar value used by concise metadata bags.', true)
|
|
247
|
+
])
|
|
248
|
+
])
|
|
249
|
+
|
|
250
|
+
expect(output).toContain(`// Flat scalar value used by concise metadata bags.\nexport type MetadataScalar = null | boolean | number | number | string;`)
|
|
251
|
+
expect(output).not.toContain('export // Flat scalar value used by concise metadata bags.')
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('should emit extensible object properties as index signatures', () => {
|
|
255
|
+
const output = transform([
|
|
256
|
+
variable('metadata-scalar', ['null', 'bool', 'int', 'float', 'text']),
|
|
257
|
+
group('message-metadata', [
|
|
258
|
+
property('provider', 'text', {
|
|
259
|
+
Occurrence: { n: 0, m: 1 }
|
|
260
|
+
}),
|
|
261
|
+
property('model', 'text', {
|
|
262
|
+
Occurrence: { n: 0, m: 1 }
|
|
263
|
+
}),
|
|
264
|
+
property('text', groupRef('metadata-scalar'), {
|
|
265
|
+
Occurrence: { n: 0, m: Infinity }
|
|
266
|
+
})
|
|
267
|
+
])
|
|
268
|
+
])
|
|
269
|
+
|
|
270
|
+
expect(output).toContain('export interface MessageMetadata {')
|
|
271
|
+
expect(output).toContain('provider?: string;')
|
|
272
|
+
expect(output).toContain('model?: string;')
|
|
273
|
+
expect(output).toContain('[key: string]: MetadataScalar | undefined;')
|
|
274
|
+
expect(output).not.toContain('text?: MetadataScalar;')
|
|
275
|
+
})
|
|
276
|
+
|
|
243
277
|
it('should throw clear errors for unsupported inputs', () => {
|
|
244
278
|
expect(() => transform([
|
|
245
279
|
variable('unknown-native', 'nope')
|