@kubb/ast 5.12.8 → 5.13.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 CHANGED
@@ -195,6 +195,7 @@ const VISITOR_KEYS = {
195
195
  "members",
196
196
  "additionalProperties",
197
197
  "patternProperties",
198
+ "propertyNames",
198
199
  "rest"
199
200
  ],
200
201
  Property: ["schema"],
@@ -241,24 +242,102 @@ function isNodeRecord(value) {
241
242
  * // returns parameters, the request body, and responses
242
243
  * ```
243
244
  */
244
- function* getChildren(node, recurse) {
245
- const keys = node.kind === "Schema" && !recurse ? void 0 : VISITOR_KEYS[node.kind];
246
- if (!keys) return;
245
+ function getChildren(node, recurse) {
246
+ if (node.kind === "Schema" && !recurse) return [];
247
+ const children = [];
248
+ visitChildNodes(node, (child) => {
249
+ children.push(child);
250
+ return false;
251
+ });
252
+ return children;
253
+ }
254
+ /**
255
+ * Visits each direct child node in the AST's traversal order.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * forEachChild(operationNode, (child) => {
260
+ * console.log(child.kind)
261
+ * })
262
+ * ```
263
+ */
264
+ function forEachChild(node, visit) {
265
+ visitChildNodes(node, (child) => {
266
+ visit(child);
267
+ return false;
268
+ });
269
+ }
270
+ function visitChildNodes(node, visit) {
271
+ const keys = VISITOR_KEYS[node.kind];
272
+ if (!keys) return false;
247
273
  const record = node;
248
274
  for (const key of keys) {
249
275
  const value = record[key];
250
276
  if (Array.isArray(value)) {
251
- for (const item of value) if (isNode(item)) yield item;
277
+ for (const item of value) if (isNode(item) && visit(item)) return true;
252
278
  continue;
253
279
  }
254
280
  if (isNode(value)) {
255
- yield value;
281
+ if (visit(value)) return true;
256
282
  continue;
257
283
  }
258
- if (isNodeRecord(value)) {
259
- for (const item of Object.values(value)) if (isNode(item)) yield item;
284
+ if (isNodeRecord(value)) for (const name in value) {
285
+ if (!Object.hasOwn(value, name)) continue;
286
+ const item = value[name];
287
+ if (isNode(item) && visit(item)) return true;
260
288
  }
261
289
  }
290
+ return false;
291
+ }
292
+ /**
293
+ * Visits each schema in a node's child tree in depth-first order. Includes `node` when it is a schema.
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * forEachSchema(operationNode, (schema) => {
298
+ * console.log(schema.type)
299
+ * })
300
+ * ```
301
+ */
302
+ function forEachSchema(node, visit) {
303
+ const walk = (current) => {
304
+ if (current.kind === "Schema") visit(current);
305
+ forEachChild(current, walk);
306
+ };
307
+ walk(node);
308
+ }
309
+ /**
310
+ * Finds the first schema that matches `predicate` in depth-first order. Set `followRefs` to include resolved ref targets.
311
+ *
312
+ * @example
313
+ * ```ts
314
+ * const firstString = findSchema(operationNode, {
315
+ * predicate: (schema) => schema.type === 'string',
316
+ * })
317
+ * ```
318
+ */
319
+ function findSchema(node, { predicate, followRefs = false }) {
320
+ const seen = followRefs ? /* @__PURE__ */ new WeakSet() : void 0;
321
+ const walk = (current) => {
322
+ if (current.kind === "Schema") {
323
+ if (predicate(current)) return current;
324
+ if (seen) {
325
+ if (seen.has(current)) return void 0;
326
+ seen.add(current);
327
+ if (current.type === "ref" && current.schema) {
328
+ const found = walk(current.schema);
329
+ if (found) return found;
330
+ }
331
+ }
332
+ }
333
+ let found;
334
+ visitChildNodes(current, (child) => {
335
+ found = walk(child);
336
+ return found !== void 0;
337
+ });
338
+ if (found) return found;
339
+ };
340
+ return walk(node);
262
341
  }
263
342
  /**
264
343
  * Runs the visitor callback that matches `node.kind` with the traversal
@@ -395,7 +474,15 @@ function* collectNode(node, visitor, recurse, parent) {
395
474
  * ```
396
475
  */
397
476
  function collectSync(node, options) {
398
- return Array.from(collect(node, options));
477
+ const { depth, parent, ...visitor } = options;
478
+ const result = [];
479
+ collectInto(node, visitor, (depth ?? "deep") === "deep", parent, result);
480
+ return result;
481
+ }
482
+ function collectInto(node, visitor, recurse, parent, result) {
483
+ const v = applyVisitor(node, visitor, parent);
484
+ if (v != null) result.push(v);
485
+ for (const child of getChildren(node, recurse)) collectInto(child, visitor, recurse, node, result);
399
486
  }
400
487
  //#endregion
401
488
  //#region src/defineMacro.ts
@@ -635,6 +722,33 @@ function resolveRefName(node) {
635
722
  return node.name ?? node.schema?.name ?? null;
636
723
  }
637
724
  /**
725
+ * Follows a resolved ref chain to its target schema. Returns an unresolved or cyclic ref as is.
726
+ *
727
+ * @example
728
+ * ```ts
729
+ * const target = resolveRef(refSchema)
730
+ * ```
731
+ */
732
+ function resolveRef(node) {
733
+ if (node.type !== "ref" || !node.schema) return node;
734
+ const seen = /* @__PURE__ */ new WeakSet();
735
+ let current = node;
736
+ while (current.type === "ref" && current.schema) {
737
+ if (seen.has(current)) return current;
738
+ seen.add(current);
739
+ current = current.schema;
740
+ }
741
+ return current;
742
+ }
743
+ /** Fields of a ref node that describe the ref itself, so they never override the resolved schema. */
744
+ const REF_OWN_KEYS = /* @__PURE__ */ new Set([
745
+ "kind",
746
+ "type",
747
+ "name",
748
+ "ref",
749
+ "schema"
750
+ ]);
751
+ /**
638
752
  * Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
639
753
  *
640
754
  * Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
@@ -650,12 +764,12 @@ function resolveRefName(node) {
650
764
  */
651
765
  function mergeRefWithSchema(node) {
652
766
  if (!node.schema) return node;
653
- const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = node;
654
- const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0));
655
- return {
656
- ...node.schema,
657
- ...definedOverrides
658
- };
767
+ const merged = { ...node.schema };
768
+ for (const key of Object.keys(node)) {
769
+ const value = node[key];
770
+ if (value !== void 0 && !REF_OWN_KEYS.has(key)) merged[key] = value;
771
+ }
772
+ return merged;
659
773
  }
