cddl 0.6.0 → 0.7.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/.editorconfig ADDED
@@ -0,0 +1,18 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # editorconfig.org
4
+
5
+ root = true
6
+
7
+ [*]
8
+
9
+ indent_style = space
10
+ indent_size = 4
11
+
12
+ end_of_line = lf
13
+ charset = utf-8
14
+ trim_trailing_whitespace = true
15
+ insert_final_newline = true
16
+
17
+ [{.travis.yml,**/*.json,.github/**/*.yml}]
18
+ indent_size = 2
package/README.md CHANGED
@@ -20,7 +20,25 @@ $ npm install cddl
20
20
 
21
21
  ## Using this package
22
22
 
23
- Currently, you can use this package to parse a CDDL file into an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST). For example, given the following CDDL file:
23
+ This package exposes a CLI as well as a programmatic interface for parsing and transforming CDDL.
24
+
25
+ ### CLI
26
+
27
+ The `cddl` CLI offers a `validate` command that helps identify invalid CDDL formats, e.g.:
28
+
29
+ ```sh
30
+ npx cddl validate ./path/to/interface.cddl
31
+ ✅ Valid CDDL file!
32
+ ```
33
+
34
+ ### Programmatic Interface
35
+
36
+ You can also use this package to parse a CDDL file into:
37
+
38
+ - an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST).
39
+ - or a [TypeScript](https://www.typescriptlang.org/) definition
40
+
41
+ For example, given the following CDDL file:
24
42
 
25
43
  ```cddl
26
44
  person = {
@@ -29,7 +47,7 @@ person = {
29
47
  }
30
48
  ```
31
49
 
32
- You can use this package to parse the file into an abstract syntax tree (AST):
50
+ By default the package parses the content into an AST:
33
51
 
34
52
  ```js
35
53
  import { parse } from 'cddl'
@@ -49,6 +67,35 @@ console.log(ast)
49
67
  */
50
68
  ```
51
69
 
70
+ You can apply a target specifier to transform the AST into a different language or format (currently supported: `ts` for TypeScript). Note that this is highly experimental and work in progress.
71
+
72
+ ```js
73
+ import { parse } from 'cddl'
74
+
75
+ /**
76
+ * spec.cddl:
77
+ *
78
+ * session.CapabilityRequest = {
79
+ * ?acceptInsecureCerts: bool,
80
+ * ?browserName: text,
81
+ * ?browserVersion: text,
82
+ * ?platformName: text,
83
+ * };
84
+ */
85
+ const ts = parse('./spec.cddl', { target: 'ts' })
86
+ console.log(ts)
87
+ /**
88
+ * outputs:
89
+ *
90
+ * interface SessionCapabilityRequest {
91
+ * acceptInsecureCerts?: boolean,
92
+ * browserName?: string,
93
+ * browserVersion?: string,
94
+ * platformName?: string,
95
+ * }
96
+ */
97
+ ```
98
+
52
99
  ---
53
100
 
54
101
  If you are interested in this project, please feel free to contribute ideas or code patches. Have a look at our [contributing](https://github.com/christian-bromann/cddl/blob/master/LICENSE) guidelines](https://github.com/christian-bromann/cddl/blob/master/LICENSE) to get started.
package/build/ast.d.ts CHANGED
@@ -13,6 +13,7 @@ export type Group = {
13
13
  Name: string;
14
14
  IsChoiceAddition: boolean;
15
15
  Properties: (Property | Property[])[];
16
+ Comments: Comment[];
16
17
  };
17
18
  /**
18
19
  * an array definition
@@ -26,6 +27,7 @@ export type Array = {
26
27
  Type: 'array';
27
28
  Name: string;
28
29
  Values: (Property | Property[])[];
30
+ Comments: Comment[];
29
31
  };
30
32
  /**
31
33
  * a tag definition
@@ -49,6 +51,7 @@ export type Variable = {
49
51
  IsChoiceAddition: boolean;
50
52
  PropertyType: PropertyType | PropertyType[];
51
53
  Operator?: Operator;
54
+ Comments: Comment[];
52
55
  };
53
56
  /**
54
57
  * a comment statement
@@ -59,6 +62,7 @@ export type Variable = {
59
62
  export type Comment = {
60
63
  Type: 'comment';
61
64
  Content: string;
65
+ Leading: boolean;
62
66
  };
63
67
  export type Occurrence = {
64
68
  n: number;
@@ -69,7 +73,7 @@ export type Property = {
69
73
  Occurrence: Occurrence;
70
74
  Name: PropertyName;
71
75
  Type: PropertyType | PropertyType[];
72
- Comment: string;
76
+ Comments: Comment[];
73
77
  Operator?: Operator;
74
78
  };
75
79
  export declare enum Type {
@@ -93,7 +97,12 @@ export declare enum Type {
93
97
  BSTR = "bstr",
94
98
  BYTES = "bytes",
95
99
  TSTR = "tstr",
96
- TEXT = "text"
100
+ TEXT = "text",
101
+ /**
102
+ * null types
103
+ */
104
+ NIL = "nil",
105
+ NULL = "null"
97
106
  }
98
107
  /**
99
108
  * can be a number, e.g. "foo = 0..10"
@@ -122,7 +131,7 @@ export interface Operator {
122
131
  Type: OperatorType;
123
132
  Value: PropertyType;
124
133
  }
125
- export type PropertyReferenceType = 'literal' | 'group' | 'group_array' | 'range' | 'tag';
134
+ export type PropertyReferenceType = 'literal' | 'group' | 'group_array' | 'array' | 'range' | 'tag';
126
135
  export type PropertyReference = {
127
136
  Type: PropertyReferenceType;
128
137
  Value: string | number | boolean | Group | Array | Range | Tag;
@@ -130,10 +139,10 @@ export type PropertyReference = {
130
139
  Operator?: Operator;
131
140
  };
132
141
  export interface NativeTypeWithOperator {
133
- Type: Type;
142
+ Type: Type | PropertyReference;
134
143
  Operator?: Operator;
135
144
  }
136
- export type Assignment = Group | Array | Variable | Comment;
145
+ export type Assignment = Group | Array | Variable;
137
146
  export type PropertyType = Assignment | Array | PropertyReference | string | NativeTypeWithOperator;
138
147
  export type PropertyName = string;
139
148
  //# sourceMappingURL=ast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
package/build/ast.js CHANGED
@@ -33,4 +33,9 @@ export var Type;
33
33
  Type["TSTR"] = "tstr";
34
34
  // Text string (major type 3)
35
35
  Type["TEXT"] = "text";
36
+ /**
37
+ * null types
38
+ */
39
+ Type["NIL"] = "nil";
40
+ Type["NULL"] = "null";
36
41
  })(Type || (Type = {}));
