@kubb/ast 5.5.6 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +254 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +32 -5
- 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 +2 -6
package/dist/index.js
CHANGED
|
@@ -331,63 +331,6 @@ const contentDef = defineNode({
|
|
|
331
331
|
*/
|
|
332
332
|
const createContent = contentDef.create;
|
|
333
333
|
//#endregion
|
|
334
|
-
//#region ../../internals/utils/src/fs.ts
|
|
335
|
-
/**
|
|
336
|
-
* Strips the file extension from a path or file name.
|
|
337
|
-
* Only removes the last `.ext` segment when the dot is not part of a directory name.
|
|
338
|
-
*
|
|
339
|
-
* @example
|
|
340
|
-
* trimExtName('petStore.ts') // 'petStore'
|
|
341
|
-
* trimExtName('/src/models/pet.ts') // '/src/models/pet'
|
|
342
|
-
* trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
|
|
343
|
-
* trimExtName('noExtension') // 'noExtension'
|
|
344
|
-
*/
|
|
345
|
-
function trimExtName(text) {
|
|
346
|
-
const dotIndex = text.lastIndexOf(".");
|
|
347
|
-
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
348
|
-
return text;
|
|
349
|
-
}
|
|
350
|
-
//#endregion
|
|
351
|
-
//#region ../../internals/utils/src/promise.ts
|
|
352
|
-
/**
|
|
353
|
-
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
354
|
-
*
|
|
355
|
-
* Pass a `WeakMap` for object keys (results are GC-eligible when the key is
|
|
356
|
-
* collected) or a `Map` for primitive keys. For multi-argument functions,
|
|
357
|
-
* nest two `memoize` calls — the outer keyed by the first argument, the
|
|
358
|
-
* inner (created once per outer miss) keyed by the second.
|
|
359
|
-
*
|
|
360
|
-
* Because the cache is owned by the caller, it can be shared, inspected, or
|
|
361
|
-
* cleared independently of the memoized function.
|
|
362
|
-
*
|
|
363
|
-
* @example Single WeakMap key
|
|
364
|
-
* ```ts
|
|
365
|
-
* const cache = new WeakMap<SchemaNode, Set<string>>()
|
|
366
|
-
* const getRefs = memoize(cache, (node) => collectRefs(node))
|
|
367
|
-
* ```
|
|
368
|
-
*
|
|
369
|
-
* @example Single Map key (primitive)
|
|
370
|
-
* ```ts
|
|
371
|
-
* const cache = new Map<string, Resolver>()
|
|
372
|
-
* const getResolver = memoize(cache, (name) => buildResolver(name))
|
|
373
|
-
* ```
|
|
374
|
-
*
|
|
375
|
-
* @example Two-level (object + primitive)
|
|
376
|
-
* ```ts
|
|
377
|
-
* const outer = new WeakMap<Params[], Map<string, Params[]>>()
|
|
378
|
-
* const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
|
|
379
|
-
* fn(params)('camelcase')
|
|
380
|
-
* ```
|
|
381
|
-
*/
|
|
382
|
-
function memoize(store, factory) {
|
|
383
|
-
return (key) => {
|
|
384
|
-
if (store.has(key)) return store.get(key);
|
|
385
|
-
const value = factory(key);
|
|
386
|
-
store.set(key, value);
|
|
387
|
-
return value;
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
//#endregion
|
|
391
334
|
//#region src/utils/extractStringsFromNodes.ts
|
|
392
335
|
/**
|
|
393
336
|
* Extracts all string content from a `CodeNode` tree recursively.
|
|
@@ -403,15 +346,11 @@ function extractStringsFromNodes(nodes) {
|
|
|
403
346
|
if (node) collected.push(node);
|
|
404
347
|
continue;
|
|
405
348
|
}
|
|
406
|
-
if (node.kind === "Text") {
|
|
349
|
+
if (node.kind === "Text" || node.kind === "Jsx") {
|
|
407
350
|
if (node.value) collected.push(node.value);
|
|
408
351
|
continue;
|
|
409
352
|
}
|
|
410
353
|
if (node.kind === "Break") continue;
|
|
411
|
-
if (node.kind === "Jsx") {
|
|
412
|
-
if (node.value) collected.push(node.value);
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
354
|
const parts = [];
|
|
416
355
|
if ("params" in node && node.params) parts.push(node.params);
|
|
417
356
|
if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
|
|
@@ -431,22 +370,11 @@ const IDENTIFIER_RUN = /[\w$]+/g;
|
|
|
431
370
|
*/
|
|
432
371
|
const INDEX_ABOVE_IMPORTS = 128;
|
|
433
372
|
/**
|
|
434
|
-
*
|
|
373
|
+
* Returns the local binding an import name item introduces: the alias when there is one, else the
|
|
374
|
+
* imported name.
|
|
435
375
|
*/
|
|
436
|
-
function
|
|
437
|
-
return
|
|
438
|
-
}
|
|
439
|
-
function sourceKey(source) {
|
|
440
|
-
return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
441
|
-
}
|
|
442
|
-
function pathTypeKey(path, isTypeOnly) {
|
|
443
|
-
return `${path}:${isTypeOnly ?? false}`;
|
|
444
|
-
}
|
|
445
|
-
function exportKey(path, name, isTypeOnly, asAlias) {
|
|
446
|
-
return `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
447
|
-
}
|
|
448
|
-
function importKey(path, name, isTypeOnly) {
|
|
449
|
-
return `${path}:${name ?? ""}:${isTypeOnly ?? false}`;
|
|
376
|
+
function importLocalName(item) {
|
|
377
|
+
return typeof item === "string" ? item : item.name ?? item.propertyName;
|
|
450
378
|
}
|
|
451
379
|
/**
|
|
452
380
|
* Computes a multi-level sort key for exports and imports:
|
|
@@ -460,6 +388,36 @@ function sortKey(node) {
|
|
|
460
388
|
return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
|
|
461
389
|
}
|
|
462
390
|
/**
|
|
391
|
+
* Sorts members by {@link sortKey}. Keys are computed once per node, not per comparison.
|
|
392
|
+
*/
|
|
393
|
+
function sortMembers(nodes) {
|
|
394
|
+
return nodes.map((node) => ({
|
|
395
|
+
node,
|
|
396
|
+
key: sortKey(node)
|
|
397
|
+
})).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ node }) => node);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Returns a collector for array-named members. A member whose path and `isTypeOnly` were already
|
|
401
|
+
* seen folds its names into that entry, otherwise it joins `result` as a new entry.
|
|
402
|
+
*/
|
|
403
|
+
function mergeByPath(result) {
|
|
404
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
405
|
+
return ({ node, names }) => {
|
|
406
|
+
const key = `${node.path}:${node.isTypeOnly ?? false}`;
|
|
407
|
+
const existing = byPath.get(key);
|
|
408
|
+
if (existing && Array.isArray(existing.name)) {
|
|
409
|
+
existing.name = [.../* @__PURE__ */ new Set([...existing.name, ...names])];
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const item = {
|
|
413
|
+
...node,
|
|
414
|
+
name: names
|
|
415
|
+
};
|
|
416
|
+
result.push(item);
|
|
417
|
+
byPath.set(key, item);
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
463
421
|
* Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
|
|
464
422
|
* key. Unnamed sources fall back to their extracted node strings as the name part of the key. Returns
|
|
465
423
|
* the deduplicated array in original order.
|
|
@@ -467,22 +425,12 @@ function sortKey(node) {
|
|
|
467
425
|
function combineSources(sources) {
|
|
468
426
|
const seen = /* @__PURE__ */ new Map();
|
|
469
427
|
for (const source of sources) {
|
|
470
|
-
const key =
|
|
428
|
+
const key = `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
471
429
|
if (!seen.has(key)) seen.set(key, source);
|
|
472
430
|
}
|
|
473
431
|
return [...seen.values()];
|
|
474
432
|
}
|
|
475
433
|
/**
|
|
476
|
-
* Merges `incoming` names into `existing`, preserving order and dropping duplicates.
|
|
477
|
-
*
|
|
478
|
-
* Shared by `combineExports` and `combineImports` for the same-path name-merge case.
|
|
479
|
-
*/
|
|
480
|
-
function mergeNameArrays(existing, incoming) {
|
|
481
|
-
const merged = new Set(existing);
|
|
482
|
-
for (const name of incoming) merged.add(name);
|
|
483
|
-
return [...merged];
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
434
|
* Deduplicates and merges `ExportNode` objects by path and type.
|
|
487
435
|
*
|
|
488
436
|
* Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
|
|
@@ -490,34 +438,21 @@ function mergeNameArrays(existing, incoming) {
|
|
|
490
438
|
*/
|
|
491
439
|
function combineExports(exports) {
|
|
492
440
|
const result = [];
|
|
493
|
-
const
|
|
441
|
+
const merge = mergeByPath(result);
|
|
494
442
|
const seen = /* @__PURE__ */ new Set();
|
|
495
|
-
const
|
|
496
|
-
node,
|
|
497
|
-
key: sortKey(node)
|
|
498
|
-
}));
|
|
499
|
-
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
500
|
-
for (const { node: curr } of keyed) {
|
|
443
|
+
for (const curr of sortMembers(exports)) {
|
|
501
444
|
const { name, path, isTypeOnly, asAlias } = curr;
|
|
502
445
|
if (Array.isArray(name)) {
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
namedByPath.set(key, newItem);
|
|
514
|
-
}
|
|
515
|
-
} else {
|
|
516
|
-
const key = exportKey(path, name, isTypeOnly, asAlias);
|
|
517
|
-
if (!seen.has(key)) {
|
|
518
|
-
result.push(curr);
|
|
519
|
-
seen.add(key);
|
|
520
|
-
}
|
|
446
|
+
if (name.length) merge({
|
|
447
|
+
node: curr,
|
|
448
|
+
names: [...new Set(name)]
|
|
449
|
+
});
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const key = `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
453
|
+
if (!seen.has(key)) {
|
|
454
|
+
result.push(curr);
|
|
455
|
+
seen.add(key);
|
|
521
456
|
}
|
|
522
457
|
}
|
|
523
458
|
return result;
|
|
@@ -530,7 +465,7 @@ function combineExports(exports) {
|
|
|
530
465
|
*/
|
|
531
466
|
function combineImports(imports, exports, source) {
|
|
532
467
|
const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
|
|
533
|
-
const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ?
|
|
468
|
+
const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? new Set(source.match(IDENTIFIER_RUN)) : null;
|
|
534
469
|
const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
|
|
535
470
|
const importNameMemo = /* @__PURE__ */ new Map();
|
|
536
471
|
const canonicalizeName = (n) => {
|
|
@@ -540,43 +475,26 @@ function combineImports(imports, exports, source) {
|
|
|
540
475
|
return importNameMemo.get(key);
|
|
541
476
|
};
|
|
542
477
|
const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
|
|
543
|
-
for (const node of imports)
|
|
544
|
-
if (!Array.isArray(node.name)) continue;
|
|
545
|
-
if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
|
|
546
|
-
}
|
|
478
|
+
for (const node of imports) if (Array.isArray(node.name) && node.name.some((item) => isUsed(importLocalName(item)))) pathsWithUsedNamedImport.add(node.path);
|
|
547
479
|
const result = [];
|
|
548
|
-
const
|
|
480
|
+
const merge = mergeByPath(result);
|
|
549
481
|
const seen = /* @__PURE__ */ new Set();
|
|
550
|
-
const
|
|
551
|
-
node,
|
|
552
|
-
key: sortKey(node)
|
|
553
|
-
}));
|
|
554
|
-
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
555
|
-
for (const { node: curr } of keyed) {
|
|
482
|
+
for (const curr of sortMembers(imports)) {
|
|
556
483
|
if (curr.path === curr.root) continue;
|
|
557
|
-
const { path, isTypeOnly } = curr;
|
|
558
|
-
let { name } = curr;
|
|
484
|
+
const { name, path, isTypeOnly } = curr;
|
|
559
485
|
if (Array.isArray(name)) {
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
} else {
|
|
574
|
-
if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
575
|
-
const key = importKey(path, name, isTypeOnly);
|
|
576
|
-
if (!seen.has(key)) {
|
|
577
|
-
result.push(curr);
|
|
578
|
-
seen.add(key);
|
|
579
|
-
}
|
|
486
|
+
const names = [...new Set(name.map(canonicalizeName))].filter((item) => isUsed(importLocalName(item)));
|
|
487
|
+
if (names.length) merge({
|
|
488
|
+
node: curr,
|
|
489
|
+
names
|
|
490
|
+
});
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (!isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
494
|
+
const key = `${path}:${name}:${isTypeOnly ?? false}`;
|
|
495
|
+
if (!seen.has(key)) {
|
|
496
|
+
result.push(curr);
|
|
497
|
+
seen.add(key);
|
|
580
498
|
}
|
|
581
499
|
}
|
|
582
500
|
return result;
|
|
@@ -681,12 +599,10 @@ function createFile(input) {
|
|
|
681
599
|
if (item.name) localNames.add(item.name);
|
|
682
600
|
}
|
|
683
601
|
const source = sourceParts.join("\n") || void 0;
|
|
684
|
-
|
|
685
|
-
const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
|
|
686
|
-
return combinedImports.flatMap((imp) => {
|
|
602
|
+
return combineImports(input.imports, resolvedExports, source).flatMap((imp) => {
|
|
687
603
|
if (imp.path === input.path) return [];
|
|
688
604
|
if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
|
|
689
|
-
const kept = imp.name.filter((item) => !localNames.has(
|
|
605
|
+
const kept = imp.name.filter((item) => !localNames.has(importLocalName(item)));
|
|
690
606
|
if (!kept.length) return [];
|
|
691
607
|
return [kept.length === imp.name.length ? imp : {
|
|
692
608
|
...imp,
|
|
@@ -699,7 +615,7 @@ function createFile(input) {
|
|
|
699
615
|
kind: "File",
|
|
700
616
|
...input,
|
|
701
617
|
id: hash("sha256", input.path, "hex"),
|
|
702
|
-
name:
|
|
618
|
+
name: path.basename(input.baseName, extname),
|
|
703
619
|
extname,
|
|
704
620
|
imports: resolvedImports,
|
|
705
621
|
exports: resolvedExports,
|
|
@@ -714,14 +630,15 @@ function createFile(input) {
|
|
|
714
630
|
*/
|
|
715
631
|
const inputDef = defineNode({
|
|
716
632
|
kind: "Input",
|
|
717
|
-
|
|
633
|
+
build: (props) => ({
|
|
718
634
|
schemas: [],
|
|
719
635
|
operations: [],
|
|
720
636
|
meta: {
|
|
721
637
|
circularNames: [],
|
|
722
638
|
enumNames: []
|
|
723
|
-
}
|
|
724
|
-
|
|
639
|
+
},
|
|
640
|
+
...props
|
|
641
|
+
}),
|
|
725
642
|
children: ["schemas", "operations"],
|
|
726
643
|
visitorKey: "input"
|
|
727
644
|
});
|
|
@@ -819,22 +736,27 @@ function optionality(schema, required) {
|
|
|
819
736
|
nullish: !required && nullable ? true : void 0
|
|
820
737
|
};
|
|
821
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Defaults `required` to `false` and derives the schema's `optional`/`nullish` flags from it.
|
|
741
|
+
* Shared build step of `createProperty` and `createParameter`.
|
|
742
|
+
*/
|
|
743
|
+
function withRequired(props) {
|
|
744
|
+
const required = props.required ?? false;
|
|
745
|
+
return {
|
|
746
|
+
...props,
|
|
747
|
+
required,
|
|
748
|
+
schema: optionality(props.schema, required)
|
|
749
|
+
};
|
|
750
|
+
}
|
|
822
751
|
//#endregion
|
|
823
752
|
//#region src/nodes/parameter.ts
|
|
824
753
|
/**
|
|
825
754
|
* Definition for the {@link ParameterNode}. `required` defaults to `false`, and the schema's
|
|
826
|
-
* `optional`/`nullish` flags are derived from it through {@link
|
|
755
|
+
* `optional`/`nullish` flags are derived from it through {@link withRequired}.
|
|
827
756
|
*/
|
|
828
757
|
const parameterDef = defineNode({
|
|
829
758
|
kind: "Parameter",
|
|
830
|
-
build:
|
|
831
|
-
const required = props.required ?? false;
|
|
832
|
-
return {
|
|
833
|
-
...props,
|
|
834
|
-
required,
|
|
835
|
-
schema: optionality(props.schema, required)
|
|
836
|
-
};
|
|
837
|
-
},
|
|
759
|
+
build: withRequired,
|
|
838
760
|
children: ["schema"],
|
|
839
761
|
visitorKey: "parameter"
|
|
840
762
|
});
|
|
@@ -856,18 +778,11 @@ const createParameter = parameterDef.create;
|
|
|
856
778
|
//#region src/nodes/property.ts
|
|
857
779
|
/**
|
|
858
780
|
* Definition for the {@link PropertyNode}. `required` defaults to `false`, and the schema's
|
|
859
|
-
* `optional`/`nullish` flags are derived from it through {@link
|
|
781
|
+
* `optional`/`nullish` flags are derived from it through {@link withRequired}.
|
|
860
782
|
*/
|
|
861
783
|
const propertyDef = defineNode({
|
|
862
784
|
kind: "Property",
|
|
863
|
-
build:
|
|
864
|
-
const required = props.required ?? false;
|
|
865
|
-
return {
|
|
866
|
-
...props,
|
|
867
|
-
required,
|
|
868
|
-
schema: optionality(props.schema, required)
|
|
869
|
-
};
|
|
870
|
-
},
|
|
785
|
+
build: withRequired,
|
|
871
786
|
children: ["schema"],
|
|
872
787
|
visitorKey: "property"
|
|
873
788
|
});
|
|
@@ -968,7 +883,9 @@ const schemaDef = defineNode({
|
|
|
968
883
|
"properties",
|
|
969
884
|
"items",
|
|
970
885
|
"members",
|
|
971
|
-
"additionalProperties"
|
|
886
|
+
"additionalProperties",
|
|
887
|
+
"patternProperties",
|
|
888
|
+
"rest"
|
|
972
889
|
],
|
|
973
890
|
visitorKey: "schema"
|
|
974
891
|
});
|
|
@@ -1023,9 +940,21 @@ function isNode(value) {
|
|
|
1023
940
|
return typeof value === "object" && value !== null && typeof value.kind === "string";
|
|
1024
941
|
}
|
|
1025
942
|
/**
|
|
1026
|
-
* Returns
|
|
1027
|
-
|
|
1028
|
-
|
|
943
|
+
* Returns `true` for a plain object that maps names to nodes, such as `patternProperties`.
|
|
944
|
+
*/
|
|
945
|
+
function isNodeRecord(value) {
|
|
946
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Returns the child fields of `node` to walk, from {@link VISITOR_KEYS}, or `undefined` when there
|
|
950
|
+
* is nothing to walk. `Schema` children are only walked when `recurse` is `true`.
|
|
951
|
+
*/
|
|
952
|
+
function childKeys(node, recurse) {
|
|
953
|
+
if (node.kind === "Schema" && !recurse) return void 0;
|
|
954
|
+
return visitorKeysByKind[node.kind];
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Returns the immediate traversable children of `node`.
|
|
1029
958
|
*
|
|
1030
959
|
* @example
|
|
1031
960
|
* ```ts
|
|
@@ -1034,8 +963,7 @@ function isNode(value) {
|
|
|
1034
963
|
* ```
|
|
1035
964
|
*/
|
|
1036
965
|
function* getChildren(node, recurse) {
|
|
1037
|
-
|
|
1038
|
-
const keys = visitorKeysByKind[node.kind];
|
|
966
|
+
const keys = childKeys(node, recurse);
|
|
1039
967
|
if (!keys) return;
|
|
1040
968
|
const record = node;
|
|
1041
969
|
for (const key of keys) {
|
|
@@ -1044,7 +972,13 @@ function* getChildren(node, recurse) {
|
|
|
1044
972
|
for (const item of value) if (isNode(item)) yield item;
|
|
1045
973
|
continue;
|
|
1046
974
|
}
|
|
1047
|
-
if (isNode(value))
|
|
975
|
+
if (isNode(value)) {
|
|
976
|
+
yield value;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (isNodeRecord(value)) {
|
|
980
|
+
for (const item of Object.values(value)) if (isNode(item)) yield item;
|
|
981
|
+
}
|
|
1048
982
|
}
|
|
1049
983
|
}
|
|
1050
984
|
/**
|
|
@@ -1062,6 +996,30 @@ function applyVisitor(node, visitor, parent) {
|
|
|
1062
996
|
const fn = visitor[key];
|
|
1063
997
|
return fn?.(node, { parent });
|
|
1064
998
|
}
|
|
999
|
+
/**
|
|
1000
|
+
* Synchronous depth-first transform. Each visitor callback can return a
|
|
1001
|
+
* replacement node. Returning `undefined` keeps the original.
|
|
1002
|
+
*
|
|
1003
|
+
* The original tree is never mutated, a new tree is returned. Pass
|
|
1004
|
+
* `depth: 'shallow'` to skip recursion into children.
|
|
1005
|
+
*
|
|
1006
|
+
* @example Prefix every operationId
|
|
1007
|
+
* ```ts
|
|
1008
|
+
* const next = transform(root, {
|
|
1009
|
+
* operation(node) {
|
|
1010
|
+
* return { ...node, operationId: `prefixed_${node.operationId}` }
|
|
1011
|
+
* },
|
|
1012
|
+
* })
|
|
1013
|
+
* ```
|
|
1014
|
+
*
|
|
1015
|
+
* @example Replace only the root node
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* const next = transform(root, {
|
|
1018
|
+
* depth: 'shallow',
|
|
1019
|
+
* input: (node) => ({ ...node, meta: { ...node.meta, title: 'Rewritten' } }),
|
|
1020
|
+
* })
|
|
1021
|
+
* ```
|
|
1022
|
+
*/
|
|
1065
1023
|
function transform(node, options) {
|
|
1066
1024
|
const { depth, parent, ...visitor } = options;
|
|
1067
1025
|
return transformNode(node, visitor, (depth ?? visitorDepths.deep) === visitorDepths.deep, parent);
|
|
@@ -1075,13 +1033,11 @@ function transformNode(node, visitor, recurse, parent) {
|
|
|
1075
1033
|
return transformChildren(applyVisitor(node, visitor, parent) ?? node, visitor, recurse);
|
|
1076
1034
|
}
|
|
1077
1035
|
/**
|
|
1078
|
-
* Immutably rebuilds a node's children
|
|
1079
|
-
*
|
|
1080
|
-
* `Schema` children are skipped in shallow mode.
|
|
1036
|
+
* Immutably rebuilds a node's children, transforming each child node and leaving non-node values
|
|
1037
|
+
* (e.g. `additionalProperties: true`) intact.
|
|
1081
1038
|
*/
|
|
1082
1039
|
function transformChildren(node, visitor, recurse) {
|
|
1083
|
-
|
|
1084
|
-
const keys = visitorKeysByKind[node.kind];
|
|
1040
|
+
const keys = childKeys(node, recurse);
|
|
1085
1041
|
if (!keys) return node;
|
|
1086
1042
|
const record = node;
|
|
1087
1043
|
let updates;
|
|
@@ -1104,6 +1060,15 @@ function transformChildren(node, visitor, recurse) {
|
|
|
1104
1060
|
if (isNode(value)) {
|
|
1105
1061
|
const next = transformNode(value, visitor, recurse, node);
|
|
1106
1062
|
if (next !== value) (updates ??= {})[key] = next;
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
if (isNodeRecord(value)) {
|
|
1066
|
+
let mapped;
|
|
1067
|
+
for (const [name, item] of Object.entries(value)) {
|
|
1068
|
+
const next = isNode(item) ? transformNode(item, visitor, recurse, node) : item;
|
|
1069
|
+
if (next !== item) (mapped ??= { ...value })[name] = next;
|
|
1070
|
+
}
|
|
1071
|
+
if (mapped) (updates ??= {})[key] = mapped;
|
|
1107
1072
|
}
|
|
1108
1073
|
}
|
|
1109
1074
|
if (!updates) return node;
|
|
@@ -1297,21 +1262,18 @@ function createPrinter(build) {
|
|
|
1297
1262
|
...overrides
|
|
1298
1263
|
} : nodes;
|
|
1299
1264
|
const collectedImports = [];
|
|
1265
|
+
const dispatch = (table) => (node) => {
|
|
1266
|
+
const handler = table[node.type];
|
|
1267
|
+
if (!handler) return null;
|
|
1268
|
+
return handler.call(context, node);
|
|
1269
|
+
};
|
|
1300
1270
|
const context = {
|
|
1301
1271
|
options: resolvedOptions,
|
|
1302
1272
|
import: (node) => {
|
|
1303
1273
|
collectedImports.push(node);
|
|
1304
1274
|
},
|
|
1305
|
-
transform: (
|
|
1306
|
-
|
|
1307
|
-
if (!handler) return null;
|
|
1308
|
-
return handler.call(context, node);
|
|
1309
|
-
},
|
|
1310
|
-
base: (node) => {
|
|
1311
|
-
const handler = nodes[node.type];
|
|
1312
|
-
if (!handler) return null;
|
|
1313
|
-
return handler.call(context, node);
|
|
1314
|
-
}
|
|
1275
|
+
transform: dispatch(merged),
|
|
1276
|
+
base: dispatch(nodes)
|
|
1315
1277
|
};
|
|
1316
1278
|
return {
|
|
1317
1279
|
name,
|
|
@@ -1344,6 +1306,29 @@ function resolveRefName(node) {
|
|
|
1344
1306
|
return node.name ?? node.schema?.name ?? null;
|
|
1345
1307
|
}
|
|
1346
1308
|
/**
|
|
1309
|
+
* Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
|
|
1310
|
+
*
|
|
1311
|
+
* Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
|
|
1312
|
+
* of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
|
|
1313
|
+
* left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
|
|
1314
|
+
* is returned as is.
|
|
1315
|
+
*
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```ts
|
|
1318
|
+
* const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
|
|
1319
|
+
* mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
|
|
1320
|
+
* ```
|
|
1321
|
+
*/
|
|
1322
|
+
function mergeRefWithSchema(node) {
|
|
1323
|
+
if (!node.schema) return node;
|
|
1324
|
+
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = node;
|
|
1325
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0));
|
|
1326
|
+
return {
|
|
1327
|
+
...node.schema,
|
|
1328
|
+
...definedOverrides
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1347
1332
|
* Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
|
|
1348
1333
|
*
|
|
1349
1334
|
* Modifiers inherited from the resolved schema are included in the check because a ref emits the
|
|
@@ -1351,21 +1336,62 @@ function resolveRefName(node) {
|
|
|
1351
1336
|
*/
|
|
1352
1337
|
function isBareRef(node, keysToOmit) {
|
|
1353
1338
|
if (!node || node.type !== "ref") return false;
|
|
1354
|
-
const meta = (
|
|
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
|
-
})();
|
|
1339
|
+
const meta = mergeRefWithSchema(node);
|
|
1363
1340
|
return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
|
|
1364
1341
|
}
|
|
1365
1342
|
//#endregion
|
|
1343
|
+
//#region ../../internals/utils/src/promise.ts
|
|
1344
|
+
/**
|
|
1345
|
+
* Wraps `factory` with a keyed cache backed by the provided store.
|
|
1346
|
+
*
|
|
1347
|
+
* Pass a `WeakMap` for object keys (results are GC-eligible when the key is
|
|
1348
|
+
* collected) or a `Map` for primitive keys. For multi-argument functions,
|
|
1349
|
+
* nest two `memoize` calls — the outer keyed by the first argument, the
|
|
1350
|
+
* inner (created once per outer miss) keyed by the second.
|
|
1351
|
+
*
|
|
1352
|
+
* Because the cache is owned by the caller, it can be shared, inspected, or
|
|
1353
|
+
* cleared independently of the memoized function.
|
|
1354
|
+
*
|
|
1355
|
+
* @example Single WeakMap key
|
|
1356
|
+
* ```ts
|
|
1357
|
+
* const cache = new WeakMap<SchemaNode, Set<string>>()
|
|
1358
|
+
* const getRefs = memoize(cache, (node) => collectRefs(node))
|
|
1359
|
+
* ```
|
|
1360
|
+
*
|
|
1361
|
+
* @example Single Map key (primitive)
|
|
1362
|
+
* ```ts
|
|
1363
|
+
* const cache = new Map<string, Resolver>()
|
|
1364
|
+
* const getResolver = memoize(cache, (name) => buildResolver(name))
|
|
1365
|
+
* ```
|
|
1366
|
+
*
|
|
1367
|
+
* @example Two-level (object + primitive)
|
|
1368
|
+
* ```ts
|
|
1369
|
+
* const outer = new WeakMap<Params[], Map<string, Params[]>>()
|
|
1370
|
+
* const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
|
|
1371
|
+
* fn(params)('camelcase')
|
|
1372
|
+
* ```
|
|
1373
|
+
*/
|
|
1374
|
+
function memoize(store, factory) {
|
|
1375
|
+
return (key) => {
|
|
1376
|
+
if (store.has(key)) return store.get(key);
|
|
1377
|
+
const value = factory(key);
|
|
1378
|
+
store.set(key, value);
|
|
1379
|
+
return value;
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
//#endregion
|
|
1366
1383
|
//#region src/utils/schemaGraph.ts
|
|
1367
1384
|
/**
|
|
1368
|
-
*
|
|
1385
|
+
* Collects the names of every schema a node's subtree references, resolved with `resolveRefName`.
|
|
1386
|
+
* Each ref contributes its name only, so the schema it points to is never traversed. The walk is
|
|
1387
|
+
* memoized by node identity, which lets `findCircularSchemas` and `containsCircularRef` share one
|
|
1388
|
+
* scan per schema.
|
|
1389
|
+
*
|
|
1390
|
+
* @example
|
|
1391
|
+
* ```ts
|
|
1392
|
+
* collectSchemaRefs(petSchema)
|
|
1393
|
+
* // Set { 'Category', 'Tag' }
|
|
1394
|
+
* ```
|
|
1369
1395
|
*/
|
|
1370
1396
|
const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
1371
1397
|
const refs = /* @__PURE__ */ new Set();
|
|
@@ -1378,31 +1404,6 @@ const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
|
1378
1404
|
return refs;
|
|
1379
1405
|
});
|
|
1380
1406
|
/**
|
|
1381
|
-
* Collects the names of every ref found anywhere inside a node's own subtree.
|
|
1382
|
-
*
|
|
1383
|
-
* Each ref contributes its name only, so the schema it points to is never traversed here. Pass `out`
|
|
1384
|
-
* to accumulate names from several nodes into one set.
|
|
1385
|
-
*
|
|
1386
|
-
* @example Collect refs from a single schema
|
|
1387
|
-
* ```ts
|
|
1388
|
-
* const names = collectReferencedSchemaNames(petSchema)
|
|
1389
|
-
* // Set { 'Category', 'Tag' }
|
|
1390
|
-
* ```
|
|
1391
|
-
*
|
|
1392
|
-
* @example Accumulate refs from multiple schemas into one set
|
|
1393
|
-
* ```ts
|
|
1394
|
-
* const out = new Set<string>()
|
|
1395
|
-
* for (const schema of schemas) {
|
|
1396
|
-
* collectReferencedSchemaNames(schema, out)
|
|
1397
|
-
* }
|
|
1398
|
-
* ```
|
|
1399
|
-
*/
|
|
1400
|
-
function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1401
|
-
if (!node) return out;
|
|
1402
|
-
for (const name of collectSchemaRefs(node)) out.add(name);
|
|
1403
|
-
return out;
|
|
1404
|
-
}
|
|
1405
|
-
/**
|
|
1406
1407
|
* Collects the de-duplicated target names of every pointer-carrying ref in a node's subtree, in
|
|
1407
1408
|
* first-occurrence order. The walk is memoized by node identity, so the subtree is scanned once and
|
|
1408
1409
|
* `resolver.imports` reads the same result across the ts, zod, and faker plugins instead of
|
|
@@ -1410,8 +1411,7 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1410
1411
|
*
|
|
1411
1412
|
* Only refs that carry a `$ref` pointer count, so a synthesized ref pointing at a sibling in the
|
|
1412
1413
|
* same file (a union member created by name) is left out. That leaves exactly the set
|
|
1413
|
-
* `resolver.imports` emits.
|
|
1414
|
-
* {@link collectReferencedSchemaNames}, which returns an unordered set for graph analysis.
|
|
1414
|
+
* `resolver.imports` emits.
|
|
1415
1415
|
*
|
|
1416
1416
|
* @example
|
|
1417
1417
|
* ```ts
|
|
@@ -1432,24 +1432,6 @@ const collectImportedRefNames = memoize(/* @__PURE__ */ new WeakMap(), (node) =>
|
|
|
1432
1432
|
} });
|
|
1433
1433
|
return names;
|
|
1434
1434
|
});
|
|
1435
|
-
function computeUsedSchemaNames(operations, schemas) {
|
|
1436
|
-
const schemaMap = /* @__PURE__ */ new Map();
|
|
1437
|
-
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1438
|
-
const result = /* @__PURE__ */ new Set();
|
|
1439
|
-
function visitSchema(schema) {
|
|
1440
|
-
const directRefs = collectReferencedSchemaNames(schema);
|
|
1441
|
-
for (const name of directRefs) if (!result.has(name)) {
|
|
1442
|
-
result.add(name);
|
|
1443
|
-
const namedSchema = schemaMap.get(name);
|
|
1444
|
-
if (namedSchema) visitSchema(namedSchema);
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
for (const op of operations) for (const schema of collect(op, {
|
|
1448
|
-
depth: "shallow",
|
|
1449
|
-
schema: (node) => node
|
|
1450
|
-
})) visitSchema(schema);
|
|
1451
|
-
return result;
|
|
1452
|
-
}
|
|
1453
1435
|
/**
|
|
1454
1436
|
* Collects the names of all top-level schemas transitively used by a set of operations.
|
|
1455
1437
|
*
|
|
@@ -1471,9 +1453,22 @@ function computeUsedSchemaNames(operations, schemas) {
|
|
|
1471
1453
|
* ```
|
|
1472
1454
|
*/
|
|
1473
1455
|
function collectUsedSchemaNames(operations, schemas) {
|
|
1474
|
-
|
|
1456
|
+
const schemaMap = /* @__PURE__ */ new Map();
|
|
1457
|
+
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1458
|
+
const result = /* @__PURE__ */ new Set();
|
|
1459
|
+
function visitSchema(schema) {
|
|
1460
|
+
for (const name of collectSchemaRefs(schema)) if (!result.has(name)) {
|
|
1461
|
+
result.add(name);
|
|
1462
|
+
const namedSchema = schemaMap.get(name);
|
|
1463
|
+
if (namedSchema) visitSchema(namedSchema);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
for (const op of operations) for (const schema of collect(op, {
|
|
1467
|
+
depth: "shallow",
|
|
1468
|
+
schema: (node) => node
|
|
1469
|
+
})) visitSchema(schema);
|
|
1470
|
+
return result;
|
|
1475
1471
|
}
|
|
1476
|
-
const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
|
|
1477
1472
|
/**
|
|
1478
1473
|
* Finds every schema that takes part in a circular dependency chain in a schema dependency graph
|
|
1479
1474
|
* that maps each schema name to the names it references directly.
|
|
@@ -1510,14 +1505,6 @@ function findCircularSchemasFromGraph(graph) {
|
|
|
1510
1505
|
}
|
|
1511
1506
|
return circular;
|
|
1512
1507
|
}
|
|
1513
|
-
const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
|
|
1514
|
-
const graph = /* @__PURE__ */ new Map();
|
|
1515
|
-
for (const schema of schemas) {
|
|
1516
|
-
if (!schema.name) continue;
|
|
1517
|
-
graph.set(schema.name, collectReferencedSchemaNames(schema));
|
|
1518
|
-
}
|
|
1519
|
-
return findCircularSchemasFromGraph(graph);
|
|
1520
|
-
});
|
|
1521
1508
|
/**
|
|
1522
1509
|
* Finds every schema that takes part in a circular dependency chain, including direct self-loops.
|
|
1523
1510
|
*
|
|
@@ -1528,8 +1515,9 @@ const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas)
|
|
|
1528
1515
|
* @note Call this once on the full graph, then check individual schemas with `containsCircularRef()`.
|
|
1529
1516
|
*/
|
|
1530
1517
|
function findCircularSchemas(schemas) {
|
|
1531
|
-
|
|
1532
|
-
|
|
1518
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1519
|
+
for (const schema of schemas) if (schema.name) graph.set(schema.name, collectSchemaRefs(schema));
|
|
1520
|
+
return findCircularSchemasFromGraph(graph);
|
|
1533
1521
|
}
|
|
1534
1522
|
//#endregion
|
|
1535
1523
|
//#region src/utils/schemaProperties.ts
|
|
@@ -1704,6 +1692,7 @@ var exports_exports = /* @__PURE__ */ __exportAll({
|
|
|
1704
1692
|
breakDef: () => breakDef,
|
|
1705
1693
|
collect: () => collect,
|
|
1706
1694
|
collectImportedRefNames: () => collectImportedRefNames,
|
|
1695
|
+
collectSchemaRefs: () => collectSchemaRefs,
|
|
1707
1696
|
collectSync: () => collectSync,
|
|
1708
1697
|
collectUsedSchemaNames: () => collectUsedSchemaNames,
|
|
1709
1698
|
composeMacros: () => composeMacros,
|
|
@@ -1726,6 +1715,7 @@ var exports_exports = /* @__PURE__ */ __exportAll({
|
|
|
1726
1715
|
isBareRef: () => isBareRef,
|
|
1727
1716
|
isHttpOperationNode: () => isHttpOperationNode,
|
|
1728
1717
|
jsxDef: () => jsxDef,
|
|
1718
|
+
mergeRefWithSchema: () => mergeRefWithSchema,
|
|
1729
1719
|
narrowSchema: () => narrowSchema,
|
|
1730
1720
|
nodeDefs: () => nodeDefs,
|
|
1731
1721
|
operationDef: () => operationDef,
|
|
@@ -1745,6 +1735,6 @@ var exports_exports = /* @__PURE__ */ __exportAll({
|
|
|
1745
1735
|
typeDef: () => typeDef
|
|
1746
1736
|
});
|
|
1747
1737
|
//#endregion
|
|
1748
|
-
export { applyMacros, arrowFunctionDef, exports_exports as ast, breakDef, collect, collectImportedRefNames, collectSync, collectUsedSchemaNames, composeMacros, constDef, contentDef, createPrinter, defineMacro, defineNode, exportDef, extractStringsFromNodes, factory_exports as factory, fileDef, findCircularSchemas, findCircularSchemasFromGraph, functionDef, getSchemaLiteralValues, importDef, inferDiscriminatorPropertyName, inputDef, isBareRef, isHttpOperationNode, jsxDef, narrowSchema, nodeDefs, operationDef, optionality, outputDef, parameterDef, propertyDef, requestBodyDef, resolveRefName, resolveSchemaProperties, responseDef, schemaDef, schemaTypes, sourceDef, textDef, transform, typeDef };
|
|
1738
|
+
export { applyMacros, arrowFunctionDef, exports_exports as ast, breakDef, collect, collectImportedRefNames, collectSchemaRefs, collectSync, collectUsedSchemaNames, composeMacros, constDef, contentDef, createPrinter, defineMacro, defineNode, exportDef, extractStringsFromNodes, factory_exports as factory, fileDef, findCircularSchemas, findCircularSchemasFromGraph, functionDef, getSchemaLiteralValues, importDef, inferDiscriminatorPropertyName, inputDef, isBareRef, isHttpOperationNode, jsxDef, mergeRefWithSchema, narrowSchema, nodeDefs, operationDef, optionality, outputDef, parameterDef, propertyDef, requestBodyDef, resolveRefName, resolveSchemaProperties, responseDef, schemaDef, schemaTypes, sourceDef, textDef, transform, typeDef };
|
|
1749
1739
|
|
|
1750
1740
|
//# sourceMappingURL=index.js.map
|