@kubb/ast 5.0.0-alpha.66 → 5.0.0-alpha.69
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/index.cjs +776 -664
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +72 -1
- package/dist/index.js +773 -665
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +12 -1
- package/src/utils.ts +132 -0
package/dist/index.cjs
CHANGED
|
@@ -381,212 +381,527 @@ isKind("FunctionParameter");
|
|
|
381
381
|
isKind("ParameterGroup");
|
|
382
382
|
isKind("FunctionParameters");
|
|
383
383
|
//#endregion
|
|
384
|
-
//#region src/
|
|
385
|
-
const plainStringTypes = new Set([
|
|
386
|
-
"string",
|
|
387
|
-
"uuid",
|
|
388
|
-
"email",
|
|
389
|
-
"url",
|
|
390
|
-
"datetime"
|
|
391
|
-
]);
|
|
384
|
+
//#region src/refs.ts
|
|
392
385
|
/**
|
|
393
|
-
* Returns
|
|
394
|
-
* (base from the referenced definition) with any usage-site sibling fields set directly
|
|
395
|
-
* on the ref node (description, readOnly, nullable, deprecated, etc.).
|
|
386
|
+
* Returns the last path segment of a reference string.
|
|
396
387
|
*
|
|
397
|
-
*
|
|
388
|
+
* Example: `#/components/schemas/Pet` becomes `Pet`.
|
|
398
389
|
*
|
|
399
|
-
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```ts
|
|
392
|
+
* extractRefName('#/components/schemas/Pet') // 'Pet'
|
|
393
|
+
* ```
|
|
400
394
|
*/
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
if (!ref) return node;
|
|
404
|
-
if (!ref.schema) return node;
|
|
405
|
-
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = ref;
|
|
406
|
-
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== void 0));
|
|
407
|
-
return createSchema({
|
|
408
|
-
...ref.schema,
|
|
409
|
-
...definedOverrides
|
|
410
|
-
});
|
|
395
|
+
function extractRefName(ref) {
|
|
396
|
+
return ref.split("/").at(-1) ?? ref;
|
|
411
397
|
}
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/visitor.ts
|
|
412
400
|
/**
|
|
413
|
-
*
|
|
401
|
+
* Creates a small async concurrency limiter.
|
|
414
402
|
*
|
|
415
|
-
*
|
|
416
|
-
* - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
|
|
403
|
+
* At most `concurrency` tasks are in flight at once. Extra tasks are queued.
|
|
417
404
|
*
|
|
418
405
|
* @example
|
|
419
406
|
* ```ts
|
|
420
|
-
*
|
|
421
|
-
*
|
|
407
|
+
* const limit = createLimit(2)
|
|
408
|
+
* for (const task of [taskA, taskB, taskC]) {
|
|
409
|
+
* await limit(() => task())
|
|
410
|
+
* }
|
|
411
|
+
* // only 2 tasks run at the same time
|
|
422
412
|
* ```
|
|
423
413
|
*/
|
|
424
|
-
function
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
414
|
+
function createLimit(concurrency) {
|
|
415
|
+
let active = 0;
|
|
416
|
+
const queue = [];
|
|
417
|
+
function next() {
|
|
418
|
+
if (active < concurrency && queue.length > 0) {
|
|
419
|
+
active++;
|
|
420
|
+
queue.shift()();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return function limit(fn) {
|
|
424
|
+
return new Promise((resolve, reject) => {
|
|
425
|
+
queue.push(() => {
|
|
426
|
+
Promise.resolve(fn()).then(resolve, reject).finally(() => {
|
|
427
|
+
active--;
|
|
428
|
+
next();
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
next();
|
|
432
|
+
});
|
|
433
|
+
};
|
|
429
434
|
}
|
|
430
435
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* The input array is not mutated.
|
|
434
|
-
* If `casing` is not set, the original array is returned unchanged.
|
|
436
|
+
* Returns the immediate traversable children of `node`.
|
|
435
437
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* `
|
|
438
|
+
* For `Schema` nodes, children (`properties`, `items`, `members`, and non-boolean
|
|
439
|
+
* `additionalProperties`) are only included
|
|
440
|
+
* when `recurse` is `true`; shallow mode skips them.
|
|
439
441
|
*
|
|
440
442
|
* @example
|
|
441
443
|
* ```ts
|
|
442
|
-
* const
|
|
443
|
-
*
|
|
444
|
-
* // cased[0].name === 'petId'
|
|
444
|
+
* const children = getChildren(operationNode, true)
|
|
445
|
+
* // returns parameters, requestBody schema (if present), and responses
|
|
445
446
|
* ```
|
|
446
447
|
*/
|
|
447
|
-
function
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
return
|
|
452
|
-
...
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
448
|
+
function getChildren(node, recurse) {
|
|
449
|
+
switch (node.kind) {
|
|
450
|
+
case "Input": return [...node.schemas, ...node.operations];
|
|
451
|
+
case "Output": return [];
|
|
452
|
+
case "Operation": return [
|
|
453
|
+
...node.parameters,
|
|
454
|
+
...node.requestBody?.content?.flatMap((c) => c.schema ? [c.schema] : []) ?? [],
|
|
455
|
+
...node.responses
|
|
456
|
+
];
|
|
457
|
+
case "Schema": {
|
|
458
|
+
const children = [];
|
|
459
|
+
if (!recurse) return [];
|
|
460
|
+
if ("properties" in node && node.properties.length > 0) children.push(...node.properties);
|
|
461
|
+
if ("items" in node && node.items) children.push(...node.items);
|
|
462
|
+
if ("members" in node && node.members) children.push(...node.members);
|
|
463
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
464
|
+
return children;
|
|
465
|
+
}
|
|
466
|
+
case "Property": return [node.schema];
|
|
467
|
+
case "Parameter": return [node.schema];
|
|
468
|
+
case "Response": return node.schema ? [node.schema] : [];
|
|
469
|
+
case "FunctionParameter":
|
|
470
|
+
case "ParameterGroup":
|
|
471
|
+
case "FunctionParameters":
|
|
472
|
+
case "Type": return [];
|
|
473
|
+
default: return [];
|
|
474
|
+
}
|
|
456
475
|
}
|
|
457
476
|
/**
|
|
458
|
-
*
|
|
477
|
+
* Depth-first traversal for side effects. Visitor return values are ignored.
|
|
478
|
+
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
479
|
+
* (default: `WALK_CONCURRENCY`).
|
|
459
480
|
*
|
|
460
481
|
* @example
|
|
461
482
|
* ```ts
|
|
462
|
-
*
|
|
463
|
-
*
|
|
483
|
+
* await walk(root, {
|
|
484
|
+
* operation(node) {
|
|
485
|
+
* console.log(node.operationId)
|
|
486
|
+
* },
|
|
487
|
+
* })
|
|
464
488
|
* ```
|
|
465
|
-
*/
|
|
466
|
-
function createDiscriminantNode({ propertyName, value }) {
|
|
467
|
-
return createSchema({
|
|
468
|
-
type: "object",
|
|
469
|
-
primitive: "object",
|
|
470
|
-
properties: [createProperty({
|
|
471
|
-
name: propertyName,
|
|
472
|
-
schema: createSchema({
|
|
473
|
-
type: "enum",
|
|
474
|
-
primitive: "string",
|
|
475
|
-
enumValues: [value]
|
|
476
|
-
}),
|
|
477
|
-
required: true
|
|
478
|
-
})]
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
function resolveParamsType({ node, param, resolver }) {
|
|
482
|
-
if (!resolver) return createParamsType({
|
|
483
|
-
variant: "reference",
|
|
484
|
-
name: param.schema.primitive ?? "unknown"
|
|
485
|
-
});
|
|
486
|
-
const individualName = resolver.resolveParamName(node, param);
|
|
487
|
-
const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
|
|
488
|
-
const groupResolvers = {
|
|
489
|
-
path: resolver.resolvePathParamsName,
|
|
490
|
-
query: resolver.resolveQueryParamsName,
|
|
491
|
-
header: resolver.resolveHeaderParamsName
|
|
492
|
-
};
|
|
493
|
-
const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
|
|
494
|
-
if (groupName && groupName !== individualName) return createParamsType({
|
|
495
|
-
variant: "member",
|
|
496
|
-
base: groupName,
|
|
497
|
-
key: param.name
|
|
498
|
-
});
|
|
499
|
-
return createParamsType({
|
|
500
|
-
variant: "reference",
|
|
501
|
-
name: individualName
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* Converts an {@link OperationNode} into a {@link FunctionParametersNode}.
|
|
506
|
-
*
|
|
507
|
-
* Centralizes the per-plugin `getParams()` pattern. Provide a `resolver` for
|
|
508
|
-
* type resolution and `extraParams` for plugin-specific trailing parameters.
|
|
509
489
|
*
|
|
510
490
|
* @example
|
|
511
491
|
* ```ts
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
* pathParamsType: 'inline',
|
|
515
|
-
* resolver: tsResolver,
|
|
516
|
-
* extraParams: [createFunctionParameter({ name: 'options', type: createParamsType({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
|
|
517
|
-
* })
|
|
492
|
+
* // Visit only the current node
|
|
493
|
+
* await walk(root, { depth: 'shallow', root: () => {} })
|
|
518
494
|
* ```
|
|
519
495
|
*/
|
|
520
|
-
function
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
496
|
+
async function walk(node, options) {
|
|
497
|
+
return _walk(node, options, (options.depth ?? visitorDepths.deep) === visitorDepths.deep, createLimit(options.concurrency ?? 30), void 0);
|
|
498
|
+
}
|
|
499
|
+
async function _walk(node, visitor, recurse, limit, parent) {
|
|
500
|
+
switch (node.kind) {
|
|
501
|
+
case "Input":
|
|
502
|
+
await limit(() => visitor.input?.(node, { parent }));
|
|
503
|
+
break;
|
|
504
|
+
case "Output":
|
|
505
|
+
await limit(() => visitor.output?.(node, { parent }));
|
|
506
|
+
break;
|
|
507
|
+
case "Operation":
|
|
508
|
+
await limit(() => visitor.operation?.(node, { parent }));
|
|
509
|
+
break;
|
|
510
|
+
case "Schema":
|
|
511
|
+
await limit(() => visitor.schema?.(node, { parent }));
|
|
512
|
+
break;
|
|
513
|
+
case "Property":
|
|
514
|
+
await limit(() => visitor.property?.(node, { parent }));
|
|
515
|
+
break;
|
|
516
|
+
case "Parameter":
|
|
517
|
+
await limit(() => visitor.parameter?.(node, { parent }));
|
|
518
|
+
break;
|
|
519
|
+
case "Response":
|
|
520
|
+
await limit(() => visitor.response?.(node, { parent }));
|
|
521
|
+
break;
|
|
522
|
+
case "FunctionParameter":
|
|
523
|
+
case "ParameterGroup":
|
|
524
|
+
case "FunctionParameters": break;
|
|
525
|
+
}
|
|
526
|
+
const children = getChildren(node, recurse);
|
|
527
|
+
for (const child of children) await _walk(child, visitor, recurse, limit, node);
|
|
528
|
+
}
|
|
529
|
+
function transform(node, options) {
|
|
530
|
+
const { depth, parent, ...visitor } = options;
|
|
531
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
532
|
+
switch (node.kind) {
|
|
533
|
+
case "Input": {
|
|
534
|
+
let input = node;
|
|
535
|
+
const replaced = visitor.input?.(input, { parent });
|
|
536
|
+
if (replaced) input = replaced;
|
|
537
|
+
return {
|
|
538
|
+
...input,
|
|
539
|
+
schemas: input.schemas.map((s) => transform(s, {
|
|
540
|
+
...options,
|
|
541
|
+
parent: input
|
|
542
|
+
})),
|
|
543
|
+
operations: input.operations.map((op) => transform(op, {
|
|
544
|
+
...options,
|
|
545
|
+
parent: input
|
|
546
|
+
}))
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
case "Output": {
|
|
550
|
+
let output = node;
|
|
551
|
+
const replaced = visitor.output?.(output, { parent });
|
|
552
|
+
if (replaced) output = replaced;
|
|
553
|
+
return output;
|
|
554
|
+
}
|
|
555
|
+
case "Operation": {
|
|
556
|
+
let op = node;
|
|
557
|
+
const replaced = visitor.operation?.(op, { parent });
|
|
558
|
+
if (replaced) op = replaced;
|
|
559
|
+
return {
|
|
560
|
+
...op,
|
|
561
|
+
parameters: op.parameters.map((p) => transform(p, {
|
|
562
|
+
...options,
|
|
563
|
+
parent: op
|
|
564
|
+
})),
|
|
565
|
+
requestBody: op.requestBody ? {
|
|
566
|
+
...op.requestBody,
|
|
567
|
+
content: op.requestBody.content?.map((c) => ({
|
|
568
|
+
...c,
|
|
569
|
+
schema: c.schema ? transform(c.schema, {
|
|
570
|
+
...options,
|
|
571
|
+
parent: op
|
|
572
|
+
}) : void 0
|
|
573
|
+
}))
|
|
574
|
+
} : void 0,
|
|
575
|
+
responses: op.responses.map((r) => transform(r, {
|
|
576
|
+
...options,
|
|
577
|
+
parent: op
|
|
578
|
+
}))
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
case "Schema": {
|
|
582
|
+
let schema = node;
|
|
583
|
+
const replaced = visitor.schema?.(schema, { parent });
|
|
584
|
+
if (replaced) schema = replaced;
|
|
585
|
+
const childOptions = {
|
|
586
|
+
...options,
|
|
587
|
+
parent: schema
|
|
588
|
+
};
|
|
589
|
+
return {
|
|
590
|
+
...schema,
|
|
591
|
+
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
|
|
592
|
+
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
|
|
593
|
+
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
|
|
594
|
+
..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
case "Property": {
|
|
598
|
+
let prop = node;
|
|
599
|
+
const replaced = visitor.property?.(prop, { parent });
|
|
600
|
+
if (replaced) prop = replaced;
|
|
601
|
+
return createProperty({
|
|
602
|
+
...prop,
|
|
603
|
+
schema: transform(prop.schema, {
|
|
604
|
+
...options,
|
|
605
|
+
parent: prop
|
|
606
|
+
})
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
case "Parameter": {
|
|
610
|
+
let param = node;
|
|
611
|
+
const replaced = visitor.parameter?.(param, { parent });
|
|
612
|
+
if (replaced) param = replaced;
|
|
613
|
+
return createParameter({
|
|
614
|
+
...param,
|
|
615
|
+
schema: transform(param.schema, {
|
|
616
|
+
...options,
|
|
617
|
+
parent: param
|
|
618
|
+
})
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
case "Response": {
|
|
622
|
+
let response = node;
|
|
623
|
+
const replaced = visitor.response?.(response, { parent });
|
|
624
|
+
if (replaced) response = replaced;
|
|
625
|
+
return {
|
|
626
|
+
...response,
|
|
627
|
+
schema: transform(response.schema, {
|
|
628
|
+
...options,
|
|
629
|
+
parent: response
|
|
630
|
+
})
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
case "FunctionParameter":
|
|
634
|
+
case "ParameterGroup":
|
|
635
|
+
case "FunctionParameters":
|
|
636
|
+
case "Type": return node;
|
|
637
|
+
default: return node;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Runs a depth-first synchronous collection pass.
|
|
642
|
+
*
|
|
643
|
+
* Non-`undefined` values returned by visitor callbacks are appended to the result.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```ts
|
|
647
|
+
* const ids = collect(root, {
|
|
648
|
+
* operation(node) {
|
|
649
|
+
* return node.operationId
|
|
650
|
+
* },
|
|
651
|
+
* })
|
|
652
|
+
* ```
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```ts
|
|
656
|
+
* // Collect from only the current node
|
|
657
|
+
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
658
|
+
* ```
|
|
659
|
+
*/
|
|
660
|
+
function collect(node, options) {
|
|
661
|
+
const { depth, parent, ...visitor } = options;
|
|
662
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
663
|
+
const results = [];
|
|
664
|
+
let v;
|
|
665
|
+
switch (node.kind) {
|
|
666
|
+
case "Input":
|
|
667
|
+
v = visitor.input?.(node, { parent });
|
|
668
|
+
break;
|
|
669
|
+
case "Output":
|
|
670
|
+
v = visitor.output?.(node, { parent });
|
|
671
|
+
break;
|
|
672
|
+
case "Operation":
|
|
673
|
+
v = visitor.operation?.(node, { parent });
|
|
674
|
+
break;
|
|
675
|
+
case "Schema":
|
|
676
|
+
v = visitor.schema?.(node, { parent });
|
|
677
|
+
break;
|
|
678
|
+
case "Property":
|
|
679
|
+
v = visitor.property?.(node, { parent });
|
|
680
|
+
break;
|
|
681
|
+
case "Parameter":
|
|
682
|
+
v = visitor.parameter?.(node, { parent });
|
|
683
|
+
break;
|
|
684
|
+
case "Response":
|
|
685
|
+
v = visitor.response?.(node, { parent });
|
|
686
|
+
break;
|
|
687
|
+
case "FunctionParameter":
|
|
688
|
+
case "ParameterGroup":
|
|
689
|
+
case "FunctionParameters": break;
|
|
690
|
+
}
|
|
691
|
+
if (v !== void 0) results.push(v);
|
|
692
|
+
for (const child of getChildren(node, recurse)) for (const item of collect(child, {
|
|
693
|
+
...options,
|
|
694
|
+
parent: node
|
|
695
|
+
})) results.push(item);
|
|
696
|
+
return results;
|
|
697
|
+
}
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/utils.ts
|
|
700
|
+
const plainStringTypes = new Set([
|
|
701
|
+
"string",
|
|
702
|
+
"uuid",
|
|
703
|
+
"email",
|
|
704
|
+
"url",
|
|
705
|
+
"datetime"
|
|
706
|
+
]);
|
|
707
|
+
/**
|
|
708
|
+
* Returns a merged schema view for a ref node, combining the resolved `node.schema`
|
|
709
|
+
* (base from the referenced definition) with any usage-site sibling fields set directly
|
|
710
|
+
* on the ref node (description, readOnly, nullable, deprecated, etc.).
|
|
711
|
+
*
|
|
712
|
+
* Usage-site fields take precedence over the resolved schema's own fields when both are defined.
|
|
713
|
+
*
|
|
714
|
+
* For non-ref nodes the node itself is returned unchanged.
|
|
715
|
+
*/
|
|
716
|
+
function syncSchemaRef(node) {
|
|
717
|
+
const ref = narrowSchema(node, "ref");
|
|
718
|
+
if (!ref) return node;
|
|
719
|
+
if (!ref.schema) return node;
|
|
720
|
+
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = ref;
|
|
721
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== void 0));
|
|
722
|
+
return createSchema({
|
|
723
|
+
...ref.schema,
|
|
724
|
+
...definedOverrides
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Returns `true` when a schema is emitted as a plain `string` type.
|
|
729
|
+
*
|
|
730
|
+
* - `string`, `uuid`, `email`, `url`, `datetime` are always plain strings.
|
|
731
|
+
* - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
|
|
732
|
+
*
|
|
733
|
+
* @example
|
|
734
|
+
* ```ts
|
|
735
|
+
* isStringType(createSchema({ type: 'uuid' })) // true
|
|
736
|
+
* isStringType(createSchema({ type: 'date', representation: 'date' })) // false
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
function isStringType(node) {
|
|
740
|
+
if (plainStringTypes.has(node.type)) return true;
|
|
741
|
+
const temporal = narrowSchema(node, "date") ?? narrowSchema(node, "time");
|
|
742
|
+
if (temporal) return temporal.representation !== "date";
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Applies casing rules to parameter names and returns a new parameter array.
|
|
747
|
+
*
|
|
748
|
+
* The input array is not mutated.
|
|
749
|
+
* If `casing` is not set, the original array is returned unchanged.
|
|
750
|
+
*
|
|
751
|
+
* Use this before passing parameters to schema builders so that property keys
|
|
752
|
+
* in generated output match the desired casing while preserving
|
|
753
|
+
* `OperationNode.parameters` for other consumers.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```ts
|
|
757
|
+
* const params = [createParameter({ name: 'pet_id', in: 'query', schema: createSchema({ type: 'string' }) })]
|
|
758
|
+
* const cased = caseParams(params, 'camelcase')
|
|
759
|
+
* // cased[0].name === 'petId'
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
function caseParams(params, casing) {
|
|
763
|
+
if (!casing) return params;
|
|
764
|
+
return params.map((param) => {
|
|
765
|
+
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
766
|
+
return {
|
|
767
|
+
...param,
|
|
768
|
+
name: transformed
|
|
769
|
+
};
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```ts
|
|
777
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
778
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
782
|
+
return createSchema({
|
|
783
|
+
type: "object",
|
|
784
|
+
primitive: "object",
|
|
785
|
+
properties: [createProperty({
|
|
786
|
+
name: propertyName,
|
|
787
|
+
schema: createSchema({
|
|
788
|
+
type: "enum",
|
|
789
|
+
primitive: "string",
|
|
790
|
+
enumValues: [value]
|
|
791
|
+
}),
|
|
792
|
+
required: true
|
|
793
|
+
})]
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
function resolveParamsType({ node, param, resolver }) {
|
|
797
|
+
if (!resolver) return createParamsType({
|
|
798
|
+
variant: "reference",
|
|
799
|
+
name: param.schema.primitive ?? "unknown"
|
|
800
|
+
});
|
|
801
|
+
const individualName = resolver.resolveParamName(node, param);
|
|
802
|
+
const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
|
|
803
|
+
const groupResolvers = {
|
|
804
|
+
path: resolver.resolvePathParamsName,
|
|
805
|
+
query: resolver.resolveQueryParamsName,
|
|
806
|
+
header: resolver.resolveHeaderParamsName
|
|
807
|
+
};
|
|
808
|
+
const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
|
|
809
|
+
if (groupName && groupName !== individualName) return createParamsType({
|
|
810
|
+
variant: "member",
|
|
811
|
+
base: groupName,
|
|
812
|
+
key: param.name
|
|
813
|
+
});
|
|
814
|
+
return createParamsType({
|
|
815
|
+
variant: "reference",
|
|
816
|
+
name: individualName
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Converts an {@link OperationNode} into a {@link FunctionParametersNode}.
|
|
821
|
+
*
|
|
822
|
+
* Centralizes the per-plugin `getParams()` pattern. Provide a `resolver` for
|
|
823
|
+
* type resolution and `extraParams` for plugin-specific trailing parameters.
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```ts
|
|
827
|
+
* const params = createOperationParams(node, {
|
|
828
|
+
* paramsType: 'inline',
|
|
829
|
+
* pathParamsType: 'inline',
|
|
830
|
+
* resolver: tsResolver,
|
|
831
|
+
* extraParams: [createFunctionParameter({ name: 'options', type: createParamsType({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
|
|
832
|
+
* })
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
function createOperationParams(node, options) {
|
|
836
|
+
const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
|
|
837
|
+
const dataName = paramNames?.data ?? "data";
|
|
838
|
+
const paramsName = paramNames?.params ?? "params";
|
|
839
|
+
const headersName = paramNames?.headers ?? "headers";
|
|
840
|
+
const pathName = paramNames?.path ?? "pathParams";
|
|
841
|
+
const wrapType = (type) => createParamsType({
|
|
842
|
+
variant: "reference",
|
|
843
|
+
name: typeWrapper ? typeWrapper(type) : type
|
|
844
|
+
});
|
|
845
|
+
const wrapTypeNode = (type) => type.kind === "ParamsType" && type.variant === "reference" ? wrapType(type.name) : type;
|
|
846
|
+
const casedParams = caseParams(node.parameters, paramsCasing);
|
|
847
|
+
const pathParams = casedParams.filter((p) => p.in === "path");
|
|
848
|
+
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
849
|
+
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
850
|
+
const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
|
|
851
|
+
const bodyRequired = node.requestBody?.required ?? false;
|
|
852
|
+
const queryGroupType = resolver ? resolveGroupType({
|
|
853
|
+
node,
|
|
854
|
+
params: queryParams,
|
|
855
|
+
groupMethod: resolver.resolveQueryParamsName,
|
|
856
|
+
resolver
|
|
857
|
+
}) : void 0;
|
|
858
|
+
const headerGroupType = resolver ? resolveGroupType({
|
|
859
|
+
node,
|
|
860
|
+
params: headerParams,
|
|
861
|
+
groupMethod: resolver.resolveHeaderParamsName,
|
|
862
|
+
resolver
|
|
863
|
+
}) : void 0;
|
|
864
|
+
const params = [];
|
|
865
|
+
if (paramsType === "object") {
|
|
866
|
+
const children = [
|
|
867
|
+
...pathParams.map((p) => {
|
|
868
|
+
const type = resolveParamsType({
|
|
869
|
+
node,
|
|
870
|
+
param: p,
|
|
871
|
+
resolver
|
|
872
|
+
});
|
|
873
|
+
return createFunctionParameter({
|
|
874
|
+
name: p.name,
|
|
875
|
+
type: wrapTypeNode(type),
|
|
876
|
+
optional: !p.required
|
|
877
|
+
});
|
|
878
|
+
}),
|
|
879
|
+
...bodyType ? [createFunctionParameter({
|
|
880
|
+
name: dataName,
|
|
881
|
+
type: bodyType,
|
|
882
|
+
optional: !bodyRequired
|
|
883
|
+
})] : [],
|
|
884
|
+
...buildGroupParam({
|
|
885
|
+
name: paramsName,
|
|
886
|
+
node,
|
|
887
|
+
params: queryParams,
|
|
888
|
+
groupType: queryGroupType,
|
|
889
|
+
resolver,
|
|
890
|
+
wrapType
|
|
891
|
+
}),
|
|
892
|
+
...buildGroupParam({
|
|
893
|
+
name: headersName,
|
|
894
|
+
node,
|
|
895
|
+
params: headerParams,
|
|
896
|
+
groupType: headerGroupType,
|
|
897
|
+
resolver,
|
|
898
|
+
wrapType
|
|
899
|
+
})
|
|
900
|
+
];
|
|
901
|
+
if (children.length) params.push(createParameterGroup({
|
|
902
|
+
properties: children,
|
|
903
|
+
default: children.every((c) => c.optional) ? "{}" : void 0
|
|
904
|
+
}));
|
|
590
905
|
} else {
|
|
591
906
|
if (pathParams.length) if (pathParamsType === "inlineSpread") {
|
|
592
907
|
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]) ?? void 0;
|
|
@@ -850,6 +1165,114 @@ function extractStringsFromNodes(nodes) {
|
|
|
850
1165
|
return parts.join("\n");
|
|
851
1166
|
}).filter(Boolean).join("\n");
|
|
852
1167
|
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Resolves the referenced schema name of a `ref` node, falling back through
|
|
1170
|
+
* `ref` → `name` → nested `schema.name`. Returns `undefined` for non-ref
|
|
1171
|
+
* nodes or when no name can be resolved.
|
|
1172
|
+
*
|
|
1173
|
+
* @example
|
|
1174
|
+
* ```ts
|
|
1175
|
+
* resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' })
|
|
1176
|
+
* // => 'Pet'
|
|
1177
|
+
* ```
|
|
1178
|
+
*/
|
|
1179
|
+
function resolveRefName(node) {
|
|
1180
|
+
if (!node || node.type !== "ref") return void 0;
|
|
1181
|
+
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ?? void 0;
|
|
1182
|
+
return node.name ?? node.schema?.name ?? void 0;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Recursively collects every named schema referenced (transitively) from
|
|
1186
|
+
* `node` via `ref` edges. Refs are followed by name only — the resolved
|
|
1187
|
+
* `node.schema` of a ref is not traversed inline.
|
|
1188
|
+
*
|
|
1189
|
+
* @example
|
|
1190
|
+
* ```ts
|
|
1191
|
+
* const refs = collectReferencedSchemaNames(petSchema)
|
|
1192
|
+
* // => Set { 'Cat', 'Dog' }
|
|
1193
|
+
* ```
|
|
1194
|
+
*/
|
|
1195
|
+
function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1196
|
+
if (!node) return out;
|
|
1197
|
+
collect(node, { schema(child) {
|
|
1198
|
+
if (child.type === "ref") {
|
|
1199
|
+
const name = resolveRefName(child);
|
|
1200
|
+
if (name) out.add(name);
|
|
1201
|
+
}
|
|
1202
|
+
} });
|
|
1203
|
+
return out;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Identifies every named schema that participates in a circular dependency
|
|
1207
|
+
* chain — including direct self-loops (e.g. `TreeNode → TreeNode`) and indirect
|
|
1208
|
+
* cycles spanning multiple schemas (e.g. `Pet → Cat → Pet`).
|
|
1209
|
+
*
|
|
1210
|
+
* The returned set contains schema names. Plugins that translate schemas into
|
|
1211
|
+
* a host language can use this to wrap recursive positions in a deferred
|
|
1212
|
+
* construct (lazy getter, `z.lazy(() => …)`, etc.) and avoid runtime stack
|
|
1213
|
+
* overflows when the generated code is executed.
|
|
1214
|
+
*
|
|
1215
|
+
* Refs are followed by name only — `node.schema` (the resolved referent) is
|
|
1216
|
+
* not traversed inline, which keeps the algorithm linear in the size of the
|
|
1217
|
+
* schema graph.
|
|
1218
|
+
*
|
|
1219
|
+
* @example
|
|
1220
|
+
* ```ts
|
|
1221
|
+
* const circular = findCircularSchemas(inputNode.schemas)
|
|
1222
|
+
* if (circular.has('Pet')) {
|
|
1223
|
+
* // emit lazy wrapper for any property whose schema references Pet
|
|
1224
|
+
* }
|
|
1225
|
+
* ```
|
|
1226
|
+
*/
|
|
1227
|
+
function findCircularSchemas(schemas) {
|
|
1228
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1229
|
+
for (const schema of schemas) {
|
|
1230
|
+
if (!schema.name) continue;
|
|
1231
|
+
graph.set(schema.name, collectReferencedSchemaNames(schema));
|
|
1232
|
+
}
|
|
1233
|
+
const circular = /* @__PURE__ */ new Set();
|
|
1234
|
+
for (const start of graph.keys()) {
|
|
1235
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1236
|
+
const stack = [...graph.get(start) ?? []];
|
|
1237
|
+
while (stack.length > 0) {
|
|
1238
|
+
const node = stack.pop();
|
|
1239
|
+
if (node === start) {
|
|
1240
|
+
circular.add(start);
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
if (visited.has(node)) continue;
|
|
1244
|
+
visited.add(node);
|
|
1245
|
+
const next = graph.get(node);
|
|
1246
|
+
if (next) for (const r of next) stack.push(r);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return circular;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Returns true when `node` (or anything nested within it) carries a `ref`
|
|
1253
|
+
* whose resolved name belongs to `circularSchemas`.
|
|
1254
|
+
*
|
|
1255
|
+
* When `excludeName` is provided, refs to that name are ignored — useful
|
|
1256
|
+
* when self-references are already handled separately from cross-schema
|
|
1257
|
+
* cycles (e.g. the faker plugin emits `undefined as any` for direct
|
|
1258
|
+
* self-recursion but a lazy getter for indirect cycles).
|
|
1259
|
+
*
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```ts
|
|
1262
|
+
* const circular = findCircularSchemas(schemas)
|
|
1263
|
+
* if (containsCircularRef(property.schema, { circularSchemas: circular, excludeName: 'Pet' })) {
|
|
1264
|
+
* // emit `get foo() { return fakeCat() }` instead of eager call
|
|
1265
|
+
* }
|
|
1266
|
+
* ```
|
|
1267
|
+
*/
|
|
1268
|
+
function containsCircularRef(node, { circularSchemas, excludeName }) {
|
|
1269
|
+
if (!node || circularSchemas.size === 0) return false;
|
|
1270
|
+
return collect(node, { schema(child) {
|
|
1271
|
+
if (child.type !== "ref") return void 0;
|
|
1272
|
+
const name = resolveRefName(child);
|
|
1273
|
+
return name && name !== excludeName && circularSchemas.has(name) ? true : void 0;
|
|
1274
|
+
} }).length > 0;
|
|
1275
|
+
}
|
|
853
1276
|
//#endregion
|
|
854
1277
|
//#region src/factory.ts
|
|
855
1278
|
/**
|
|
@@ -1318,545 +1741,230 @@ function createFile(input) {
|
|
|
1318
1741
|
*/
|
|
1319
1742
|
function createConst(props) {
|
|
1320
1743
|
return {
|
|
1321
|
-
...props,
|
|
1322
|
-
kind: "Const"
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
* Creates a `TypeNode` representing a TypeScript `type` alias declaration.
|
|
1327
|
-
*
|
|
1328
|
-
* Mirrors the `Type` component from `@kubb/renderer-jsx`.
|
|
1329
|
-
* The component's `children` are represented as `nodes`.
|
|
1330
|
-
*
|
|
1331
|
-
* @example Simple type alias
|
|
1332
|
-
* ```ts
|
|
1333
|
-
* createType({ name: 'Pet' })
|
|
1334
|
-
* // type Pet = ...
|
|
1335
|
-
* ```
|
|
1336
|
-
*
|
|
1337
|
-
* @example Exported type with JSDoc
|
|
1338
|
-
* ```ts
|
|
1339
|
-
* createType({
|
|
1340
|
-
* name: 'PetStatus',
|
|
1341
|
-
* export: true,
|
|
1342
|
-
* JSDoc: { comments: ['@description Status of a pet'] },
|
|
1343
|
-
* })
|
|
1344
|
-
* // export type PetStatus = ...
|
|
1345
|
-
* ```
|
|
1346
|
-
*/
|
|
1347
|
-
function createType(props) {
|
|
1348
|
-
return {
|
|
1349
|
-
...props,
|
|
1350
|
-
kind: "Type"
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
/**
|
|
1354
|
-
* Creates a `FunctionNode` representing a TypeScript `function` declaration.
|
|
1355
|
-
*
|
|
1356
|
-
* Mirrors the `Function` component from `@kubb/renderer-jsx`.
|
|
1357
|
-
* The component's `children` are represented as `nodes`.
|
|
1358
|
-
*
|
|
1359
|
-
* @example Simple function
|
|
1360
|
-
* ```ts
|
|
1361
|
-
* createFunction({ name: 'getPet' })
|
|
1362
|
-
* // function getPet() { ... }
|
|
1363
|
-
* ```
|
|
1364
|
-
*
|
|
1365
|
-
* @example Exported async function with return type
|
|
1366
|
-
* ```ts
|
|
1367
|
-
* createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
|
|
1368
|
-
* // export async function fetchPet(): Promise<Pet> { ... }
|
|
1369
|
-
* ```
|
|
1370
|
-
*
|
|
1371
|
-
* @example Function with generics and params
|
|
1372
|
-
* ```ts
|
|
1373
|
-
* createFunction({
|
|
1374
|
-
* name: 'identity',
|
|
1375
|
-
* export: true,
|
|
1376
|
-
* generics: ['T'],
|
|
1377
|
-
* params: 'value: T',
|
|
1378
|
-
* returnType: 'T',
|
|
1379
|
-
* })
|
|
1380
|
-
* // export function identity<T>(value: T): T { ... }
|
|
1381
|
-
* ```
|
|
1382
|
-
*/
|
|
1383
|
-
function createFunction(props) {
|
|
1384
|
-
return {
|
|
1385
|
-
...props,
|
|
1386
|
-
kind: "Function"
|
|
1387
|
-
};
|
|
1388
|
-
}
|
|
1389
|
-
/**
|
|
1390
|
-
* Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
|
|
1391
|
-
*
|
|
1392
|
-
* Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
1393
|
-
* The component's `children` are represented as `nodes`.
|
|
1394
|
-
*
|
|
1395
|
-
* @example Simple arrow function
|
|
1396
|
-
* ```ts
|
|
1397
|
-
* createArrowFunction({ name: 'getPet' })
|
|
1398
|
-
* // const getPet = () => { ... }
|
|
1399
|
-
* ```
|
|
1400
|
-
*
|
|
1401
|
-
* @example Single-line exported arrow function
|
|
1402
|
-
* ```ts
|
|
1403
|
-
* createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
|
|
1404
|
-
* // export const double = (n: number) => ...
|
|
1405
|
-
* ```
|
|
1406
|
-
*
|
|
1407
|
-
* @example Async arrow function with generics
|
|
1408
|
-
* ```ts
|
|
1409
|
-
* createArrowFunction({
|
|
1410
|
-
* name: 'fetchPet',
|
|
1411
|
-
* export: true,
|
|
1412
|
-
* async: true,
|
|
1413
|
-
* generics: ['T'],
|
|
1414
|
-
* params: 'id: string',
|
|
1415
|
-
* returnType: 'T',
|
|
1416
|
-
* })
|
|
1417
|
-
* // export const fetchPet = async <T>(id: string): Promise<T> => { ... }
|
|
1418
|
-
* ```
|
|
1419
|
-
*/
|
|
1420
|
-
function createArrowFunction(props) {
|
|
1421
|
-
return {
|
|
1422
|
-
...props,
|
|
1423
|
-
kind: "ArrowFunction"
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
|
-
/**
|
|
1427
|
-
* Creates a {@link TextNode} representing a raw string fragment in the source output.
|
|
1428
|
-
*
|
|
1429
|
-
* Use this instead of bare strings when building `nodes` arrays so that every
|
|
1430
|
-
* entry in the array is a typed {@link CodeNode}.
|
|
1431
|
-
*
|
|
1432
|
-
* @example
|
|
1433
|
-
* ```ts
|
|
1434
|
-
* createText('return fetch(id)')
|
|
1435
|
-
* // { kind: 'Text', value: 'return fetch(id)' }
|
|
1436
|
-
* ```
|
|
1437
|
-
*/
|
|
1438
|
-
function createText(value) {
|
|
1439
|
-
return {
|
|
1440
|
-
value,
|
|
1441
|
-
kind: "Text"
|
|
1442
|
-
};
|
|
1443
|
-
}
|
|
1444
|
-
/**
|
|
1445
|
-
* Creates a {@link BreakNode} representing a line break in the source output.
|
|
1446
|
-
*
|
|
1447
|
-
* Corresponds to `<br/>` in JSX components. Prints as an empty string which,
|
|
1448
|
-
* when joined with `\n` by `printNodes`, produces a blank line.
|
|
1449
|
-
*
|
|
1450
|
-
* @example
|
|
1451
|
-
* ```ts
|
|
1452
|
-
* createBreak()
|
|
1453
|
-
* // { kind: 'Break' }
|
|
1454
|
-
* ```
|
|
1455
|
-
*/
|
|
1456
|
-
function createBreak() {
|
|
1457
|
-
return { kind: "Break" };
|
|
1458
|
-
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
|
|
1461
|
-
*
|
|
1462
|
-
* Use this to embed JSX markup (including fragments `<>…</>`) directly in generated code.
|
|
1463
|
-
*
|
|
1464
|
-
* @example
|
|
1465
|
-
* ```ts
|
|
1466
|
-
* createJsx('<>\n <a href={href}>Open</a>\n</>')
|
|
1467
|
-
* // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
|
|
1468
|
-
* ```
|
|
1469
|
-
*/
|
|
1470
|
-
function createJsx(value) {
|
|
1471
|
-
return {
|
|
1472
|
-
value,
|
|
1473
|
-
kind: "Jsx"
|
|
1744
|
+
...props,
|
|
1745
|
+
kind: "Const"
|
|
1474
1746
|
};
|
|
1475
1747
|
}
|
|
1476
|
-
//#endregion
|
|
1477
|
-
//#region src/printer.ts
|
|
1478
1748
|
/**
|
|
1479
|
-
* Creates a
|
|
1480
|
-
*
|
|
1481
|
-
* This function wraps a builder and makes options optional at call sites.
|
|
1482
|
-
*
|
|
1483
|
-
* The builder receives resolved options and returns:
|
|
1484
|
-
* - `name` — a unique identifier for the printer
|
|
1485
|
-
* - `options` — options stored on the returned printer instance
|
|
1486
|
-
* - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
|
|
1487
|
-
* - `print` _(optional)_ — top-level override exposed as `printer.print`
|
|
1488
|
-
* - Inside this function, use `this.transform(node)` to dispatch to the `nodes` map
|
|
1489
|
-
* - This keeps recursion safe and avoids self-calls
|
|
1749
|
+
* Creates a `TypeNode` representing a TypeScript `type` alias declaration.
|
|
1490
1750
|
*
|
|
1491
|
-
*
|
|
1751
|
+
* Mirrors the `Type` component from `@kubb/renderer-jsx`.
|
|
1752
|
+
* The component's `children` are represented as `nodes`.
|
|
1492
1753
|
*
|
|
1493
|
-
* @example
|
|
1754
|
+
* @example Simple type alias
|
|
1494
1755
|
* ```ts
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
* export const zodPrinter = definePrinter<PrinterZod>((options) => ({
|
|
1498
|
-
* name: 'zod',
|
|
1499
|
-
* options: { strict: options.strict ?? true },
|
|
1500
|
-
* nodes: {
|
|
1501
|
-
* string: () => 'z.string()',
|
|
1502
|
-
* object(node) {
|
|
1503
|
-
* const props = node.properties.map(p => `${p.name}: ${this.transform(p.schema)}`).join(', ')
|
|
1504
|
-
* return `z.object({ ${props} })`
|
|
1505
|
-
* },
|
|
1506
|
-
* },
|
|
1507
|
-
* }))
|
|
1756
|
+
* createType({ name: 'Pet' })
|
|
1757
|
+
* // type Pet = ...
|
|
1508
1758
|
* ```
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
return createPrinterFactory((node) => node.type)(build);
|
|
1512
|
-
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Generic printer-factory function used by `definePrinter` and `defineFunctionPrinter`.
|
|
1515
|
-
**
|
|
1516
|
-
* @example
|
|
1759
|
+
*
|
|
1760
|
+
* @example Exported type with JSDoc
|
|
1517
1761
|
* ```ts
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
*
|
|
1762
|
+
* createType({
|
|
1763
|
+
* name: 'PetStatus',
|
|
1764
|
+
* export: true,
|
|
1765
|
+
* JSDoc: { comments: ['@description Status of a pet'] },
|
|
1766
|
+
* })
|
|
1767
|
+
* // export type PetStatus = ...
|
|
1521
1768
|
* ```
|
|
1522
1769
|
*/
|
|
1523
|
-
function
|
|
1524
|
-
return
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const context = {
|
|
1528
|
-
options: resolvedOptions,
|
|
1529
|
-
transform: (node) => {
|
|
1530
|
-
const key = getKey(node);
|
|
1531
|
-
if (key === void 0) return null;
|
|
1532
|
-
const handler = nodes[key];
|
|
1533
|
-
if (!handler) return null;
|
|
1534
|
-
return handler.call(context, node);
|
|
1535
|
-
}
|
|
1536
|
-
};
|
|
1537
|
-
return {
|
|
1538
|
-
name,
|
|
1539
|
-
options: resolvedOptions,
|
|
1540
|
-
transform: context.transform,
|
|
1541
|
-
print: printOverride ? printOverride.bind(context) : context.transform
|
|
1542
|
-
};
|
|
1543
|
-
};
|
|
1770
|
+
function createType(props) {
|
|
1771
|
+
return {
|
|
1772
|
+
...props,
|
|
1773
|
+
kind: "Type"
|
|
1544
1774
|
};
|
|
1545
1775
|
}
|
|
1546
|
-
//#endregion
|
|
1547
|
-
//#region src/refs.ts
|
|
1548
1776
|
/**
|
|
1549
|
-
*
|
|
1777
|
+
* Creates a `FunctionNode` representing a TypeScript `function` declaration.
|
|
1550
1778
|
*
|
|
1551
|
-
*
|
|
1779
|
+
* Mirrors the `Function` component from `@kubb/renderer-jsx`.
|
|
1780
|
+
* The component's `children` are represented as `nodes`.
|
|
1552
1781
|
*
|
|
1553
|
-
* @example
|
|
1782
|
+
* @example Simple function
|
|
1554
1783
|
* ```ts
|
|
1555
|
-
*
|
|
1784
|
+
* createFunction({ name: 'getPet' })
|
|
1785
|
+
* // function getPet() { ... }
|
|
1556
1786
|
* ```
|
|
1557
|
-
*/
|
|
1558
|
-
function extractRefName(ref) {
|
|
1559
|
-
return ref.split("/").at(-1) ?? ref;
|
|
1560
|
-
}
|
|
1561
|
-
//#endregion
|
|
1562
|
-
//#region src/visitor.ts
|
|
1563
|
-
/**
|
|
1564
|
-
* Creates a small async concurrency limiter.
|
|
1565
1787
|
*
|
|
1566
|
-
*
|
|
1788
|
+
* @example Exported async function with return type
|
|
1789
|
+
* ```ts
|
|
1790
|
+
* createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
|
|
1791
|
+
* // export async function fetchPet(): Promise<Pet> { ... }
|
|
1792
|
+
* ```
|
|
1567
1793
|
*
|
|
1568
|
-
* @example
|
|
1794
|
+
* @example Function with generics and params
|
|
1569
1795
|
* ```ts
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1796
|
+
* createFunction({
|
|
1797
|
+
* name: 'identity',
|
|
1798
|
+
* export: true,
|
|
1799
|
+
* generics: ['T'],
|
|
1800
|
+
* params: 'value: T',
|
|
1801
|
+
* returnType: 'T',
|
|
1802
|
+
* })
|
|
1803
|
+
* // export function identity<T>(value: T): T { ... }
|
|
1575
1804
|
* ```
|
|
1576
1805
|
*/
|
|
1577
|
-
function
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
if (active < concurrency && queue.length > 0) {
|
|
1582
|
-
active++;
|
|
1583
|
-
queue.shift()();
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
return function limit(fn) {
|
|
1587
|
-
return new Promise((resolve, reject) => {
|
|
1588
|
-
queue.push(() => {
|
|
1589
|
-
Promise.resolve(fn()).then(resolve, reject).finally(() => {
|
|
1590
|
-
active--;
|
|
1591
|
-
next();
|
|
1592
|
-
});
|
|
1593
|
-
});
|
|
1594
|
-
next();
|
|
1595
|
-
});
|
|
1806
|
+
function createFunction(props) {
|
|
1807
|
+
return {
|
|
1808
|
+
...props,
|
|
1809
|
+
kind: "Function"
|
|
1596
1810
|
};
|
|
1597
1811
|
}
|
|
1598
1812
|
/**
|
|
1599
|
-
*
|
|
1813
|
+
* Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
|
|
1600
1814
|
*
|
|
1601
|
-
*
|
|
1602
|
-
* `
|
|
1603
|
-
* when `recurse` is `true`; shallow mode skips them.
|
|
1815
|
+
* Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
1816
|
+
* The component's `children` are represented as `nodes`.
|
|
1604
1817
|
*
|
|
1605
|
-
* @example
|
|
1818
|
+
* @example Simple arrow function
|
|
1606
1819
|
* ```ts
|
|
1607
|
-
*
|
|
1608
|
-
* //
|
|
1820
|
+
* createArrowFunction({ name: 'getPet' })
|
|
1821
|
+
* // const getPet = () => { ... }
|
|
1609
1822
|
* ```
|
|
1610
|
-
*/
|
|
1611
|
-
function getChildren(node, recurse) {
|
|
1612
|
-
switch (node.kind) {
|
|
1613
|
-
case "Input": return [...node.schemas, ...node.operations];
|
|
1614
|
-
case "Output": return [];
|
|
1615
|
-
case "Operation": return [
|
|
1616
|
-
...node.parameters,
|
|
1617
|
-
...node.requestBody?.content?.flatMap((c) => c.schema ? [c.schema] : []) ?? [],
|
|
1618
|
-
...node.responses
|
|
1619
|
-
];
|
|
1620
|
-
case "Schema": {
|
|
1621
|
-
const children = [];
|
|
1622
|
-
if (!recurse) return [];
|
|
1623
|
-
if ("properties" in node && node.properties.length > 0) children.push(...node.properties);
|
|
1624
|
-
if ("items" in node && node.items) children.push(...node.items);
|
|
1625
|
-
if ("members" in node && node.members) children.push(...node.members);
|
|
1626
|
-
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
1627
|
-
return children;
|
|
1628
|
-
}
|
|
1629
|
-
case "Property": return [node.schema];
|
|
1630
|
-
case "Parameter": return [node.schema];
|
|
1631
|
-
case "Response": return node.schema ? [node.schema] : [];
|
|
1632
|
-
case "FunctionParameter":
|
|
1633
|
-
case "ParameterGroup":
|
|
1634
|
-
case "FunctionParameters":
|
|
1635
|
-
case "Type": return [];
|
|
1636
|
-
default: return [];
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Depth-first traversal for side effects. Visitor return values are ignored.
|
|
1641
|
-
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
1642
|
-
* (default: `WALK_CONCURRENCY`).
|
|
1643
1823
|
*
|
|
1644
|
-
* @example
|
|
1824
|
+
* @example Single-line exported arrow function
|
|
1645
1825
|
* ```ts
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
* console.log(node.operationId)
|
|
1649
|
-
* },
|
|
1650
|
-
* })
|
|
1826
|
+
* createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
|
|
1827
|
+
* // export const double = (n: number) => ...
|
|
1651
1828
|
* ```
|
|
1652
1829
|
*
|
|
1653
|
-
* @example
|
|
1830
|
+
* @example Async arrow function with generics
|
|
1654
1831
|
* ```ts
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1832
|
+
* createArrowFunction({
|
|
1833
|
+
* name: 'fetchPet',
|
|
1834
|
+
* export: true,
|
|
1835
|
+
* async: true,
|
|
1836
|
+
* generics: ['T'],
|
|
1837
|
+
* params: 'id: string',
|
|
1838
|
+
* returnType: 'T',
|
|
1839
|
+
* })
|
|
1840
|
+
* // export const fetchPet = async <T>(id: string): Promise<T> => { ... }
|
|
1657
1841
|
* ```
|
|
1658
1842
|
*/
|
|
1659
|
-
|
|
1660
|
-
return
|
|
1843
|
+
function createArrowFunction(props) {
|
|
1844
|
+
return {
|
|
1845
|
+
...props,
|
|
1846
|
+
kind: "ArrowFunction"
|
|
1847
|
+
};
|
|
1661
1848
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
case "Parameter":
|
|
1680
|
-
await limit(() => visitor.parameter?.(node, { parent }));
|
|
1681
|
-
break;
|
|
1682
|
-
case "Response":
|
|
1683
|
-
await limit(() => visitor.response?.(node, { parent }));
|
|
1684
|
-
break;
|
|
1685
|
-
case "FunctionParameter":
|
|
1686
|
-
case "ParameterGroup":
|
|
1687
|
-
case "FunctionParameters": break;
|
|
1688
|
-
}
|
|
1689
|
-
const children = getChildren(node, recurse);
|
|
1690
|
-
for (const child of children) await _walk(child, visitor, recurse, limit, node);
|
|
1849
|
+
/**
|
|
1850
|
+
* Creates a {@link TextNode} representing a raw string fragment in the source output.
|
|
1851
|
+
*
|
|
1852
|
+
* Use this instead of bare strings when building `nodes` arrays so that every
|
|
1853
|
+
* entry in the array is a typed {@link CodeNode}.
|
|
1854
|
+
*
|
|
1855
|
+
* @example
|
|
1856
|
+
* ```ts
|
|
1857
|
+
* createText('return fetch(id)')
|
|
1858
|
+
* // { kind: 'Text', value: 'return fetch(id)' }
|
|
1859
|
+
* ```
|
|
1860
|
+
*/
|
|
1861
|
+
function createText(value) {
|
|
1862
|
+
return {
|
|
1863
|
+
value,
|
|
1864
|
+
kind: "Text"
|
|
1865
|
+
};
|
|
1691
1866
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
operations: input.operations.map((op) => transform(op, {
|
|
1707
|
-
...options,
|
|
1708
|
-
parent: input
|
|
1709
|
-
}))
|
|
1710
|
-
};
|
|
1711
|
-
}
|
|
1712
|
-
case "Output": {
|
|
1713
|
-
let output = node;
|
|
1714
|
-
const replaced = visitor.output?.(output, { parent });
|
|
1715
|
-
if (replaced) output = replaced;
|
|
1716
|
-
return output;
|
|
1717
|
-
}
|
|
1718
|
-
case "Operation": {
|
|
1719
|
-
let op = node;
|
|
1720
|
-
const replaced = visitor.operation?.(op, { parent });
|
|
1721
|
-
if (replaced) op = replaced;
|
|
1722
|
-
return {
|
|
1723
|
-
...op,
|
|
1724
|
-
parameters: op.parameters.map((p) => transform(p, {
|
|
1725
|
-
...options,
|
|
1726
|
-
parent: op
|
|
1727
|
-
})),
|
|
1728
|
-
requestBody: op.requestBody ? {
|
|
1729
|
-
...op.requestBody,
|
|
1730
|
-
content: op.requestBody.content?.map((c) => ({
|
|
1731
|
-
...c,
|
|
1732
|
-
schema: c.schema ? transform(c.schema, {
|
|
1733
|
-
...options,
|
|
1734
|
-
parent: op
|
|
1735
|
-
}) : void 0
|
|
1736
|
-
}))
|
|
1737
|
-
} : void 0,
|
|
1738
|
-
responses: op.responses.map((r) => transform(r, {
|
|
1739
|
-
...options,
|
|
1740
|
-
parent: op
|
|
1741
|
-
}))
|
|
1742
|
-
};
|
|
1743
|
-
}
|
|
1744
|
-
case "Schema": {
|
|
1745
|
-
let schema = node;
|
|
1746
|
-
const replaced = visitor.schema?.(schema, { parent });
|
|
1747
|
-
if (replaced) schema = replaced;
|
|
1748
|
-
const childOptions = {
|
|
1749
|
-
...options,
|
|
1750
|
-
parent: schema
|
|
1751
|
-
};
|
|
1752
|
-
return {
|
|
1753
|
-
...schema,
|
|
1754
|
-
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
|
|
1755
|
-
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
|
|
1756
|
-
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
|
|
1757
|
-
..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
case "Property": {
|
|
1761
|
-
let prop = node;
|
|
1762
|
-
const replaced = visitor.property?.(prop, { parent });
|
|
1763
|
-
if (replaced) prop = replaced;
|
|
1764
|
-
return createProperty({
|
|
1765
|
-
...prop,
|
|
1766
|
-
schema: transform(prop.schema, {
|
|
1767
|
-
...options,
|
|
1768
|
-
parent: prop
|
|
1769
|
-
})
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
1772
|
-
case "Parameter": {
|
|
1773
|
-
let param = node;
|
|
1774
|
-
const replaced = visitor.parameter?.(param, { parent });
|
|
1775
|
-
if (replaced) param = replaced;
|
|
1776
|
-
return createParameter({
|
|
1777
|
-
...param,
|
|
1778
|
-
schema: transform(param.schema, {
|
|
1779
|
-
...options,
|
|
1780
|
-
parent: param
|
|
1781
|
-
})
|
|
1782
|
-
});
|
|
1783
|
-
}
|
|
1784
|
-
case "Response": {
|
|
1785
|
-
let response = node;
|
|
1786
|
-
const replaced = visitor.response?.(response, { parent });
|
|
1787
|
-
if (replaced) response = replaced;
|
|
1788
|
-
return {
|
|
1789
|
-
...response,
|
|
1790
|
-
schema: transform(response.schema, {
|
|
1791
|
-
...options,
|
|
1792
|
-
parent: response
|
|
1793
|
-
})
|
|
1794
|
-
};
|
|
1795
|
-
}
|
|
1796
|
-
case "FunctionParameter":
|
|
1797
|
-
case "ParameterGroup":
|
|
1798
|
-
case "FunctionParameters":
|
|
1799
|
-
case "Type": return node;
|
|
1800
|
-
default: return node;
|
|
1801
|
-
}
|
|
1867
|
+
/**
|
|
1868
|
+
* Creates a {@link BreakNode} representing a line break in the source output.
|
|
1869
|
+
*
|
|
1870
|
+
* Corresponds to `<br/>` in JSX components. Prints as an empty string which,
|
|
1871
|
+
* when joined with `\n` by `printNodes`, produces a blank line.
|
|
1872
|
+
*
|
|
1873
|
+
* @example
|
|
1874
|
+
* ```ts
|
|
1875
|
+
* createBreak()
|
|
1876
|
+
* // { kind: 'Break' }
|
|
1877
|
+
* ```
|
|
1878
|
+
*/
|
|
1879
|
+
function createBreak() {
|
|
1880
|
+
return { kind: "Break" };
|
|
1802
1881
|
}
|
|
1803
1882
|
/**
|
|
1804
|
-
*
|
|
1883
|
+
* Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
|
|
1805
1884
|
*
|
|
1806
|
-
*
|
|
1885
|
+
* Use this to embed JSX markup (including fragments `<>…</>`) directly in generated code.
|
|
1807
1886
|
*
|
|
1808
1887
|
* @example
|
|
1809
1888
|
* ```ts
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
1812
|
-
* return node.operationId
|
|
1813
|
-
* },
|
|
1814
|
-
* })
|
|
1889
|
+
* createJsx('<>\n <a href={href}>Open</a>\n</>')
|
|
1890
|
+
* // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
|
|
1815
1891
|
* ```
|
|
1892
|
+
*/
|
|
1893
|
+
function createJsx(value) {
|
|
1894
|
+
return {
|
|
1895
|
+
value,
|
|
1896
|
+
kind: "Jsx"
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
//#endregion
|
|
1900
|
+
//#region src/printer.ts
|
|
1901
|
+
/**
|
|
1902
|
+
* Creates a schema printer factory.
|
|
1903
|
+
*
|
|
1904
|
+
* This function wraps a builder and makes options optional at call sites.
|
|
1905
|
+
*
|
|
1906
|
+
* The builder receives resolved options and returns:
|
|
1907
|
+
* - `name` — a unique identifier for the printer
|
|
1908
|
+
* - `options` — options stored on the returned printer instance
|
|
1909
|
+
* - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
|
|
1910
|
+
* - `print` _(optional)_ — top-level override exposed as `printer.print`
|
|
1911
|
+
* - Inside this function, use `this.transform(node)` to dispatch to the `nodes` map
|
|
1912
|
+
* - This keeps recursion safe and avoids self-calls
|
|
1913
|
+
*
|
|
1914
|
+
* When no `print` override is provided, `printer.print` falls back to `printer.transform` (the node-level dispatcher).
|
|
1915
|
+
*
|
|
1916
|
+
* @example Basic usage — Zod schema printer
|
|
1917
|
+
* ```ts
|
|
1918
|
+
* type PrinterZod = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
|
|
1816
1919
|
*
|
|
1920
|
+
* export const zodPrinter = definePrinter<PrinterZod>((options) => ({
|
|
1921
|
+
* name: 'zod',
|
|
1922
|
+
* options: { strict: options.strict ?? true },
|
|
1923
|
+
* nodes: {
|
|
1924
|
+
* string: () => 'z.string()',
|
|
1925
|
+
* object(node) {
|
|
1926
|
+
* const props = node.properties.map(p => `${p.name}: ${this.transform(p.schema)}`).join(', ')
|
|
1927
|
+
* return `z.object({ ${props} })`
|
|
1928
|
+
* },
|
|
1929
|
+
* },
|
|
1930
|
+
* }))
|
|
1931
|
+
* ```
|
|
1932
|
+
*/
|
|
1933
|
+
function definePrinter(build) {
|
|
1934
|
+
return createPrinterFactory((node) => node.type)(build);
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Generic printer-factory function used by `definePrinter` and `defineFunctionPrinter`.
|
|
1938
|
+
**
|
|
1817
1939
|
* @example
|
|
1818
1940
|
* ```ts
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1941
|
+
* export const defineFunctionPrinter = createPrinterFactory<FunctionNode, FunctionNodeType, FunctionNodeByType>(
|
|
1942
|
+
* (node) => kindToHandlerKey[node.kind],
|
|
1943
|
+
* )
|
|
1821
1944
|
* ```
|
|
1822
1945
|
*/
|
|
1823
|
-
function
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
v = visitor.parameter?.(node, { parent });
|
|
1846
|
-
break;
|
|
1847
|
-
case "Response":
|
|
1848
|
-
v = visitor.response?.(node, { parent });
|
|
1849
|
-
break;
|
|
1850
|
-
case "FunctionParameter":
|
|
1851
|
-
case "ParameterGroup":
|
|
1852
|
-
case "FunctionParameters": break;
|
|
1853
|
-
}
|
|
1854
|
-
if (v !== void 0) results.push(v);
|
|
1855
|
-
for (const child of getChildren(node, recurse)) for (const item of collect(child, {
|
|
1856
|
-
...options,
|
|
1857
|
-
parent: node
|
|
1858
|
-
})) results.push(item);
|
|
1859
|
-
return results;
|
|
1946
|
+
function createPrinterFactory(getKey) {
|
|
1947
|
+
return function(build) {
|
|
1948
|
+
return (options) => {
|
|
1949
|
+
const { name, options: resolvedOptions, nodes, print: printOverride } = build(options ?? {});
|
|
1950
|
+
const context = {
|
|
1951
|
+
options: resolvedOptions,
|
|
1952
|
+
transform: (node) => {
|
|
1953
|
+
const key = getKey(node);
|
|
1954
|
+
if (key === void 0) return null;
|
|
1955
|
+
const handler = nodes[key];
|
|
1956
|
+
if (!handler) return null;
|
|
1957
|
+
return handler.call(context, node);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
return {
|
|
1961
|
+
name,
|
|
1962
|
+
options: resolvedOptions,
|
|
1963
|
+
transform: context.transform,
|
|
1964
|
+
print: printOverride ? printOverride.bind(context) : context.transform
|
|
1965
|
+
};
|
|
1966
|
+
};
|
|
1967
|
+
};
|
|
1860
1968
|
}
|
|
1861
1969
|
//#endregion
|
|
1862
1970
|
//#region src/resolvers.ts
|
|
@@ -1998,6 +2106,8 @@ exports.caseParams = caseParams;
|
|
|
1998
2106
|
exports.childName = childName;
|
|
1999
2107
|
exports.collect = collect;
|
|
2000
2108
|
exports.collectImports = collectImports;
|
|
2109
|
+
exports.collectReferencedSchemaNames = collectReferencedSchemaNames;
|
|
2110
|
+
exports.containsCircularRef = containsCircularRef;
|
|
2001
2111
|
exports.createArrowFunction = createArrowFunction;
|
|
2002
2112
|
exports.createBreak = createBreak;
|
|
2003
2113
|
exports.createConst = createConst;
|
|
@@ -2027,6 +2137,7 @@ exports.definePrinter = definePrinter;
|
|
|
2027
2137
|
exports.enumPropName = enumPropName;
|
|
2028
2138
|
exports.extractRefName = extractRefName;
|
|
2029
2139
|
exports.extractStringsFromNodes = extractStringsFromNodes;
|
|
2140
|
+
exports.findCircularSchemas = findCircularSchemas;
|
|
2030
2141
|
exports.findDiscriminator = findDiscriminator;
|
|
2031
2142
|
exports.httpMethods = httpMethods;
|
|
2032
2143
|
exports.isInputNode = isInputNode;
|
|
@@ -2039,6 +2150,7 @@ exports.mediaTypes = mediaTypes;
|
|
|
2039
2150
|
exports.mergeAdjacentObjects = mergeAdjacentObjects;
|
|
2040
2151
|
exports.narrowSchema = narrowSchema;
|
|
2041
2152
|
exports.nodeKinds = nodeKinds;
|
|
2153
|
+
exports.resolveRefName = resolveRefName;
|
|
2042
2154
|
exports.schemaTypes = schemaTypes;
|
|
2043
2155
|
exports.setDiscriminatorEnum = setDiscriminatorEnum;
|
|
2044
2156
|
exports.setEnumName = setEnumName;
|