@kubb/core 5.0.0-beta.63 → 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.
- package/dist/{diagnostics-IjkPEgAO.d.ts → diagnostics-BqiNAWVS.d.ts} +9 -10
- package/dist/index.cjs +24 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +25 -35
- package/dist/index.js.map +1 -1
- package/dist/{memoryStorage-CWFzAz4o.js → memoryStorage-DWnhqUf2.js} +3 -3
- package/dist/{memoryStorage-CWFzAz4o.js.map → memoryStorage-DWnhqUf2.js.map} +1 -1
- package/dist/{memoryStorage-CUj1hrxa.cjs → memoryStorage-mojU6pbA.cjs} +2 -2
- package/dist/{memoryStorage-CUj1hrxa.cjs.map → memoryStorage-mojU6pbA.cjs.map} +1 -1
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.d.ts +2 -2
- package/dist/mocks.js +2 -2
- package/package.json +4 -5
- package/src/FileManager.ts +0 -137
- package/src/FileProcessor.ts +0 -212
- package/src/KubbDriver.ts +0 -900
- package/src/Transform.ts +0 -105
- package/src/constants.ts +0 -126
- package/src/createAdapter.ts +0 -127
- package/src/createKubb.ts +0 -195
- package/src/createRenderer.ts +0 -72
- package/src/createReporter.ts +0 -134
- package/src/createStorage.ts +0 -83
- package/src/defineGenerator.ts +0 -211
- package/src/defineParser.ts +0 -63
- package/src/definePlugin.ts +0 -438
- package/src/defineResolver.ts +0 -711
- package/src/diagnostics.ts +0 -662
- package/src/index.ts +0 -20
- package/src/mocks.ts +0 -249
- package/src/reporters/cliReporter.ts +0 -89
- package/src/reporters/fileReporter.ts +0 -102
- package/src/reporters/jsonReporter.ts +0 -20
- package/src/reporters/report.ts +0 -85
- package/src/storages/fsStorage.ts +0 -82
- package/src/storages/memoryStorage.ts +0 -55
- package/src/types.ts +0 -829
- /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/src/Transform.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { Macro, OperationNode, SchemaNode, Visitor } from '@kubb/ast'
|
|
2
|
-
import { composeMacros, transform } from '@kubb/ast'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Holds an ordered list of macros per plugin, keyed by plugin name. Each plugin's macros run in
|
|
6
|
-
* isolation on the original adapter node and are composed into a single `Visitor` that the
|
|
7
|
-
* `@kubb/ast` `transform` primitive applies. `applyTo` is a per-plugin lookup, not a cross-plugin
|
|
8
|
-
* chain, so plugin A's macros never see plugin B's output. When a plugin has no macros, `applyTo`
|
|
9
|
-
* returns the original node reference, and `transform` does the same when the composed visitor
|
|
10
|
-
* leaves the tree untouched, so callers can detect a no-op by identity.
|
|
11
|
-
*
|
|
12
|
-
* Registration order matches the order setup hooks fire, which the driver has already sorted by
|
|
13
|
-
* `enforce` and dependency edges. The registry preserves that order. Macro `enforce` only reorders
|
|
14
|
-
* within a single plugin's list.
|
|
15
|
-
*/
|
|
16
|
-
export class Transform {
|
|
17
|
-
readonly #macros = new Map<string, Array<Macro>>()
|
|
18
|
-
// Composed visitor per plugin, rebuilt lazily after the macro list changes.
|
|
19
|
-
readonly #composed = new Map<string, Visitor>()
|
|
20
|
-
// Memoized results per plugin. Repeated `applyTo` calls return the same node identity, so
|
|
21
|
-
// downstream WeakMap caches keyed by node (the resolver's resolveOptions memo) hit when the
|
|
22
|
-
// driver resolves a node a second time, and a stateful macro runs once per node.
|
|
23
|
-
readonly #memo = new Map<string, WeakMap<SchemaNode | OperationNode, SchemaNode | OperationNode>>()
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Number of plugins with at least one registered macro.
|
|
27
|
-
*/
|
|
28
|
-
get size(): number {
|
|
29
|
-
return this.#macros.size
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Appends `macro` to the plugin's list, after any macros already registered.
|
|
34
|
-
*/
|
|
35
|
-
add(pluginName: string, macro: Macro): void {
|
|
36
|
-
const list = this.#macros.get(pluginName)
|
|
37
|
-
if (list) list.push(macro)
|
|
38
|
-
else this.#macros.set(pluginName, [macro])
|
|
39
|
-
this.#invalidate(pluginName)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Replaces the plugin's macro list with `macros`.
|
|
44
|
-
*/
|
|
45
|
-
set(pluginName: string, macros: ReadonlyArray<Macro>): void {
|
|
46
|
-
this.#macros.set(pluginName, [...macros])
|
|
47
|
-
this.#invalidate(pluginName)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Looks up the composed visitor for `pluginName`, or `undefined` when the plugin has no macros.
|
|
52
|
-
*/
|
|
53
|
-
get(pluginName: string): Visitor | undefined {
|
|
54
|
-
return this.#visitorFor(pluginName)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Runs the plugin's macros on `node`. Returns the original node reference when the plugin has no
|
|
59
|
-
* macros, so callers can compare by identity to detect a no-op.
|
|
60
|
-
*/
|
|
61
|
-
applyTo<TNode extends SchemaNode | OperationNode>(pluginName: string, node: TNode): TNode {
|
|
62
|
-
const visitor = this.#visitorFor(pluginName)
|
|
63
|
-
if (!visitor) return node
|
|
64
|
-
|
|
65
|
-
let memo = this.#memo.get(pluginName)
|
|
66
|
-
if (!memo) {
|
|
67
|
-
memo = new WeakMap()
|
|
68
|
-
this.#memo.set(pluginName, memo)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const cached = memo.get(node)
|
|
72
|
-
if (cached) return cached as TNode
|
|
73
|
-
|
|
74
|
-
const result = transform(node, visitor) as TNode
|
|
75
|
-
memo.set(node, result)
|
|
76
|
-
return result
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Clears every registration. Called from the driver's `dispose()` so macros do not leak across
|
|
81
|
-
* builds.
|
|
82
|
-
*/
|
|
83
|
-
dispose(): void {
|
|
84
|
-
this.#macros.clear()
|
|
85
|
-
this.#composed.clear()
|
|
86
|
-
this.#memo.clear()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
#invalidate(pluginName: string): void {
|
|
90
|
-
this.#composed.delete(pluginName)
|
|
91
|
-
this.#memo.delete(pluginName)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
#visitorFor(pluginName: string): Visitor | undefined {
|
|
95
|
-
const macros = this.#macros.get(pluginName)
|
|
96
|
-
if (!macros || macros.length === 0) return undefined
|
|
97
|
-
|
|
98
|
-
let composed = this.#composed.get(pluginName)
|
|
99
|
-
if (!composed) {
|
|
100
|
-
composed = composeMacros(macros)
|
|
101
|
-
this.#composed.set(pluginName, composed)
|
|
102
|
-
}
|
|
103
|
-
return composed
|
|
104
|
-
}
|
|
105
|
-
}
|
package/src/constants.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Number of file writes to batch in parallel during `flushPendingFiles`.
|
|
3
|
-
*/
|
|
4
|
-
export const STREAM_FLUSH_EVERY = 50
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Maximum number of █ characters in a plugin timing bar.
|
|
8
|
-
*/
|
|
9
|
-
export const SUMMARY_MAX_BAR_LENGTH = 10 as const
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Divides elapsed milliseconds into bar-length units (1 block per 100 ms).
|
|
13
|
-
*/
|
|
14
|
-
export const SUMMARY_TIME_SCALE_DIVISOR = 100 as const
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Number of schema/operation nodes to dispatch concurrently during generation.
|
|
18
|
-
*/
|
|
19
|
-
export const SCHEMA_PARALLEL = 8
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Upper bound of hook listeners a single plugin can add to one event (its schema, operation,
|
|
23
|
-
* and operations generators, plus lifecycle hooks). Used to size the hooks emitter's
|
|
24
|
-
* max-listener ceiling so a multi-generator plugin set does not trip Node's leak warning.
|
|
25
|
-
*/
|
|
26
|
-
export const HOOK_LISTENERS_PER_PLUGIN = 4
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Plugin `include` filter types that select operations directly. When one of these is set
|
|
30
|
-
* without a `schemaName` include, the generate phase pre-scans operations to compute the set
|
|
31
|
-
* of schemas they reach, so unreachable schemas can be pruned for that plugin.
|
|
32
|
-
*/
|
|
33
|
-
export const OPERATION_FILTER_TYPES: ReadonlySet<string> = new Set(['tag', 'operationId', 'path', 'method', 'contentType'])
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
|
|
37
|
-
* and stays stable so it can be referenced in tooling and (later) docs. Reference
|
|
38
|
-
* these instead of inlining the string at a throw site.
|
|
39
|
-
*/
|
|
40
|
-
export const diagnosticCode = {
|
|
41
|
-
/**
|
|
42
|
-
* Fallback for an unstructured error with no specific code.
|
|
43
|
-
*/
|
|
44
|
-
unknown: 'KUBB_UNKNOWN',
|
|
45
|
-
/**
|
|
46
|
-
* The `input.path` file or URL could not be read.
|
|
47
|
-
*/
|
|
48
|
-
inputNotFound: 'KUBB_INPUT_NOT_FOUND',
|
|
49
|
-
/**
|
|
50
|
-
* An adapter was configured without an `input`.
|
|
51
|
-
*/
|
|
52
|
-
inputRequired: 'KUBB_INPUT_REQUIRED',
|
|
53
|
-
/**
|
|
54
|
-
* A `$ref` (or equivalent reference) could not be resolved in the source document.
|
|
55
|
-
*/
|
|
56
|
-
refNotFound: 'KUBB_REF_NOT_FOUND',
|
|
57
|
-
/**
|
|
58
|
-
* A server variable value is not allowed by its `enum`.
|
|
59
|
-
*/
|
|
60
|
-
invalidServerVariable: 'KUBB_INVALID_SERVER_VARIABLE',
|
|
61
|
-
/**
|
|
62
|
-
* A required plugin is missing from the config.
|
|
63
|
-
*/
|
|
64
|
-
pluginNotFound: 'KUBB_PLUGIN_NOT_FOUND',
|
|
65
|
-
/**
|
|
66
|
-
* A plugin threw while generating.
|
|
67
|
-
*/
|
|
68
|
-
pluginFailed: 'KUBB_PLUGIN_FAILED',
|
|
69
|
-
/**
|
|
70
|
-
* A plugin reported a non-fatal warning through `ctx.warn`.
|
|
71
|
-
*/
|
|
72
|
-
pluginWarning: 'KUBB_PLUGIN_WARNING',
|
|
73
|
-
/**
|
|
74
|
-
* A plugin reported an informational message through `ctx.info`.
|
|
75
|
-
*/
|
|
76
|
-
pluginInfo: 'KUBB_PLUGIN_INFO',
|
|
77
|
-
/**
|
|
78
|
-
* A schema uses a `format` Kubb does not map to a specific type. Reserved for
|
|
79
|
-
* adapters to emit as a `warning`.
|
|
80
|
-
*/
|
|
81
|
-
unsupportedFormat: 'KUBB_UNSUPPORTED_FORMAT',
|
|
82
|
-
/**
|
|
83
|
-
* A referenced schema or operation is marked `deprecated`. Reserved for adapters
|
|
84
|
-
* to emit as an `info`.
|
|
85
|
-
*/
|
|
86
|
-
deprecated: 'KUBB_DEPRECATED',
|
|
87
|
-
/**
|
|
88
|
-
* An adapter is required but the config has none. The build cannot read the input
|
|
89
|
-
* without one.
|
|
90
|
-
*/
|
|
91
|
-
adapterRequired: 'KUBB_ADAPTER_REQUIRED',
|
|
92
|
-
/**
|
|
93
|
-
* A resolved output path escapes the output directory, which can stem from a path
|
|
94
|
-
* traversal in the spec or a misconfigured `group.name`.
|
|
95
|
-
*/
|
|
96
|
-
pathTraversal: 'KUBB_PATH_TRAVERSAL',
|
|
97
|
-
/**
|
|
98
|
-
* A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
|
|
99
|
-
*/
|
|
100
|
-
invalidPluginOptions: 'KUBB_INVALID_PLUGIN_OPTIONS',
|
|
101
|
-
/**
|
|
102
|
-
* A post-generate shell hook (`hooks.done`) exited with a failure.
|
|
103
|
-
*/
|
|
104
|
-
hookFailed: 'KUBB_HOOK_FAILED',
|
|
105
|
-
/**
|
|
106
|
-
* The formatter pass over the generated files failed.
|
|
107
|
-
*/
|
|
108
|
-
formatFailed: 'KUBB_FORMAT_FAILED',
|
|
109
|
-
/**
|
|
110
|
-
* The linter pass over the generated files failed.
|
|
111
|
-
*/
|
|
112
|
-
lintFailed: 'KUBB_LINT_FAILED',
|
|
113
|
-
/**
|
|
114
|
-
* Not a failure. Carries a plugin's elapsed time, summed into the run total.
|
|
115
|
-
*/
|
|
116
|
-
performance: 'KUBB_PERFORMANCE',
|
|
117
|
-
/**
|
|
118
|
-
* Not a failure. A newer Kubb version is available on npm.
|
|
119
|
-
*/
|
|
120
|
-
updateAvailable: 'KUBB_UPDATE_AVAILABLE',
|
|
121
|
-
} as const
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Union of the stable {@link diagnosticCode} values.
|
|
125
|
-
*/
|
|
126
|
-
export type DiagnosticCode = (typeof diagnosticCode)[keyof typeof diagnosticCode]
|
package/src/createAdapter.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { PossiblePromise } from '@internals/utils'
|
|
2
|
-
import type { ImportNode, InputNode, SchemaNode } from '@kubb/ast'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Source data handed to an adapter's `parse` function. Mirrors the config
|
|
6
|
-
* input shape with paths resolved to absolute.
|
|
7
|
-
*
|
|
8
|
-
* - `{ type: 'path' }`: single file on disk.
|
|
9
|
-
* - `{ type: 'paths' }`: multiple files (e.g. split spec).
|
|
10
|
-
* - `{ type: 'data' }`: raw string or parsed object provided inline.
|
|
11
|
-
*/
|
|
12
|
-
export type AdapterSource = { type: 'path'; path: string } | { type: 'data'; data: string | unknown } | { type: 'paths'; paths: Array<string> }
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Generic parameters used by `createAdapter` and the resulting `Adapter` type.
|
|
16
|
-
*
|
|
17
|
-
* - `TName`: unique adapter identifier (`'oas'`, `'asyncapi'`, ...).
|
|
18
|
-
* - `TOptions`: user-facing options accepted by the adapter factory.
|
|
19
|
-
* - `TResolvedOptions`: options after defaults are applied.
|
|
20
|
-
* - `TDocument`: type of the parsed source document.
|
|
21
|
-
*/
|
|
22
|
-
export type AdapterFactoryOptions<
|
|
23
|
-
TName extends string = string,
|
|
24
|
-
TOptions extends object = object,
|
|
25
|
-
TResolvedOptions extends object = TOptions,
|
|
26
|
-
TDocument = unknown,
|
|
27
|
-
> = {
|
|
28
|
-
name: TName
|
|
29
|
-
options: TOptions
|
|
30
|
-
resolvedOptions: TResolvedOptions
|
|
31
|
-
document: TDocument
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Converts input files or inline data into Kubb's universal AST `InputNode`.
|
|
36
|
-
*
|
|
37
|
-
* Adapters live between the spec format and the plugins. The built-in
|
|
38
|
-
* `@kubb/adapter-oas` handles OpenAPI 2.0, 3.0, and 3.1. A custom adapter can
|
|
39
|
-
* support GraphQL, gRPC, or another schema language.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* import { defineConfig } from 'kubb'
|
|
44
|
-
* import { adapterOas } from '@kubb/adapter-oas'
|
|
45
|
-
* import { pluginTs } from '@kubb/plugin-ts'
|
|
46
|
-
*
|
|
47
|
-
* export default defineConfig({
|
|
48
|
-
* input: { path: './petStore.yaml' },
|
|
49
|
-
* output: { path: './src/gen' },
|
|
50
|
-
* adapter: adapterOas(),
|
|
51
|
-
* plugins: [pluginTs()],
|
|
52
|
-
* })
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
|
|
56
|
-
/**
|
|
57
|
-
* Human-readable adapter identifier (e.g. `'oas'`, `'asyncapi'`).
|
|
58
|
-
*/
|
|
59
|
-
name: TOptions['name']
|
|
60
|
-
/**
|
|
61
|
-
* Resolved adapter options after defaults have been applied.
|
|
62
|
-
*/
|
|
63
|
-
options: TOptions['resolvedOptions']
|
|
64
|
-
/**
|
|
65
|
-
* Parsed source document after the first `parse()` call. `null` before parsing.
|
|
66
|
-
*/
|
|
67
|
-
document: TOptions['document'] | null
|
|
68
|
-
/**
|
|
69
|
-
* Parse the source into a universal `InputNode`.
|
|
70
|
-
*/
|
|
71
|
-
parse: (source: AdapterSource) => PossiblePromise<InputNode>
|
|
72
|
-
/**
|
|
73
|
-
* Extract `ImportNode` entries for a schema tree.
|
|
74
|
-
* Returns an empty array before the first `parse()` call.
|
|
75
|
-
*
|
|
76
|
-
* The `resolve` callback receives the collision-corrected schema name and must
|
|
77
|
-
* return `{ name, path }` for the import, or `undefined` to skip it.
|
|
78
|
-
*/
|
|
79
|
-
getImports: (node: SchemaNode, resolve: (schemaName: string) => { name: string; path: string }) => Array<ImportNode>
|
|
80
|
-
/**
|
|
81
|
-
* Validate the document at the given path or URL.
|
|
82
|
-
*/
|
|
83
|
-
validate: (input: string, options?: { throwOnError?: boolean }) => Promise<void>
|
|
84
|
-
/**
|
|
85
|
-
* Memory-efficient streaming variant of `parse()`.
|
|
86
|
-
*
|
|
87
|
-
* Returns an `InputNode<true>` whose `schemas` and `operations` are `AsyncIterable`.
|
|
88
|
-
* Each `for await` loop creates a fresh parse pass over the cached in-memory document.
|
|
89
|
-
* No pre-built arrays are held in memory.
|
|
90
|
-
*/
|
|
91
|
-
stream?: (source: AdapterSource) => Promise<InputNode<true>>
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
type AdapterBuilder<T extends AdapterFactoryOptions> = (options: T['options']) => Adapter<T>
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Defines a custom adapter that translates a spec format into Kubb's universal
|
|
98
|
-
* AST, for example GraphQL, gRPC, or AsyncAPI. The built-in `@kubb/adapter-oas`
|
|
99
|
-
* handles OpenAPI/Swagger documents.
|
|
100
|
-
*
|
|
101
|
-
* Adapters must return an `InputNode` from `parse`. That node is what every
|
|
102
|
-
* plugin in the build consumes.
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```ts
|
|
106
|
-
* import { createAdapter, ast, type AdapterFactoryOptions } from '@kubb/core'
|
|
107
|
-
*
|
|
108
|
-
* type MyAdapter = AdapterFactoryOptions<'my-adapter', { validate?: boolean }>
|
|
109
|
-
*
|
|
110
|
-
* export const myAdapter = createAdapter<MyAdapter>((options) => ({
|
|
111
|
-
* name: 'my-adapter',
|
|
112
|
-
* options,
|
|
113
|
-
* document: null,
|
|
114
|
-
* async parse(_source) {
|
|
115
|
-
* // Convert the source (path or inline data) into an InputNode.
|
|
116
|
-
* return ast.factory.createInput()
|
|
117
|
-
* },
|
|
118
|
-
* getImports: () => [],
|
|
119
|
-
* async validate() {
|
|
120
|
-
* // Throw here when the spec is invalid.
|
|
121
|
-
* },
|
|
122
|
-
* }))
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
export function createAdapter<T extends AdapterFactoryOptions = AdapterFactoryOptions>(build: AdapterBuilder<T>): (options?: T['options']) => Adapter<T> {
|
|
126
|
-
return (options) => build(options ?? ({} as T['options']))
|
|
127
|
-
}
|
package/src/createKubb.ts
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import { AsyncEventEmitter, BuildError } from '@internals/utils'
|
|
3
|
-
import { HOOK_LISTENERS_PER_PLUGIN } from './constants.ts'
|
|
4
|
-
import { Diagnostics } from './diagnostics.ts'
|
|
5
|
-
import { createStorage, type Storage } from './createStorage.ts'
|
|
6
|
-
import { KubbDriver } from './KubbDriver.ts'
|
|
7
|
-
import { fsStorage } from './storages/fsStorage.ts'
|
|
8
|
-
import type { BuildOutput, Config, KubbHooks, UserConfig } from './types.ts'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Builds a `Storage` view scoped to the file paths produced by the current build.
|
|
12
|
-
* Reads delegate to the underlying `storage` so source bytes stay where they were
|
|
13
|
-
* written. Writes register the key so subsequent reads and `getKeys` are scoped
|
|
14
|
-
* to this build's output.
|
|
15
|
-
*/
|
|
16
|
-
function createSourcesView(storage: Storage): Storage {
|
|
17
|
-
const paths = new Set<string>()
|
|
18
|
-
|
|
19
|
-
return createStorage(() => ({
|
|
20
|
-
name: `${storage.name}:sources`,
|
|
21
|
-
async hasItem(key: string) {
|
|
22
|
-
return paths.has(key) && (await storage.hasItem(key))
|
|
23
|
-
},
|
|
24
|
-
async getItem(key: string) {
|
|
25
|
-
return paths.has(key) ? storage.getItem(key) : null
|
|
26
|
-
},
|
|
27
|
-
async setItem(key: string, value: string) {
|
|
28
|
-
paths.add(key)
|
|
29
|
-
await storage.setItem(key, value)
|
|
30
|
-
},
|
|
31
|
-
async removeItem(key: string) {
|
|
32
|
-
paths.delete(key)
|
|
33
|
-
await storage.removeItem(key)
|
|
34
|
-
},
|
|
35
|
-
async getKeys(base?: string) {
|
|
36
|
-
if (!base) return [...paths]
|
|
37
|
-
const result: Array<string> = []
|
|
38
|
-
for (const key of paths) {
|
|
39
|
-
if (key.startsWith(base)) result.push(key)
|
|
40
|
-
}
|
|
41
|
-
return result
|
|
42
|
-
},
|
|
43
|
-
async clear() {
|
|
44
|
-
paths.clear()
|
|
45
|
-
await storage.clear()
|
|
46
|
-
},
|
|
47
|
-
}))()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function resolveConfig(userConfig: UserConfig): Config {
|
|
51
|
-
return {
|
|
52
|
-
...userConfig,
|
|
53
|
-
root: userConfig.root || process.cwd(),
|
|
54
|
-
parsers: userConfig.parsers ?? [],
|
|
55
|
-
output: {
|
|
56
|
-
format: false,
|
|
57
|
-
lint: false,
|
|
58
|
-
extension: { '.ts': '.ts' },
|
|
59
|
-
defaultBanner: 'simple',
|
|
60
|
-
...userConfig.output,
|
|
61
|
-
},
|
|
62
|
-
storage: userConfig.storage ?? fsStorage(),
|
|
63
|
-
reporters: userConfig.reporters ?? [],
|
|
64
|
-
plugins: userConfig.plugins ?? [],
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type CreateKubbOptions = {
|
|
69
|
-
hooks?: AsyncEventEmitter<KubbHooks>
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Kubb code-generation instance bound to a single config entry. Resolves the user
|
|
74
|
-
* config in the constructor, so `config` is available right away, and shares `hooks`,
|
|
75
|
-
* `storage`, and `driver` across the `setup → build` lifecycle.
|
|
76
|
-
*
|
|
77
|
-
* `createKubb` takes a plain config object (the shape `defineConfig` produces),
|
|
78
|
-
* not a fluent builder.
|
|
79
|
-
*
|
|
80
|
-
* Attach event listeners to `.hooks` before calling `setup()` or `build()`.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```ts
|
|
84
|
-
* const kubb = createKubb(userConfig)
|
|
85
|
-
* kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => console.log(plugin.name, duration))
|
|
86
|
-
* const { files, diagnostics } = await kubb.safeBuild()
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
|
-
export class Kubb {
|
|
90
|
-
readonly hooks: AsyncEventEmitter<KubbHooks>
|
|
91
|
-
readonly config: Config
|
|
92
|
-
#driver: KubbDriver | null = null
|
|
93
|
-
#storage: Storage | null = null
|
|
94
|
-
|
|
95
|
-
constructor(userConfig: UserConfig, options: CreateKubbOptions = {}) {
|
|
96
|
-
this.config = resolveConfig(userConfig)
|
|
97
|
-
this.hooks = options.hooks ?? new AsyncEventEmitter<KubbHooks>()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
get storage(): Storage {
|
|
101
|
-
if (!this.#storage) throw new Error('[kubb] setup() must be called before accessing storage')
|
|
102
|
-
return this.#storage
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
get driver(): KubbDriver {
|
|
106
|
-
if (!this.#driver) throw new Error('[kubb] setup() must be called before accessing driver')
|
|
107
|
-
return this.#driver
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Initializes the driver and storage. `build()` calls this automatically.
|
|
112
|
-
*/
|
|
113
|
-
async setup(): Promise<void> {
|
|
114
|
-
const config = this.config
|
|
115
|
-
const driver = new KubbDriver(config, { hooks: this.hooks })
|
|
116
|
-
const storage = createSourcesView(config.storage)
|
|
117
|
-
|
|
118
|
-
// Each generator a plugin registers adds a listener to the shared hooks emitter, so size the
|
|
119
|
-
// ceiling to the plugin count. Without this, a multi-generator plugin set trips Node's
|
|
120
|
-
// EventEmitter leak warning at the default 10.
|
|
121
|
-
this.hooks.setMaxListeners(Math.max(10, config.plugins.length * HOOK_LISTENERS_PER_PLUGIN))
|
|
122
|
-
|
|
123
|
-
if (config.output.clean) {
|
|
124
|
-
await config.storage.clear(resolve(config.root, config.output.path))
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
await driver.setup()
|
|
128
|
-
|
|
129
|
-
this.#driver = driver
|
|
130
|
-
this.#storage = storage
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Runs the full pipeline and throws on any plugin error.
|
|
135
|
-
* Automatically calls `setup()` if needed.
|
|
136
|
-
*/
|
|
137
|
-
async build(): Promise<BuildOutput> {
|
|
138
|
-
const out = await this.safeBuild()
|
|
139
|
-
if (Diagnostics.hasError(out.diagnostics)) {
|
|
140
|
-
const errors = out.diagnostics
|
|
141
|
-
.filter(Diagnostics.isProblem)
|
|
142
|
-
.filter((diagnostic) => diagnostic.severity === 'error')
|
|
143
|
-
.map((diagnostic) => diagnostic.cause ?? new Diagnostics.Error(diagnostic))
|
|
144
|
-
throw new BuildError(`Build failed with ${errors.length} ${errors.length === 1 ? 'error' : 'errors'}`, { errors })
|
|
145
|
-
}
|
|
146
|
-
return out
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Runs the full pipeline and captures errors in `BuildOutput` instead of throwing.
|
|
151
|
-
* Automatically calls `setup()` if needed. This is the canonical call: it never throws on
|
|
152
|
-
* plugin errors, so callers stay in control of how failures surface.
|
|
153
|
-
*/
|
|
154
|
-
async safeBuild(): Promise<BuildOutput> {
|
|
155
|
-
if (!this.#driver) await this.setup()
|
|
156
|
-
using cleanup = this
|
|
157
|
-
const driver = cleanup.driver
|
|
158
|
-
const storage = cleanup.storage
|
|
159
|
-
const { diagnostics } = await driver.run({ storage })
|
|
160
|
-
|
|
161
|
-
return { diagnostics, files: driver.fileManager.files, driver, storage }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
dispose(): void {
|
|
165
|
-
this.#driver?.dispose()
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
[Symbol.dispose](): void {
|
|
169
|
-
this.dispose()
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Constructs a {@link Kubb} build orchestrator from a user config. Equivalent
|
|
175
|
-
* to `new Kubb(userConfig, options)` and the canonical public entry point.
|
|
176
|
-
*
|
|
177
|
-
* @example
|
|
178
|
-
* ```ts
|
|
179
|
-
* import { createKubb } from '@kubb/core'
|
|
180
|
-
* import { adapterOas } from '@kubb/adapter-oas'
|
|
181
|
-
* import { pluginTs } from '@kubb/plugin-ts'
|
|
182
|
-
*
|
|
183
|
-
* const kubb = createKubb({
|
|
184
|
-
* input: { path: './petStore.yaml' },
|
|
185
|
-
* output: { path: './src/gen' },
|
|
186
|
-
* adapter: adapterOas(),
|
|
187
|
-
* plugins: [pluginTs()],
|
|
188
|
-
* })
|
|
189
|
-
*
|
|
190
|
-
* await kubb.build()
|
|
191
|
-
* ```
|
|
192
|
-
*/
|
|
193
|
-
export function createKubb(userConfig: UserConfig, options: CreateKubbOptions = {}): Kubb {
|
|
194
|
-
return new Kubb(userConfig, options)
|
|
195
|
-
}
|
package/src/createRenderer.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { FileNode } from '@kubb/ast'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Minimal interface any Kubb renderer must satisfy.
|
|
5
|
-
*
|
|
6
|
-
* `TElement` is the type the renderer accepts, for example `KubbReactElement`
|
|
7
|
-
* for `@kubb/renderer-jsx` or a custom type for your own renderer. Defaults to
|
|
8
|
-
* `unknown` so generators that don't care about the element type work without
|
|
9
|
-
* specifying it.
|
|
10
|
-
*/
|
|
11
|
-
export type Renderer<TElement = unknown> = {
|
|
12
|
-
/**
|
|
13
|
-
* Renders `element` and populates {@link files} with the resulting {@link FileNode} objects.
|
|
14
|
-
* Called once per render cycle. Must resolve before {@link files} is read.
|
|
15
|
-
*/
|
|
16
|
-
render(element: TElement): Promise<void>
|
|
17
|
-
/**
|
|
18
|
-
* Accumulated {@link FileNode} results produced by the last {@link render} call.
|
|
19
|
-
* Not populated when {@link stream} is implemented.
|
|
20
|
-
*/
|
|
21
|
-
readonly files: Array<FileNode>
|
|
22
|
-
/**
|
|
23
|
-
* When present, core calls this instead of {@link render} and {@link files},
|
|
24
|
-
* forwarding each file to `FileManager` as soon as it is ready.
|
|
25
|
-
*/
|
|
26
|
-
stream?(element: TElement): Iterable<FileNode>
|
|
27
|
-
/**
|
|
28
|
-
* Disposer hook so renderers participate in `using` blocks: `using r = rendererFactory()`
|
|
29
|
-
* runs cleanup on every exit path, including thrown errors.
|
|
30
|
-
*/
|
|
31
|
-
[Symbol.dispose](): void
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* A factory function that produces a fresh {@link Renderer} per render cycle.
|
|
36
|
-
*
|
|
37
|
-
* Generators use this to declare which renderer handles their output.
|
|
38
|
-
*/
|
|
39
|
-
export type RendererFactory<TElement = unknown> = () => Renderer<TElement>
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Defines a renderer factory. Renderers turn the generator's return value
|
|
43
|
-
* (JSX, a template string, a tree of any shape) into `FileNode`s that get
|
|
44
|
-
* written to disk.
|
|
45
|
-
*
|
|
46
|
-
* A renderer can target output formats beyond JSX, for instance a Handlebars
|
|
47
|
-
* renderer or one that writes binary files. Plugins and generators pick the
|
|
48
|
-
* renderer to use via the `renderer` field on `defineGenerator`.
|
|
49
|
-
*
|
|
50
|
-
* @example A minimal renderer that wraps a custom runtime
|
|
51
|
-
* ```ts
|
|
52
|
-
* import { createRenderer } from '@kubb/core'
|
|
53
|
-
*
|
|
54
|
-
* export const myRenderer = createRenderer(() => {
|
|
55
|
-
* const runtime = new MyRuntime()
|
|
56
|
-
* return {
|
|
57
|
-
* async render(element) {
|
|
58
|
-
* await runtime.render(element)
|
|
59
|
-
* },
|
|
60
|
-
* get files() {
|
|
61
|
-
* return runtime.files
|
|
62
|
-
* },
|
|
63
|
-
* [Symbol.dispose]() {
|
|
64
|
-
* runtime.dispose()
|
|
65
|
-
* },
|
|
66
|
-
* }
|
|
67
|
-
* })
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export function createRenderer<TElement = unknown>(factory: RendererFactory<TElement>): RendererFactory<TElement> {
|
|
71
|
-
return factory
|
|
72
|
-
}
|