cddl2ts 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;AAwCb,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAIzC,MAAM,WAAW,gBAAgB;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,SAAS,CAAA;CACxB;AAED,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,UA8B/E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAYH,KAAK,UAAU,EAOlB,MAAM,MAAM,CAAA;AAwCb,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAIzC,MAAM,WAAW,gBAAgB;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,SAAS,CAAA;CACxB;AAED,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,UA8B/E"}
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import camelcase from 'camelcase';
2
2
  import { parse, print, types } from 'recast';
3
3
  import typescriptParser from 'recast/parsers/typescript.js';
4
- import { isCDDLArray, isGroup, isNamedGroupReference, isLiteralWithValue, isNativeTypeWithOperator, isUnNamedProperty, isPropertyReference, isRange, isVariable, pascalCase } from 'cddl';
4
+ import { getRegexpPattern, isCDDLArray, isGroup, isNamedGroupReference, isLiteralWithValue, isNativeTypeWithOperator, isUnNamedProperty, isPropertyReference, isRange, isVariable, pascalCase } from 'cddl';
5
5
  import { pkg } from './constants.js';
6
6
  const b = types.builders;
7
7
  const NATIVE_TYPES = {
@@ -524,6 +524,48 @@ function parseObjectType(props, options) {
524
524
  }
525
525
  return propItems;
526
526
  }
