cddl2ts 0.7.0 → 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.d.ts.map +1 -1
- package/build/index.js +64 -18
- package/package.json +2 -2
- package/src/index.ts +78 -18
- 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.test.ts +74 -11
- package/tests/transform_edge_cases.test.ts +36 -2
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAWH,KAAK,UAAU,EAMlB,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAWH,KAAK,UAAU,EAMlB,MAAM,MAAM,CAAA;AAyCb,MAAM,WAAW,gBAAgB;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,UAwB/E"}
|
package/build/index.js
CHANGED
|
@@ -7,17 +7,35 @@ const b = types.builders;
|
|
|
7
7
|
const NATIVE_TYPES = {
|
|
8
8
|
any: b.tsAnyKeyword(),
|
|
9
9
|
number: b.tsNumberKeyword(),
|
|
10
|
+
integer: b.tsNumberKeyword(),
|
|
10
11
|
int: b.tsNumberKeyword(),
|
|
11
|
-
float: b.tsNumberKeyword(),
|
|
12
12
|
uint: b.tsNumberKeyword(),
|
|
13
|
+
nint: b.tsNumberKeyword(),
|
|
14
|
+
unsigned: b.tsNumberKeyword(),
|
|
15
|
+
float: b.tsNumberKeyword(),
|
|
16
|
+
float16: b.tsNumberKeyword(),
|
|
17
|
+
float32: b.tsNumberKeyword(),
|
|
18
|
+
float64: b.tsNumberKeyword(),
|
|
19
|
+
'float16-32': b.tsNumberKeyword(),
|
|
20
|
+
'float32-64': b.tsNumberKeyword(),
|
|
13
21
|
bool: b.tsBooleanKeyword(),
|
|
22
|
+
false: b.tsBooleanKeyword(),
|
|
23
|
+
true: b.tsBooleanKeyword(),
|
|
24
|
+
bstr: b.tsTypeReference(b.identifier('Uint8Array')),
|
|
25
|
+
bytes: b.tsTypeReference(b.identifier('Uint8Array')),
|
|
14
26
|
str: b.tsStringKeyword(),
|
|
15
27
|
text: b.tsStringKeyword(),
|
|
16
28
|
tstr: b.tsStringKeyword(),
|
|
17
29
|
range: b.tsNumberKeyword(),
|
|
30
|
+
undefined: b.tsUndefinedKeyword(),
|
|
18
31
|
nil: b.tsNullKeyword(),
|
|
19
32
|
null: b.tsNullKeyword()
|
|
20
33
|
};
|
|
34
|
+
const RECORD_KEY_TYPES = new Set([
|
|
35
|
+
'int', 'uint', 'nint', 'integer', 'unsigned', 'number',
|
|
36
|
+
'float', 'float16', 'float32', 'float64', 'float16-32', 'float32-64',
|
|
37
|
+
'str', 'text', 'tstr'
|
|
38
|
+
]);
|
|
21
39
|
export function transform(assignments, options) {
|
|
22
40
|
if (options?.useUnknown) {
|
|
23
41
|
NATIVE_TYPES.any = b.tsUnknownKeyword();
|
|
@@ -39,6 +57,21 @@ export function transform(assignments, options) {
|
|
|
39
57
|
}
|
|
40
58
|
return print(ast).code;
|
|
41
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
|
+
}
|
|
42
75
|
function parseAssignment(assignment) {
|
|
43
76
|
if (isVariable(assignment)) {
|
|
44
77
|
const propType = Array.isArray(assignment.PropertyType)
|
|
@@ -54,8 +87,8 @@ function parseAssignment(assignment) {
|
|
|
54
87
|
typeParameters = b.tsUnionType(propType.map(parseUnionType));
|
|
55
88
|
}
|
|
56
89
|
const expr = b.tsTypeAliasDeclaration(id, typeParameters);
|
|
57
|
-
expr.comments = assignment
|
|
58
|
-
return
|
|
90
|
+
expr.comments = getAssignmentComments(assignment);
|
|
91
|
+
return exportWithComments(expr);
|
|
59
92
|
}
|
|
60
93
|
if (isGroup(assignment)) {
|
|
61
94
|
const id = b.identifier(pascalCase(assignment.Name));
|
|
@@ -136,8 +169,8 @@ function parseAssignment(assignment) {
|
|
|
136
169
|
value = b.tsIntersectionType(intersections);
|
|
137
170
|
}
|
|
138
171
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
139
|
-
expr.comments = assignment
|
|
140
|
-
return
|
|
172
|
+
expr.comments = getAssignmentComments(assignment);
|
|
173
|
+
return exportWithComments(expr);
|
|
141
174
|
}
|
|
142
175
|
const props = properties;
|
|
143
176
|
/**
|
|
@@ -146,11 +179,11 @@ function parseAssignment(assignment) {
|
|
|
146
179
|
if (props.length === 1) {
|
|
147
180
|
const prop = props[0];
|
|
148
181
|
const propType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
|
|
149
|
-
if (propType.length === 1 &&
|
|
182
|
+
if (propType.length === 1 && RECORD_KEY_TYPES.has(prop.Name)) {
|
|
150
183
|
const value = parseUnionType(assignment);
|
|
151
184
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
152
|
-
expr.comments = assignment
|
|
153
|
-
return
|
|
185
|
+
expr.comments = getAssignmentComments(assignment);
|
|
186
|
+
return exportWithComments(expr);
|
|
154
187
|
}
|
|
155
188
|
}
|
|
156
189
|
// Check if extended interfaces are likely unions or conflicting types
|
|
@@ -367,14 +400,14 @@ function parseAssignment(assignment) {
|
|
|
367
400
|
value = b.tsIntersectionType(intersections);
|
|
368
401
|
}
|
|
369
402
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
370
|
-
expr.comments = assignment
|
|
371
|
-
return
|
|
403
|
+
expr.comments = getAssignmentComments(assignment);
|
|
404
|
+
return exportWithComments(expr);
|
|
372
405
|
}
|
|
373
406
|
// Fallback to interface if no mixins (pure object)
|
|
374
407
|
const objectType = parseObjectType(props);
|
|
375
408
|
const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType));
|
|
376
|
-
expr.comments = assignment
|
|
377
|
-
return
|
|
409
|
+
expr.comments = getAssignmentComments(assignment);
|
|
410
|
+
return exportWithComments(expr);
|
|
378
411
|
}
|
|
379
412
|
if (isCDDLArray(assignment)) {
|
|
380
413
|
const id = b.identifier(pascalCase(assignment.Name));
|
|
@@ -390,8 +423,8 @@ function parseAssignment(assignment) {
|
|
|
390
423
|
});
|
|
391
424
|
const value = b.tsArrayType(b.tsParenthesizedType(b.tsUnionType(obj)));
|
|
392
425
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
393
|
-
expr.comments = assignment
|
|
394
|
-
return
|
|
426
|
+
expr.comments = getAssignmentComments(assignment);
|
|
427
|
+
return exportWithComments(expr);
|
|
395
428
|
}
|
|
396
429
|
// Standard array
|
|
397
430
|
const firstType = assignmentValues.Type;
|
|
@@ -404,8 +437,8 @@ function parseAssignment(assignment) {
|
|
|
404
437
|
? obj[0]
|
|
405
438
|
: b.tsParenthesizedType(b.tsUnionType(obj)));
|
|
406
439
|
const expr = b.tsTypeAliasDeclaration(id, value);
|
|
407
|
-
expr.comments = assignment
|
|
408
|
-
return
|
|
440
|
+
expr.comments = getAssignmentComments(assignment);
|
|
441
|
+
return exportWithComments(expr);
|
|
409
442
|
}
|
|
410
443
|
throw new Error(`Unknown assignment type "${assignment.Type}"`);
|
|
411
444
|
}
|
|
@@ -426,9 +459,22 @@ function parseObjectType(props) {
|
|
|
426
459
|
if (isUnNamedProperty(prop)) {
|
|
427
460
|
continue;
|
|
428
461
|
}
|
|
429
|
-
const id = b.identifier(camelcase(prop.Name));
|
|
430
462
|
const cddlType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
|
|
431
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));
|
|
432
478
|
if (prop.Operator && prop.Operator.Type === 'default') {
|
|
433
479
|
const defaultValue = parseDefaultValue(prop.Operator);
|
|
434
480
|
defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
|
|
@@ -511,7 +557,7 @@ function parseUnionType(t) {
|
|
|
511
557
|
/**
|
|
512
558
|
* {*text => text} which will be transformed to `Record<string, string>`
|
|
513
559
|
*/
|
|
514
|
-
if (prop.length === 1 &&
|
|
560
|
+
if (prop.length === 1 && RECORD_KEY_TYPES.has(prop[0].Name)) {
|
|
515
561
|
return b.tsTypeReference(b.identifier('Record'), b.tsTypeParameterInstantiation([
|
|
516
562
|
NATIVE_TYPES[prop[0].Name],
|
|
517
563
|
parseUnionType(prop[0].Type[0])
|
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
|
@@ -27,17 +27,35 @@ const b = types.builders
|
|
|
27
27
|
const NATIVE_TYPES: Record<string, any> = {
|
|
28
28
|
any: b.tsAnyKeyword(),
|
|
29
29
|
number: b.tsNumberKeyword(),
|
|
30
|
+
integer: b.tsNumberKeyword(),
|
|
30
31
|
int: b.tsNumberKeyword(),
|
|
31
|
-
float: b.tsNumberKeyword(),
|
|
32
32
|
uint: b.tsNumberKeyword(),
|
|
33
|
+
nint: b.tsNumberKeyword(),
|
|
34
|
+
unsigned: b.tsNumberKeyword(),
|
|
35
|
+
float: b.tsNumberKeyword(),
|
|
36
|
+
float16: b.tsNumberKeyword(),
|
|
37
|
+
float32: b.tsNumberKeyword(),
|
|
38
|
+
float64: b.tsNumberKeyword(),
|
|
39
|
+
'float16-32': b.tsNumberKeyword(),
|
|
40
|
+
'float32-64': b.tsNumberKeyword(),
|
|
33
41
|
bool: b.tsBooleanKeyword(),
|
|
42
|
+
false: b.tsBooleanKeyword(),
|
|
43
|
+
true: b.tsBooleanKeyword(),
|
|
44
|
+
bstr: b.tsTypeReference(b.identifier('Uint8Array')),
|
|
45
|
+
bytes: b.tsTypeReference(b.identifier('Uint8Array')),
|
|
34
46
|
str: b.tsStringKeyword(),
|
|
35
47
|
text: b.tsStringKeyword(),
|
|
36
48
|
tstr: b.tsStringKeyword(),
|
|
37
49
|
range: b.tsNumberKeyword(),
|
|
50
|
+
undefined: b.tsUndefinedKeyword(),
|
|
38
51
|
nil: b.tsNullKeyword(),
|
|
39
52
|
null: b.tsNullKeyword()
|
|
40
53
|
}
|
|
54
|
+
const RECORD_KEY_TYPES = new Set([
|
|
55
|
+
'int', 'uint', 'nint', 'integer', 'unsigned', 'number',
|
|
56
|
+
'float', 'float16', 'float32', 'float64', 'float16-32', 'float32-64',
|
|
57
|
+
'str', 'text', 'tstr'
|
|
58
|
+
])
|
|
41
59
|
type ObjectEntry = types.namedTypes.TSCallSignatureDeclaration | types.namedTypes.TSConstructSignatureDeclaration | types.namedTypes.TSIndexSignature | types.namedTypes.TSMethodSignature | types.namedTypes.TSPropertySignature
|
|
42
60
|
type ObjectBody = ObjectEntry[]
|
|
43
61
|
type TSTypeKind = types.namedTypes.TSAsExpression['typeAnnotation']
|
|
@@ -72,6 +90,27 @@ export function transform (assignments: Assignment[], options?: TransformOptions
|
|
|
72
90
|
return print(ast).code
|
|
73
91
|
}
|
|
74
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
|
+
|
|
75
114
|
function parseAssignment (assignment: Assignment) {
|
|
76
115
|
if (isVariable(assignment)) {
|
|
77
116
|
const propType = Array.isArray(assignment.PropertyType)
|
|
@@ -89,8 +128,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
89
128
|
}
|
|
90
129
|
|
|
91
130
|
const expr = b.tsTypeAliasDeclaration(id, typeParameters)
|
|
92
|
-
expr.comments = assignment
|
|
93
|
-
return
|
|
131
|
+
expr.comments = getAssignmentComments(assignment)
|
|
132
|
+
return exportWithComments(expr)
|
|
94
133
|
}
|
|
95
134
|
|
|
96
135
|
if (isGroup(assignment)) {
|
|
@@ -183,8 +222,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
183
222
|
}
|
|
184
223
|
|
|
185
224
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
186
|
-
expr.comments = assignment
|
|
187
|
-
return
|
|
225
|
+
expr.comments = getAssignmentComments(assignment)
|
|
226
|
+
return exportWithComments(expr)
|
|
188
227
|
}
|
|
189
228
|
|
|
190
229
|
const props = properties as Property[]
|
|
@@ -195,11 +234,11 @@ function parseAssignment (assignment: Assignment) {
|
|
|
195
234
|
if (props.length === 1) {
|
|
196
235
|
const prop = props[0]
|
|
197
236
|
const propType = Array.isArray(prop.Type) ? prop.Type : [prop.Type]
|
|
198
|
-
if (propType.length === 1 &&
|
|
237
|
+
if (propType.length === 1 && RECORD_KEY_TYPES.has(prop.Name)) {
|
|
199
238
|
const value = parseUnionType(assignment)
|
|
200
239
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
201
|
-
expr.comments = assignment
|
|
202
|
-
return
|
|
240
|
+
expr.comments = getAssignmentComments(assignment)
|
|
241
|
+
return exportWithComments(expr)
|
|
203
242
|
}
|
|
204
243
|
}
|
|
205
244
|
|
|
@@ -413,16 +452,16 @@ function parseAssignment (assignment: Assignment) {
|
|
|
413
452
|
}
|
|
414
453
|
|
|
415
454
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
416
|
-
expr.comments = assignment
|
|
417
|
-
return
|
|
455
|
+
expr.comments = getAssignmentComments(assignment)
|
|
456
|
+
return exportWithComments(expr)
|
|
418
457
|
}
|
|
419
458
|
|
|
420
459
|
// Fallback to interface if no mixins (pure object)
|
|
421
460
|
const objectType = parseObjectType(props)
|
|
422
461
|
|
|
423
462
|
const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType))
|
|
424
|
-
expr.comments = assignment
|
|
425
|
-
return
|
|
463
|
+
expr.comments = getAssignmentComments(assignment)
|
|
464
|
+
return exportWithComments(expr)
|
|
426
465
|
}
|
|
427
466
|
|
|
428
467
|
if (isCDDLArray(assignment)) {
|
|
@@ -441,8 +480,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
441
480
|
})
|
|
442
481
|
const value = b.tsArrayType(b.tsParenthesizedType(b.tsUnionType(obj)))
|
|
443
482
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
444
|
-
expr.comments = assignment
|
|
445
|
-
return
|
|
483
|
+
expr.comments = getAssignmentComments(assignment)
|
|
484
|
+
return exportWithComments(expr)
|
|
446
485
|
}
|
|
447
486
|
|
|
448
487
|
// Standard array
|
|
@@ -459,8 +498,8 @@ function parseAssignment (assignment: Assignment) {
|
|
|
459
498
|
: b.tsParenthesizedType(b.tsUnionType(obj))
|
|
460
499
|
)
|
|
461
500
|
const expr = b.tsTypeAliasDeclaration(id, value)
|
|
462
|
-
expr.comments = assignment
|
|
463
|
-
return
|
|
501
|
+
expr.comments = getAssignmentComments(assignment)
|
|
502
|
+
return exportWithComments(expr)
|
|
464
503
|
}
|
|
465
504
|
|
|
466
505
|
throw new Error(`Unknown assignment type "${(assignment as any).Type}"`)
|
|
@@ -484,10 +523,31 @@ function parseObjectType (props: Property[]): ObjectBody {
|
|
|
484
523
|
continue
|
|
485
524
|
}
|
|
486
525
|
|
|
487
|
-
const id = b.identifier(camelcase(prop.Name))
|
|
488
526
|
const cddlType: PropertyType[] = Array.isArray(prop.Type) ? prop.Type : [prop.Type]
|
|
489
527
|
const comments: string[] = prop.Comments.map((c) => ` ${c.Content}`)
|
|
490
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
|
+
|
|
491
551
|
if (prop.Operator && prop.Operator.Type === 'default') {
|
|
492
552
|
const defaultValue = parseDefaultValue(prop.Operator)
|
|
493
553
|
defaultValue && comments.length && comments.push('') // add empty line if we have previous comments
|
|
@@ -578,7 +638,7 @@ function parseUnionType (t: PropertyType | Assignment): TSTypeKind {
|
|
|
578
638
|
/**
|
|
579
639
|
* {*text => text} which will be transformed to `Record<string, string>`
|
|
580
640
|
*/
|
|
581
|
-
if (prop.length === 1 &&
|
|
641
|
+
if (prop.length === 1 && RECORD_KEY_TYPES.has((prop[0] as Property).Name)) {
|
|
582
642
|
return b.tsTypeReference(
|
|
583
643
|
b.identifier('Record'),
|
|
584
644
|
b.tsTypeParameterInstantiation([
|
|
@@ -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
|
})
|
package/tests/transform.test.ts
CHANGED
|
@@ -1,21 +1,84 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
2
|
import { transform } from '../src/index.js'
|
|
3
|
-
import type { Variable } from 'cddl'
|
|
3
|
+
import type { Group, Property, Variable } from 'cddl'
|
|
4
|
+
|
|
5
|
+
function variable(name: string, propertyType: Variable['PropertyType']): Variable {
|
|
6
|
+
return {
|
|
7
|
+
Type: 'variable',
|
|
8
|
+
Name: name,
|
|
9
|
+
PropertyType: propertyType,
|
|
10
|
+
Comments: [],
|
|
11
|
+
IsChoiceAddition: false
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function property(name: string, type: Property['Type']): Property {
|
|
16
|
+
return {
|
|
17
|
+
HasCut: false,
|
|
18
|
+
Occurrence: { n: 1, m: 1 },
|
|
19
|
+
Name: name,
|
|
20
|
+
Type: type,
|
|
21
|
+
Comments: []
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function group(name: string, properties: Group['Properties']): Group {
|
|
26
|
+
return {
|
|
27
|
+
Type: 'group',
|
|
28
|
+
Name: name,
|
|
29
|
+
IsChoiceAddition: false,
|
|
30
|
+
Properties: properties,
|
|
31
|
+
Comments: []
|
|
32
|
+
}
|
|
33
|
+
}
|
|
4
34
|
|
|
5
35
|
describe('literal transformation direct', () => {
|
|
6
36
|
it('should transform bigint literals correctly', () => {
|
|
7
|
-
const assignment
|
|
8
|
-
Type: '
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Type: 'literal',
|
|
12
|
-
Value: 9007199254740995n
|
|
13
|
-
} as any,
|
|
14
|
-
Comments: [],
|
|
15
|
-
IsChoiceAddition: false
|
|
16
|
-
}
|
|
37
|
+
const assignment = variable('MyBigInt', {
|
|
38
|
+
Type: 'literal',
|
|
39
|
+
Value: 9007199254740995n
|
|
40
|
+
} as any)
|
|
17
41
|
|
|
18
42
|
const output = transform([assignment])
|
|
19
43
|
expect(output).toContain('export type MyBigInt = 9007199254740995n;')
|
|
20
44
|
})
|
|
45
|
+
|
|
46
|
+
it('should transform float32 aliases correctly', () => {
|
|
47
|
+
const assignment = variable('score', 'float32')
|
|
48
|
+
|
|
49
|
+
const output = transform([assignment])
|
|
50
|
+
expect(output).toContain('export type Score = number;')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it.each([
|
|
54
|
+
['integer-value', 'integer', 'number'],
|
|
55
|
+
['negative-value', 'nint', 'number'],
|
|
56
|
+
['unsigned-value', 'unsigned', 'number'],
|
|
57
|
+
['half-float', 'float16', 'number'],
|
|
58
|
+
['double-float', 'float64', 'number'],
|
|
59
|
+
['float-window', 'float16-32', 'number'],
|
|
60
|
+
['float-range', 'float32-64', 'number'],
|
|
61
|
+
['binary-payload', 'bytes', 'Uint8Array'],
|
|
62
|
+
['binary-blob', 'bstr', 'Uint8Array'],
|
|
63
|
+
['missing-value', 'undefined', 'undefined']
|
|
64
|
+
] as const)('should map %s (%s) to %s', (name, propertyType, expectedType) => {
|
|
65
|
+
const output = transform([variable(name, propertyType)])
|
|
66
|
+
expect(output).toContain(`export type ${name.split('-').map((part) => `${part[0]!.toUpperCase()}${part.slice(1)}`).join('')} = ${expectedType};`)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('should keep bytes fields as object properties instead of record aliases', () => {
|
|
70
|
+
const output = transform([
|
|
71
|
+
group('network-get-data-result', [
|
|
72
|
+
property('bytes', {
|
|
73
|
+
Type: 'group',
|
|
74
|
+
Value: 'network.BytesValue',
|
|
75
|
+
Unwrapped: false
|
|
76
|
+
} as any)
|
|
77
|
+
])
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
expect(output).toContain('export interface NetworkGetDataResult {')
|
|
81
|
+
expect(output).toContain('bytes: NetworkBytesValue;')
|
|
82
|
+
expect(output).not.toContain('export type NetworkGetDataResult = Record')
|
|
83
|
+
})
|
|
21
84
|
})
|
|
@@ -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')
|