@kubb/ast 5.5.6 → 5.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/index.cjs +254 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +31 -4
- package/dist/index.js +253 -263
- package/dist/index.js.map +1 -1
- package/dist/{types-D10wBFnV.d.ts → types-NNGUervC.d.ts} +928 -959
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -363,63 +363,6 @@ const contentDef = defineNode({
|
|
|
363
363
|
*/
|
|
364
364
|
const createContent = contentDef.create;
|
|
365
365
|
//#endregion
|
|
366
|
-
//#region ../../internals/utils/src/fs.ts
|
|
367
|
-
/**
|
|
368
|
-
* Strips the file extension from a path or file name.
|
|
369
|
-
* Only removes the last `.ext` segment when the dot is not part of a directory name.
|
|
370
|
-
*
|
|
371
|
-
* @example
|
|
372
|
-
* trimExtName('petStore.ts') // 'petStore'
|
|
373
|
-
* trimExtName('/src/models/pet.ts') // '/src/models/pet'
|
|
374
|
-
* trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
|
|
375
|
-
* trimExtName('noExtension') // 'noExtension'
|
|
376
|
-
*/
|
|
377
|
-
function trimExtName(text) {
|
|
378
|
-
const dotIndex = text.lastIndexOf(".");
|
|
379
|
-
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
380
|
-
return text;
|
|
381
|
-
}
|
|
382
|
-
//#endregion
|
|
383
|
-
//#region ../../internals/utils/src/promise.ts
|
|
384
|
-
/**
|
|
385
|
-
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
386
|
-
*
|
|
387
|
-
* Pass a `WeakMap` for object keys (results are GC-eligible when the key is
|
|
388
|
-
* collected) or a `Map` for primitive keys. For multi-argument functions,
|
|
389
|
-
* nest two `memoize` calls — the outer keyed by the first argument, the
|
|
390
|
-
* inner (created once per outer miss) keyed by the second.
|
|
391
|
-
*
|
|
392
|
-
* Because the cache is owned by the caller, it can be shared, inspected, or
|
|
393
|
-
* cleared independently of the memoized function.
|
|
394
|
-
*
|
|
395
|
-
* @example Single WeakMap key
|
|
396
|
-
* ```ts
|
|
397
|
-
* const cache = new WeakMap<SchemaNode, Set<string>>()
|
|
398
|
-
* const getRefs = memoize(cache, (node) => collectRefs(node))
|
|
399
|
-
* ```
|
|
400
|
-
*
|
|
401
|
-
* @example Single Map key (primitive)
|
|
402
|
-
* ```ts
|
|
403
|
-
* const cache = new Map<string, Resolver>()
|
|
404
|
-
* const getResolver = memoize(cache, (name) => buildResolver(name))
|
|
405
|
-
* ```
|
|
406
|
-
*
|
|
407
|
-
* @example Two-level (object + primitive)
|
|
408
|
-
* ```ts
|
|
409
|
-
* const outer = new WeakMap<Params[], Map<string, Params[]>>()
|
|
410
|
-
* const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
|
|
411
|
-
* fn(params)('camelcase')
|
|
412
|
-
* ```
|
|
413
|
-
*/
|
|
414
|
-
function memoize(store, factory) {
|
|
415
|
-
return (key) => {
|
|
416
|
-
if (store.has(key)) return store.get(key);
|
|
417
|
-
const value = factory(key);
|
|
418
|
-
store.set(key, value);
|
|
419
|
-
return value;
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
//#endregion
|
|
423
366
|
//#region src/utils/extractStringsFromNodes.ts
|
|
424
367
|
/**
|
|
425
368
|
* Extracts all string content from a `CodeNode` tree recursively.
|
|
@@ -435,15 +378,11 @@ function extractStringsFromNodes(nodes) {
|
|
|
435
378
|
if (node) collected.push(node);
|
|
436
379
|
continue;
|
|
437
380
|
}
|
|
438
|
-
if (node.kind === "Text") {
|
|
381
|
+
if (node.kind === "Text" || node.kind === "Jsx") {
|
|
439
382
|
if (node.value) collected.push(node.value);
|
|
440
383
|
continue;
|
|
441
384
|
}
|
|
442
385
|
if (node.kind === "Break") continue;
|
|
443
|
-
if (node.kind === "Jsx") {
|
|
444
|
-
if (node.value) collected.push(node.value);
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
386
|
const parts = [];
|
|
448
387
|
if ("params" in node && node.params) parts.push(node.params);
|
|
449
388
|
if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
|
|
@@ -463,22 +402,11 @@ const IDENTIFIER_RUN = /[\w$]+/g;
|
|
|
463
402
|
*/
|
|
464
403
|
const INDEX_ABOVE_IMPORTS = 128;
|
|
465
404
|
/**
|
|
466
|
-
*
|
|
405
|
+
* Returns the local binding an import name item introduces: the alias when there is one, else the
|
|
406
|
+
* imported name.
|
|
467
407
|
*/
|
|
468
|
-
function
|
|
469
|
-
return
|
|
470
|
-
}
|
|
471
|
-
function sourceKey(source) {
|
|
472
|
-
return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
473
|
-
}
|
|
474
|
-
function pathTypeKey(path, isTypeOnly) {
|
|
475
|
-
return `${path}:${isTypeOnly ?? false}`;
|
|
476
|
-
}
|
|
477
|
-
function exportKey(path, name, isTypeOnly, asAlias) {
|
|
478
|
-
return `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
479
|
-
}
|
|
480
|
-
function importKey(path, name, isTypeOnly) {
|
|
481
|
-
return `${path}:${name ?? ""}:${isTypeOnly ?? false}`;
|
|
408
|
+
function importLocalName(item) {
|
|
409
|
+
return typeof item === "string" ? item : item.name ?? item.propertyName;
|
|
482
410
|
}
|
|
483
411
|
/**
|
|
484
412
|
* Computes a multi-level sort key for exports and imports:
|
|
@@ -492,6 +420,36 @@ function sortKey(node) {
|
|
|
492
420
|
return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
|
|
493
421
|
}
|
|
494
422
|
/**
|
|
423
|
+
* Sorts members by {@link sortKey}. Keys are computed once per node, not per comparison.
|
|
424
|
+
*/
|
|
425
|
+
function sortMembers(nodes) {
|
|
426
|
+
return nodes.map((node) => ({
|
|
427
|
+
node,
|
|
428
|
+
key: sortKey(node)
|
|
429
|
+
})).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ node }) => node);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Returns a collector for array-named members. A member whose path and `isTypeOnly` were already
|
|
433
|
+
* seen folds its names into that entry, otherwise it joins `result` as a new entry.
|
|
434
|
+
*/
|
|
435
|
+
function mergeByPath(result) {
|
|
436
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
437
|
+
return ({ node, names }) => {
|
|
438
|
+
const key = `${node.path}:${node.isTypeOnly ?? false}`;
|
|
439
|
+
const existing = byPath.get(key);
|
|
440
|
+
if (existing && Array.isArray(existing.name)) {
|
|
441
|
+
existing.name = [.../* @__PURE__ */ new Set([...existing.name, ...names])];
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const item = {
|
|
445
|
+
...node,
|
|
446
|
+
name: names
|
|
447
|
+
};
|
|
448
|
+
result.push(item);
|
|
449
|
+
byPath.set(key, item);
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
495
453
|
* Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
|
|
496
454
|
* key. Unnamed sources fall back to their extracted node strings as the name part of the key. Returns
|
|
497
455
|
* the deduplicated array in original order.
|
|
@@ -499,22 +457,12 @@ function sortKey(node) {
|
|
|
499
457
|
function combineSources(sources) {
|
|
500
458
|
const seen = /* @__PURE__ */ new Map();
|
|
501
459
|
for (const source of sources) {
|
|
502
|
-
const key =
|
|
460
|
+
const key = `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
503
461
|
if (!seen.has(key)) seen.set(key, source);
|
|
504
462
|
}
|
|
505
463
|
return [...seen.values()];
|
|
506
464
|
}
|
|
507
465
|
/**
|
|
508
|
-
* Merges `incoming` names into `existing`, preserving order and dropping duplicates.
|
|
509
|
-
*
|
|
510
|
-
* Shared by `combineExports` and `combineImports` for the same-path name-merge case.
|
|
511
|
-
*/
|
|
512
|
-
function mergeNameArrays(existing, incoming) {
|
|
513
|
-
const merged = new Set(existing);
|
|
514
|
-
for (const name of incoming) merged.add(name);
|
|
515
|
-
return [...merged];
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
466
|
* Deduplicates and merges `ExportNode` objects by path and type.
|
|
519
467
|
*
|
|
520
468
|
* Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
|
|
@@ -522,34 +470,21 @@ function mergeNameArrays(existing, incoming) {
|
|
|
522
470
|
*/
|
|
523
471
|
function combineExports(exports) {
|
|
524
472
|
const result = [];
|
|
525
|
-
const
|
|
473
|
+
const merge = mergeByPath(result);
|
|
526
474
|
const seen = /* @__PURE__ */ new Set();
|
|
527
|
-
const
|
|
528
|
-
node,
|
|
529
|
-
key: sortKey(node)
|
|
530
|
-
}));
|
|
531
|
-
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
532
|
-
for (const { node: curr } of keyed) {
|
|
475
|
+
for (const curr of sortMembers(exports)) {
|
|
533
476
|
const { name, path, isTypeOnly, asAlias } = curr;
|
|
534
477
|
if (Array.isArray(name)) {
|
|
535
|
-
if (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
namedByPath.set(key, newItem);
|
|
546
|
-
}
|
|
547
|
-
} else {
|
|
548
|
-
const key = exportKey(path, name, isTypeOnly, asAlias);
|
|
549
|
-
if (!seen.has(key)) {
|
|
550
|
-
result.push(curr);
|
|
551
|
-
seen.add(key);
|
|
552
|
-
}
|
|
478
|
+
if (name.length) merge({
|
|
479
|
+
node: curr,
|
|
480
|
+
names: [...new Set(name)]
|
|
481
|
+
});
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
const key = `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
485
|
+
if (!seen.has(key)) {
|
|
486
|
+
result.push(curr);
|
|
487
|
+
seen.add(key);
|
|
553
488
|
}
|
|
554
489
|
}
|
|
555
490
|
return result;
|
|
@@ -562,7 +497,7 @@ function combineExports(exports) {
|
|
|
562
497
|
*/
|
|
563
498
|
function combineImports(imports, exports, source) {
|
|
564
499
|
const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
|
|
565
|
-
const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ?
|
|
500
|
+
const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? new Set(source.match(IDENTIFIER_RUN)) : null;
|
|
566
501
|
const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
|
|
567
502
|
const importNameMemo = /* @__PURE__ */ new Map();
|
|
568
503
|
const canonicalizeName = (n) => {
|
|
@@ -572,43 +507,26 @@ function combineImports(imports, exports, source) {
|
|
|
572
507
|
return importNameMemo.get(key);
|
|
573
508
|
};
|
|
574
509
|
const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
|
|
575
|
-
for (const node of imports)
|
|
576
|
-
if (!Array.isArray(node.name)) continue;
|
|
577
|
-
if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
|
|
578
|
-
}
|
|
510
|
+
for (const node of imports) if (Array.isArray(node.name) && node.name.some((item) => isUsed(importLocalName(item)))) pathsWithUsedNamedImport.add(node.path);
|
|
579
511
|
const result = [];
|
|
580
|
-
const
|
|
512
|
+
const merge = mergeByPath(result);
|
|
581
513
|
const seen = /* @__PURE__ */ new Set();
|
|
582
|
-
const
|
|
583
|
-
node,
|
|
584
|
-
key: sortKey(node)
|
|
585
|
-
}));
|
|
586
|
-
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
587
|
-
for (const { node: curr } of keyed) {
|
|
514
|
+
for (const curr of sortMembers(imports)) {
|
|
588
515
|
if (curr.path === curr.root) continue;
|
|
589
|
-
const { path, isTypeOnly } = curr;
|
|
590
|
-
let { name } = curr;
|
|
516
|
+
const { name, path, isTypeOnly } = curr;
|
|
591
517
|
if (Array.isArray(name)) {
|
|
592
|
-
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
} else {
|
|
606
|
-
if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
607
|
-
const key = importKey(path, name, isTypeOnly);
|
|
608
|
-
if (!seen.has(key)) {
|
|
609
|
-
result.push(curr);
|
|
610
|
-
seen.add(key);
|
|
611
|
-
}
|
|
518
|
+
const names = [...new Set(name.map(canonicalizeName))].filter((item) => isUsed(importLocalName(item)));
|
|
519
|
+
if (names.length) merge({
|
|
520
|
+
node: curr,
|
|
521
|
+
names
|
|
522
|
+
});
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (!isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
526
|
+
const key = `${path}:${name}:${isTypeOnly ?? false}`;
|
|
527
|
+
if (!seen.has(key)) {
|
|
528
|
+
result.push(curr);
|
|
529
|
+
seen.add(key);
|
|
612
530
|
}
|
|
613
531
|
}
|
|
614
532
|
return result;
|
|
@@ -713,12 +631,10 @@ function createFile(input) {
|
|
|
713
631
|
if (item.name) localNames.add(item.name);
|
|
714
632
|
}
|
|
715
633
|
const source = sourceParts.join("\n") || void 0;
|
|
716
|
-
|
|
717
|
-
const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
|
|
718
|
-
return combinedImports.flatMap((imp) => {
|
|
634
|
+
return combineImports(input.imports, resolvedExports, source).flatMap((imp) => {
|
|
719
635
|
if (imp.path === input.path) return [];
|
|
720
636
|
if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
|
|
721
|
-
const kept = imp.name.filter((item) => !localNames.has(
|
|
637
|
+
const kept = imp.name.filter((item) => !localNames.has(importLocalName(item)));
|
|
722
638
|
if (!kept.length) return [];
|
|
723
639
|
return [kept.length === imp.name.length ? imp : {
|
|
724
640
|
...imp,
|
|
@@ -731,7 +647,7 @@ function createFile(input) {
|
|
|
731
647
|
kind: "File",
|
|
732
648
|
...input,
|
|
733
649
|
id: (0, node_crypto.hash)("sha256", input.path, "hex"),
|
|
734
|
-
name:
|
|
650
|
+
name: node_path.default.basename(input.baseName, extname),
|
|
735
651
|
extname,
|
|
736
652
|
imports: resolvedImports,
|
|
737
653
|
exports: resolvedExports,
|
|
@@ -746,14 +662,15 @@ function createFile(input) {
|
|
|
746
662
|
*/
|
|
747
663
|
const inputDef = defineNode({
|
|
748
664
|
kind: "Input",
|
|
749
|
-
|
|
665
|
+
build: (props) => ({
|
|
750
666
|
schemas: [],
|
|
751
667
|
operations: [],
|
|
752
668
|
meta: {
|
|
753
669
|
circularNames: [],
|
|
754
670
|
enumNames: []
|
|
755
|
-
}
|
|
756
|
-
|
|
671
|
+
},
|
|
672
|
+
...props
|
|
673
|
+
}),
|
|
757
674
|
children: ["schemas", "operations"],
|
|
758
675
|
visitorKey: "input"
|
|
759
676
|
});
|
|
@@ -851,22 +768,27 @@ function optionality(schema, required) {
|
|
|
851
768
|
nullish: !required && nullable ? true : void 0
|
|
852
769
|
};
|
|
853
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Defaults `required` to `false` and derives the schema's `optional`/`nullish` flags from it.
|
|
773
|
+
* Shared build step of `createProperty` and `createParameter`.
|
|
774
|
+
*/
|
|
775
|
+
function withRequired(props) {
|
|
776
|
+
const required = props.required ?? false;
|
|
777
|
+
return {
|
|
778
|
+
...props,
|
|
779
|
+
required,
|
|
780
|
+
schema: optionality(props.schema, required)
|
|
781
|
+
};
|
|
782
|
+
}
|
|
854
783
|
//#endregion
|
|
855
784
|
//#region src/nodes/parameter.ts
|
|
856
785
|
/**
|
|
857
786
|
* Definition for the {@link ParameterNode}. `required` defaults to `false`, and the schema's
|
|
858
|
-
* `optional`/`nullish` flags are derived from it through {@link
|
|
787
|
+
* `optional`/`nullish` flags are derived from it through {@link withRequired}.
|
|
859
788
|
*/
|
|
860
789
|
const parameterDef = defineNode({
|
|
861
790
|
kind: "Parameter",
|
|
862
|
-
build:
|
|
863
|
-
const required = props.required ?? false;
|
|
864
|
-
return {
|
|
865
|
-
...props,
|
|
866
|
-
required,
|
|
867
|
-
schema: optionality(props.schema, required)
|
|
868
|
-
};
|
|
869
|
-
},
|
|
791
|
+
build: withRequired,
|
|
870
792
|
children: ["schema"],
|
|
871
793
|
visitorKey: "parameter"
|
|
872
794
|
});
|
|
@@ -888,18 +810,11 @@ const createParameter = parameterDef.create;
|
|
|
888
810
|
//#region src/nodes/property.ts
|
|
889
811
|
/**
|
|
890
812
|
* Definition for the {@link PropertyNode}. `required` defaults to `false`, and the schema's
|
|
891
|
-
* `optional`/`nullish` flags are derived from it through {@link
|
|
813
|
+
* `optional`/`nullish` flags are derived from it through {@link withRequired}.
|
|
892
814
|
*/
|
|
893
815
|
const propertyDef = defineNode({
|
|
894
816
|
kind: "Property",
|
|
895
|
-
build:
|
|
896
|
-
const required = props.required ?? false;
|
|
897
|
-
return {
|
|
898
|
-
...props,
|
|
899
|
-
required,
|
|
900
|
-
schema: optionality(props.schema, required)
|
|
901
|
-
};
|
|
902
|
-
},
|
|
817
|
+
build: withRequired,
|
|
903
818
|
children: ["schema"],
|
|
904
819
|
visitorKey: "property"
|
|
905
820
|
});
|
|
@@ -1000,7 +915,9 @@ const schemaDef = defineNode({
|
|
|
1000
915
|
"properties",
|
|
1001
916
|
"items",
|
|
1002
917
|
"members",
|
|
1003
|
-
"additionalProperties"
|
|
918
|
+
"additionalProperties",
|
|
919
|
+
"patternProperties",
|
|
920
|
+
"rest"
|
|
1004
921
|
],
|
|
1005
922
|
visitorKey: "schema"
|
|
1006
923
|
});
|
|
@@ -1055,9 +972,21 @@ function isNode(value) {
|
|
|
1055
972
|
return typeof value === "object" && value !== null && typeof value.kind === "string";
|
|
1056
973
|
}
|
|
1057
974
|
/**
|
|
1058
|
-
* Returns
|
|
1059
|
-
|
|
1060
|
-
|
|
975
|
+
* Returns `true` for a plain object that maps names to nodes, such as `patternProperties`.
|
|
976
|
+
*/
|
|
977
|
+
function isNodeRecord(value) {
|
|
978
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Returns the child fields of `node` to walk, from {@link VISITOR_KEYS}, or `undefined` when there
|
|
982
|
+
* is nothing to walk. `Schema` children are only walked when `recurse` is `true`.
|
|
983
|
+
*/
|
|
984
|
+
function childKeys(node, recurse) {
|
|
985
|
+
if (node.kind === "Schema" && !recurse) return void 0;
|
|
986
|
+
return visitorKeysByKind[node.kind];
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Returns the immediate traversable children of `node`.
|
|
1061
990
|
*
|
|
1062
991
|
* @example
|
|
1063
992
|
* ```ts
|
|
@@ -1066,8 +995,7 @@ function isNode(value) {
|
|
|
1066
995
|
* ```
|
|
1067
996
|
*/
|
|
1068
997
|
function* getChildren(node, recurse) {
|
|
1069
|
-
|
|
1070
|
-
const keys = visitorKeysByKind[node.kind];
|
|
998
|
+
const keys = childKeys(node, recurse);
|
|
1071
999
|
if (!keys) return;
|
|
1072
1000
|
const record = node;
|
|
1073
1001
|
for (const key of keys) {
|
|
@@ -1076,7 +1004,13 @@ function* getChildren(node, recurse) {
|
|
|
1076
1004
|
for (const item of value) if (isNode(item)) yield item;
|
|
1077
1005
|
continue;
|
|
1078
1006
|
}
|
|
1079
|
-
if (isNode(value))
|
|
1007
|
+
if (isNode(value)) {
|
|
1008
|
+
yield value;
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (isNodeRecord(value)) {
|
|
1012
|
+
for (const item of Object.values(value)) if (isNode(item)) yield item;
|
|
1013
|
+
}
|
|
1080
1014
|
}
|
|
1081
1015
|
}
|
|
1082
1016
|
/**
|
|
@@ -1094,6 +1028,30 @@ function applyVisitor(node, visitor, parent) {
|
|
|
1094
1028
|
const fn = visitor[key];
|
|
1095
1029
|
return fn?.(node, { parent });
|
|
1096
1030
|
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Synchronous depth-first transform. Each visitor callback can return a
|
|
1033
|
+
* replacement node. Returning `undefined` keeps the original.
|
|
1034
|
+
*
|
|
1035
|
+
* The original tree is never mutated, a new tree is returned. Pass
|
|
1036
|
+
* `depth: 'shallow'` to skip recursion into children.
|
|
1037
|
+
*
|
|
1038
|
+
* @example Prefix every operationId
|
|
1039
|
+
* ```ts
|
|
1040
|
+
* const next = transform(root, {
|
|
1041
|
+
* operation(node) {
|
|
1042
|
+
* return { ...node, operationId: `prefixed_${node.operationId}` }
|
|
1043
|
+
* },
|
|
1044
|
+
* })
|
|
1045
|
+
* ```
|
|
1046
|
+
*
|
|
1047
|
+
* @example Replace only the root node
|
|
1048
|
+
* ```ts
|
|
1049
|
+
* const next = transform(root, {
|
|
1050
|
+
* depth: 'shallow',
|
|
1051
|
+
* input: (node) => ({ ...node, meta: { ...node.meta, title: 'Rewritten' } }),
|
|
1052
|
+
* })
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1097
1055
|
function transform(node, options) {
|
|
1098
1056
|
const { depth, parent, ...visitor } = options;
|
|
1099
1057
|
return transformNode(node, visitor, (depth ?? visitorDepths.deep) === visitorDepths.deep, parent);
|
|
@@ -1107,13 +1065,11 @@ function transformNode(node, visitor, recurse, parent) {
|
|
|
1107
1065
|
return transformChildren(applyVisitor(node, visitor, parent) ?? node, visitor, recurse);
|
|
1108
1066
|
}
|
|
1109
1067
|
/**
|
|
1110
|
-
* Immutably rebuilds a node's children
|
|
1111
|
-
*
|
|
1112
|
-
* `Schema` children are skipped in shallow mode.
|
|
1068
|
+
* Immutably rebuilds a node's children, transforming each child node and leaving non-node values
|
|
1069
|
+
* (e.g. `additionalProperties: true`) intact.
|
|
1113
1070
|
*/
|
|
1114
1071
|
function transformChildren(node, visitor, recurse) {
|
|
1115
|
-
|
|
1116
|
-
const keys = visitorKeysByKind[node.kind];
|
|
1072
|
+
const keys = childKeys(node, recurse);
|
|
1117
1073
|
if (!keys) return node;
|
|
1118
1074
|
const record = node;
|
|
1119
1075
|
let updates;
|
|
@@ -1136,6 +1092,15 @@ function transformChildren(node, visitor, recurse) {
|
|
|
1136
1092
|
if (isNode(value)) {
|
|
1137
1093
|
const next = transformNode(value, visitor, recurse, node);
|
|
1138
1094
|
if (next !== value) (updates ??= {})[key] = next;
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
if (isNodeRecord(value)) {
|
|
1098
|
+
let mapped;
|
|
1099
|
+
for (const [name, item] of Object.entries(value)) {
|
|
1100
|
+
const next = isNode(item) ? transformNode(item, visitor, recurse, node) : item;
|
|
1101
|
+
if (next !== item) (mapped ??= { ...value })[name] = next;
|
|
1102
|
+
}
|
|
1103
|
+
if (mapped) (updates ??= {})[key] = mapped;
|
|
1139
1104
|
}
|
|
1140
1105
|
}
|
|
1141
1106
|
if (!updates) return node;
|
|
@@ -1329,21 +1294,18 @@ function createPrinter(build) {
|
|
|
1329
1294
|
...overrides
|
|
1330
1295
|
} : nodes;
|
|
1331
1296
|
const collectedImports = [];
|
|
1297
|
+
const dispatch = (table) => (node) => {
|
|
1298
|
+
const handler = table[node.type];
|
|
1299
|
+
if (!handler) return null;
|
|
1300
|
+
return handler.call(context, node);
|
|
1301
|
+
};
|
|
1332
1302
|
const context = {
|
|
1333
1303
|
options: resolvedOptions,
|
|
1334
1304
|
import: (node) => {
|
|
1335
1305
|
collectedImports.push(node);
|
|
1336
1306
|
},
|
|
1337
|
-
transform: (
|
|
1338
|
-
|
|
1339
|
-
if (!handler) return null;
|
|
1340
|
-
return handler.call(context, node);
|
|
1341
|
-
},
|
|
1342
|
-
base: (node) => {
|
|
1343
|
-
const handler = nodes[node.type];
|
|
1344
|
-
if (!handler) return null;
|
|
1345
|
-
return handler.call(context, node);
|
|
1346
|
-
}
|
|
1307
|
+
transform: dispatch(merged),
|
|
1308
|
+
base: dispatch(nodes)
|
|
1347
1309
|
};
|
|
1348
1310
|
return {
|
|
1349
1311
|
name,
|
|
@@ -1376,6 +1338,29 @@ function resolveRefName(node) {
|
|
|
1376
1338
|
return node.name ?? node.schema?.name ?? null;
|
|
1377
1339
|
}
|
|
1378
1340
|
/**
|
|
1341
|
+
* Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
|
|
1342
|
+
*
|
|
1343
|
+
* Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
|
|
1344
|
+
* of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
|
|
1345
|
+
* left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
|
|
1346
|
+
* is returned as is.
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* ```ts
|
|
1350
|
+
* const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
|
|
1351
|
+
* mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
|
|
1352
|
+
* ```
|
|
1353
|
+
*/
|
|
1354
|
+
function mergeRefWithSchema(node) {
|
|
1355
|
+
if (!node.schema) return node;
|
|
1356
|
+
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = node;
|
|
1357
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0));
|
|
1358
|
+
return {
|
|
1359
|
+
...node.schema,
|
|
1360
|
+
...definedOverrides
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1379
1364
|
* Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
|
|
1380
1365
|
*
|
|
1381
1366
|
* Modifiers inherited from the resolved schema are included in the check because a ref emits the
|
|
@@ -1383,21 +1368,62 @@ function resolveRefName(node) {
|
|
|
1383
1368
|
*/
|
|
1384
1369
|
function isBareRef(node, keysToOmit) {
|
|
1385
1370
|
if (!node || node.type !== "ref") return false;
|
|
1386
|
-
const meta = (
|
|
1387
|
-
if (!node.schema) return node;
|
|
1388
|
-
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = node;
|
|
1389
|
-
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0));
|
|
1390
|
-
return {
|
|
1391
|
-
...node.schema,
|
|
1392
|
-
...definedOverrides
|
|
1393
|
-
};
|
|
1394
|
-
})();
|
|
1371
|
+
const meta = mergeRefWithSchema(node);
|
|
1395
1372
|
return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
|
|
1396
1373
|
}
|
|
1397
1374
|
//#endregion
|
|
1375
|
+
//#region ../../internals/utils/src/promise.ts
|
|
1376
|
+
/**
|
|
1377
|
+
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
1378
|
+
*
|
|
1379
|
+
* Pass a `WeakMap` for object keys (results are GC-eligible when the key is
|
|
1380
|
+
* collected) or a `Map` for primitive keys. For multi-argument functions,
|
|
1381
|
+
* nest two `memoize` calls — the outer keyed by the first argument, the
|
|
1382
|
+
* inner (created once per outer miss) keyed by the second.
|
|
1383
|
+
*
|
|
1384
|
+
* Because the cache is owned by the caller, it can be shared, inspected, or
|
|
1385
|
+
* cleared independently of the memoized function.
|
|
1386
|
+
*
|
|
1387
|
+
* @example Single WeakMap key
|
|
1388
|
+
* ```ts
|
|
1389
|
+
* const cache = new WeakMap<SchemaNode, Set<string>>()
|
|
1390
|
+
* const getRefs = memoize(cache, (node) => collectRefs(node))
|
|
1391
|
+
* ```
|
|
1392
|
+
*
|
|
1393
|
+
* @example Single Map key (primitive)
|
|
1394
|
+
* ```ts
|
|
1395
|
+
* const cache = new Map<string, Resolver>()
|
|
1396
|
+
* const getResolver = memoize(cache, (name) => buildResolver(name))
|
|
1397
|
+
* ```
|
|
1398
|
+
*
|
|
1399
|
+
* @example Two-level (object + primitive)
|
|
1400
|
+
* ```ts
|
|
1401
|
+
* const outer = new WeakMap<Params[], Map<string, Params[]>>()
|
|
1402
|
+
* const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
|
|
1403
|
+
* fn(params)('camelcase')
|
|
1404
|
+
* ```
|
|
1405
|
+
*/
|
|
1406
|
+
function memoize(store, factory) {
|
|
1407
|
+
return (key) => {
|
|
1408
|
+
if (store.has(key)) return store.get(key);
|
|
1409
|
+
const value = factory(key);
|
|
1410
|
+
store.set(key, value);
|
|
1411
|
+
return value;
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
//#endregion
|
|
1398
1415
|
//#region src/utils/schemaGraph.ts
|
|
1399
1416
|
/**
|
|
1400
|
-
*
|
|
1417
|
+
* Collects the names of every schema a node's subtree references, resolved with `resolveRefName`.
|
|
1418
|
+
* Each ref contributes its name only, so the schema it points to is never traversed. The walk is
|
|
1419
|
+
* memoized by node identity, which lets `findCircularSchemas` and `containsCircularRef` share one
|
|
1420
|
+
* scan per schema.
|
|
1421
|
+
*
|
|
1422
|
+
* @example
|
|
1423
|
+
* ```ts
|
|
1424
|
+
* collectSchemaRefs(petSchema)
|
|
1425
|
+
* // Set { 'Category', 'Tag' }
|
|
1426
|
+
* ```
|
|
1401
1427
|
*/
|
|
1402
1428
|
const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
1403
1429
|
const refs = /* @__PURE__ */ new Set();
|
|
@@ -1410,31 +1436,6 @@ const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
|
1410
1436
|
return refs;
|
|
1411
1437
|
});
|
|
1412
1438
|
/**
|
|
1413
|
-
* Collects the names of every ref found anywhere inside a node's own subtree.
|
|
1414
|
-
*
|
|
1415
|
-
* Each ref contributes its name only, so the schema it points to is never traversed here. Pass `out`
|
|
1416
|
-
* to accumulate names from several nodes into one set.
|
|
1417
|
-
*
|
|
1418
|
-
* @example Collect refs from a single schema
|
|
1419
|
-
* ```ts
|
|
1420
|
-
* const names = collectReferencedSchemaNames(petSchema)
|
|
1421
|
-
* // Set { 'Category', 'Tag' }
|
|
1422
|
-
* ```
|
|
1423
|
-
*
|
|
1424
|
-
* @example Accumulate refs from multiple schemas into one set
|
|
1425
|
-
* ```ts
|
|
1426
|
-
* const out = new Set<string>()
|
|
1427
|
-
* for (const schema of schemas) {
|
|
1428
|
-
* collectReferencedSchemaNames(schema, out)
|
|
1429
|
-
* }
|
|
1430
|
-
* ```
|
|
1431
|
-
*/
|
|
1432
|
-
function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1433
|
-
if (!node) return out;
|
|
1434
|
-
for (const name of collectSchemaRefs(node)) out.add(name);
|
|
1435
|
-
return out;
|
|
1436
|
-
}
|
|
1437
|
-
/**
|
|
1438
1439
|
* Collects the de-duplicated target names of every pointer-carrying ref in a node's subtree, in
|
|
1439
1440
|
* first-occurrence order. The walk is memoized by node identity, so the subtree is scanned once and
|
|
1440
1441
|
* `resolver.imports` reads the same result across the ts, zod, and faker plugins instead of
|
|
@@ -1442,8 +1443,7 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1442
1443
|
*
|
|
1443
1444
|
* Only refs that carry a `$ref` pointer count, so a synthesized ref pointing at a sibling in the
|
|
1444
1445
|
* same file (a union member created by name) is left out. That leaves exactly the set
|
|
1445
|
-
* `resolver.imports` emits.
|
|
1446
|
-
* {@link collectReferencedSchemaNames}, which returns an unordered set for graph analysis.
|
|
1446
|
+
* `resolver.imports` emits.
|
|
1447
1447
|
*
|
|
1448
1448
|
* @example
|
|
1449
1449
|
* ```ts
|
|
@@ -1464,24 +1464,6 @@ const collectImportedRefNames = memoize(/* @__PURE__ */ new WeakMap(), (node) =>
|
|
|
1464
1464
|
} });
|
|
1465
1465
|
return names;
|
|
1466
1466
|
});
|
|
1467
|
-
function computeUsedSchemaNames(operations, schemas) {
|
|
1468
|
-
const schemaMap = /* @__PURE__ */ new Map();
|
|
1469
|
-
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1470
|
-
const result = /* @__PURE__ */ new Set();
|
|
1471
|
-
function visitSchema(schema) {
|
|
1472
|
-
const directRefs = collectReferencedSchemaNames(schema);
|
|
1473
|
-
for (const name of directRefs) if (!result.has(name)) {
|
|
1474
|
-
result.add(name);
|
|
1475
|
-
const namedSchema = schemaMap.get(name);
|
|
1476
|
-
if (namedSchema) visitSchema(namedSchema);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
for (const op of operations) for (const schema of collect(op, {
|
|
1480
|
-
depth: "shallow",
|
|
1481
|
-
schema: (node) => node
|
|
1482
|
-
})) visitSchema(schema);
|
|
1483
|
-
return result;
|
|
1484
|
-
}
|
|
1485
1467
|
/**
|
|
1486
1468
|
* Collects the names of all top-level schemas transitively used by a set of operations.
|
|
1487
1469
|
*
|
|
@@ -1503,9 +1485,22 @@ function computeUsedSchemaNames(operations, schemas) {
|
|
|
1503
1485
|
* ```
|
|
1504
1486
|
*/
|
|
1505
1487
|
function collectUsedSchemaNames(operations, schemas) {
|
|
1506
|
-
|
|
1488
|
+
const schemaMap = /* @__PURE__ */ new Map();
|
|
1489
|
+
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1490
|
+
const result = /* @__PURE__ */ new Set();
|
|
1491
|
+
function visitSchema(schema) {
|
|
1492
|
+
for (const name of collectSchemaRefs(schema)) if (!result.has(name)) {
|
|
1493
|
+
result.add(name);
|
|
1494
|
+
const namedSchema = schemaMap.get(name);
|
|
1495
|
+
if (namedSchema) visitSchema(namedSchema);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
for (const op of operations) for (const schema of collect(op, {
|
|
1499
|
+
depth: "shallow",
|
|
1500
|
+
schema: (node) => node
|
|
1501
|
+
})) visitSchema(schema);
|
|
1502
|
+
return result;
|
|
1507
1503
|
}
|
|
1508
|
-
const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
|
|
1509
1504
|
/**
|
|
1510
1505
|
* Finds every schema that takes part in a circular dependency chain in a schema dependency graph
|
|
1511
1506
|
* that maps each schema name to the names it references directly.
|
|
@@ -1542,14 +1537,6 @@ function findCircularSchemasFromGraph(graph) {
|
|
|
1542
1537
|
}
|
|
1543
1538
|
return circular;
|
|
1544
1539
|
}
|
|
1545
|
-
const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
|
|
1546
|
-
const graph = /* @__PURE__ */ new Map();
|
|
1547
|
-
for (const schema of schemas) {
|
|
1548
|
-
if (!schema.name) continue;
|
|
1549
|
-
graph.set(schema.name, collectReferencedSchemaNames(schema));
|
|
1550
|
-
}
|
|
1551
|
-
return findCircularSchemasFromGraph(graph);
|
|
1552
|
-
});
|
|
1553
1540
|
/**
|
|
1554
1541
|
* Finds every schema that takes part in a circular dependency chain, including direct self-loops.
|
|
1555
1542
|
*
|
|
@@ -1560,8 +1547,9 @@ const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas)
|
|
|
1560
1547
|
* @note Call this once on the full graph, then check individual schemas with `containsCircularRef()`.
|
|
1561
1548
|
*/
|
|
1562
1549
|
function findCircularSchemas(schemas) {
|
|
1563
|
-
|
|
1564
|
-
|
|
1550
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1551
|
+
for (const schema of schemas) if (schema.name) graph.set(schema.name, collectSchemaRefs(schema));
|
|
1552
|
+
return findCircularSchemasFromGraph(graph);
|
|
1565
1553
|
}
|
|
1566
1554
|
//#endregion
|
|
1567
1555
|
//#region src/utils/schemaProperties.ts
|
|
@@ -1736,6 +1724,7 @@ var exports_exports = /* @__PURE__ */ __exportAll({
|
|
|
1736
1724
|
breakDef: () => breakDef,
|
|
1737
1725
|
collect: () => collect,
|
|
1738
1726
|
collectImportedRefNames: () => collectImportedRefNames,
|
|
1727
|
+
collectSchemaRefs: () => collectSchemaRefs,
|
|
1739
1728
|
collectSync: () => collectSync,
|
|
1740
1729
|
collectUsedSchemaNames: () => collectUsedSchemaNames,
|
|
1741
1730
|
composeMacros: () => composeMacros,
|
|
@@ -1758,6 +1747,7 @@ var exports_exports = /* @__PURE__ */ __exportAll({
|
|
|
1758
1747
|
isBareRef: () => isBareRef,
|
|
1759
1748
|
isHttpOperationNode: () => isHttpOperationNode,
|
|
1760
1749
|
jsxDef: () => jsxDef,
|
|
1750
|
+
mergeRefWithSchema: () => mergeRefWithSchema,
|
|
1761
1751
|
narrowSchema: () => narrowSchema,
|
|
1762
1752
|
nodeDefs: () => nodeDefs,
|
|
1763
1753
|
operationDef: () => operationDef,
|
|
@@ -1788,6 +1778,7 @@ Object.defineProperty(exports, "ast", {
|
|
|
1788
1778
|
exports.breakDef = breakDef;
|
|
1789
1779
|
exports.collect = collect;
|
|
1790
1780
|
exports.collectImportedRefNames = collectImportedRefNames;
|
|
1781
|
+
exports.collectSchemaRefs = collectSchemaRefs;
|
|
1791
1782
|
exports.collectSync = collectSync;
|
|
1792
1783
|
exports.collectUsedSchemaNames = collectUsedSchemaNames;
|
|
1793
1784
|
exports.composeMacros = composeMacros;
|
|
@@ -1815,6 +1806,7 @@ exports.inputDef = inputDef;
|
|
|
1815
1806
|
exports.isBareRef = isBareRef;
|
|
1816
1807
|
exports.isHttpOperationNode = isHttpOperationNode;
|
|
1817
1808
|
exports.jsxDef = jsxDef;
|
|
1809
|
+
exports.mergeRefWithSchema = mergeRefWithSchema;
|
|
1818
1810
|
exports.narrowSchema = narrowSchema;
|
|
1819
1811
|
exports.nodeDefs = nodeDefs;
|
|
1820
1812
|
exports.operationDef = operationDef;
|