@kubb/core 5.0.0-alpha.3 → 5.0.0-alpha.31
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/PluginDriver-D0dY_hpJ.d.ts +1986 -0
- package/dist/{chunk-ByKO4r7w.cjs → chunk-MlS0t1Af.cjs} +15 -0
- package/dist/chunk-O_arW02_.js +17 -0
- package/dist/hooks.cjs +13 -28
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +11 -37
- package/dist/hooks.js +14 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +1469 -831
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +572 -191
- package/dist/index.js +1443 -826
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Kubb.ts +38 -56
- package/src/KubbFile.ts +143 -0
- package/src/{PluginManager.ts → PluginDriver.ts} +159 -170
- package/src/build.ts +213 -65
- package/src/constants.ts +39 -6
- package/src/createAdapter.ts +25 -0
- package/src/createPlugin.ts +30 -0
- package/src/createStorage.ts +58 -0
- package/src/{config.ts → defineConfig.ts} +11 -16
- package/src/defineGenerator.ts +126 -0
- package/src/defineLogger.ts +13 -3
- package/src/defineParser.ts +57 -0
- package/src/definePresets.ts +16 -0
- package/src/defineResolver.ts +454 -0
- package/src/hooks/index.ts +1 -6
- package/src/hooks/useDriver.ts +11 -0
- package/src/hooks/useMode.ts +4 -4
- package/src/hooks/usePlugin.ts +3 -3
- package/src/index.ts +22 -10
- package/src/renderNode.tsx +25 -0
- package/src/storages/fsStorage.ts +2 -2
- package/src/storages/memoryStorage.ts +2 -2
- package/src/types.ts +639 -52
- package/src/utils/FunctionParams.ts +2 -2
- package/src/utils/TreeNode.ts +40 -2
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +29 -10
- package/src/utils/formatters.ts +10 -21
- package/src/utils/getBarrelFiles.ts +80 -10
- package/src/utils/getConfigs.ts +9 -23
- package/src/utils/getPreset.ts +78 -0
- package/src/utils/isInputPath.ts +8 -0
- package/src/utils/linters.ts +23 -3
- package/src/utils/packageJSON.ts +76 -0
- package/dist/chunk--u3MIqq1.js +0 -8
- package/dist/types-CiPWLv-5.d.ts +0 -1001
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PromiseManager.ts +0 -40
- package/src/defineAdapter.ts +0 -22
- package/src/definePlugin.ts +0 -12
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/useKubb.ts +0 -22
- package/src/hooks/usePluginManager.ts +0 -11
- package/src/utils/getPlugins.ts +0 -23
package/src/build.ts
CHANGED
|
@@ -1,41 +1,72 @@
|
|
|
1
1
|
import { dirname, relative, resolve } from 'node:path'
|
|
2
|
-
import { AsyncEventEmitter, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
|
|
3
|
-
import
|
|
4
|
-
import type {
|
|
2
|
+
import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
|
|
3
|
+
import { transform, walk } from '@kubb/ast'
|
|
4
|
+
import type { OperationNode } from '@kubb/ast/types'
|
|
5
|
+
import type { Fabric as FabricType } from '@kubb/fabric-core/types'
|
|
5
6
|
import { createFabric } from '@kubb/react-fabric'
|
|
6
|
-
import { typescriptParser } from '@kubb/react-fabric/parsers'
|
|
7
7
|
import { fsPlugin } from '@kubb/react-fabric/plugins'
|
|
8
|
-
import { isInputPath } from './config.ts'
|
|
9
8
|
import { BARREL_FILENAME, DEFAULT_BANNER, DEFAULT_CONCURRENCY, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
9
|
+
import { defineParser } from './defineParser.ts'
|
|
10
|
+
import type * as KubbFile from './KubbFile.ts'
|
|
11
|
+
import { PluginDriver } from './PluginDriver.ts'
|
|
12
|
+
import { applyHookResult } from './renderNode.tsx'
|
|
12
13
|
import { fsStorage } from './storages/fsStorage.ts'
|
|
13
|
-
import type { AdapterSource, Config,
|
|
14
|
+
import type { AdapterSource, Config, KubbEvents, Plugin, PluginContext, Storage, UserConfig } from './types.ts'
|
|
14
15
|
import { getDiagnosticInfo } from './utils/diagnostics.ts'
|
|
15
16
|
import type { FileMetaBase } from './utils/getBarrelFiles.ts'
|
|
17
|
+
import { getBarrelFiles } from './utils/getBarrelFiles.ts'
|
|
18
|
+
import { isInputPath } from './utils/isInputPath.ts'
|
|
16
19
|
|
|
17
20
|
type BuildOptions = {
|
|
18
21
|
config: UserConfig
|
|
19
22
|
events?: AsyncEventEmitter<KubbEvents>
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Full output produced by a successful or failed build.
|
|
27
|
+
*/
|
|
22
28
|
type BuildOutput = {
|
|
29
|
+
/**
|
|
30
|
+
* Plugins that threw during installation, paired with the caught error.
|
|
31
|
+
*/
|
|
23
32
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
24
|
-
fabric:
|
|
33
|
+
fabric: FabricType
|
|
25
34
|
files: Array<KubbFile.ResolvedFile>
|
|
26
|
-
|
|
35
|
+
driver: PluginDriver
|
|
36
|
+
/**
|
|
37
|
+
* Elapsed time in milliseconds for each plugin, keyed by plugin name.
|
|
38
|
+
*/
|
|
27
39
|
pluginTimings: Map<string, number>
|
|
28
40
|
error?: Error
|
|
41
|
+
/**
|
|
42
|
+
* Raw generated source, keyed by absolute file path.
|
|
43
|
+
*/
|
|
29
44
|
sources: Map<KubbFile.Path, string>
|
|
30
45
|
}
|
|
31
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Intermediate result returned by {@link setup} and accepted by {@link safeBuild}.
|
|
49
|
+
*/
|
|
32
50
|
type SetupResult = {
|
|
33
51
|
events: AsyncEventEmitter<KubbEvents>
|
|
34
|
-
fabric:
|
|
35
|
-
|
|
52
|
+
fabric: FabricType
|
|
53
|
+
driver: PluginDriver
|
|
36
54
|
sources: Map<KubbFile.Path, string>
|
|
55
|
+
config: Config
|
|
37
56
|
}
|
|
38
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
60
|
+
*
|
|
61
|
+
* - Validates the input path (when applicable).
|
|
62
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
63
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
64
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
65
|
+
* When no adapter is supplied and `@kubb/adapter-oas` is installed as an
|
|
66
|
+
*
|
|
67
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
68
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
69
|
+
*/
|
|
39
70
|
export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
40
71
|
const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
|
|
41
72
|
|
|
@@ -87,9 +118,15 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
87
118
|
}
|
|
88
119
|
}
|
|
89
120
|
|
|
90
|
-
|
|
121
|
+
if (!userConfig.adapter) {
|
|
122
|
+
throw new Error('Adapter should be defined')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const config: Config = {
|
|
91
126
|
root: userConfig.root || process.cwd(),
|
|
92
127
|
...userConfig,
|
|
128
|
+
parsers: userConfig.parsers ?? [],
|
|
129
|
+
adapter: userConfig.adapter,
|
|
93
130
|
output: {
|
|
94
131
|
write: true,
|
|
95
132
|
barrelType: 'named',
|
|
@@ -110,19 +147,35 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
110
147
|
// storage or fall back to fsStorage (backwards-compatible default).
|
|
111
148
|
// Keys are root-relative (e.g. `src/gen/api/getPets.ts`) so fsStorage()
|
|
112
149
|
// needs no configuration — it resolves them against process.cwd().
|
|
113
|
-
const storage:
|
|
150
|
+
const storage: Storage | null = config.output.write === false ? null : (config.output.storage ?? fsStorage())
|
|
114
151
|
|
|
115
|
-
if (
|
|
152
|
+
if (config.output.clean) {
|
|
116
153
|
await events.emit('debug', {
|
|
117
154
|
date: new Date(),
|
|
118
|
-
logs: ['Cleaning output directories', ` • Output: ${
|
|
155
|
+
logs: ['Cleaning output directories', ` • Output: ${config.output.path}`],
|
|
119
156
|
})
|
|
120
|
-
await storage?.clear(resolve(
|
|
157
|
+
await storage?.clear(resolve(config.root, config.output.path))
|
|
121
158
|
}
|
|
122
159
|
|
|
123
160
|
const fabric = createFabric()
|
|
124
161
|
fabric.use(fsPlugin)
|
|
125
|
-
|
|
162
|
+
|
|
163
|
+
for (const parser of config.parsers) {
|
|
164
|
+
fabric.use(parser)
|
|
165
|
+
}
|
|
166
|
+
// Catch-all fallback: joins all source values for any unhandled extension
|
|
167
|
+
fabric.use(
|
|
168
|
+
defineParser({
|
|
169
|
+
name: 'fallback',
|
|
170
|
+
extNames: undefined,
|
|
171
|
+
parse(file) {
|
|
172
|
+
return file.sources
|
|
173
|
+
.map((item) => item.value)
|
|
174
|
+
.filter((value): value is string => value != null)
|
|
175
|
+
.join('\n\n')
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
)
|
|
126
179
|
|
|
127
180
|
fabric.context.on('files:processing:start', (files) => {
|
|
128
181
|
events.emit('files:processing:start', files)
|
|
@@ -136,13 +189,13 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
136
189
|
const { file, source } = params
|
|
137
190
|
await events.emit('file:processing:update', {
|
|
138
191
|
...params,
|
|
139
|
-
config:
|
|
192
|
+
config: config,
|
|
140
193
|
source,
|
|
141
194
|
})
|
|
142
195
|
|
|
143
196
|
if (source) {
|
|
144
197
|
// Key is root-relative so it's meaningful for any backend (fs, S3, Redis…)
|
|
145
|
-
const key = relative(resolve(
|
|
198
|
+
const key = relative(resolve(config.root), file.path)
|
|
146
199
|
await storage?.setItem(key, source)
|
|
147
200
|
sources.set(file.path, source)
|
|
148
201
|
}
|
|
@@ -158,51 +211,57 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
158
211
|
|
|
159
212
|
await events.emit('debug', {
|
|
160
213
|
date: new Date(),
|
|
161
|
-
logs: [
|
|
162
|
-
'✓ Fabric initialized',
|
|
163
|
-
` • Storage: ${storage ? storage.name : 'disabled (dry-run)'}`,
|
|
164
|
-
` • Barrel type: ${definedConfig.output.barrelType || 'none'}`,
|
|
165
|
-
],
|
|
214
|
+
logs: ['✓ Fabric initialized', ` • Storage: ${storage ? storage.name : 'disabled (dry-run)'}`, ` • Barrel type: ${config.output.barrelType || 'none'}`],
|
|
166
215
|
})
|
|
167
216
|
|
|
168
|
-
const
|
|
217
|
+
const driver = new PluginDriver(config, {
|
|
169
218
|
fabric,
|
|
170
219
|
events,
|
|
171
220
|
concurrency: DEFAULT_CONCURRENCY,
|
|
172
221
|
})
|
|
173
222
|
|
|
174
|
-
// Run the adapter
|
|
175
|
-
if (definedConfig.adapter) {
|
|
176
|
-
const source = inputToAdapterSource(definedConfig)
|
|
223
|
+
// Run the adapter to produce the universal RootNode.
|
|
177
224
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
225
|
+
const adapter = config.adapter
|
|
226
|
+
if (!adapter) {
|
|
227
|
+
throw new Error('No adapter configured. Please provide an adapter in your kubb.config.ts.')
|
|
228
|
+
}
|
|
229
|
+
const source = inputToAdapterSource(config)
|
|
182
230
|
|
|
183
|
-
|
|
184
|
-
|
|
231
|
+
await events.emit('debug', {
|
|
232
|
+
date: new Date(),
|
|
233
|
+
logs: [`Running adapter: ${adapter.name}`],
|
|
234
|
+
})
|
|
185
235
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
236
|
+
driver.adapter = adapter
|
|
237
|
+
driver.rootNode = await adapter.parse(source)
|
|
238
|
+
|
|
239
|
+
await events.emit('debug', {
|
|
240
|
+
date: new Date(),
|
|
241
|
+
logs: [
|
|
242
|
+
`✓ Adapter '${adapter.name}' resolved RootNode`,
|
|
243
|
+
` • Schemas: ${driver.rootNode.schemas.length}`,
|
|
244
|
+
` • Operations: ${driver.rootNode.operations.length}`,
|
|
245
|
+
],
|
|
246
|
+
})
|
|
195
247
|
|
|
196
248
|
return {
|
|
249
|
+
config,
|
|
197
250
|
events,
|
|
198
251
|
fabric,
|
|
199
|
-
|
|
252
|
+
driver,
|
|
200
253
|
sources,
|
|
201
254
|
}
|
|
202
255
|
}
|
|
203
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
259
|
+
*
|
|
260
|
+
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
261
|
+
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
262
|
+
*/
|
|
204
263
|
export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
205
|
-
const { fabric, files,
|
|
264
|
+
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
|
|
206
265
|
|
|
207
266
|
if (error) {
|
|
208
267
|
throw error
|
|
@@ -218,27 +277,88 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
|
|
|
218
277
|
failedPlugins,
|
|
219
278
|
fabric,
|
|
220
279
|
files,
|
|
221
|
-
|
|
280
|
+
driver,
|
|
222
281
|
pluginTimings,
|
|
223
282
|
error: undefined,
|
|
224
283
|
sources,
|
|
225
284
|
}
|
|
226
285
|
}
|
|
227
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
289
|
+
* (`schema`, `operation`, `operations`).
|
|
290
|
+
*
|
|
291
|
+
* - Each hook accepts a single handler **or an array** — all entries are called in sequence.
|
|
292
|
+
* - Nodes that are excluded by `exclude`/`include` plugin options are skipped automatically.
|
|
293
|
+
* - Return values are handled via `applyHookResult`: React elements are rendered,
|
|
294
|
+
* `KubbFile.File[]` are written via upsert, and `void` is a no-op (manual handling).
|
|
295
|
+
* - Barrel files are generated automatically when `output.barrelType` is set.
|
|
296
|
+
*/
|
|
297
|
+
async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promise<void> {
|
|
298
|
+
const { adapter, rootNode, resolver, fabric } = context
|
|
299
|
+
const { exclude, include, override } = plugin.options
|
|
300
|
+
|
|
301
|
+
if (!adapter || !rootNode) {
|
|
302
|
+
throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const collectedOperations: Array<OperationNode> = []
|
|
306
|
+
|
|
307
|
+
await walk(rootNode, {
|
|
308
|
+
depth: 'shallow',
|
|
309
|
+
async schema(node) {
|
|
310
|
+
if (!plugin.schema) return
|
|
311
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
312
|
+
const options = resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
313
|
+
if (options === null) return
|
|
314
|
+
const result = await plugin.schema.call(context, transformedNode, options)
|
|
315
|
+
|
|
316
|
+
await applyHookResult(result, fabric)
|
|
317
|
+
},
|
|
318
|
+
async operation(node) {
|
|
319
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
320
|
+
const options = resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
321
|
+
if (options !== null) {
|
|
322
|
+
collectedOperations.push(transformedNode)
|
|
323
|
+
if (plugin.operation) {
|
|
324
|
+
const result = await plugin.operation.call(context, transformedNode, options)
|
|
325
|
+
await applyHookResult(result, fabric)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
if (plugin.operations && collectedOperations.length > 0) {
|
|
332
|
+
const result = await plugin.operations.call(context, collectedOperations, plugin.options)
|
|
333
|
+
|
|
334
|
+
await applyHookResult(result, fabric)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Runs a full Kubb build and captures errors instead of throwing.
|
|
340
|
+
*
|
|
341
|
+
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
342
|
+
* - Generates the root barrel file when `output.barrelType` is set.
|
|
343
|
+
* - Writes all files through Fabric.
|
|
344
|
+
*
|
|
345
|
+
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
346
|
+
* `failedPlugins` to determine whether the build succeeded.
|
|
347
|
+
*/
|
|
228
348
|
export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
229
|
-
const { fabric,
|
|
349
|
+
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options)
|
|
230
350
|
|
|
231
351
|
const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
|
|
232
352
|
// in ms
|
|
233
353
|
const pluginTimings = new Map<string, number>()
|
|
234
|
-
const config =
|
|
354
|
+
const config = driver.config
|
|
235
355
|
|
|
236
356
|
try {
|
|
237
|
-
for (const plugin of
|
|
238
|
-
const context =
|
|
357
|
+
for (const plugin of driver.plugins.values()) {
|
|
358
|
+
const context = driver.getContext(plugin)
|
|
239
359
|
const hrStart = process.hrtime()
|
|
240
|
-
|
|
241
|
-
const
|
|
360
|
+
const { output } = plugin.options ?? {}
|
|
361
|
+
const root = resolve(config.root, config.output.path)
|
|
242
362
|
|
|
243
363
|
try {
|
|
244
364
|
const timestamp = new Date()
|
|
@@ -247,10 +367,26 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
247
367
|
|
|
248
368
|
await events.emit('debug', {
|
|
249
369
|
date: timestamp,
|
|
250
|
-
logs: ['
|
|
370
|
+
logs: ['Starting plugin...', ` • Plugin Name: ${plugin.name}`],
|
|
251
371
|
})
|
|
252
372
|
|
|
253
|
-
|
|
373
|
+
// Call buildStart() for any custom plugin logic
|
|
374
|
+
await plugin.buildStart.call(context)
|
|
375
|
+
|
|
376
|
+
// Dispatch schema/operation/operations hooks (direct hooks or composed via composeGenerators)
|
|
377
|
+
if (plugin.schema || plugin.operation || plugin.operations) {
|
|
378
|
+
await runPluginAstHooks(plugin, context)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (output) {
|
|
382
|
+
const barrelFiles = await getBarrelFiles(fabric.files, {
|
|
383
|
+
type: output.barrelType ?? 'named',
|
|
384
|
+
root,
|
|
385
|
+
output,
|
|
386
|
+
meta: { pluginName: plugin.name },
|
|
387
|
+
})
|
|
388
|
+
await context.upsertFile(...barrelFiles)
|
|
389
|
+
}
|
|
254
390
|
|
|
255
391
|
const duration = getElapsedMs(hrStart)
|
|
256
392
|
pluginTimings.set(plugin.name, duration)
|
|
@@ -259,7 +395,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
259
395
|
|
|
260
396
|
await events.emit('debug', {
|
|
261
397
|
date: new Date(),
|
|
262
|
-
logs: [`✓ Plugin
|
|
398
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
|
|
263
399
|
})
|
|
264
400
|
} catch (caughtError) {
|
|
265
401
|
const error = caughtError as Error
|
|
@@ -275,7 +411,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
275
411
|
await events.emit('debug', {
|
|
276
412
|
date: errorTimestamp,
|
|
277
413
|
logs: [
|
|
278
|
-
'✗ Plugin
|
|
414
|
+
'✗ Plugin start failed',
|
|
279
415
|
` • Plugin Name: ${plugin.name}`,
|
|
280
416
|
` • Error: ${error.constructor.name} - ${error.message}`,
|
|
281
417
|
' • Stack Trace:',
|
|
@@ -314,7 +450,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
314
450
|
const rootFile: KubbFile.File = {
|
|
315
451
|
path: rootPath,
|
|
316
452
|
baseName: BARREL_FILENAME,
|
|
317
|
-
exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
453
|
+
exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
|
|
318
454
|
sources: [],
|
|
319
455
|
imports: [],
|
|
320
456
|
meta: {},
|
|
@@ -332,11 +468,19 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
332
468
|
|
|
333
469
|
await fabric.write({ extension: config.output.extension })
|
|
334
470
|
|
|
471
|
+
// Call buildEnd() on each plugin after all files are written
|
|
472
|
+
for (const plugin of driver.plugins.values()) {
|
|
473
|
+
if (plugin.buildEnd) {
|
|
474
|
+
const context = driver.getContext(plugin)
|
|
475
|
+
await plugin.buildEnd.call(context)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
335
479
|
return {
|
|
336
480
|
failedPlugins,
|
|
337
481
|
fabric,
|
|
338
482
|
files,
|
|
339
|
-
|
|
483
|
+
driver,
|
|
340
484
|
pluginTimings,
|
|
341
485
|
sources,
|
|
342
486
|
}
|
|
@@ -345,7 +489,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
345
489
|
failedPlugins,
|
|
346
490
|
fabric,
|
|
347
491
|
files: [],
|
|
348
|
-
|
|
492
|
+
driver,
|
|
349
493
|
pluginTimings,
|
|
350
494
|
error: error as Error,
|
|
351
495
|
sources,
|
|
@@ -358,12 +502,12 @@ type BuildBarrelExportsParams = {
|
|
|
358
502
|
rootDir: string
|
|
359
503
|
existingExports: Set<string>
|
|
360
504
|
config: Config
|
|
361
|
-
|
|
505
|
+
driver: PluginDriver
|
|
362
506
|
}
|
|
363
507
|
|
|
364
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config,
|
|
508
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): KubbFile.Export[] {
|
|
365
509
|
const pluginNameMap = new Map<string, Plugin>()
|
|
366
|
-
for (const plugin of
|
|
510
|
+
for (const plugin of driver.plugins.values()) {
|
|
367
511
|
pluginNameMap.set(plugin.name, plugin)
|
|
368
512
|
}
|
|
369
513
|
|
|
@@ -377,7 +521,7 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, plu
|
|
|
377
521
|
|
|
378
522
|
const meta = file.meta as FileMetaBase | undefined
|
|
379
523
|
const plugin = meta?.pluginName ? pluginNameMap.get(meta.pluginName) : undefined
|
|
380
|
-
const pluginOptions = plugin?.options
|
|
524
|
+
const pluginOptions = plugin?.options
|
|
381
525
|
|
|
382
526
|
if (!pluginOptions || pluginOptions.output?.barrelType === false) {
|
|
383
527
|
return []
|
|
@@ -407,7 +551,7 @@ function inputToAdapterSource(config: Config): AdapterSource {
|
|
|
407
551
|
if (Array.isArray(config.input)) {
|
|
408
552
|
return {
|
|
409
553
|
type: 'paths',
|
|
410
|
-
paths: config.input.map((i) => resolve(config.root, i.path)),
|
|
554
|
+
paths: config.input.map((i) => (new URLPath(i.path).isURL ? i.path : resolve(config.root, i.path))),
|
|
411
555
|
}
|
|
412
556
|
}
|
|
413
557
|
|
|
@@ -415,6 +559,10 @@ function inputToAdapterSource(config: Config): AdapterSource {
|
|
|
415
559
|
return { type: 'data', data: config.input.data }
|
|
416
560
|
}
|
|
417
561
|
|
|
562
|
+
if (new URLPath(config.input.path).isURL) {
|
|
563
|
+
return { type: 'path', path: config.input.path }
|
|
564
|
+
}
|
|
565
|
+
|
|
418
566
|
const resolved = resolve(config.root, config.input.path)
|
|
419
567
|
return { type: 'path', path: resolved }
|
|
420
568
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type * as KubbFile from './KubbFile.ts'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Base URL for the Kubb Studio web app.
|
|
5
|
+
*/
|
|
3
6
|
export const DEFAULT_STUDIO_URL = 'https://studio.kubb.dev' as const
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Default number of plugins that may run concurrently during a build.
|
|
10
|
+
*/
|
|
9
11
|
export const DEFAULT_CONCURRENCY = 15
|
|
10
12
|
|
|
13
|
+
/**
|
|
14
|
+
* File name used for generated barrel (index) files.
|
|
15
|
+
*/
|
|
11
16
|
export const BARREL_FILENAME = 'index.ts' as const
|
|
12
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Default banner style written at the top of every generated file.
|
|
20
|
+
*/
|
|
13
21
|
export const DEFAULT_BANNER = 'simple' as const
|
|
14
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
25
|
+
*/
|
|
15
26
|
export const DEFAULT_EXTENSION: Record<KubbFile.Extname, KubbFile.Extname | ''> = { '.ts': '.ts' }
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Characters recognized as path separators on both POSIX and Windows.
|
|
30
|
+
*/
|
|
31
|
+
export const PATH_SEPARATORS = new Set(['/', '\\'] as const)
|
|
18
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
35
|
+
*
|
|
36
|
+
* Higher numbers are more verbose.
|
|
37
|
+
*/
|
|
19
38
|
export const logLevel = {
|
|
20
39
|
silent: Number.NEGATIVE_INFINITY,
|
|
21
40
|
error: 0,
|
|
@@ -25,6 +44,13 @@ export const logLevel = {
|
|
|
25
44
|
debug: 5,
|
|
26
45
|
} as const
|
|
27
46
|
|
|
47
|
+
/**
|
|
48
|
+
* CLI command descriptors for each supported linter.
|
|
49
|
+
*
|
|
50
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
51
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
52
|
+
* the linter is not found.
|
|
53
|
+
*/
|
|
28
54
|
export const linters = {
|
|
29
55
|
eslint: {
|
|
30
56
|
command: 'eslint',
|
|
@@ -43,6 +69,13 @@ export const linters = {
|
|
|
43
69
|
},
|
|
44
70
|
} as const
|
|
45
71
|
|
|
72
|
+
/**
|
|
73
|
+
* CLI command descriptors for each supported code formatter.
|
|
74
|
+
*
|
|
75
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
76
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
77
|
+
* the formatter is not found.
|
|
78
|
+
*/
|
|
46
79
|
export const formatters = {
|
|
47
80
|
prettier: {
|
|
48
81
|
command: 'prettier',
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Adapter, AdapterFactoryOptions } from './types.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builder type for an {@link Adapter} — takes options and returns the adapter instance.
|
|
5
|
+
*/
|
|
6
|
+
type AdapterBuilder<T extends AdapterFactoryOptions> = (options: T['options']) => Adapter<T>
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates an adapter factory. Call the returned function with optional options to get the adapter instance.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* export const myAdapter = createAdapter<MyAdapter>((options) => {
|
|
13
|
+
* return {
|
|
14
|
+
* name: 'my-adapter',
|
|
15
|
+
* options,
|
|
16
|
+
* async parse(source) { ... },
|
|
17
|
+
* }
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // instantiate
|
|
21
|
+
* const adapter = myAdapter({ validate: true })
|
|
22
|
+
*/
|
|
23
|
+
export function createAdapter<T extends AdapterFactoryOptions = AdapterFactoryOptions>(build: AdapterBuilder<T>): (options?: T['options']) => Adapter<T> {
|
|
24
|
+
return (options) => build(options ?? ({} as T['options']))
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PluginFactoryOptions, UserPluginWithLifeCycle } from './types.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builder type for a {@link UserPluginWithLifeCycle} — takes options and returns the plugin instance.
|
|
5
|
+
*/
|
|
6
|
+
type PluginBuilder<T extends PluginFactoryOptions = PluginFactoryOptions> = (options: T['options']) => UserPluginWithLifeCycle<T>
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a plugin factory. Call the returned function with optional options to get the plugin instance.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* export const myPlugin = createPlugin<MyPlugin>((options) => {
|
|
14
|
+
* return {
|
|
15
|
+
* name: 'my-plugin',
|
|
16
|
+
* get options() { return options },
|
|
17
|
+
* resolvePath(baseName) { ... },
|
|
18
|
+
* resolveName(name, type) { ... },
|
|
19
|
+
* }
|
|
20
|
+
* })
|
|
21
|
+
*
|
|
22
|
+
* // instantiate
|
|
23
|
+
* const plugin = myPlugin({ output: { path: 'src/gen' } })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function createPlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(
|
|
27
|
+
build: PluginBuilder<T>,
|
|
28
|
+
): (options?: T['options']) => UserPluginWithLifeCycle<T> {
|
|
29
|
+
return (options) => build(options ?? ({} as T['options']))
|
|
30
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type Storage = {
|
|
2
|
+
/**
|
|
3
|
+
* Identifier used for logging and debugging (e.g. `'fs'`, `'s3'`).
|
|
4
|
+
*/
|
|
5
|
+
readonly name: string
|
|
6
|
+
/**
|
|
7
|
+
* Returns `true` when an entry for `key` exists in storage.
|
|
8
|
+
*/
|
|
9
|
+
hasItem(key: string): Promise<boolean>
|
|
10
|
+
/**
|
|
11
|
+
* Returns the stored string value, or `null` when `key` does not exist.
|
|
12
|
+
*/
|
|
13
|
+
getItem(key: string): Promise<string | null>
|
|
14
|
+
/**
|
|
15
|
+
* Persists `value` under `key`, creating any required structure.
|
|
16
|
+
*/
|
|
17
|
+
setItem(key: string, value: string): Promise<void>
|
|
18
|
+
/**
|
|
19
|
+
* Removes the entry for `key`. No-ops when the key does not exist.
|
|
20
|
+
*/
|
|
21
|
+
removeItem(key: string): Promise<void>
|
|
22
|
+
/**
|
|
23
|
+
* Returns all keys, optionally filtered to those starting with `base`.
|
|
24
|
+
*/
|
|
25
|
+
getKeys(base?: string): Promise<Array<string>>
|
|
26
|
+
/**
|
|
27
|
+
* Removes all entries, optionally scoped to those starting with `base`.
|
|
28
|
+
*/
|
|
29
|
+
clear(base?: string): Promise<void>
|
|
30
|
+
/**
|
|
31
|
+
* Optional teardown hook called after the build completes.
|
|
32
|
+
*/
|
|
33
|
+
dispose?(): Promise<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates a storage factory. Call the returned function with optional options to get the storage instance.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* export const memoryStorage = createStorage(() => {
|
|
41
|
+
* const store = new Map<string, string>()
|
|
42
|
+
* return {
|
|
43
|
+
* name: 'memory',
|
|
44
|
+
* async hasItem(key) { return store.has(key) },
|
|
45
|
+
* async getItem(key) { return store.get(key) ?? null },
|
|
46
|
+
* async setItem(key, value) { store.set(key, value) },
|
|
47
|
+
* async removeItem(key) { store.delete(key) },
|
|
48
|
+
* async getKeys(base) {
|
|
49
|
+
* const keys = [...store.keys()]
|
|
50
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
51
|
+
* },
|
|
52
|
+
* async clear(base) { if (!base) store.clear() },
|
|
53
|
+
* }
|
|
54
|
+
* })
|
|
55
|
+
*/
|
|
56
|
+
export function createStorage<TOptions = Record<string, never>>(build: (options: TOptions) => Storage): (options?: TOptions) => Storage {
|
|
57
|
+
return (options) => build(options ?? ({} as TOptions))
|
|
58
|
+
}
|