660
774
  /**
661
775
  * Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
@@ -1109,16 +1223,23 @@ function mergeByPath(result) {
1109
1223
  return ({ node, names }) => {
1110
1224
  const key = `${node.path}:${node.isTypeOnly ?? false}`;
1111
1225
  const existing = byPath.get(key);
1112
- if (existing && Array.isArray(existing.name)) {
1113
- existing.name = [.../* @__PURE__ */ new Set([...existing.name, ...names])];
1226
+ if (existing) {
1227
+ for (const name of names) {
1228
+ if (existing.seen.has(name)) continue;
1229
+ existing.seen.add(name);
1230
+ existing.names.push(name);
1231
+ }
1114
1232
  return;
1115
1233
  }
1116
1234
  const item = {
1117
1235
  ...node,
1118
- name: names
1236
+ name: [...names]
1119
1237
  };
1120
1238
  result.push(item);
1121
- byPath.set(key, item);
1239
+ byPath.set(key, {
1240
+ names: item.name,
1241
+ seen: new Set(names)
1242
+ });
1122
1243
  };
1123
1244
  }
1124
1245
  /**
@@ -1513,6 +1634,9 @@ var src_exports = /* @__PURE__ */ __exportAll({
1513
1634
  factory: () => nodes_exports,
1514
1635
  findCircularSchemas: () => findCircularSchemas,
1515
1636
  findCircularSchemasFromGraph: () => findCircularSchemasFromGraph,
1637
+ findSchema: () => findSchema,
1638
+ forEachChild: () => forEachChild,
1639
+ forEachSchema: () => forEachSchema,
1516
1640
  getSchemaLiteralValues: () => getSchemaLiteralValues,
1517
1641
  inferDiscriminatorPropertyName: () => inferDiscriminatorPropertyName,
1518
1642
  isBareRef: () => isBareRef,
@@ -1521,6 +1645,7 @@ var src_exports = /* @__PURE__ */ __exportAll({
1521
1645
  narrowSchema: () => narrowSchema,
1522
1646
  optionality: () => optionality,
1523
1647
  refName: () => refName,
1648
+ resolveRef: () => resolveRef,
1524
1649
  resolveRefName: () => resolveRefName,
1525
1650
  resolveSchemaProperties: () => resolveSchemaProperties,
1526
1651
  schemaTypes: () => schemaTypes,
@@ -1551,6 +1676,9 @@ Object.defineProperty(exports, "factory", {
1551
1676
  });
1552
1677
  exports.findCircularSchemas = findCircularSchemas;
1553
1678
  exports.findCircularSchemasFromGraph = findCircularSchemasFromGraph;
1679
+ exports.findSchema = findSchema;
1680
+ exports.forEachChild = forEachChild;
1681
+ exports.forEachSchema = forEachSchema;
1554
1682
  exports.getSchemaLiteralValues = getSchemaLiteralValues;
1555
1683
  exports.inferDiscriminatorPropertyName = inferDiscriminatorPropertyName;
1556
1684
  exports.isBareRef = isBareRef;
@@ -1559,6 +1687,7 @@ exports.mergeRefWithSchema = mergeRefWithSchema;
1559
1687
  exports.narrowSchema = narrowSchema;
1560
1688
  exports.optionality = optionality;
1561
1689
  exports.refName = refName;
1690
+ exports.resolveRef = resolveRef;
1562
1691
  exports.resolveRefName = resolveRefName;
1563
1692
  exports.resolveSchemaProperties = resolveSchemaProperties;
1564
1693
  exports.schemaTypes = schemaTypes;