527
+ function parseTemplateLiteralType(template) {
528
+ const ast = parse(`type __CDDLTemplate = ${template};`, {
529
+ parser: typescriptParser,
530
+ sourceFileName: 'cddl2Ts.ts',
531
+ sourceRoot: process.cwd()
532
+ });
533
+ return ast.program.body[0].typeAnnotation;
534
+ }
535
+ function escapeTemplateLiteralSegment(segment) {
536
+ return segment
537
+ .replace(/\\/g, '\\\\')
538
+ .replace(/`/g, '\\`')
539
+ .replace(/\$\{/g, '\\${');
540
+ }
541
+ function regexpPatternToTemplateLiteral(pattern) {
542
+ const normalized = pattern.startsWith('^') && pattern.endsWith('$')
543
+ ? pattern.slice(1, -1)
544
+ : pattern;
545
+ if (!normalized.includes('.+')) {
546
+ return;
547
+ }
548
+ const wildcardOnlyPattern = normalized.replace(/(\.\+)+/g, '');
549
+ if (wildcardOnlyPattern.includes('(') || wildcardOnlyPattern.includes('[') || wildcardOnlyPattern.includes('\\')) {
550
+ return;
551
+ }
552
+ const segments = normalized.split(/(?:\.\+)+/g);
553
+ if (segments.length <= 1) {
554
+ return;
555
+ }
556
+ return `\`${segments.map(escapeTemplateLiteralSegment).join('${string}')}\``;
557
+ }
558
+ function parseNativeTypeWithOperator(t) {
559
+ if (typeof t.Type !== 'string') {
560
+ return;
561
+ }
562
+ const regexpPattern = getRegexpPattern(t);
563
+ const templateLiteral = regexpPattern && regexpPatternToTemplateLiteral(regexpPattern);
564
+ if (templateLiteral) {
565
+ return parseTemplateLiteralType(templateLiteral);
566
+ }
567
+ return NATIVE_TYPES[t.Type];
568
+ }
527
569
  function parseUnionType(t, options) {
528
570
  if (typeof t === 'string') {
529
571
  if (!NATIVE_TYPES[t]) {
@@ -531,6 +573,13 @@ function parseUnionType(t, options) {
531
573
  }
532
574
  return NATIVE_TYPES[t];
533
575
  }
576
+ else if (isNativeTypeWithOperator(t) && typeof t.Type === 'string') {
577
+ const nativeType = parseNativeTypeWithOperator(t);
578
+ if (!nativeType) {
579
+ throw new Error(`Unknown native type with operator: ${JSON.stringify(t)}`);
580
+ }
581
+ return nativeType;
582
+ }
534
583
  else if (t.Type && typeof t.Type === 'string' && NATIVE_TYPES[t.Type]) {
535
584
  return NATIVE_TYPES[t.Type];
536
585
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cddl2ts",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
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.2"
35
+ "cddl": "0.20.1"
36
36
  },
37
37
  "scripts": {
38
38
  "release": "release-it --config .release-it.ts --VV",
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import { parse, print, types } from 'recast'
3
3
  import typescriptParser from 'recast/parsers/typescript.js'
4
4
 
5
5
  import {
6
+ getRegexpPattern,
6
7
  isCDDLArray,
7
8
  isGroup,
8
9
  isNamedGroupReference,
@@ -14,6 +15,7 @@ import {
14
15
  isVariable,
15
16
  pascalCase,
16
17
  type Assignment,
18
+ type NativeTypeWithOperator,
17
19
  type PropertyType,
18
20
  type PropertyReference,
19
21
  type Property,
@@ -610,12 +612,70 @@ function parseObjectType (props: Property[], options: TransformSettings): Object
610
612
  return propItems
611
613
  }
612
614
 
615
+ function parseTemplateLiteralType (template: string): TSTypeKind {
616
+ const ast = parse(`type __CDDLTemplate = ${template};`, {
617
+ parser: typescriptParser,
618
+ sourceFileName: 'cddl2Ts.ts',
619
+ sourceRoot: process.cwd()
620
+ }) satisfies types.namedTypes.File
621
+ return (ast.program.body[0] as types.namedTypes.TSTypeAliasDeclaration).typeAnnotation
622
+ }
623
+
624
+ function escapeTemplateLiteralSegment (segment: string): string {
625
+ return segment
626
+ .replace(/\\/g, '\\\\')
627
+ .replace(/`/g, '\\`')
628
+ .replace(/\$\{/g, '\\${')
629
+ }
630
+
631
+ function regexpPatternToTemplateLiteral (pattern: string): string | undefined {
632
+ const normalized = pattern.startsWith('^') && pattern.endsWith('$')
633
+ ? pattern.slice(1, -1)
634
+ : pattern
635
+
636
+ if (!normalized.includes('.+')) {
637
+ return
638
+ }
639
+
640
+ const wildcardOnlyPattern = normalized.replace(/(\.\+)+/g, '')
641
+ if (wildcardOnlyPattern.includes('(') || wildcardOnlyPattern.includes('[') || wildcardOnlyPattern.includes('\\')) {
642
+ return
643
+ }
644
+
645
+ const segments = normalized.split(/(?:\.\+)+/g)
646
+ if (segments.length <= 1) {
647
+ return
648
+ }
649
+
650
+ return `\`${segments.map(escapeTemplateLiteralSegment).join('${string}')}\``
651
+ }
652
+
653
+ function parseNativeTypeWithOperator (t: NativeTypeWithOperator): TSTypeKind | undefined {
654
+ if (typeof t.Type !== 'string') {
655
+ return
656
+ }
657
+
658
+ const regexpPattern = getRegexpPattern(t)
659
+ const templateLiteral = regexpPattern && regexpPatternToTemplateLiteral(regexpPattern)
660
+ if (templateLiteral) {
661
+ return parseTemplateLiteralType(templateLiteral)
662
+ }
663
+
664
+ return NATIVE_TYPES[t.Type]
665
+ }
666
+
613
667
  function parseUnionType (t: PropertyType | Assignment, options: TransformSettings): TSTypeKind {
614
668
  if (typeof t === 'string') {
615
669
  if (!NATIVE_TYPES[t]) {
616
670
  throw new Error(`Unknown native type: "${t}`)
617
671
  }
618
672
  return NATIVE_TYPES[t]
673
+ } else if (isNativeTypeWithOperator(t) && typeof t.Type === 'string') {
674
+ const nativeType = parseNativeTypeWithOperator(t)
675
+ if (!nativeType) {
676
+ throw new Error(`Unknown native type with operator: ${JSON.stringify(t)}`)
677
+ }
678
+ return nativeType
619
679
  } else if ((t as any).Type && typeof (t as any).Type === 'string' && NATIVE_TYPES[(t as any).Type]) {
620
680
  return NATIVE_TYPES[(t as any).Type]
621
681
  } else if (isNativeTypeWithOperator(t) && NATIVE_TYPES[(t.Type as any).Type]) {
@@ -123,6 +123,62 @@ describe('transform edge cases', () => {
123
123
  expect(output).toContain('export type MaybeValue = unknown;')
124
124
  })
125
125
 
126
+ it('should map simple wildcard regexp strings to template literal types', () => {
127
+ const output = transform([
128
+ variable('channel', {
129
+ Type: 'tstr',
130
+ Operator: {
131
+ Type: 'regexp',
132
+ Value: literal('custom:.+')
133
+ }
134
+ } as any),
135
+ group('event-envelope', [
136
+ property('channel', {
137
+ Type: 'tstr',
138
+ Operator: {
139
+ Type: 'regexp',
140
+ Value: literal('custom:.+')
141
+ }
142
+ } as any)
143
+ ]),
144
+ variable('email-address', {
145
+ Type: 'tstr',
146
+ Operator: {
147
+ Type: 'regexp',
148
+ Value: literal('[^@]+@[^@]+')
149
+ }
150
+ } as any),
151
+ variable('prefixed-name', {
152
+ Type: 'tstr',
153
+ Operator: {
154
+ Type: 'regexp',
155
+ Value: literal('foo_.+')
156
+ }
157
+ } as any),
158
+ variable('wrapped-name', {
159
+ Type: 'tstr',
160
+ Operator: {
161
+ Type: 'regexp',
162
+ Value: literal('some_.+_name')
163
+ }
164
+ } as any),
165
+ variable('double-wildcard', {
166
+ Type: 'tstr',
167
+ Operator: {
168
+ Type: 'regexp',
169
+ Value: literal('some_.+_middle_.+')
170
+ }
171
+ } as any)
172
+ ])
173
+
174
+ expect(output).toContain('export type Channel = `custom:${string}`;')
175
+ expect(output).toContain('channel: `custom:${string}`;')
176
+ expect(output).toContain('export type EmailAddress = string;')
177
+ expect(output).toContain('export type PrefixedName = `foo_${string}`;')
178
+ expect(output).toContain('export type WrappedName = `some_${string}_name`;')
179
+ expect(output).toContain('export type DoubleWildcard = `some_${string}_middle_${string}`;')
180
+ })
181
+
126
182
  it('should keep camelCase fields by default', () => {
127
183
  const output = transform([
128
184
  group('session-capability-request', [