@kubb/ast 5.0.0-beta.84 → 5.0.0-beta.85

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
@@ -641,7 +641,7 @@ function extractStringsFromNodes(nodes) {
641
641
  return collected.join("\n");
642
642
  }
643
643
  //#endregion
644
- //#region src/utils/fileMerge.ts
644
+ //#region src/utils/combineFileMembers.ts
645
645
  function sourceKey(source) {
646
646
  return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
647
647
  }
@@ -873,16 +873,23 @@ const createSource = sourceDef.create;
873
873
  * ```
874
874
  */
875
875
  function createFile(input) {
876
- const extname = node_path.default.extname(input.baseName) || (input.baseName.startsWith(".") ? input.baseName : "");
876
+ const extname = node_path.default.extname(input.baseName);
877
877
  if (!extname) throw new Error(`No extname found for ${input.baseName}`);
878
878
  const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
879
- let resolvedImports = [];
880
- if (input.imports?.length) {
881
- const source = extractStringsFromNodes((input.sources ?? []).flatMap((item) => item.nodes ?? [])) || void 0;
879
+ const resolvedImports = (() => {
880
+ if (!input.imports?.length) return [];
881
+ const sourceParts = [];
882
+ const localNames = /* @__PURE__ */ new Set();
883
+ for (const item of input.sources ?? []) {
884
+ const extracted = item.nodes && extractStringsFromNodes(item.nodes);
885
+ if (extracted) sourceParts.push(extracted);
886
+ if (item.name) localNames.add(item.name);
887
+ }
888
+ const source = sourceParts.join("\n") || void 0;
882
889
  const combinedImports = combineImports(input.imports, resolvedExports, source);
883
- const localNames = new Set((input.sources ?? []).map((item) => item.name).filter((name) => Boolean(name)));
884
890
  const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
885
- resolvedImports = combinedImports.filter((imp) => imp.path !== input.path).flatMap((imp) => {
891
+ return combinedImports.flatMap((imp) => {
892
+ if (imp.path === input.path) return [];
886
893
  if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
887
894
  const kept = imp.name.filter((item) => !localNames.has(nameOf(item)));
888
895
  if (!kept.length) return [];
@@ -891,7 +898,7 @@ function createFile(input) {
891
898
  name: kept
892
899
  }];
893
900
  });
894
- }
901
+ })();
895
902
  const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
896
903
  return {
897
904
  kind: "File",
@@ -924,31 +931,16 @@ const inputDef = defineNode({
924
931
  visitorKey: "input"
925
932
  });
926
933
  /**
927
- * Creates an `InputNode`. Pass `stream: true` for the streaming variant whose `schemas` and
928
- * `operations` are `AsyncIterable` sources. Otherwise it builds the eager variant with array
929
- * `schemas`/`operations`. Both variants get the defaulted `meta`.
934
+ * Creates an `InputNode`, defaulting `schemas`/`operations` to empty arrays and `meta` per
935
+ * {@link inputDef}.
930
936
  *
931
- * @example Eager
937
+ * @example
932
938
  * ```ts
933
939
  * const input = createInput()
934
940
  * // { kind: 'Input', schemas: [], operations: [] }
