@amritk/generate-parsers 0.5.0 → 0.6.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/generators/build-schema.d.ts +4 -1
- package/dist/generators/generate-files.d.ts +5 -0
- package/dist/generators/generate-parser-function.d.ts +6 -1
- package/dist/helpers/collect-imports.d.ts +6 -0
- package/dist/index.js +82 -76
- package/package.json +2 -2
- package/src/generators/build-schema.test.ts +17 -17
- package/src/generators/build-schema.ts +7 -1
- package/src/generators/generate-files.test.ts +65 -52
- package/src/generators/generate-files.ts +11 -4
- package/src/generators/generate-parser-function.test.ts +254 -258
- package/src/generators/generate-parser-function.ts +87 -58
- package/src/helpers/collect-imports.test.ts +44 -84
- package/src/helpers/collect-imports.ts +8 -1
|
@@ -16,8 +16,8 @@ describe('collect-imports', () => {
|
|
|
16
16
|
const result = collectImports(schema)
|
|
17
17
|
|
|
18
18
|
expect(result).toEqual([
|
|
19
|
-
"import { type
|
|
20
|
-
"import { type
|
|
19
|
+
"import { type Contact, parseContact, validateContactShape } from './contact';",
|
|
20
|
+
"import { type Server, parseServer, validateServerShape } from './server';",
|
|
21
21
|
])
|
|
22
22
|
})
|
|
23
23
|
|
|
@@ -34,9 +34,7 @@ describe('collect-imports', () => {
|
|
|
34
34
|
|
|
35
35
|
const result = collectImports(schema)
|
|
36
36
|
|
|
37
|
-
expect(result).toEqual([
|
|
38
|
-
"import { type ServerObject, parseServerObject, validateServerObjectShape } from './server';",
|
|
39
|
-
])
|
|
37
|
+
expect(result).toEqual(["import { type Server, parseServer, validateServerShape } from './server';"])
|
|
40
38
|
})
|
|
41
39
|
|
|
42
40
|
it('collects imports from additionalProperties with $ref', () => {
|
|
@@ -52,9 +50,7 @@ describe('collect-imports', () => {
|
|
|
52
50
|
|
|
53
51
|
const result = collectImports(schema)
|
|
54
52
|
|
|
55
|
-
expect(result).toEqual([
|
|
56
|
-
"import { type PathItemObject, parsePathItemObject, validatePathItemObjectShape } from './path-item';",
|
|
57
|
-
])
|
|
53
|
+
expect(result).toEqual(["import { type PathItem, parsePathItem, validatePathItemShape } from './path-item';"])
|
|
58
54
|
})
|
|
59
55
|
|
|
60
56
|
it('collects imports from non-extension patternProperties with $ref', () => {
|
|
@@ -67,9 +63,7 @@ describe('collect-imports', () => {
|
|
|
67
63
|
|
|
68
64
|
const result = collectImports(schema)
|
|
69
65
|
|
|
70
|
-
expect(result).toEqual([
|
|
71
|
-
"import { type PathItemObject, parsePathItemObject, validatePathItemObjectShape } from './path-item';",
|
|
72
|
-
])
|
|
66
|
+
expect(result).toEqual(["import { type PathItem, parsePathItem, validatePathItemShape } from './path-item';"])
|
|
73
67
|
})
|
|
74
68
|
|
|
75
69
|
it('collects imports from patternProperties with $ref', () => {
|
|
@@ -82,9 +76,7 @@ describe('collect-imports', () => {
|
|
|
82
76
|
|
|
83
77
|
const result = collectImports(schema)
|
|
84
78
|
|
|
85
|
-
expect(result).toEqual([
|
|
86
|
-
"import { type PathItemObject, parsePathItemObject, validatePathItemObjectShape } from './path-item';",
|
|
87
|
-
])
|
|
79
|
+
expect(result).toEqual(["import { type PathItem, parsePathItem, validatePathItemShape } from './path-item';"])
|
|
88
80
|
})
|
|
89
81
|
|
|
90
82
|
it('collects imports from oneOf with $ref', () => {
|
|
@@ -100,8 +92,8 @@ describe('collect-imports', () => {
|
|
|
100
92
|
const result = collectImports(schema)
|
|
101
93
|
|
|
102
94
|
expect(result).toEqual([
|
|
103
|
-
"import { type
|
|
104
|
-
"import { type
|
|
95
|
+
"import { type Reference, parseReference, validateReferenceShape } from './reference';",
|
|
96
|
+
"import { type Response, parseResponse, validateResponseShape } from './response';",
|
|
105
97
|
])
|
|
106
98
|
})
|
|
107
99
|
|
|
@@ -118,8 +110,8 @@ describe('collect-imports', () => {
|
|
|
118
110
|
const result = collectImports(schema)
|
|
119
111
|
|
|
120
112
|
expect(result).toEqual([
|
|
121
|
-
"import { type
|
|
122
|
-
"import { type
|
|
113
|
+
"import { type NumberValue, parseNumberValue, validateNumberValueShape } from './number-value';",
|
|
114
|
+
"import { type StringValue, parseStringValue, validateStringValueShape } from './string-value';",
|
|
123
115
|
])
|
|
124
116
|
})
|
|
125
117
|
|
|
@@ -136,8 +128,8 @@ describe('collect-imports', () => {
|
|
|
136
128
|
const result = collectImports(schema)
|
|
137
129
|
|
|
138
130
|
expect(result).toEqual([
|
|
139
|
-
"import { type
|
|
140
|
-
"import { type
|
|
131
|
+
"import { type Base, parseBase, validateBaseShape } from './base';",
|
|
132
|
+
"import { type Extension, parseExtension, validateExtensionShape } from './extension';",
|
|
141
133
|
])
|
|
142
134
|
})
|
|
143
135
|
|
|
@@ -152,9 +144,7 @@ describe('collect-imports', () => {
|
|
|
152
144
|
|
|
153
145
|
const result = collectImports(schema)
|
|
154
146
|
|
|
155
|
-
expect(result).toEqual([
|
|
156
|
-
"import { type ContactObject, parseContactObject, validateContactObjectShape } from './contact';",
|
|
157
|
-
])
|
|
147
|
+
expect(result).toEqual(["import { type Contact, parseContact, validateContactShape } from './contact';"])
|
|
158
148
|
})
|
|
159
149
|
|
|
160
150
|
it('collects imports from $ref properties', () => {
|
|
@@ -167,9 +157,7 @@ describe('collect-imports', () => {
|
|
|
167
157
|
|
|
168
158
|
const result = collectImports(schema)
|
|
169
159
|
|
|
170
|
-
expect(result).toEqual([
|
|
171
|
-
"import { type ResponseObject, parseResponseObject, validateResponseObjectShape } from './response';",
|
|
172
|
-
])
|
|
160
|
+
expect(result).toEqual(["import { type Response, parseResponse, validateResponseShape } from './response';"])
|
|
173
161
|
})
|
|
174
162
|
|
|
175
163
|
it('handles nested refs in complex schemas', () => {
|
|
@@ -190,8 +178,8 @@ describe('collect-imports', () => {
|
|
|
190
178
|
const result = collectImports(schema)
|
|
191
179
|
|
|
192
180
|
expect(result).toEqual([
|
|
193
|
-
"import { type
|
|
194
|
-
"import { type
|
|
181
|
+
"import { type Callback, parseCallback, validateCallbackShape } from './callback';",
|
|
182
|
+
"import { type Server, parseServer, validateServerShape } from './server';",
|
|
195
183
|
])
|
|
196
184
|
})
|
|
197
185
|
|
|
@@ -203,12 +191,10 @@ describe('collect-imports', () => {
|
|
|
203
191
|
|
|
204
192
|
const result = collectImports(schema)
|
|
205
193
|
|
|
206
|
-
expect(result).toEqual([
|
|
207
|
-
"import { type PathItemObject, parsePathItemObject, validatePathItemObjectShape } from './path-item';",
|
|
208
|
-
])
|
|
194
|
+
expect(result).toEqual(["import { type PathItem, parsePathItem, validatePathItemShape } from './path-item';"])
|
|
209
195
|
})
|
|
210
196
|
|
|
211
|
-
it('imports
|
|
197
|
+
it('imports Schema parser and type from generated schema.ts', () => {
|
|
212
198
|
const schema: JSONSchema = {
|
|
213
199
|
type: 'object',
|
|
214
200
|
properties: {
|
|
@@ -218,9 +204,7 @@ describe('collect-imports', () => {
|
|
|
218
204
|
|
|
219
205
|
const result = collectImports(schema)
|
|
220
206
|
|
|
221
|
-
expect(result).toEqual([
|
|
222
|
-
"import { type SchemaObject, parseSchemaObject, validateSchemaObjectShape } from './schema';",
|
|
223
|
-
])
|
|
207
|
+
expect(result).toEqual(["import { type Schema, parseSchema, validateSchemaShape } from './schema';"])
|
|
224
208
|
})
|
|
225
209
|
|
|
226
210
|
it('handles empty schema', () => {
|
|
@@ -255,8 +239,8 @@ describe('collect-imports', () => {
|
|
|
255
239
|
const result = collectImports(schema)
|
|
256
240
|
|
|
257
241
|
expect(result).toEqual([
|
|
258
|
-
"import { type
|
|
259
|
-
"import { type
|
|
242
|
+
"import { type TypeApikey, parseTypeApikey, validateTypeApikeyShape } from './type-apikey';",
|
|
243
|
+
"import { type TypeHttp, parseTypeHttp, validateTypeHttpShape } from './type-http';",
|
|
260
244
|
])
|
|
261
245
|
})
|
|
262
246
|
|
|
@@ -272,8 +256,8 @@ describe('collect-imports', () => {
|
|
|
272
256
|
const result = collectImports(schema)
|
|
273
257
|
|
|
274
258
|
expect(result).toEqual([
|
|
275
|
-
"import { type
|
|
276
|
-
"import { type
|
|
259
|
+
"import { type BaseSchema, parseBaseSchema, validateBaseSchemaShape } from './base-schema';",
|
|
260
|
+
"import { type StylesForForm, parseStylesForForm, validateStylesForFormShape } from './styles-for-form';",
|
|
277
261
|
])
|
|
278
262
|
})
|
|
279
263
|
|
|
@@ -288,10 +272,7 @@ describe('collect-imports', () => {
|
|
|
288
272
|
|
|
289
273
|
const result = collectImports(schema, { typesOnly: true })
|
|
290
274
|
|
|
291
|
-
expect(result).toEqual([
|
|
292
|
-
"import type { ContactObject } from './contact';",
|
|
293
|
-
"import type { ServerObject } from './server';",
|
|
294
|
-
])
|
|
275
|
+
expect(result).toEqual(["import type { Contact } from './contact';", "import type { Server } from './server';"])
|
|
295
276
|
})
|
|
296
277
|
|
|
297
278
|
it('does not include parser names in type-only imports', () => {
|
|
@@ -304,8 +285,8 @@ describe('collect-imports', () => {
|
|
|
304
285
|
|
|
305
286
|
const result = collectImports(schema, { typesOnly: true })
|
|
306
287
|
|
|
307
|
-
expect(result).toEqual(["import type {
|
|
308
|
-
expect(result[0]).not.toContain('
|
|
288
|
+
expect(result).toEqual(["import type { Contact } from './contact';"])
|
|
289
|
+
expect(result[0]).not.toContain('parseContact')
|
|
309
290
|
})
|
|
310
291
|
|
|
311
292
|
it('collects type-only imports from $ref properties in typesOnly mode', () => {
|
|
@@ -318,7 +299,7 @@ describe('collect-imports', () => {
|
|
|
318
299
|
|
|
319
300
|
const result = collectImports(schema, { typesOnly: true })
|
|
320
301
|
|
|
321
|
-
expect(result).toEqual(["import type {
|
|
302
|
+
expect(result).toEqual(["import type { Response } from './response';"])
|
|
322
303
|
})
|
|
323
304
|
|
|
324
305
|
it('generates type-only imports from array items $ref in typesOnly mode', () => {
|
|
@@ -334,7 +315,7 @@ describe('collect-imports', () => {
|
|
|
334
315
|
|
|
335
316
|
const result = collectImports(schema, { typesOnly: true })
|
|
336
317
|
|
|
337
|
-
expect(result).toEqual(["import type {
|
|
318
|
+
expect(result).toEqual(["import type { Server } from './server';"])
|
|
338
319
|
})
|
|
339
320
|
|
|
340
321
|
it('generates type-only imports from additionalProperties $ref in typesOnly mode', () => {
|
|
@@ -345,7 +326,7 @@ describe('collect-imports', () => {
|
|
|
345
326
|
|
|
346
327
|
const result = collectImports(schema, { typesOnly: true })
|
|
347
328
|
|
|
348
|
-
expect(result).toEqual(["import type {
|
|
329
|
+
expect(result).toEqual(["import type { PathItem } from './path-item';"])
|
|
349
330
|
})
|
|
350
331
|
|
|
351
332
|
it('generates type-only imports from anyOf $ref in typesOnly mode', () => {
|
|
@@ -361,8 +342,8 @@ describe('collect-imports', () => {
|
|
|
361
342
|
const result = collectImports(schema, { typesOnly: true })
|
|
362
343
|
|
|
363
344
|
expect(result).toEqual([
|
|
364
|
-
"import type {
|
|
365
|
-
"import type {
|
|
345
|
+
"import type { NumberValue } from './number-value';",
|
|
346
|
+
"import type { StringValue } from './string-value';",
|
|
366
347
|
])
|
|
367
348
|
})
|
|
368
349
|
|
|
@@ -374,10 +355,7 @@ describe('collect-imports', () => {
|
|
|
374
355
|
|
|
375
356
|
const result = collectImports(schema, { typesOnly: true })
|
|
376
357
|
|
|
377
|
-
expect(result).toEqual([
|
|
378
|
-
"import type { BaseObject } from './base';",
|
|
379
|
-
"import type { ExtensionObject } from './extension';",
|
|
380
|
-
])
|
|
358
|
+
expect(result).toEqual(["import type { Base } from './base';", "import type { Extension } from './extension';"])
|
|
381
359
|
})
|
|
382
360
|
|
|
383
361
|
it('deduplicates type-only imports by filename', () => {
|
|
@@ -391,7 +369,7 @@ describe('collect-imports', () => {
|
|
|
391
369
|
|
|
392
370
|
const result = collectImports(schema, { typesOnly: true })
|
|
393
371
|
|
|
394
|
-
expect(result).toEqual(["import type {
|
|
372
|
+
expect(result).toEqual(["import type { Contact } from './contact';"])
|
|
395
373
|
})
|
|
396
374
|
|
|
397
375
|
it('returns empty array for schema with no $ref in typesOnly mode', () => {
|
|
@@ -416,9 +394,7 @@ describe('collect-imports', () => {
|
|
|
416
394
|
|
|
417
395
|
const result = collectImports(schema)
|
|
418
396
|
|
|
419
|
-
expect(result).toEqual([
|
|
420
|
-
"import { type ServerObject, parseServerObject, validateServerObjectShape } from './server';",
|
|
421
|
-
])
|
|
397
|
+
expect(result).toEqual(["import { type Server, parseServer, validateServerShape } from './server';"])
|
|
422
398
|
})
|
|
423
399
|
|
|
424
400
|
it('collects imports from root-level array items $ref in typesOnly mode', () => {
|
|
@@ -429,7 +405,7 @@ describe('collect-imports', () => {
|
|
|
429
405
|
|
|
430
406
|
const result = collectImports(schema, { typesOnly: true })
|
|
431
407
|
|
|
432
|
-
expect(result).toEqual(["import type {
|
|
408
|
+
expect(result).toEqual(["import type { Parameter } from './parameter';"])
|
|
433
409
|
})
|
|
434
410
|
|
|
435
411
|
it('collects imports from root-level oneOf refs', () => {
|
|
@@ -440,12 +416,8 @@ describe('collect-imports', () => {
|
|
|
440
416
|
|
|
441
417
|
const result = collectImports(schema)
|
|
442
418
|
|
|
443
|
-
expect(result).toContain(
|
|
444
|
-
|
|
445
|
-
)
|
|
446
|
-
expect(result).toContain(
|
|
447
|
-
"import { type ServerObject, parseServerObject, validateServerObjectShape } from './server';",
|
|
448
|
-
)
|
|
419
|
+
expect(result).toContain("import { type Contact, parseContact, validateContactShape } from './contact';")
|
|
420
|
+
expect(result).toContain("import { type Server, parseServer, validateServerShape } from './server';")
|
|
449
421
|
})
|
|
450
422
|
|
|
451
423
|
it('collects imports from root-level anyOf refs', () => {
|
|
@@ -456,12 +428,8 @@ describe('collect-imports', () => {
|
|
|
456
428
|
|
|
457
429
|
const result = collectImports(schema)
|
|
458
430
|
|
|
459
|
-
expect(result).toContain(
|
|
460
|
-
|
|
461
|
-
)
|
|
462
|
-
expect(result).toContain(
|
|
463
|
-
"import { type ServerObject, parseServerObject, validateServerObjectShape } from './server';",
|
|
464
|
-
)
|
|
431
|
+
expect(result).toContain("import { type Contact, parseContact, validateContactShape } from './contact';")
|
|
432
|
+
expect(result).toContain("import { type Server, parseServer, validateServerShape } from './server';")
|
|
465
433
|
})
|
|
466
434
|
|
|
467
435
|
it('collects imports from root-level if branch', () => {
|
|
@@ -472,9 +440,7 @@ describe('collect-imports', () => {
|
|
|
472
440
|
|
|
473
441
|
const result = collectImports(schema)
|
|
474
442
|
|
|
475
|
-
expect(result).toContain(
|
|
476
|
-
"import { type ContactObject, parseContactObject, validateContactObjectShape } from './contact';",
|
|
477
|
-
)
|
|
443
|
+
expect(result).toContain("import { type Contact, parseContact, validateContactShape } from './contact';")
|
|
478
444
|
})
|
|
479
445
|
|
|
480
446
|
it('collects imports from root-level then branch', () => {
|
|
@@ -485,9 +451,7 @@ describe('collect-imports', () => {
|
|
|
485
451
|
|
|
486
452
|
const result = collectImports(schema)
|
|
487
453
|
|
|
488
|
-
expect(result).toContain(
|
|
489
|
-
"import { type ServerObject, parseServerObject, validateServerObjectShape } from './server';",
|
|
490
|
-
)
|
|
454
|
+
expect(result).toContain("import { type Server, parseServer, validateServerShape } from './server';")
|
|
491
455
|
})
|
|
492
456
|
|
|
493
457
|
it('collects imports from root-level else branch', () => {
|
|
@@ -498,9 +462,7 @@ describe('collect-imports', () => {
|
|
|
498
462
|
|
|
499
463
|
const result = collectImports(schema)
|
|
500
464
|
|
|
501
|
-
expect(result).toContain(
|
|
502
|
-
"import { type ContactObject, parseContactObject, validateContactObjectShape } from './contact';",
|
|
503
|
-
)
|
|
465
|
+
expect(result).toContain("import { type Contact, parseContact, validateContactShape } from './contact';")
|
|
504
466
|
})
|
|
505
467
|
|
|
506
468
|
it('does not generate a self-import when a schema references its own $defs key via a property', () => {
|
|
@@ -518,9 +480,7 @@ describe('collect-imports', () => {
|
|
|
518
480
|
const result = collectImports(schema, { selfRef: '#/$defs/encoding' })
|
|
519
481
|
|
|
520
482
|
expect(result).not.toContain("'./encoding'")
|
|
521
|
-
expect(result).toContain(
|
|
522
|
-
"import { type HeaderObject, parseHeaderObject, validateHeaderObjectShape } from './header';",
|
|
523
|
-
)
|
|
483
|
+
expect(result).toContain("import { type Header, parseHeader, validateHeaderShape } from './header';")
|
|
524
484
|
})
|
|
525
485
|
|
|
526
486
|
it('does not generate a self-import when a schema references its own $defs key via additionalProperties', () => {
|
|
@@ -582,6 +542,6 @@ describe('collect-imports', () => {
|
|
|
582
542
|
const result = collectImports(schema, { typesOnly: true, selfRef: '#/$defs/encoding' })
|
|
583
543
|
|
|
584
544
|
expect(result).not.toContain("'./encoding'")
|
|
585
|
-
expect(result).toContain("import type {
|
|
545
|
+
expect(result).toContain("import type { Header } from './header';")
|
|
586
546
|
})
|
|
587
547
|
})
|
|
@@ -27,6 +27,12 @@ type CollectImportsOptions = {
|
|
|
27
27
|
* imports for external schemas that were never generated as files.
|
|
28
28
|
*/
|
|
29
29
|
readonly rootSchema?: Record<string, unknown> | undefined
|
|
30
|
+
/**
|
|
31
|
+
* Suffix appended to every type name derived from a `$ref`. Must match the
|
|
32
|
+
* suffix used when generating the referenced files so imports resolve.
|
|
33
|
+
* Defaults to `''` (no suffix).
|
|
34
|
+
*/
|
|
35
|
+
readonly typeSuffix?: string
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
/**
|
|
@@ -72,6 +78,7 @@ export const collectImports = (schema: JSONSchema, options?: CollectImportsOptio
|
|
|
72
78
|
const typesOnly = options?.typesOnly === true
|
|
73
79
|
const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : undefined
|
|
74
80
|
const rootSchema = options?.rootSchema
|
|
81
|
+
const typeSuffix = options?.typeSuffix
|
|
75
82
|
const refs = new Set<string>()
|
|
76
83
|
|
|
77
84
|
const collectRefsFromValue = (value: unknown): void => {
|
|
@@ -247,7 +254,7 @@ export const collectImports = (schema: JSONSchema, options?: CollectImportsOptio
|
|
|
247
254
|
if (!resolveRef(ref, rootSchema)) continue
|
|
248
255
|
}
|
|
249
256
|
|
|
250
|
-
const typeName = refToName(ref)
|
|
257
|
+
const typeName = refToName(ref, typeSuffix)
|
|
251
258
|
const filename = refToFilename(ref)
|
|
252
259
|
|
|
253
260
|
// Skip self-referential imports — a file must not import from itself
|