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

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 (86) hide show
  1. package/dist/{defineMacro-Bv9R_9a2.cjs → defineMacro-BryDiuQl.cjs} +12 -20
  2. package/dist/defineMacro-BryDiuQl.cjs.map +1 -0
  3. package/dist/{defineMacro-BTXvS8nI.js → defineMacro-CNkUpxia.js} +12 -19
  4. package/dist/defineMacro-CNkUpxia.js.map +1 -0
  5. package/dist/{defineMacro-BLIR6k-j.d.ts → defineMacro-Vw-xQvq7.d.ts} +9 -17
  6. package/dist/factory.cjs +64 -4
  7. package/dist/factory.cjs.map +1 -0
  8. package/dist/factory.d.ts +2 -37
  9. package/dist/factory.js +58 -3
  10. package/dist/factory.js.map +1 -0
  11. package/dist/{index-BzjwdK2M.d.ts → index-CTaN_ee_.d.ts} +235 -198
  12. package/dist/index.cjs +19 -20
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +36 -37
  15. package/dist/index.js +19 -17
  16. package/dist/index.js.map +1 -1
  17. package/dist/macros.cjs +33 -20
  18. package/dist/macros.cjs.map +1 -1
  19. package/dist/macros.d.ts +13 -11
  20. package/dist/macros.js +33 -20
  21. package/dist/macros.js.map +1 -1
  22. package/dist/{operationParams-BZ07xDm0.d.ts → operationParams-B44p6EQy.d.ts} +2 -2
  23. package/dist/{response-hnSw2NKE.cjs → response-BeG6hq2D.cjs} +394 -147
  24. package/dist/response-BeG6hq2D.cjs.map +1 -0
  25. package/dist/{response-KUdWiDWw.js → response-Rd1uisM1.js} +376 -136
  26. package/dist/response-Rd1uisM1.js.map +1 -0
  27. package/dist/{types-DyDzizSf.d.ts → types-DQTgVSwE.d.ts} +4 -54
  28. package/dist/types.d.ts +5 -6
  29. package/dist/{utils-BLJwyza-.cjs → utils-BDI3kXlb.cjs} +5 -5
  30. package/dist/{utils-BLJwyza-.cjs.map → utils-BDI3kXlb.cjs.map} +1 -1
  31. package/dist/{utils-CF_-Pn_c.js → utils-DW9rnfsD.js} +5 -5
  32. package/dist/{utils-CF_-Pn_c.js.map → utils-DW9rnfsD.js.map} +1 -1
  33. package/dist/utils.cjs +4 -4
  34. package/dist/utils.d.ts +11 -3
  35. package/dist/utils.js +3 -3
  36. package/dist/{visitor-DpKZ9Tk0.cjs → visitor-B7Zlnp4c.cjs} +3 -53
  37. package/dist/visitor-B7Zlnp4c.cjs.map +1 -0
  38. package/dist/{visitor-DJ6ZEJvq.js → visitor-DepQEKyp.js} +4 -42
  39. package/dist/visitor-DepQEKyp.js.map +1 -0
  40. package/package.json +1 -1
  41. package/src/constants.ts +0 -30
  42. package/src/dedupe.ts +1 -9
  43. package/src/defineMacro.ts +19 -12
  44. package/src/{node.ts → defineNode.ts} +19 -42
  45. package/src/dialect.ts +42 -19
  46. package/src/factory.ts +3 -90
  47. package/src/index.ts +6 -8
  48. package/src/macros/macroDiscriminatorEnum.ts +7 -1
  49. package/src/macros/macroEnumName.ts +9 -1
  50. package/src/macros/macroSimplifyUnion.ts +29 -19
  51. package/src/nodes/code.ts +32 -75
  52. package/src/nodes/content.ts +2 -7
  53. package/src/nodes/file.ts +102 -14
  54. package/src/nodes/function.ts +46 -46
  55. package/src/nodes/index.ts +2 -3
  56. package/src/nodes/input.ts +13 -9
  57. package/src/nodes/operation.ts +5 -5
  58. package/src/nodes/output.ts +1 -1
  59. package/src/nodes/parameter.ts +6 -6
  60. package/src/nodes/property.ts +6 -6
  61. package/src/nodes/requestBody.ts +6 -13
  62. package/src/nodes/response.ts +93 -8
  63. package/src/nodes/schema.ts +1 -1
  64. package/src/registry.ts +1 -1
  65. package/src/types.ts +4 -5
  66. package/src/utils/extractStringsFromNodes.ts +1 -0
  67. package/src/utils/operationParams.ts +1 -1
  68. package/src/visitor.ts +2 -13
  69. package/dist/defineMacro-BTXvS8nI.js.map +0 -1
  70. package/dist/defineMacro-Bv9R_9a2.cjs.map +0 -1
  71. package/dist/extractStringsFromNodes-Cja-xxx5.js +0 -29
  72. package/dist/extractStringsFromNodes-Cja-xxx5.js.map +0 -1
  73. package/dist/extractStringsFromNodes-DKgDjFO0.cjs +0 -34
  74. package/dist/extractStringsFromNodes-DKgDjFO0.cjs.map +0 -1
  75. package/dist/extractStringsFromNodes-p4mX1TQD.d.ts +0 -14
  76. package/dist/factory-CZNOGI-N.js +0 -283
  77. package/dist/factory-CZNOGI-N.js.map +0 -1
  78. package/dist/factory-DG1CVkEb.cjs +0 -300
  79. package/dist/factory-DG1CVkEb.cjs.map +0 -1
  80. package/dist/response-KUdWiDWw.js.map +0 -1
  81. package/dist/response-hnSw2NKE.cjs.map +0 -1
  82. package/dist/visitor-DJ6ZEJvq.js.map +0 -1
  83. package/dist/visitor-DpKZ9Tk0.cjs.map +0 -1
  84. package/src/mocks.ts +0 -56
  85. package/src/nodes/http.ts +0 -85
  86. /package/src/{printer.ts → definePrinter.ts} +0 -0
