@kubb/ast 5.0.0-beta.62 → 5.0.0-beta.64

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.
Files changed (92) hide show
  1. package/dist/{defineMacro-CNkUpxia.js → defineMacro-B76LsJwO.js} +4 -4
  2. package/dist/defineMacro-B76LsJwO.js.map +1 -0
  3. package/dist/{defineMacro-Vw-xQvq7.d.ts → defineMacro-B7qm3zTd.d.ts} +5 -6
  4. package/dist/{defineMacro-BryDiuQl.cjs → defineMacro-CEZHaCXE.cjs} +5 -5
  5. package/dist/defineMacro-CEZHaCXE.cjs.map +1 -0
  6. package/dist/factory-wJLzHeXT.d.ts +27 -0
  7. package/dist/factory.cjs +56 -56
  8. package/dist/factory.cjs.map +1 -1
  9. package/dist/factory.d.ts +3 -27
  10. package/dist/factory.js +6 -6
  11. package/dist/factory.js.map +1 -1
  12. package/dist/{index-CTaN_ee_.d.ts → index-BKD4drsX.d.ts} +47 -114
  13. package/dist/index.cjs +193 -270
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +31 -21
  16. package/dist/index.js +154 -236
  17. package/dist/index.js.map +1 -1
  18. package/dist/macros.cjs +12 -12
  19. package/dist/macros.cjs.map +1 -1
  20. package/dist/macros.d.ts +3 -3
  21. package/dist/macros.js +5 -5
  22. package/dist/macros.js.map +1 -1
  23. package/dist/{operationParams-B44p6EQy.d.ts → operationParams-ByVfpYr7.d.ts} +10 -3
  24. package/dist/{visitor-DepQEKyp.js → refs-Dx6U5LoE.js} +116 -121
  25. package/dist/refs-Dx6U5LoE.js.map +1 -0
  26. package/dist/{visitor-B7Zlnp4c.cjs → refs-u5SDdyV7.cjs} +139 -144
  27. package/dist/refs-u5SDdyV7.cjs.map +1 -0
  28. package/dist/{response-BeG6hq2D.cjs → schema-BkvrrOAr.cjs} +171 -153
  29. package/dist/schema-BkvrrOAr.cjs.map +1 -0
  30. package/dist/{response-Rd1uisM1.js → schema-YNbOtTCM.js} +167 -155
  31. package/dist/schema-YNbOtTCM.js.map +1 -0
  32. package/dist/{types-DQTgVSwE.d.ts → types-BP9BZoq-.d.ts} +56 -80
  33. package/dist/types.d.ts +5 -5
  34. package/dist/{utils-DW9rnfsD.js → utils-BJi0y-xg.js} +57 -50
  35. package/dist/utils-BJi0y-xg.js.map +1 -0
  36. package/dist/{utils-BDI3kXlb.cjs → utils-CEepwqmb.cjs} +74 -67
  37. package/dist/utils-CEepwqmb.cjs.map +1 -0
  38. package/dist/utils.cjs +11 -11
  39. package/dist/utils.d.ts +45 -30
  40. package/dist/utils.js +3 -3
  41. package/package.json +1 -2
  42. package/dist/defineMacro-BryDiuQl.cjs.map +0 -1
  43. package/dist/defineMacro-CNkUpxia.js.map +0 -1
  44. package/dist/response-BeG6hq2D.cjs.map +0 -1
  45. package/dist/response-Rd1uisM1.js.map +0 -1
  46. package/dist/utils-BDI3kXlb.cjs.map +0 -1
  47. package/dist/utils-DW9rnfsD.js.map +0 -1
  48. package/dist/visitor-B7Zlnp4c.cjs.map +0 -1
  49. package/dist/visitor-DepQEKyp.js.map +0 -1
  50. package/src/constants.ts +0 -153
  51. package/src/dedupe.ts +0 -231
  52. package/src/defineMacro.ts +0 -139
  53. package/src/defineNode.ts +0 -105
  54. package/src/definePrinter.ts +0 -256
  55. package/src/dialect.ts +0 -76
  56. package/src/factory.ts +0 -44
  57. package/src/guards.ts +0 -28
  58. package/src/index.ts +0 -13
  59. package/src/infer.ts +0 -132
  60. package/src/macros/index.ts +0 -3
  61. package/src/macros/macroDiscriminatorEnum.ts +0 -50
  62. package/src/macros/macroEnumName.ts +0 -33
  63. package/src/macros/macroSimplifyUnion.ts +0 -60
  64. package/src/nodes/base.ts +0 -49
  65. package/src/nodes/code.ts +0 -352
  66. package/src/nodes/content.ts +0 -51
  67. package/src/nodes/file.ts +0 -379
  68. package/src/nodes/function.ts +0 -283
  69. package/src/nodes/index.ts +0 -93
  70. package/src/nodes/input.ts +0 -144
  71. package/src/nodes/operation.ts +0 -165
  72. package/src/nodes/output.ts +0 -49
  73. package/src/nodes/parameter.ts +0 -71
  74. package/src/nodes/property.ts +0 -67
  75. package/src/nodes/requestBody.ts +0 -54
  76. package/src/nodes/response.ts +0 -173
  77. package/src/nodes/schema.ts +0 -732
  78. package/src/registry.ts +0 -75
  79. package/src/signature.ts +0 -207
  80. package/src/types.ts +0 -9
  81. package/src/utils/codegen.ts +0 -104
  82. package/src/utils/extractStringsFromNodes.ts +0 -35
  83. package/src/utils/fileMerge.ts +0 -184
  84. package/src/utils/index.ts +0 -11
  85. package/src/utils/operationParams.ts +0 -353
  86. package/src/utils/refs.ts +0 -138
  87. package/src/utils/schemaGraph.ts +0 -169
  88. package/src/utils/schemaMerge.ts +0 -34
  89. package/src/utils/schemaTraversal.ts +0 -86
  90. package/src/utils/strings.ts +0 -139
  91. package/src/visitor.ts +0 -519
  92. /package/dist/{chunk-CNktS9qV.js → rolldown-runtime-CNktS9qV.js} +0 -0