package/build/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import Parser from './parser.js';
3
3
  import { ParseTargets } from './constants.js';
4
4
  import type { ParseOptions } from './types.js';
5
5
  declare const _default: {
6
- parse: (filePath: string, opts: ParseOptions) => string | import("./ast.js").Assignment[];
6
+ parse: (filePath: string, opts?: ParseOptions) => string | import("./ast.js").Assignment[];
7
7
  };
8
8
  export default _default;
9
9
  export { Lexer, Parser, ParseTargets };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,MAAM,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;;sBAGxB,MAAM,QAAQ,YAAY;;AADhD,wBAcC;AAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,MAAM,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;;sBAGxB,MAAM,SAAQ,YAAY;;AADhD,wBAcC;AAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA"}
package/build/index.js CHANGED
@@ -3,7 +3,7 @@ import Parser from './parser.js';
3
3
  import { transform as transformTS } from './transform/ts.js';
4
4
  import { ParseTargets } from './constants.js';
5
5
  export default {
6
- parse: (filePath, opts) => {
6
+ parse: (filePath, opts = { target: ParseTargets.AST }) => {
7
7
  const parser = new Parser(filePath);
8
8
  const ast = parser.parse();
9
9
  if (opts.target === ParseTargets.AST) {
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,OAAO,EAAE,KAAK,EAAU,MAAM,aAAa,CAAC;AAG5C,OAAO,EAE2C,UAAU,EAE3D,MAAM,UAAU,CAAA;AAoBjB,MAAM,CAAC,OAAO,OAAO,MAAM;;IAEvB,CAAC,EAAE,KAAK,CAAC;IAET,QAAQ,EAAE,KAAK,CAAa;IAC5B,SAAS,EAAE,KAAK,CAAa;gBAEhB,QAAQ,EAAE,MAAM;IAQ7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,oBAAoB;IAyW5B,OAAO,CAAC,wBAAwB;IAahC;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,iBAAiB;IA2HzB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,gBAAgB;IA4DxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB,KAAK;IAuBL,OAAO,CAAC,WAAW;CAKtB"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,OAAO,EAAE,KAAK,EAAU,MAAM,aAAa,CAAC;AAG5C,OAAO,EAE2C,UAAU,EAE3D,MAAM,UAAU,CAAA;AAoBjB,MAAM,CAAC,OAAO,OAAO,MAAM;;IAEvB,CAAC,EAAE,KAAK,CAAC;IAET,QAAQ,EAAE,KAAK,CAAa;IAC5B,SAAS,EAAE,KAAK,CAAa;gBAEhB,QAAQ,EAAE,MAAM;IAQ7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;IA2CxB,OAAO,CAAC,oBAAoB;IA6Y5B,OAAO,CAAC,wBAAwB;IAahC;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,iBAAiB;IA6HzB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,gBAAgB;IA4DxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB,KAAK;IAaL,OAAO,CAAC,WAAW;CAKtB"}
package/build/parser.js CHANGED
@@ -38,6 +38,10 @@ export default class Parser {
38
38
  return true;
39
39
  }
40
40
  parseAssignments() {
41
+ const comments = [];
42
+ while (this.curToken.Type === Tokens.COMMENT) {
43
+ comments.push(this.parseComment());
44
+ }
41
45
  /**
42
46
  * expect group identifier, e.g.
43
47
  * groupName =
@@ -60,7 +64,14 @@ export default class Parser {
60
64
  this.nextToken(); // eat `/`
61
65
  }
62
66
  this.nextToken(); // eat `=`
63
- return this.parseAssignmentValue(groupName, isChoiceAddition);
67
+ const assignmentValue = this.parseAssignmentValue(groupName, isChoiceAddition);
68
+ // @ts-expect-error curToken can be changed by now but TS doesn't understand this
69
+ while (this.curToken.Type === Tokens.COMMENT) {
70
+ const comment = this.parseComment();
71
+ comment && comments.push(comment);
72
+ }
73
+ assignmentValue.Comments = comments;
74
+ return assignmentValue;
64
75
  }
65
76
  parseAssignmentValue(groupName, isChoiceAddition = false) {
66
77
  let isChoice = false;
@@ -79,7 +90,8 @@ export default class Parser {
79
90
  Type: 'variable',
80
91
  Name: groupName,
81
92
  IsChoiceAddition: isChoiceAddition,
82
- PropertyType: this.parsePropertyTypes()
93
+ PropertyType: this.parsePropertyTypes(),
94
+ Comments: []
83
95
  };
84
96
  return variable;
85
97
  }
@@ -116,7 +128,8 @@ export default class Parser {
116
128
  Type: 'variable',
117
129
  Name: groupName,
118
130
  IsChoiceAddition: isChoiceAddition,
119
- PropertyType: propertyType
131
+ PropertyType: propertyType,
132
+ Comments: []
120
133
  };
121
134
  if (this.isOperator()) {
122
135
  variable.Operator = this.parseOperator();
@@ -140,7 +153,8 @@ export default class Parser {
140
153
  Name: groupName,
141
154
  IsChoiceAddition: isChoiceAddition,
142
155
  PropertyType: prop,
143
- Operator: this.parseOperator()
156
+ Operator: this.parseOperator(),
157
+ Comments: []
144
158
  };
145
159
  return variable;
146
160
  }
@@ -148,10 +162,12 @@ export default class Parser {
148
162
  }
149
163
  while (!closingTokens.includes(this.curToken.Type)) {
150
164
  const propertyType = [];
165
+ const comments = [];
151
166
  let isUnwrapped = false;
152
167
  let hasCut = false;
153
168
  let propertyName = '';
154
- let comment = '';
169
+ const leadingComment = this.parseComment(true);
170
+ leadingComment && comments.push(leadingComment);
155
171
  const occurrence = this.parseOccurrences();
156
172
  /**
157
173
  * check if variable name is unwrapped
@@ -184,7 +200,7 @@ export default class Parser {
184
200
  Occurrence: occurrence,
185
201
  Name: '',
186
202
  Type: innerGroup,
187
- Comment: ''
203
+ Comments: []
188
204
  });
189
205
  continue;
190
206
  }
@@ -202,6 +218,7 @@ export default class Parser {
202
218
  if (this.curToken.Type === Tokens.COMMA || closingTokens.includes(this.curToken.Type)) {
203
219
  const tokenType = this.curToken.Type;
204
220
  let parsedComments = false;
221
+ let comment;
205
222
  /**
206
223
  * check if line has a comment
207
224
  */
@@ -221,7 +238,7 @@ export default class Parser {
221
238
  Value: propertyName,
222
239
  Unwrapped: isUnwrapped
223
240
  }],
224
- Comment: comment
241
+ Comments: comment ? [comment] : []
225
242
  });
226
243
  if (this.curToken.Literal === Tokens.COMMA || this.curToken.Literal === closingTokens[0]) {
227
244
  if (this.curToken.Literal === Tokens.COMMA) {
@@ -263,11 +280,11 @@ export default class Parser {
263
280
  Occurrence: occurrence,
264
281
  Name: '',
265
282
  Type: {
266
- Type: "group",
283
+ Type: 'group',
267
284
  Value: propertyName,
268
285
  Unwrapped: isUnwrapped
269
286
  },
270
- Comment: comment
287
+ Comments: comments
271
288
  };
272
289
  if (isChoice) {
273
290
  /**
@@ -318,14 +335,15 @@ export default class Parser {
318
335
  flipIsChoice = true;
319
336
  this.nextToken(); // eat ,
320
337
  }
321
- comment = this.parseComment();
338
+ const trailingComment = this.parseComment();
339
+ trailingComment && comments.push(trailingComment);
322
340
  const prop = {
323
341
  HasCut: hasCut,
324
342
  Occurrence: occurrence,
325
343
  Name: propertyName,
326
344
  Type: propertyType,
327
- Comment: comment,
328
- ...(operator ? operator : {})
345
+ Comments: comments,
346
+ ...(operator ? { Operator: operator } : {})
329
347
  };
330
348
  if (isChoice) {
331
349
  valuesOrProperties[valuesOrProperties.length - 1].push(prop);
@@ -364,9 +382,35 @@ export default class Parser {
364
382
  return {
365
383
  Type: 'array',
366
384
  Name: groupName || '',
367
- Values: valuesOrProperties
385
+ Values: valuesOrProperties,
386
+ Comments: []
368
387
  };
369
388
  }
389
+ /**
390
+ * simplify wrapped types, e.g. from
391
+ * {
392
+ * "Type": "group",
393
+ * "Name": "",
394
+ * "Properties": [
395
+ * {
396
+ * "HasCut": false,
397
+ * "Occurrence": {
398
+ * "n": 1,
399
+ * "m": 1
400
+ * },
401
+ * "Name": "",
402
+ * "Type": "bool",
403
+ * "Comment": ""
404
+ * }
405
+ * ],
406
+ * "IsChoiceAddition": false
407
+ * }
408
+ * back to:
409
+ * bool
410
+ */
411
+ if (!groupName && valuesOrProperties.length === 1 && PREDEFINED_IDENTIFIER.includes(valuesOrProperties[0].Type)) {
412
+ return valuesOrProperties[0].Type;
413
+ }
370
414
  /**
371
415
  * otherwise a group
372
416
  */
@@ -374,7 +418,8 @@ export default class Parser {
374
418
  Type: 'group',
375
419
  Name: groupName || '',
376
420
  Properties: valuesOrProperties,
377
- IsChoiceAddition: isChoiceAddition
421
+ IsChoiceAddition: isChoiceAddition,
422
+ Comments: []
378
423
  };
379
424
  }
380
425
  isPropertyValueSeparator() {
@@ -446,6 +491,8 @@ export default class Parser {
446
491
  case Type.BYTES:
447
492
  case Type.TSTR:
448
493
  case Type.TEXT:
494
+ case Type.NIL:
495
+ case Type.NULL:
449
496
  type = this.curToken.Literal;
450
497
  break;
451
498
  default: {
@@ -656,26 +703,20 @@ export default class Parser {
656
703
  /**
657
704
  * check if line has a comment
658
705
  */
659
- parseComment() {
660
- let comment = '';
661
- if (this.curToken.Type === Tokens.COMMENT) {
662
- comment = this.curToken.Literal.slice(2);
663
- this.nextToken();
706
+ parseComment(isLeading) {
707
+ if (this.curToken.Type !== Tokens.COMMENT) {
708
+ return;
664
709
  }
665
- return comment;
710
+ const comment = this.curToken.Literal.replace(/^;(\s*)/, '');
711
+ this.nextToken();
712
+ if (comment.trim().length === 0) {
713
+ return;
714
+ }
715
+ return { Type: 'comment', Content: comment, Leading: Boolean(isLeading) };
666
716
  }
667
717
  parse() {
668
718
  const definition = [];
669
719
  while (this.curToken.Type !== Tokens.EOF) {
670
- if (this.curToken.Type === Tokens.COMMENT) {
671
- const comment = {
672
- Type: 'comment',
673
- Content: this.curToken.Literal.slice(1).trim()
674
- };
675
- definition.push(comment);
676
- this.nextToken();
677
- continue;
678
- }
679
720
  const group = this.parseAssignments();
680
721
  if (group) {
681
722
  definition.push(group);
@@ -1 +1 @@
1
- {"version":3,"file":"ts.d.ts","sourceRoot":"","sources":["../../src/transform/ts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA6C,MAAM,QAAQ,CAAA;AAanF,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,UAoBnD"}
1
+ {"version":3,"file":"ts.d.ts","sourceRoot":"","sources":["../../src/transform/ts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAmG,MAAM,QAAQ,CAAA;AAmBzI,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,UAkBnD"}
@@ -4,14 +4,17 @@ import typescriptParser from 'recast/parsers/typescript.js';
4
4
  // @ts-ignore
5
5
  import pkg from '../../package.json' assert { type: 'json' };
6
6
  const b = types.builders;
7
- const comments = [];
8
7
  const NATIVE_TYPES = {
9
- number: 'number',
10
- uint: 'Uint32Array',
11
- bool: 'boolean',
12
- str: 'string',
13
- text: 'string',
14
- tstr: 'string'
8
+ number: b.tsNumberKeyword(),
9
+ float: b.tsNumberKeyword(),
10
+ uint: b.tsNumberKeyword(),
11
+ bool: b.tsBooleanKeyword(),
12
+ str: b.tsStringKeyword(),
13
+ text: b.tsStringKeyword(),
14
+ tstr: b.tsStringKeyword(),
15
+ range: b.tsNumberKeyword(),
16
+ nil: b.tsNullKeyword(),
17
+ null: b.tsNullKeyword()
15
18
  };
16
19
  export function transform(assignments) {
17
20
  let ast = parse(`// compiled with https://www.npmjs.com/package/cddl v${pkg.version}`, {
@@ -26,14 +29,9 @@ export function transform(assignments) {
26
29
  }
27
30
  ast.program.body.push(statement);
28
31
  }
29
- ast.program.comments = comments.map((c) => b.commentLine(c, false, false));
30
32
  return print(ast).code;
31
33
  }
32
34
  function parseAssignment(ast, assignment) {
33
- if (assignment.Type === 'comment') {
34
- comments.push(assignment.Content);
35
- return;
36
- }
37
35
  if (assignment.Type === 'variable') {
38
36
  const propType = Array.isArray(assignment.PropertyType)
39
37
  ? assignment.PropertyType
@@ -48,13 +46,18 @@ function parseAssignment(ast, assignment) {
48
46
  typeParameters = b.tsUnionType(propType.map(parsePropertyType));
49
47
  }
50
48
  const expr = b.tsTypeAliasDeclaration(id, typeParameters);
51
- expr.comments = comments.map((c) => b.commentLine(c, true));
49
+ expr.comments = assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true));
52
50
  return expr;
53
51
  }
54
52
  if (assignment.Type === 'group') {
55
53
  const id = b.identifier(camelcase(assignment.Name, { pascalCase: true }));
56
54
  const objectType = parseObjectType(assignment.Properties);
57
- const expr = b.interfaceDeclaration(id, objectType, []);
55
+ const extendInterfaces = assignment.Properties
56
+ .filter((prop) => prop.Name === '')
57
+ .map((prop) => b.tsExpressionWithTypeArguments(b.identifier(camelcase(prop.Type[0].Value, { pascalCase: true }))));
58
+ const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType));
59
+ expr.extends = extendInterfaces;
60
+ expr.comments = assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true));
58
61
  return expr;
59
62
  }
60
63
  }
@@ -63,7 +66,7 @@ function parsePropertyType(propType) {
63
66
  return b.tsStringKeyword();
64
67
  }
65
68
  if (propType.Type === 'group') {
66
- return b.tsTypeReference(b.identifier(propType.Value.toString()));
69
+ return b.tsTypeReference(b.identifier(camelcase(propType.Value.toString(), { pascalCase: true })));
67
70
  }
68
71
  if (propType.Type === 'literal') {
69
72
  return b.tsLiteralType(b.stringLiteral(propType.Value.toString()));
@@ -74,40 +77,109 @@ function parseObjectType(props) {
74
77
  const propItems = [];
75
78
  for (const prop of props) {
76
79
  /**
77
- * ToDo(Christian): support Extensible
80
+ * Empty groups like
81
+ * {
82
+ * HasCut: false,
83
+ * Occurrence: { n: 1, m: 1 },
84
+ * Name: '',
85
+ * Type: [ { Type: 'group', Value: 'Extensible', Unwrapped: false } ],
86
+ * Comment: ''
87
+ * }
88
+ * are ignored and later added as interface extensions
78
89
  */
79
90
  if (prop.Name === '') {
80
- propItems[propItems.length - 1].comments = [b.commentLine(`Missing: ${JSON.stringify(prop)}`)];
81
91
  continue;
82
92
  }
83
93
  const id = b.identifier(camelcase(prop.Name));
84
94
  const cddlType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
85
- const typeParameters = b.unionTypeAnnotation(cddlType.map((t) => {
86
- if (typeof t === 'string') {
87
- if (!NATIVE_TYPES[t]) {
88
- throw new Error(`Unknown native type: "${t}`);
89
- }
90
- return b.typeParameter(NATIVE_TYPES[t]);
91
- }
92
- else if (t.Value === 'null') {
93
- return b.nullTypeAnnotation();
94
- }
95
- else if (t.Type === 'group') {
96
- const value = t.Value;
97
- return b.typeParameter(
98
- /**
99
- * transform native CDDL types into TypeScript types
100
- */
101
- NATIVE_TYPES[value] ? value : camelcase(value.toString(), { pascalCase: true }));
102
- }
103
- else if (t.Type === 'literal' && typeof t.Value === 'string') {
104
- return b.stringLiteralTypeAnnotation(t.Value, t.Value);
95
+ const comments = prop.Comments.map((c) => ` ${c.Content}`);
96
+ if (prop.Operator && prop.Operator.Type === 'default') {
97
+ const defaultValue = parseDefaultValue(prop.Operator);
98
+ defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
99
+ defaultValue && comments.push(` @default ${defaultValue}`);
100
+ }
101
+ const type = cddlType.map((t) => {
102
+ const unionType = parseUnionType(t);
103
+ if (unionType) {
104
+ const defaultValue = parseDefaultValue(t.Operator);
105
+ defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
106
+ defaultValue && comments.push(` @default ${defaultValue}`);
107
+ return unionType;
105
108
  }
106
109
  throw new Error(`Couldn't parse property ${JSON.stringify(t)}`);
107
- }));
110
+ });
111
+ const typeAnnotation = b.tsTypeAnnotation(b.tsUnionType(type));
108
112
  const isOptional = prop.Occurrence.n === 0;
109
- propItems.push(b.objectTypeProperty(id, typeParameters, isOptional));
113
+ const propSignature = b.tsPropertySignature(id, typeAnnotation, isOptional);
114
+ propSignature.comments = comments.length ? [b.commentBlock(`*\n *${comments.join('\n *')}\n `)] : [];
115
+ propItems.push(propSignature);
116
+ }
117
+ return propItems;
118
+ }
119
+ function parseUnionType(t) {
120
+ if (typeof t === 'string') {
121
+ if (!NATIVE_TYPES[t]) {
122
+ throw new Error(`Unknown native type: "${t}`);
123
+ }
124
+ return NATIVE_TYPES[t];
125
+ }
126
+ else if (NATIVE_TYPES[t.Type]) {
127
+ return NATIVE_TYPES[t.Type];
128
+ }
129
+ else if (t.Value === 'null') {
130
+ return b.tsNullKeyword();
131
+ }
132
+ else if (t.Type === 'group') {
133
+ const value = t.Value;
134
+ /**
135
+ * check if we have
136
+ * ?attributes: {*text => text},
137
+ */
138
+ if (!value && t.Properties) {
139
+ return b.tsTypeLiteral(parseObjectType(t.Properties));
140
+ }
141
+ return b.tsTypeReference(b.identifier(camelcase(value.toString(), { pascalCase: true })));
142
+ }
143
+ else if (t.Type === 'literal' && typeof t.Value === 'string') {
144
+ return b.tsLiteralType(b.stringLiteral(t.Value));
145
+ }
146
+ else if (t.Type === 'literal' && typeof t.Value === 'number') {
147
+ return b.tsLiteralType(b.numericLiteral(t.Value));
148
+ }
149
+ else if (t.Type === 'array') {
150
+ const types = t.Values[0].Type;
151
+ const typedTypes = (Array.isArray(types) ? types : [types]).map((val) => {
152
+ return typeof val === 'string' && NATIVE_TYPES[val]
153
+ ? NATIVE_TYPES[val]
154
+ : b.tsTypeReference(b.identifier(camelcase(val.Value, { pascalCase: true })));
155
+ });
156
+ return b.tsArrayType(typedTypes.length > 1
157
+ ? b.tsParenthesizedType(b.tsUnionType(typedTypes))
158
+ : b.tsUnionType(typedTypes));
159
+ }
160
+ else if (typeof t.Type === 'object' && t.Type.Type === 'range') {
161
+ return b.tsNumberKeyword();
162
+ }
163
+ else if (typeof t.Type === 'object' && t.Type.Type === 'group') {
164
+ /**
165
+ * e.g. ?pointerType: input.PointerType .default "mouse"
166
+ */
167
+ const referenceValue = camelcase(t.Type.Value, { pascalCase: true });
168
+ return b.tsTypeReference(b.identifier(referenceValue));
169
+ }
170
+ }
171
+ function parseDefaultValue(operator) {
172
+ if (!operator || operator.Type !== 'default') {
173
+ return;
174
+ }
175
+ const operatorValue = operator.Value;
176
+ if (operator.Value === 'null') {
177
+ return operator.Value;
178
+ }
179
+ if (operatorValue.Type !== 'literal') {
180
+ throw new Error(`Can't parse operator default value of ${JSON.stringify(operator)}`);
110
181
  }
111
- const obj = b.objectTypeAnnotation(propItems);
112
- return obj;
182
+ return typeof operatorValue.Value === 'string'
183
+ ? `'${operatorValue.Value}'`
184
+ : operatorValue.Value;
113
185
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cddl",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Concise data definition language (RFC 8610) implementation and JSON validator in Node.js",
5
5
  "author": "Christian Bromann <christian@saucelabs.com>",
6
6
  "license": "MIT",