@@ -1,11 +1,16 @@
1
+ import type { Dialect } from './dialect.ts'
1
2
  import type { BaseNode, NodeKind } from './nodes/base.ts'
2
- import type { SchemaNode } from './nodes/index.ts'
3
3
 
4
4
  /**
5
- * Visitor callback names, one per traversable node kind. Kept in sync with the
6
- * keys of `Visitor` in `visitor.ts`.
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
7
  */
8
- type VisitorKey = 'input' | 'output' | 'operation' | 'schema' | 'property' | 'parameter' | 'response'
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]
9
14
 
10
15
  /**
11
16
  * Distributive `Omit` that preserves each member of a union.
@@ -27,25 +32,6 @@ function isKind<T extends BaseNode>(kind: NodeKind) {
27
32
  return (node: unknown): node is T => (node as BaseNode).kind === kind
28
33
  }
29
34
 
30
- /**
31
- * Updates a schema's `optional` and `nullish` flags from a parent's `required`
32
- * value and the schema's own `nullable`. Mirrors how OpenAPI parameters and
33
- * object properties combine "required" and "nullable" into a single AST.
34
- *
35
- * - Non-required + non-nullable → `optional: true`.
36
- * - Non-required + nullable → `nullish: true`.
37
- * - Required → both flags cleared.
38
- */
39
- export function syncOptionality(schema: SchemaNode, required: boolean): SchemaNode {
40
- const nullable = schema.nullable ?? false
41
-
42
- return {
43
- ...schema,
44
- optional: !required && !nullable ? true : undefined,
45
- nullish: !required && nullable ? true : undefined,
46
- }
47
- }
48
-
49
35
  /**
50
36
  * The single definition derived from one {@link defineNode} call: the node's
51
37
  * `create` builder, its `is` guard, and the traversal metadata the registry
@@ -57,9 +43,11 @@ export type NodeDef<TNode extends BaseNode = BaseNode, TInput = never> = {
57
43
  */
58
44
  kind: NodeKind
59
45
  /**
60
- * Builds a node from its input, applying `defaults` and the optional `build` hook.
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.
61
49
  */
62
- create: (input: TInput) => TNode
50
+ create: (input: TInput, dialect?: Dialect) => TNode
63
51
  /**
64
52
  * Type guard matching this node kind.
65
53
  */
@@ -72,20 +60,14 @@ export type NodeDef<TNode extends BaseNode = BaseNode, TInput = never> = {
72
60
  * Visitor callback name. Feeds `VISITOR_KEY_BY_KIND`.
73
61
  */
74
62
  visitorKey?: VisitorKey
75
- /**
76
- * When `true`, `create` is rerun after children are rebuilt so computed fields
77
- * stay in sync. Feeds `nodeRebuilders`.
78
- */
79
- rebuild?: boolean
80
63
  }
81
64
 