package/src/dedupe.ts DELETED
@@ -1,231 +0,0 @@
1
- import type { Node, OperationNode, SchemaNode } from './nodes/index.ts'
2
- import { createSchema } from './nodes/schema.ts'
3
- import { signatureOf } from './signature.ts'
4
- import { extractRefName } from './utils/index.ts'
5
- import { collectLazy, transform } from './visitor.ts'
6
-
7
- /**
8
- * A canonical destination for a deduplicated shape: the shared schema name and
9
- * the synthetic `$ref` path that points at it.
10
- */
11
- export type DedupeCanonical = {
12
- /**
13
- * Canonical schema name every duplicate occurrence refers to.
14
- */
15
- name: string
16
- /**
17
- * `$ref` path stored on the generated `ref` nodes (for example `#/components/schemas/Status`).
18
- */
19
- ref: string
20
- }
21
-
22
- /**
23
- * The result of {@link buildDedupePlan}: a lookup from structural signature to its
24
- * canonical target, plus the freshly hoisted definitions that must be added to
25
- * the schema list.
26
- */
27
- export type DedupePlan = {
28
- /**
29
- * Maps a structural signature to the canonical schema that represents it.
30
- */
31
- canonicalBySignature: Map<string, DedupeCanonical>
32
- /**
33
- * Maps the name of a top-level schema that duplicates a canonical one to that canonical, so
34
- * references to the duplicate can be repointed at the first schema with the same content.
35
- */
36
- aliasNames: Map<string, DedupeCanonical>
37
- /**
38
- * New top-level schema definitions created for inline shapes that had no existing
39
- * named component. Nested duplicates inside each definition are already collapsed.
40
- */
41
- hoisted: Array<SchemaNode>
42
- }
43
-
44
- /**
45
- * The lookups {@link applyDedupe} needs from a {@link DedupePlan}.
46
- */
47
- export type DedupeLookups = Pick<DedupePlan, 'canonicalBySignature' | 'aliasNames'>
48
-
49
- /**
50
- * Options that inject the naming and candidate policy into {@link buildDedupePlan}.
51
- * The mechanics (grouping, counting, rewriting) live here. The policy lives in the caller.
52
- */
53
- export type BuildDedupePlanOptions = {
54
- /**
55
- * Returns `true` when a node should be deduplicated. This is the only gate, so it must
56
- * reject both ineligible kinds (return `false` for anything other than, say, enums and
57
- * objects) and unsafe shapes (e.g. nodes that reference a circular schema).
58
- */
59
- isCandidate: (node: SchemaNode) => boolean
60
- /**
61
- * Produces the canonical name for an inline shape with no existing named component.
62
- * Return `null` to leave the shape inline (for example when no contextual name exists).
63
- */
64
- nameFor: (node: SchemaNode, signature: string) => string | null
65
- /**
66
- * Builds the `$ref` path for a canonical name.
67
- */
68
- refFor: (name: string) => string
69
- /**
70
- * Minimum number of occurrences before a shape is deduplicated.
71
- *
72
- * @default 2
73
- */
74
- minOccurrences?: number
75
- }
76
-
77
- /**
78
- * Builds the shared `ref` replacement for a duplicate occurrence, carrying the
79
- * usage-slot and documentation fields that are not part of the canonical type.
80
- */
81
- function createRefNode(node: SchemaNode, canonical: DedupeCanonical): SchemaNode {
82
- return createSchema({
83
- type: 'ref',
84
- name: canonical.name,
85
- ref: canonical.ref,
86
- optional: node.optional,
87
- nullish: node.nullish,
88
- readOnly: node.readOnly,
89
- writeOnly: node.writeOnly,
90
- deprecated: node.deprecated,
91
- description: node.description,
92
- default: node.default,
93
- example: node.example,
94
- })
95
- }
96
-
97
- /**
98
- * Rewrites a node, replacing every candidate sub-schema whose signature has a canonical
99
- * target with a `ref` to that target. Replacing a node with a `ref` prunes its subtree,
100
- * so nested duplicates inside a replaced shape are not visited again. A `ref` that points
101
- * at a duplicate top-level schema (see `aliasNames`) is repointed at the first schema with
102
- * the same content.
103
- *
104
- * Pass `skipRootMatch` when rewriting a canonical definition so its own root is not
105
- * turned into a reference to itself. Nested duplicates are still collapsed.
106
- *
107
- * @example
108
- * ```ts
109
- * const next = applyDedupe(operationNode, plan)
110
- * ```
111
- */
112
- export function applyDedupe(node: SchemaNode, plan: DedupeLookups, skipRootMatch?: boolean): SchemaNode
113
- export function applyDedupe(node: OperationNode, plan: DedupeLookups, skipRootMatch?: boolean): OperationNode
114
- export function applyDedupe(node: Node, plan: DedupeLookups, skipRootMatch = false): Node {
115
- const { canonicalBySignature, aliasNames } = plan
116
- if (canonicalBySignature.size === 0 && aliasNames.size === 0) return node
117
-
118
- const root = node
119
-
120
- return transform(node, {
121
- schema(schemaNode) {
122
- if (schemaNode.type === 'ref') {
123
- const target = schemaNode.ref ? extractRefName(schemaNode.ref) : schemaNode.name
124
- const canonical = target ? aliasNames.get(target) : undefined
125
-
126
- return canonical ? { ...schemaNode, name: canonical.name, ref: canonical.ref } : undefined
127
- }
128
-
129
- const signature = signatureOf(schemaNode)
130
- if (skipRootMatch && schemaNode === root) return undefined
131
-
132
- const canonical = canonicalBySignature.get(signature)
133
- if (!canonical) return undefined
134
-
135
- return createRefNode(schemaNode, canonical)
136
- },
137
- })
138
- }
139
-
140
- /**
141
- * Strips usage-slot flags from a hoisted definition and applies its canonical name.
142
- * A standalone definition is never optional, so `optional`/`nullish` are cleared.
143
- */
144
- function cleanDefinition(node: SchemaNode, name: string): SchemaNode {
145
- return { ...node, name, optional: undefined, nullish: undefined }
146
- }
147
-
148
- /**
149
- * Scans a forest of schema and operation nodes and produces a {@link DedupePlan}.
150
- *
151
- * A shape that occurs at least `minOccurrences` times is deduplicated: if any occurrence
152
- * is a named top-level schema, the first one becomes the canonical (so other top-level
153
- * duplicates and inline copies turn into references to it). Every other top-level name with
154
- * the same content is recorded in `aliasNames`, so refs to it can be repointed at the
155
- * canonical. Otherwise a new definition is hoisted using `nameFor`. The plan is then applied
156
- * per node with {@link applyDedupe}.
157
- *
158
- * @example
159
- * ```ts
160
- * const plan = buildDedupePlan([...schemaNodes, ...operationNodes], {
161
- * isCandidate: (node) => node.type === 'enum' || node.type === 'object',
162
- * nameFor: (node) => node.name ?? null,
163
- * refFor: (name) => `#/components/schemas/${name}`,
164
- * })
165
- * ```
166
- */
167
- export function buildDedupePlan(roots: ReadonlyArray<Node>, options: BuildDedupePlanOptions): DedupePlan {
168
- const { isCandidate, nameFor, refFor, minOccurrences = 2 } = options
169
-
170
- const topLevelNodes = new Set<SchemaNode>()
171
-
172
- type Group = {
173
- count: number
174
- representative: SchemaNode
175
- topLevelNames: Array<string>
176
- }
177
- const groups = new Map<string, Group>()
178
-
179
- function record(schemaNode: SchemaNode): void {
180
- if (!isCandidate(schemaNode)) return
181
- const signature = signatureOf(schemaNode)
182
-
183
- const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name
184
- const group = groups.get(signature)
185
- if (group) {
186
- group.count++
187
- if (isTopLevel) group.topLevelNames.push(schemaNode.name!)
188
- } else {
189
- groups.set(signature, { count: 1, representative: schemaNode, topLevelNames: isTopLevel ? [schemaNode.name!] : [] })
190
- }
191
- }
192
-
193
- for (const root of roots) {
194
- if (root.kind === 'Schema') topLevelNodes.add(root)
195
- for (const schemaNode of collectLazy<SchemaNode>(root, { schema: (node) => node })) {
196
- record(schemaNode)
197
- }
198
- }
199
-
200
- const canonicalBySignature = new Map<string, DedupeCanonical>()
201
- const aliasNames = new Map<string, DedupeCanonical>()
202
- const pendingHoists: Array<{ name: string; representative: SchemaNode }> = []
203
-
204
- for (const [signature, group] of groups) {
205
- if (group.count < minOccurrences) continue
206
-
207
- const [firstName, ...duplicateNames] = group.topLevelNames
208
- if (firstName) {
209
- const canonical: DedupeCanonical = { name: firstName, ref: refFor(firstName) }
210
- canonicalBySignature.set(signature, canonical)
211
- for (const duplicate of duplicateNames) {
212
- aliasNames.set(duplicate, canonical)
213
- }
214
- continue
215
- }
216
-
217
- const name = nameFor(group.representative, signature)
218
- if (!name) continue
219
-
220
- canonicalBySignature.set(signature, { name, ref: refFor(name) })
221
- pendingHoists.push({ name, representative: group.representative })
222
- }
223
-
224
- // Build hoisted definitions only after every canonical name is known, so nested
225
- // duplicates inside a definition also resolve to refs.
226
- const hoisted = pendingHoists.map(({ name, representative }) =>
227
- cleanDefinition(applyDedupe(representative, { canonicalBySignature, aliasNames }, true), name),
228
- )
229
-
230
- return { canonicalBySignature, aliasNames, hoisted }
231
- }
@@ -1,139 +0,0 @@
1
- import type { VisitorDepth } from './constants.ts'
2
- import type { VisitorKey } from './defineNode.ts'
3
- import { visitorKeys } from './defineNode.ts'
4
- import type { Node } from './nodes/index.ts'
5
- import type { Visitor, VisitorContext } from './visitor.ts'
6
- import { transform } from './visitor.ts'
7
-
8
- /**
9
- * Ordering hint shared by macros and plugins. `pre` runs before unmarked items, `post` after,
10
- * and `undefined` keeps declaration order.
11
- */
12
- export type Enforce = 'pre' | 'post'
13
-
14
- /**
15
- * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain
16
- * list keeps its authored order.
17
- */
18
- function enforceWeight(enforce?: Enforce): number {
19
- if (enforce === 'pre') return 0
20
- if (enforce === 'post') return 2
21
- return 1
22
- }
23
-
24
- /**
25
- * A named, composable transform over the Kubb AST. It carries the same per-kind callbacks as a
26
- * {@link Visitor} (`schema`, `operation`, …), plus a `name`, an optional `enforce` order, and an
27
- * optional `when` gate. Macros run on the shared AST, so the same macro works across every adapter
28
- * and output target. Exports follow the `macro<Name>` convention, mirroring plugins (`pluginTs`).
29
- */
30
- export type Macro = Visitor & {
31
- /**
32
- * Macro identifier, surfaced in diagnostics. Follows the `macro<Name>` convention.
33
- */
34
- name: string
35
- /**
36
- * Ordering hint. `pre` macros run before unmarked macros, `post` macros run after.
37
- * Ordering within a bucket follows list order.
38
- */
39
- enforce?: Enforce
40
- /**
41
- * Gate checked against the current node before any callback runs. When it returns `false`
42
- * the macro is skipped for that node.
43
- */
44
- when?: (node: Node) => boolean
45
- }
46
-
47
- /**
48
- * Types a macro for inference and a single construction site, mirroring `definePlugin`.
49
- * Adds no runtime behavior.
50
- *
51
- * @example
52
- * ```ts
53
- * const macroUntagged = defineMacro({
54
- * name: 'untagged',
55
- * operation(node) {
56
- * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }
57
- * },
58
- * })
59
- * ```
60
- */
61
- export function defineMacro(macro: Macro): Macro {
62
- return macro
63
- }
64
-
65
- type MacroCallback = (node: Node, context: VisitorContext) => Node | null | undefined
66
-
67
- type ChainProps = {
68
- macros: ReadonlyArray<Macro>
69
- key: VisitorKey
70
- node: Node
71
- context: VisitorContext
72
- }
73
-
74
- /**
75
- * Runs every macro's callback for one node kind in order, chaining the result so each macro sees
76
- * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the
77
- * original reference (structural sharing).
78
- */
79
- function chain({ macros, key, node, context }: ChainProps): Node | undefined {
80
- let current = node
81
-
82
- for (const macro of macros) {
83
- const callback = macro[key] as MacroCallback | undefined
84
- if (!callback) continue
85
- if (macro.when && !macro.when(current)) continue
86
-
87
- const next = callback(current, context)
88
- if (next != null) current = next
89
- }
90
-
91
- return current === node ? undefined : current
92
- }
93
-
94
- /**
95
- * Folds an ordered list of macros into a single {@link Visitor} that `transform` (and the per-plugin
96
- * transform layer in `@kubb/core`) can run. Macros are stable-sorted by `enforce`, then applied
97
- * sequentially per node so later macros see earlier output. This differs from a plain visitor, which
98
- * has no names, ordering, or composition.
99
- *
100
- * @example
101
- * ```ts
102
- * const visitor = composeMacros([macroSimplifyUnion, macroDiscriminatorEnum])
103
- * const next = transform(root, visitor)
104
- * ```
105
- */
106
- export function composeMacros(macros: ReadonlyArray<Macro>): Visitor {
107
- const ordered = [...macros].sort((a, b) => enforceWeight(a.enforce) - enforceWeight(b.enforce))
108
-
109
- const visitor: Visitor = {}
110
- for (const key of visitorKeys) {
111
- if (!ordered.some((macro) => typeof macro[key] === 'function')) continue
112
-
113
- const callback = (node: Node, context: VisitorContext) => chain({ macros: ordered, key, node, context })
114
- ;(visitor as Record<VisitorKey, MacroCallback>)[key] = callback
115
- }
116
-
117
- return visitor
118
- }
119
-
120
- /**
121
- * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s
122
- * structural sharing, so an empty or no-op macro list returns the same reference. Pass
123
- * `depth: 'shallow'` to rewrite the root node only.
124
- *
125
- * @example
126
- * ```ts
127
- * const next = applyMacros(root, [macroIntegerToString])
128
- * ```
129
- *
130
- * @example Apply to the root node only
131
- * ```ts
132
- * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })
133
- * ```
134
- */
135
- export function applyMacros<TNode extends Node>(root: TNode, macros: ReadonlyArray<Macro>, options?: { depth?: VisitorDepth }): TNode {
136
- if (macros.length === 0) return root
137
-
138
- return transform(root, { ...composeMacros(macros), ...options }) as TNode
139
- }
package/src/defineNode.ts DELETED
@@ -1,105 +0,0 @@
1
- import type { Dialect } from './dialect.ts'
2
- import type { BaseNode, NodeKind } from './nodes/base.ts'
3
-
4
- /**
5
- * Visitor callback names, one per traversable node kind, in traversal order.
6
- * Kept in sync with the keys of `Visitor` in `visitor.ts`.
7
- */
8
- export const visitorKeys = ['input', 'output', 'operation', 'schema', 'property', 'parameter', 'response'] as const
9
-
10
- /**
11
- * One of the {@link visitorKeys} callback names.
12
- */
13
- export type VisitorKey = (typeof visitorKeys)[number]
14
-
15
- /**
16
- * Distributive `Omit` that preserves each member of a union.
17
- *
18
- * @example
19
- * ```ts
20
- * type A = { kind: 'a'; keep: string; drop: number }
21
- * type B = { kind: 'b'; keep: boolean; drop: number }
22
- * type Result = DistributiveOmit<A | B, 'drop'>
23
- * // -> { kind: 'a'; keep: string } | { kind: 'b'; keep: boolean }
24
- * ```
25
- */
26
- export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never
27
-
28
- /**
29
- * Builds a type guard that matches nodes of the given `kind`.
30
- */
31
- function isKind<T extends BaseNode>(kind: NodeKind) {
32
- return (node: unknown): node is T => (node as BaseNode).kind === kind
33
- }
34
-
35
- /**
36
- * The single definition derived from one {@link defineNode} call: the node's
37
- * `create` builder, its `is` guard, and the traversal metadata the registry
38
- * collects into the visitor tables.
39
- */
40
- export type NodeDef<TNode extends BaseNode = BaseNode, TInput = never> = {
41
- /**
42
- * Node discriminator this definition owns.
43
- */
44
- kind: NodeKind
45
- /**
46
- * Builds a node from its input, applying `defaults` and the optional `build` hook. An
47
- * optional `dialect` is forwarded to `build` so nodes can derive spec-specific fields
48
- * (e.g. a schema's `optional`/`nullish`) through `dialect.schema.optionality` when one is given.
49
- */
50
- create: (input: TInput, dialect?: Dialect) => TNode
51
- /**
52
- * Type guard matching this node kind.
53
- */
54
- is: (node: unknown) => node is TNode
55
- /**
56
- * Child node fields in traversal order. Feeds `VISITOR_KEYS`.
57
- */
58
- children?: ReadonlyArray<string>
59
- /**
60
- * Visitor callback name. Feeds `VISITOR_KEY_BY_KIND`.
61
- */
62
- visitorKey?: VisitorKey
63
- }
64
-
65
- type DefineNodeConfig<TNode extends BaseNode, TInput, TBuilt extends object> = {
66
- kind: TNode['kind']
67
- defaults?: Partial<TNode>
68
- build?: (input: TInput, dialect?: Dialect) => TBuilt
69
- children?: ReadonlyArray<string>
70
- visitorKey?: VisitorKey
71
- }
72
-
73
- /**
74
- * Defines a node once and derives its `create` builder, `is` guard, and traversal
75
- * metadata. `create` merges `defaults`, the `build` hook (or the raw input), and the
76
- * `kind`, so node construction lives in one place without scattered `as` casts.
77
- *
78
- * @example Simple node
79
- * ```ts
80
- * const importDef = defineNode<ImportNode>({ kind: 'Import' })
81
- * const createImport = importDef.create
82
- * ```
83
- *
84
- * @example Node with a build hook
85
- * ```ts
86
- * const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
87
- * kind: 'Property',
88
- * build: (props) => ({ ...props, required: props.required ?? false }),
89
- * children: ['schema'],
90
- * visitorKey: 'property',
91
- * })
92
- * ```
93
- */
94
- export function defineNode<TNode extends BaseNode, TInput = Omit<TNode, 'kind'>, TBuilt extends object = Omit<TNode, 'kind'>>(
95
- config: DefineNodeConfig<TNode, TInput, TBuilt>,
96
- ): NodeDef<TNode, TInput> {
97
- const { kind, defaults, build, children, visitorKey } = config
98
-
99
- function create(input: TInput, dialect?: Dialect): TNode {
100
- const base = build ? build(input, dialect) : input
101
- return { ...defaults, ...(base as object), kind } as TNode
102
- }
103
-
104
- return { kind, create, is: isKind<TNode>(kind), children, visitorKey }
105
- }