935
941
  * ```
936
- *
937
- * @example Streaming
938
- * ```ts
939
- * const node = createInput({ stream: true, schemas: schemasIterable, operations: operationsIterable, meta: { title: 'My API' } })
940
- * ```
941
942
  */
942
- function createInput(options = {}) {
943
- const { stream, ...overrides } = options;
944
- if (stream) return {
945
- kind: "Input",
946
- meta: {
947
- circularNames: [],
948
- enumNames: []
949
- },
950
- ...overrides
951
- };
943
+ function createInput(overrides = {}) {
952
944
  return inputDef.create(overrides);
953
945
  }
954
946
  //#endregion
@@ -1661,6 +1653,51 @@ function buildList(items, brackets = ["[", "]"]) {
1661
1653
  if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
1662
1654
  return `${open}\n${items.map((item) => `${indentLines(item)},`).join("\n")}\n${close}`;
1663
1655
  }
1656
+ /**
1657
+ * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
1658
+ * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
1659
+ * of a recursive schema until first access.
1660
+ *
1661
+ * @example
1662
+ * ```ts
1663
+ * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
1664
+ * // "get parent() { return z.lazy(() => Pet) }"
1665
+ * ```
1666
+ */
1667
+ function lazyGetter({ name, body }) {
1668
+ return `get ${objectKey(name)}() { return ${body} }`;
1669
+ }
1670
+ //#endregion
1671
+ //#region src/utils/mergeAdjacentSchemas.ts
1672
+ /**
1673
+ * Merges a run of adjacent anonymous object members into one. Named or non-object members break the
1674
+ * run and pass through unchanged. The merge follows member order, so callers control which members
1675
+ * combine by where they place them in the sequence.
1676
+ *
1677
+ * @example
1678
+ * ```ts
1679
+ * const merged = [...mergeAdjacentObjectsLazy([objectA, objectB])]
1680
+ * ```
1681
+ */
1682
+ function* mergeAdjacentObjectsLazy(members) {
1683
+ let acc;
1684
+ for (const member of members) {
1685
+ const objectMember = narrowSchema(member, "object");
1686
+ if (objectMember && !objectMember.name && acc !== void 0) {
1687
+ const accObject = narrowSchema(acc, "object");
1688
+ if (accObject && !accObject.name) {
1689
+ acc = createSchema({
1690
+ ...accObject,
1691
+ properties: [...accObject.properties ?? [], ...objectMember.properties ?? []]
1692
+ });
1693
+ continue;
1694
+ }
1695
+ }
1696
+ if (acc !== void 0) yield acc;
1697
+ acc = member;
1698
+ }
1699
+ if (acc !== void 0) yield acc;
1700
+ }
1664
1701
  //#endregion
1665
1702
  //#region src/utils/refs.ts
1666
1703
  const plainStringTypes = /* @__PURE__ */ new Set([
@@ -1758,151 +1795,6 @@ function isStringType(node) {
1758
1795
  return false;
1759
1796
  }
1760
1797
  //#endregion
1761
- //#region src/utils/schemaMerge.ts
1762
- /**
1763
- * Merges a run of adjacent anonymous object members into one. Named or non-object members break the
1764
- * run and pass through unchanged. The merge follows member order, so callers control which members
1765
- * combine by where they place them in the sequence.
1766
- *
1767
- * @example
1768
- * ```ts
1769
- * const merged = [...mergeAdjacentObjectsLazy([objectA, objectB])]
1770
- * ```
1771
- */
1772
- function* mergeAdjacentObjectsLazy(members) {
1773
- let acc;
1774
- for (const member of members) {
1775
- const objectMember = narrowSchema(member, "object");
1776
- if (objectMember && !objectMember.name && acc !== void 0) {
1777
- const accObject = narrowSchema(acc, "object");
1778
- if (accObject && !accObject.name) {
1779
- acc = createSchema({
1780
- ...accObject,
1781
- properties: [...accObject.properties ?? [], ...objectMember.properties ?? []]
1782
- });
1783
- continue;
1784
- }
1785
- }
1786
- if (acc !== void 0) yield acc;
1787
- acc = member;
1788
- }
1789
- if (acc !== void 0) yield acc;
1790
- }
1791
- //#endregion
1792
- //#region src/utils/strings.ts
1793
- /**
1794
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
1795
- * Returns the string unchanged when no balanced quote pair is found.
1796
- *
1797
- * @example
1798
- * ```ts
1799
- * trimQuotes('"hello"') // 'hello'
1800
- * trimQuotes('hello') // 'hello'
1801
- * ```
1802
- */
1803
- function trimQuotes(text) {
1804
- if (text.length >= 2) {
1805
- const first = text[0];
1806
- const last = text[text.length - 1];
1807
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
1808
- }
1809
- return text;
1810
- }
1811
- /**
1812
- * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
1813
- *
1814
- * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
1815
- * code matches the repo style without a formatter.
1816
- *
1817
- * @example
1818
- * ```ts
1819
- * stringify('hello') // "'hello'"
1820
- * stringify('"hello"') // "'hello'"
1821
- * ```
1822
- */
1823
- function stringify(value) {
1824
- if (value === void 0 || value === null) return "''";
1825
- return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
1826
- }
1827
- /**
1828
- * Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
1829
- * and the Unicode line terminators U+2028 and U+2029.
1830
- *
1831
- * @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
1832
- *
1833
- * @example
1834
- * ```ts
1835
- * jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
1836
- * ```
1837
- */
1838
- function jsStringEscape(input) {
1839
- return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
1840
- switch (character) {
1841
- case "\"":
1842
- case "'":
1843
- case "\\": return `\\${character}`;
1844
- case "\n": return "\\n";
1845
- case "\r": return "\\r";
1846
- case "\u2028": return "\\u2028";
1847
- case "\u2029": return "\\u2029";
1848
- default: return "";
1849
- }
1850
- });
1851
- }
1852
- /**
1853
- * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
1854
- * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
1855
- * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
1856
- *
1857
- * @example
1858
- * ```ts
1859
- * toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
1860
- * toRegExpString('^(?im)foo', null) // '/^foo/im'
1861
- * ```
1862
- */
1863
- function toRegExpString(text, func = "RegExp") {
1864
- const raw = trimQuotes(text);
1865
- const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
1866
- const replacementTarget = match?.[1] ?? "";
1867
- const matchedFlags = match?.[2];
1868
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
1869
- const { source, flags } = new RegExp(cleaned, matchedFlags);
1870
- if (func === null) return `/${source}/${flags}`;
1871
- return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
1872
- }
1873
- /**
1874
- * Renders a plain object as multi-line `key: value` source for embedding in generated code. Nested
1875
- * objects recurse with fixed indentation, so the result drops straight into an object literal
1876
- * without re-parsing.
1877
- *
1878
- * @example
1879
- * ```ts
1880
- * stringifyObject({ foo: 'bar', nested: { a: 1 } })
1881
- * // 'foo: bar,\nnested: {\n a: 1\n }'
1882
- * ```
1883
- */
1884
- function stringifyObject(value) {
1885
- return Object.entries(value).map(([key, val]) => {
1886
- if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
1887
- return `${key}: ${val}`;
1888
- }).filter(Boolean).join(",\n");
1889
- }
1890
- /**
1891
- * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
1892
- * `accessor`. Returns `null` for an empty path.
1893
- *
1894
- * @example
1895
- * ```ts
1896
- * getNestedAccessor('pagination.next.id', 'lastPage')
1897
- * // "lastPage?.['pagination']?.['next']?.['id']"
1898
- * ```
1899
- */
1900
- function getNestedAccessor(param, accessor) {
1901
- const parts = Array.isArray(param) ? param : param.split(".");
1902
- if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
1903
- return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
1904
- }
1905
- //#endregion
1906
1798
  //#region src/utils/schemaGraph.ts
1907
1799
  /**
1908
1800
  * Memoized inner pass that walks a single node and returns the names of every schema it references.
@@ -1975,7 +1867,7 @@ function computeUsedSchemaNames(operations, schemas) {
1975
1867
  *
1976
1868
  * @example Only generate schemas referenced by included operations
1977
1869
  * ```ts