82
65
  type DefineNodeConfig<TNode extends BaseNode, TInput, TBuilt extends object> = {
83
66
  kind: TNode['kind']
84
67
  defaults?: Partial<TNode>
85
- build?: (input: TInput) => TBuilt
68
+ build?: (input: TInput, dialect?: Dialect) => TBuilt
86
69
  children?: ReadonlyArray<string>
87
70
  visitorKey?: VisitorKey
88
- rebuild?: boolean
89
71
  }
90
72
 
91
73
  /**
@@ -93,36 +75,31 @@ type DefineNodeConfig<TNode extends BaseNode, TInput, TBuilt extends object> = {
93
75
  * metadata. `create` merges `defaults`, the `build` hook (or the raw input), and the
94
76
  * `kind`, so node construction lives in one place without scattered `as` casts.
95
77
  *
96
- * Set `rebuild: true` when the `build` hook derives fields from children. After a
97
- * transform rewrites those children, the registry reruns `create` so the derived
98
- * fields stay correct.
99
- *
100
78
  * @example Simple node
101
79
  * ```ts
102
80
  * const importDef = defineNode<ImportNode>({ kind: 'Import' })
103
81
  * const createImport = importDef.create
104
82
  * ```
105
83
  *
106
- * @example Node with a build hook that is rerun on transform
84
+ * @example Node with a build hook
107
85
  * ```ts
108
86
  * const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
109
87
  * kind: 'Property',
110
88
  * build: (props) => ({ ...props, required: props.required ?? false }),
111
89
  * children: ['schema'],
112
90
  * visitorKey: 'property',
113
- * rebuild: true,
114
91
  * })
115
92
  * ```
116
93
  */
117
94
  export function defineNode<TNode extends BaseNode, TInput = Omit<TNode, 'kind'>, TBuilt extends object = Omit<TNode, 'kind'>>(
118
95
  config: DefineNodeConfig<TNode, TInput, TBuilt>,
119
96
  ): NodeDef<TNode, TInput> {
120
- const { kind, defaults, build, children, visitorKey, rebuild } = config
97
+ const { kind, defaults, build, children, visitorKey } = config
121
98
 
122
- function create(input: TInput): TNode {
123
- const base = build ? build(input) : input
99
+ function create(input: TInput, dialect?: Dialect): TNode {
100
+ const base = build ? build(input, dialect) : input
124
101
  return { ...defaults, ...(base as object), kind } as TNode
125
102
  }
126
103
 
127
- return { kind, create, is: isKind<TNode>(kind), children, visitorKey, rebuild }
104
+ return { kind, create, is: isKind<TNode>(kind), children, visitorKey }
128
105
  }
package/src/dialect.ts CHANGED
@@ -1,53 +1,76 @@
1
+ import type { SchemaNode } from './nodes/index.ts'
2
+
1
3
  /**
2
4
  * The spec-specific questions a schema parser answers while turning a source document into Kubb
3
5
  * AST nodes. The rest of the pipeline is generic JSON Schema, so this is the one seam where
4
6
  * OpenAPI, AsyncAPI, and plain JSON Schema differ.
5
7
  */
6
8
  export type SchemaDialect<TSchema = unknown, TRef = TSchema, TDiscriminated = TSchema, TDocument = unknown> = {
7
- /**
8
- * Identifies the dialect in logs and diagnostics.
9
- */
10
- name: string
11
9
  /**
12
10
  * Whether the schema is nullable.
13
11
  */
14
- isNullable: (schema?: TSchema) => boolean
12
+ isNullable(schema?: TSchema): boolean
15
13
  /**
16
14
  * Whether the value is a `$ref` pointer.
17
15
  */
18
- isReference: (value?: unknown) => value is TRef
16
+ isReference(value?: unknown): value is TRef
19
17
  /**
20
18
  * Whether the schema carries a discriminator for polymorphism.
21
19
  */
22
- isDiscriminator: (value?: unknown) => value is TDiscriminated
20
+ isDiscriminator(value?: unknown): value is TDiscriminated
23
21
  /**
24
22
  * Whether the schema is binary data, converted to a `blob` node.
25
23
  */
26
- isBinary: (schema: TSchema) => boolean
24
+ isBinary(schema: TSchema): boolean
27
25
  /**
28
26
  * Resolves a local `$ref` against the document, or nullish when it cannot.
29
27
  */
30
- resolveRef: <TResolved>(document: TDocument, ref: string) => TResolved | null | undefined
28
+ resolveRef<TResolved>(document: TDocument, ref: string): TResolved | null | undefined
29
+ /**
30
+ * Derives a schema's `optional`/`nullish` flags from a parent's `required` value and the
31
+ * schema's own `nullable`. How "required" and "nullable" combine is spec-specific, so the
32
+ * dialect owns it. Method syntax keeps a concrete dialect assignable to the base
33
+ * `SchemaDialect` (bivariant parameters).
34
+ */
35
+ optionality(schema: SchemaNode, required: boolean): SchemaNode
36
+ }
37
+
38
+ /**
39
+ * A spec adapter's dialect. `name` identifies it in logs and diagnostics; `schema` holds the
40
+ * spec-specific schema questions the parser answers.
41
+ */
42
+ export type Dialect<TSchema = unknown, TRef = TSchema, TDiscriminated = TSchema, TDocument = unknown> = {
43
+ /**
44
+ * Identifies the dialect in logs and diagnostics.
45
+ */
46
+ name: string
47
+ /**
48
+ * The spec-specific schema behavior. See {@link SchemaDialect}.
49
+ */
50
+ schema: SchemaDialect<TSchema, TRef, TDiscriminated, TDocument>
31
51
  }
32
52
 
33
53
  /**
34
- * Types a {@link SchemaDialect} for an adapter. Adds no runtime behavior and only pins the
54
+ * Types a {@link Dialect} for an adapter. Adds no runtime behavior and only pins the
35
55
  * dialect's type for inference.
36
56
  *
37
57
  * @example
38
58
  * ```ts
39
- * export const oasDialect = defineSchemaDialect({
59
+ * export const oasDialect = defineDialect({
40
60
  * name: 'oas',
41
- * isNullable,
42
- * isReference,
43
- * isDiscriminator,
44
- * isBinary: (schema) => schema.type === 'string' && schema.contentMediaType === 'application/octet-stream',
45
- * resolveRef,
61
+ * schema: {
62
+ * isNullable,
63
+ * isReference,
64
+ * isDiscriminator,
65
+ * isBinary: (schema) => schema.type === 'string' && schema.contentMediaType === 'application/octet-stream',
66
+ * resolveRef,
67
+ * optionality,
68
+ * },
46
69
  * })
47
70
  * ```
48
71
  */
49
- export function defineSchemaDialect<TSchema, TRef, TDiscriminated, TDocument>(
50
- dialect: SchemaDialect<TSchema, TRef, TDiscriminated, TDocument>,
51
- ): SchemaDialect<TSchema, TRef, TDiscriminated, TDocument> {
72
+ export function defineDialect<TSchema, TRef, TDiscriminated, TDocument>(
73
+ dialect: Dialect<TSchema, TRef, TDiscriminated, TDocument>,
74
+ ): Dialect<TSchema, TRef, TDiscriminated, TDocument> {
52
75
  return dialect
53
76
  }
package/src/factory.ts CHANGED
@@ -1,16 +1,12 @@
1
- import { hash } from 'node:crypto'
2
- import path from 'node:path'
3
- import { trimExtName } from '@internals/utils'
4
- import type { FileNode, Node } from './nodes/index.ts'
5
- import { extractStringsFromNodes } from './utils/extractStringsFromNodes.ts'
6
- import { combineExports, combineImports, combineSources } from './utils/fileMerge.ts'
1
+ import type { Node } from './nodes/index.ts'
7
2
 
8
3
  // Node constructors, grouped under the `factory` namespace the way the TypeScript compiler exposes
9
4
  // `ts.factory.createX`. Aggregating them here lets `export * as factory from './factory.ts'` in the
10
5
  // barrel surface every `createX` alongside the `createFile`/`update` helpers from a single module.
11
6
  export { createArrowFunction, createBreak, createConst, createFunction, createJsx, createText, createType } from './nodes/code.ts'
12
7
  export { createContent } from './nodes/content.ts'
13
- export { createExport, createImport, createSource } from './nodes/file.ts'
8
+ export { createExport, createFile, createImport, createSource } from './nodes/file.ts'
9
+ export type { UserFileNode } from './nodes/file.ts'
14
10
  export { createFunctionParameter, createFunctionParameters, createIndexedAccessType, createObjectBindingPattern, createTypeLiteral } from './nodes/function.ts'
15
11
  export { createInput } from './nodes/input.ts'
16
12
  export { createOperation } from './nodes/operation.ts'
@@ -46,86 +42,3 @@ export function update<T extends Node>(node: T, changes: Partial<T>): T {
46
42
 
47
43
  return node
48
44
  }
49
-
50
- /**
51
- * Input descriptor for {@link createFile}, before `id`, `name`, and `extname` are computed
52
- * and `imports`/`exports`/`sources` are deduplicated.
53
- */
54
- export type UserFileNode<TMeta extends object = object> = Omit<FileNode<TMeta>, 'kind' | 'id' | 'name' | 'extname' | 'imports' | 'exports' | 'sources'> &
55
- Pick<Partial<FileNode<TMeta>>, 'imports' | 'exports' | 'sources'>
56
-
57
- /**
58
- * Creates a fully resolved `FileNode` from a file input descriptor.
59
- *
60
- * Computes:
61
- * - `id` SHA256 hash of the file path
62
- * - `name` `baseName` without extension
63
- * - `extname` extension extracted from `baseName`
64
- *
65
- * Deduplicates:
66
- * - `sources` via `combineSources`
67
- * - `exports` via `combineExports`
68
- * - `imports` via `combineImports` (also filters unused imports)
69
- *
70
- * @throws {Error} when `baseName` has no extension.
71
- *
72
- * @example
73
- * ```ts
74
- * const file = createFile({
75
- * baseName: 'petStore.ts',
76
- * path: 'src/models/petStore.ts',
77
- * sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
78
- * imports: [createImport({ name: ['z'], path: 'zod' })],
79
- * exports: [createExport({ name: ['Pet'], path: './petStore' })],
80
- * })
81
- * // file.id = SHA256 hash of 'src/models/petStore.ts'
82
- * // file.name = 'petStore'
83
- * // file.extname = '.ts'
84
- * ```
85
- */
86
- export function createFile<TMeta extends object = object>(input: UserFileNode<TMeta>): FileNode<TMeta> {
87
- const rawExtname = path.extname(input.baseName)
88
- // Handle dotfile basename like '.ts' where path.extname returns ''
89
- const extname = (rawExtname || (input.baseName.startsWith('.') ? input.baseName : '')) as `.${string}`
90
- if (!extname) {
91
- throw new Error(`No extname found for ${input.baseName}`)
92
- }
93
-
94
- const source = (input.sources ?? [])
95
- .flatMap((item) => item.nodes ?? [])
96
- .map((node) => extractStringsFromNodes([node]))
97
- .filter(Boolean)
98
- .join('\n\n')
99
- const resolvedExports = input.exports?.length ? combineExports(input.exports) : []
100
- const combinedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || undefined) : []
101
- const localNames = new Set((input.sources ?? []).map((item) => item.name).filter((name): name is string => Boolean(name)))
102
- const nameOf = (item: string | { propertyName: string; name?: string }): string => (typeof item === 'string' ? item : (item.name ?? item.propertyName))
103
- // Drop self-imports. Consolidating output (`mode: 'file'`) can place a symbol's
104
- // definition and a cross-file import of it in the same file. The first pass catches imports that
105
- // resolve to this file's own path. The second drops imports of names the file already defines,
106
- // the case consolidation produces when the import path no longer matches `input.path`. Sources
107
- // stay intact, so the local definition remains. Bare specifiers like `'zod'` never match a path.
108
- const resolvedImports = combinedImports
109
- .filter((imp) => imp.path !== input.path)
110
- .flatMap((imp) => {
111
- if (!Array.isArray(imp.name)) {
112
- return typeof imp.name === 'string' && localNames.has(imp.name) ? [] : [imp]
113
- }
114
- const kept = imp.name.filter((item) => !localNames.has(nameOf(item)))
115
- if (!kept.length) return []
116
- return [kept.length === imp.name.length ? imp : { ...imp, name: kept }]
117
- })
118
- const resolvedSources = input.sources?.length ? combineSources(input.sources) : []
119
-
120
- return {
121
- kind: 'File',
122
- ...input,
123
- id: hash('sha256', input.path, 'hex'),
124
- name: trimExtName(input.baseName),
125
- extname,
126
- imports: resolvedImports,
127
- exports: resolvedExports,
128
- sources: resolvedSources,
129
- meta: input.meta ?? ({} as TMeta),
130
- }
131
- }
package/src/index.ts CHANGED
@@ -1,15 +1,13 @@
1
- export { httpMethods, schemaTypes } from './constants.ts'
1
+ export { schemaTypes } from './constants.ts'
2
2
  export { applyDedupe, buildDedupePlan } from './dedupe.ts'
3
- export { defineSchemaDialect } from './dialect.ts'
4
- export * as factory from './factory.ts'
3
+ export { defineDialect } from './dialect.ts'
5
4
  export { isHttpOperationNode, narrowSchema } from './guards.ts'
6
5
  export { applyMacros, composeMacros, defineMacro } from './defineMacro.ts'
7
- export { defineNode } from './node.ts'
8
- export { syncOptionality } from './node.ts'
9
- export * from './registry.ts'
10
- export { createPrinterFactory, definePrinter } from './printer.ts'
6
+ export { defineNode } from './defineNode.ts'
7
+ export { createPrinterFactory, definePrinter } from './definePrinter.ts'
11
8
  export { signatureOf } from './signature.ts'
12
- export { extractStringsFromNodes } from './utils/extractStringsFromNodes.ts'
13
9
  export { collect, transform, walk } from './visitor.ts'
14
10
 
11
+ export * as factory from './factory.ts'
12
+ export * from './registry.ts'
15
13
  export type * from './types.ts'
@@ -3,6 +3,12 @@ import { narrowSchema } from '../guards.ts'
3
3
  import { createProperty } from '../nodes/property.ts'
4
4
  import { createSchema } from '../nodes/schema.ts'
5
5
 
6
+ type Props = {
7
+ propertyName: string
8
+ values: Array<string>
9
+ enumName?: string
10
+ }
11
+
6
12
  /**
7
13
  * Builds a macro that replaces a discriminator property's schema with a string enum of the given
8
14
  * values. Object schemas that lack the property are returned unchanged.
@@ -13,7 +19,7 @@ import { createSchema } from '../nodes/schema.ts'
13
19
  * const next = applyMacros(objectSchema, [macro], { depth: 'shallow' })
14
20
  * ```
15
21
  */
16
- export function macroDiscriminatorEnum({ propertyName, values, enumName }: { propertyName: string; values: Array<string>; enumName?: string }) {
22
+ export function macroDiscriminatorEnum({ propertyName, values, enumName }: Props) {
17
23
  return defineMacro({
18
24
  name: 'discriminator-enum',
19
25
  schema(node) {
@@ -2,6 +2,12 @@ import { defineMacro } from '../defineMacro.ts'
2
2
  import { narrowSchema } from '../guards.ts'
3
3
  import { enumPropName } from '../utils/refs.ts'
4
4
 
5
+ type Props = {
6
+ parentName: string | null | undefined
7
+ propName: string
8
+ enumSuffix: string
9
+ }
10
+
5
11
  /**
6
12
  * Builds a macro that names an inline enum schema from its parent and property name. Boolean enums
7
13
  * are left anonymous; non-enum nodes are returned unchanged.
@@ -12,13 +18,15 @@ import { enumPropName } from '../utils/refs.ts'
12
18
  * const named = applyMacros(propSchema, [macro], { depth: 'shallow' })
13
19
  * ```
14
20
  */
15
- export function macroEnumName({ parentName, propName, enumSuffix }: { parentName: string | null | undefined; propName: string; enumSuffix: string }) {
21
+ export function macroEnumName({ parentName, propName, enumSuffix }: Props) {
16
22
  return defineMacro({
17
23
  name: 'enum-name',
18
24
  schema(node) {
19
25
  const enumNode = narrowSchema(node, 'enum')
26
+
20
27
  if (enumNode?.primitive === 'boolean') return { ...node, name: null }
21
28
  if (enumNode) return { ...node, name: enumPropName(parentName, propName, enumSuffix) }
29
+
22
30
  return undefined
23
31
  },
24
32
  })
@@ -1,29 +1,17 @@
1
- import { isScalarPrimitive } from '../constants.ts'
2
1
  import { defineMacro } from '../defineMacro.ts'
3
2
  import { narrowSchema } from '../guards.ts'
4
3
  import type { SchemaNode } from '../nodes/schema.ts'
5
4
 
5
+ type ScalarPrimitive = 'string' | 'number' | 'integer' | 'bigint' | 'boolean'
6
+
6
7
  /**
7
- * Removes union members a broader scalar primitive already covers, such as a multi-value string enum
8
- * sitting next to a plain `string`. Single-value enums are kept.
9
- *
10
- * @example
11
- * ```ts
12
- * const next = applyMacros(unionSchema, [macroSimplifyUnion], { depth: 'shallow' })
13
- * ```
8
+ * Scalar primitive schema types used for union simplification and type narrowing.
14
9
  */
15
- export const macroSimplifyUnion = defineMacro({
16
- name: 'simplify-union',
17
- schema(node) {
18
- const unionNode = narrowSchema(node, 'union')
19
- if (!unionNode?.members?.length) return undefined
20
-
21
- const simplified = simplifyUnionMembers(unionNode.members)
22
- if (simplified.length === unionNode.members.length) return undefined
10
+ const SCALAR_PRIMITIVE_TYPES = new Set<ScalarPrimitive>(['string', 'number', 'integer', 'bigint', 'boolean'])
23
11
 
24
- return { ...unionNode, members: simplified }
25
- },
26
- })
12
+ function isScalarPrimitive(type: string): type is ScalarPrimitive {
13
+ return SCALAR_PRIMITIVE_TYPES.has(type as ScalarPrimitive)
14
+ }
27
15
 
28
16
  /**
29
17
  * Filters union members, dropping enum members that a broader scalar primitive already covers.
@@ -48,3 +36,25 @@ function simplifyUnionMembers(members: Array<SchemaNode>): Array<SchemaNode> {
48
36
  return true
49
37
  })
50
38
  }
39
+
40
+ /**
41
+ * Removes union members a broader scalar primitive already covers, such as a multi-value string enum
42
+ * sitting next to a plain `string`. Single-value enums are kept.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const next = applyMacros(unionSchema, [macroSimplifyUnion], { depth: 'shallow' })
47
+ * ```
48
+ */
49
+ export const macroSimplifyUnion = defineMacro({
50
+ name: 'simplify-union',
51
+ schema(node) {
52
+ const unionNode = narrowSchema(node, 'union')
53
+ if (!unionNode?.members?.length) return undefined
54
+
55
+ const simplified = simplifyUnionMembers(unionNode.members)
56
+ if (simplified.length === unionNode.members.length) return undefined
57
+
58
+ return { ...unionNode, members: simplified }
59
+ },
60
+ })
package/src/nodes/code.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNode } from '../node.ts'
1
+ import { defineNode } from '../defineNode.ts'
2
2
  import type { BaseNode } from './base.ts'
3
3
 
4
4
  /**
@@ -164,55 +164,12 @@ export type FunctionNode = BaseNode & {
164
164
  * // export const getPet = () => ...
165
165
  * ```
166
166
  */
167
- export type ArrowFunctionNode = BaseNode & {
167
+ export type ArrowFunctionNode = Omit<FunctionNode, 'kind'> & {
168
168
  kind: 'ArrowFunction'
169
- /**
170
- * Name of the arrow function (used as the `const` variable name).
171
- */
172
- name: string
173
- /**
174
- * Whether the function is a default export.
175
- */
176
- default?: boolean | null
177
- /**
178
- * Function parameter list rendered as a string (e.g. from `FunctionParams.toConstructor()`).
179
- */
180
- params?: string | null
181
- /**
182
- * Whether the arrow function should be exported.
183
- */
184
- export?: boolean | null
185
- /**
186
- * Whether the arrow function is async. When `true`, the return type is wrapped in `Promise<>`.
187
- */
188
- async?: boolean | null
189
- /**
190
- * TypeScript generic type parameters.
191
- *
192
- * @example Constrained generics
193
- * `['T', 'U extends string']`
194
- */
195
- generics?: string | Array<string> | null
196
- /**
197
- * Return type annotation.
198
- *
199
- * @example Type reference
200
- * `'Pet'`
201
- */
202
- returnType?: string | null
203
- /**
204
- * JSDoc documentation metadata.
205
- */
206
- JSDoc?: JSDocNode | null
207
169
  /**
208
170
  * Render the arrow function body as a single-line expression.
209
171
  */
210
172
  singleLine?: boolean | null
211
- /**
212
- * Child nodes representing the function body (children of the `Function.Arrow` component).
213
- * Each entry is a {@link CodeNode}. Use {@link TextNode} for raw string content.
214
- */
215
- nodes?: Array<CodeNode>
216
173
  }
217
174
 
218
175
  /**
@@ -285,6 +242,36 @@ export type CodeNode = ConstNode | TypeNode | FunctionNode | ArrowFunctionNode |
285
242
  */
286
243
  export const constDef = defineNode<ConstNode>({ kind: 'Const' })
287
244
 
245
+ /**
246
+ * Definition for the {@link TypeNode}.
247
+ */
248
+ export const typeDef = defineNode<TypeNode>({ kind: 'Type' })
249
+
250
+ /**
251
+ * Definition for the {@link FunctionNode}.
252
+ */
253
+ export const functionDef = defineNode<FunctionNode>({ kind: 'Function' })
254
+
255
+ /**
256
+ * Definition for the {@link ArrowFunctionNode}.
257
+ */
258
+ export const arrowFunctionDef = defineNode<ArrowFunctionNode>({ kind: 'ArrowFunction' })
259
+
260
+ /**
261
+ * Definition for the {@link TextNode}.
262
+ */
263
+ export const textDef = defineNode<TextNode, string>({ kind: 'Text', build: (value) => ({ value }) })
264
+
265
+ /**
266
+ * Definition for the {@link BreakNode}.
267
+ */
268
+ export const breakDef = defineNode<BreakNode, void>({ kind: 'Break', build: () => ({}) })
269
+
270
+ /**
271
+ * Definition for the {@link JsxNode}.
272
+ */
273
+ export const jsxDef = defineNode<JsxNode, string>({ kind: 'Jsx', build: (value) => ({ value }) })
274
+
288
275
  /**
289
276
  * Creates a `ConstNode` representing a TypeScript `const` declaration.
290
277
  *
@@ -296,11 +283,6 @@ export const constDef = defineNode<ConstNode>({ kind: 'Const' })
296
283
  */
297
284
  export const createConst = constDef.create
298
285
 
299
- /**
300
- * Definition for the {@link TypeNode}.
301
- */
302
- export const typeDef = defineNode<TypeNode>({ kind: 'Type' })
303
-
304
286
  /**
305
287
  * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
306
288
  *
@@ -312,11 +294,6 @@ export const typeDef = defineNode<TypeNode>({ kind: 'Type' })
312
294
  */
313
295
  export const createType = typeDef.create
314
296
 
315
- /**
316
- * Definition for the {@link FunctionNode}.
317
- */
318
- export const functionDef = defineNode<FunctionNode>({ kind: 'Function' })
319
-
320
297
  /**
321
298
  * Creates a `FunctionNode` representing a TypeScript `function` declaration.
322
299
  *
@@ -328,11 +305,6 @@ export const functionDef = defineNode<FunctionNode>({ kind: 'Function' })
328
305
  */
329
306
  export const createFunction = functionDef.create
330
307
 
331
- /**
332
- * Definition for the {@link ArrowFunctionNode}.
333
- */
334
- export const arrowFunctionDef = defineNode<ArrowFunctionNode>({ kind: 'ArrowFunction' })
335
-
336
308
  /**
337
309
  * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
338
310
  *
@@ -344,11 +316,6 @@ export const arrowFunctionDef = defineNode<ArrowFunctionNode>({ kind: 'ArrowFunc
344
316
  */
345
317
  export const createArrowFunction = arrowFunctionDef.create
346
318
 
347
- /**
348
- * Definition for the {@link TextNode}.
349
- */
350
- export const textDef = defineNode<TextNode, string>({ kind: 'Text', build: (value) => ({ value }) })
351
-
352
319
  /**
353
320
  * Creates a {@link TextNode} representing a raw string fragment in the source output.
354
321
  *
@@ -360,11 +327,6 @@ export const textDef = defineNode<TextNode, string>({ kind: 'Text', build: (valu
360
327
  */
361
328
  export const createText = textDef.create
362
329
 
363
- /**
364
- * Definition for the {@link BreakNode}.
365
- */
366
- export const breakDef = defineNode<BreakNode, void>({ kind: 'Break', build: () => ({}) })
367
-
368
330
  /**
369
331
  * Creates a {@link BreakNode} representing a line break in the source output.
370
332
  *
@@ -378,11 +340,6 @@ export function createBreak(): BreakNode {
378
340
  return breakDef.create()
379
341
  }
380
342
 
381
- /**
382
- * Definition for the {@link JsxNode}.
383
- */
384
- export const jsxDef = defineNode<JsxNode, string>({ kind: 'Jsx', build: (value) => ({ value }) })
385
-
386
343
  /**
387
344
  * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
388
345
  *
@@ -1,4 +1,4 @@
1
- import { defineNode } from '../node.ts'
1
+ import { defineNode } from '../defineNode.ts'
2
2
  import type { BaseNode } from './base.ts'
3
3
  import type { SchemaNode } from './schema.ts'
4
4
 
@@ -37,15 +37,10 @@ export type ContentNode = BaseNode & {
37
37
  keysToOmit?: Array<string> | null
38
38
  }
39
39
 
40
- /**
41
- * Loosely-typed content entry accepted by the builders, normalized into a {@link ContentNode}.
42
- */
43
- export type UserContent = Omit<ContentNode, 'kind'>
44
-
45
40
  /**
46
41
  * Definition for the {@link ContentNode}.
47
42
  */
48
- export const contentDef = defineNode<ContentNode, UserContent>({
43
+ export const contentDef = defineNode<ContentNode>({
49
44
  kind: 'Content',
50
45
  children: ['schema'],
51
46
  })