1978
- * const includedOps = operations.filter((op) => resolver.resolveOptions(op, { options, include }) !== null)
1870
+ * const includedOps = operations.filter((op) => resolver.default.options(op, { options, include }) !== null)
1979
1871
  * const allowed = collectUsedSchemaNames(includedOps, schemas)
1980
1872
  *
1981
1873
  * for (const schema of schemas) {
@@ -2081,19 +1973,119 @@ function mapSchemaItems(node, transform) {
2081
1973
  output: transform(schema)
2082
1974
  }));
2083
1975
  }
1976
+ //#endregion
1977
+ //#region src/utils/strings.ts
2084
1978
  /**
2085
- * Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
2086
- * is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
2087
- * of a recursive schema until first access.
1979
+ * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
1980
+ * Returns the string unchanged when no balanced quote pair is found.
2088
1981
  *
2089
1982
  * @example
2090
1983
  * ```ts
2091
- * lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
2092
- * // "get parent() { return z.lazy(() => Pet) }"
1984
+ * trimQuotes('"hello"') // 'hello'
1985
+ * trimQuotes('hello') // 'hello'
2093
1986
  * ```
2094
1987
  */
2095
- function lazyGetter({ name, body }) {
2096
- return `get ${objectKey(name)}() { return ${body} }`;
1988
+ function trimQuotes(text) {
1989
+ if (text.length >= 2) {
1990
+ const first = text[0];
1991
+ const last = text[text.length - 1];
1992
+ if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
1993
+ }
1994
+ return text;
1995
+ }
1996
+ /**
1997
+ * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
1998
+ *
1999
+ * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
2000
+ * code matches the repo style without a formatter.
2001
+ *
2002
+ * @example
2003
+ * ```ts
2004
+ * stringify('hello') // "'hello'"
2005
+ * stringify('"hello"') // "'hello'"
2006
+ * ```
2007
+ */
2008
+ function stringify(value) {
2009
+ if (value === void 0 || value === null) return "''";
2010
+ return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
2011
+ }
2012
+ /**
2013
+ * Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
2014
+ * and the Unicode line terminators U+2028 and U+2029.
2015
+ *
2016
+ * @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
2017
+ *
2018
+ * @example
2019
+ * ```ts
2020
+ * jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
2021
+ * ```
2022
+ */
2023
+ function jsStringEscape(input) {
2024
+ return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
2025
+ switch (character) {
2026
+ case "\"":
2027
+ case "'":
2028
+ case "\\": return `\\${character}`;
2029
+ case "\n": return "\\n";
2030
+ case "\r": return "\\r";
2031
+ case "\u2028": return "\\u2028";
2032
+ case "\u2029": return "\\u2029";
2033
+ default: return "";
2034
+ }
2035
+ });
2036
+ }
2037
+ /**
2038
+ * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
2039
+ * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
2040
+ * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
2041
+ *
2042
+ * @example
2043
+ * ```ts
2044
+ * toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
2045
+ * toRegExpString('^(?im)foo', null) // '/^foo/im'
2046
+ * ```
2047
+ */
2048
+ function toRegExpString(text, func = "RegExp") {
2049
+ const raw = trimQuotes(text);
2050
+ const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
2051
+ const replacementTarget = match?.[1] ?? "";
2052
+ const matchedFlags = match?.[2];
2053
+ const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
2054
+ const { source, flags } = new RegExp(cleaned, matchedFlags);
2055
+ if (func === null) return `/${source}/${flags}`;
2056
+ return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
2057
+ }
2058
+ /**
2059
+ * Renders a plain object as multi-line `key: value` source for embedding in generated code. Nested
2060
+ * objects recurse with fixed indentation, so the result drops straight into an object literal
2061
+ * without re-parsing.
2062
+ *
2063
+ * @example
2064
+ * ```ts
2065
+ * stringifyObject({ foo: 'bar', nested: { a: 1 } })
2066
+ * // 'foo: bar,\nnested: {\n a: 1\n }'
2067
+ * ```
2068
+ */
2069
+ function stringifyObject(value) {
2070
+ return Object.entries(value).map(([key, val]) => {
2071
+ if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
2072
+ return `${key}: ${val}`;
2073
+ }).filter(Boolean).join(",\n");
2074
+ }
2075
+ /**
2076
+ * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
2077
+ * `accessor`. Returns `null` for an empty path.
2078
+ *
2079
+ * @example
2080
+ * ```ts
2081
+ * getNestedAccessor('pagination.next.id', 'lastPage')
2082
+ * // "lastPage?.['pagination']?.['next']?.['id']"
2083
+ * ```
2084
+ */
2085
+ function getNestedAccessor(param, accessor) {
2086
+ const parts = Array.isArray(param) ? param : param.split(".");
2087
+ if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
2088
+ return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
2097
2089
  }
2098
2090
  //#endregion
2099
2091
  //#region src/macros/macroDiscriminatorEnum.ts
@@ -2276,6 +2268,9 @@ var exports_exports = /* @__PURE__ */ __exportAll({
2276
2268
  childName: () => childName,
2277
2269
  collect: () => collect,
2278
2270
  collectUsedSchemaNames: () => collectUsedSchemaNames,
2271
+ combineExports: () => combineExports,
2272
+ combineImports: () => combineImports,
2273
+ combineSources: () => combineSources,
2279
2274
  composeMacros: () => composeMacros,
2280
2275
  constDef: () => constDef,
2281
2276
  containsCircularRef: () => containsCircularRef,
@@ -2347,6 +2342,9 @@ exports.buildObject = buildObject;
2347
2342
  exports.childName = childName;
2348
2343
  exports.collect = collect;
2349
2344
  exports.collectUsedSchemaNames = collectUsedSchemaNames;
2345
+ exports.combineExports = combineExports;
2346
+ exports.combineImports = combineImports;
2347
+ exports.combineSources = combineSources;
2350
2348
  exports.composeMacros = composeMacros;
2351
2349
  exports.constDef = constDef;
2352
2350
  exports.containsCircularRef = containsCircularRef;