@kubb/core 5.0.0-alpha.31 → 5.0.0-alpha.32
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 → PluginDriver-nm7tvGs9.d.ts} +105 -196
- package/dist/chunk--u3MIqq1.js +8 -0
- package/dist/{chunk-MlS0t1Af.cjs → chunk-ByKO4r7w.cjs} +0 -15
- package/dist/hooks.cjs +1 -1
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +339 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -21
- package/dist/index.js +342 -194
- package/dist/index.js.map +1 -1
- package/package.json +2 -4
- package/src/FileManager.ts +131 -0
- package/src/FileProcessor.ts +83 -0
- package/src/Kubb.ts +5 -5
- package/src/PluginDriver.ts +29 -24
- package/src/build.ts +89 -107
- package/src/constants.ts +7 -2
- package/src/defineGenerator.ts +12 -13
- package/src/defineParser.ts +4 -4
- package/src/defineResolver.ts +18 -19
- package/src/devtools.ts +14 -14
- package/src/hooks/useMode.ts +2 -3
- package/src/index.ts +0 -1
- package/src/renderNode.tsx +8 -7
- package/src/types.ts +42 -45
- package/src/utils/TreeNode.ts +7 -7
- package/src/utils/getBarrelFiles.ts +30 -25
- package/dist/chunk-O_arW02_.js +0 -17
- package/src/KubbFile.ts +0 -143
package/src/build.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { dirname,
|
|
1
|
+
import { dirname, resolve } from 'node:path'
|
|
2
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'
|
|
6
|
-
import { createFabric } from '@kubb/react-fabric'
|
|
7
|
-
import { fsPlugin } from '@kubb/react-fabric/plugins'
|
|
3
|
+
import { createExport, createFile, transform, walk } from '@kubb/ast'
|
|
4
|
+
import type { ExportNode, FileNode, OperationNode } from '@kubb/ast/types'
|
|
8
5
|
import { BARREL_FILENAME, DEFAULT_BANNER, DEFAULT_CONCURRENCY, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
6
|
+
import type { Parser } from './defineParser.ts'
|
|
7
|
+
import { FileProcessor } from './FileProcessor.ts'
|
|
11
8
|
import { PluginDriver } from './PluginDriver.ts'
|
|
12
9
|
import { applyHookResult } from './renderNode.tsx'
|
|
13
10
|
import { fsStorage } from './storages/fsStorage.ts'
|
|
@@ -30,8 +27,7 @@ type BuildOutput = {
|
|
|
30
27
|
* Plugins that threw during installation, paired with the caught error.
|
|
31
28
|
*/
|
|
32
29
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
33
|
-
|
|
34
|
-
files: Array<KubbFile.ResolvedFile>
|
|
30
|
+
files: Array<FileNode>
|
|
35
31
|
driver: PluginDriver
|
|
36
32
|
/**
|
|
37
33
|
* Elapsed time in milliseconds for each plugin, keyed by plugin name.
|
|
@@ -41,7 +37,7 @@ type BuildOutput = {
|
|
|
41
37
|
/**
|
|
42
38
|
* Raw generated source, keyed by absolute file path.
|
|
43
39
|
*/
|
|
44
|
-
sources: Map<
|
|
40
|
+
sources: Map<string, string>
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
/**
|
|
@@ -49,10 +45,10 @@ type BuildOutput = {
|
|
|
49
45
|
*/
|
|
50
46
|
type SetupResult = {
|
|
51
47
|
events: AsyncEventEmitter<KubbEvents>
|
|
52
|
-
fabric: FabricType
|
|
53
48
|
driver: PluginDriver
|
|
54
|
-
sources: Map<
|
|
49
|
+
sources: Map<string, string>
|
|
55
50
|
config: Config
|
|
51
|
+
storage: Storage | null
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
/**
|
|
@@ -61,7 +57,7 @@ type SetupResult = {
|
|
|
61
57
|
* - Validates the input path (when applicable).
|
|
62
58
|
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
63
59
|
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
64
|
-
* - Runs the adapter (if configured) to produce the universal `
|
|
60
|
+
* - Runs the adapter (if configured) to produce the universal `InputNode`.
|
|
65
61
|
* When no adapter is supplied and `@kubb/adapter-oas` is installed as an
|
|
66
62
|
*
|
|
67
63
|
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
@@ -70,7 +66,7 @@ type SetupResult = {
|
|
|
70
66
|
export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
71
67
|
const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
|
|
72
68
|
|
|
73
|
-
const sources: Map<
|
|
69
|
+
const sources: Map<string, string> = new Map<string, string>()
|
|
74
70
|
const diagnosticInfo = getDiagnosticInfo()
|
|
75
71
|
|
|
76
72
|
if (Array.isArray(userConfig.input)) {
|
|
@@ -145,8 +141,8 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
145
141
|
|
|
146
142
|
// write: false is the explicit dry-run opt-out; otherwise use the provided
|
|
147
143
|
// storage or fall back to fsStorage (backwards-compatible default).
|
|
148
|
-
//
|
|
149
|
-
//
|
|
144
|
+
// Storage keys are the absolute file.path values so fsStorage() resolves
|
|
145
|
+
// them correctly regardless of the current working directory.
|
|
150
146
|
const storage: Storage | null = config.output.write === false ? null : (config.output.storage ?? fsStorage())
|
|
151
147
|
|
|
152
148
|
if (config.output.clean) {
|
|
@@ -157,70 +153,12 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
157
153
|
await storage?.clear(resolve(config.root, config.output.path))
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
const fabric = createFabric()
|
|
161
|
-
fabric.use(fsPlugin)
|
|
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
|
-
)
|
|
179
|
-
|
|
180
|
-
fabric.context.on('files:processing:start', (files) => {
|
|
181
|
-
events.emit('files:processing:start', files)
|
|
182
|
-
events.emit('debug', {
|
|
183
|
-
date: new Date(),
|
|
184
|
-
logs: [`Writing ${files.length} files...`],
|
|
185
|
-
})
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
fabric.context.on('file:processing:update', async (params) => {
|
|
189
|
-
const { file, source } = params
|
|
190
|
-
await events.emit('file:processing:update', {
|
|
191
|
-
...params,
|
|
192
|
-
config: config,
|
|
193
|
-
source,
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
if (source) {
|
|
197
|
-
// Key is root-relative so it's meaningful for any backend (fs, S3, Redis…)
|
|
198
|
-
const key = relative(resolve(config.root), file.path)
|
|
199
|
-
await storage?.setItem(key, source)
|
|
200
|
-
sources.set(file.path, source)
|
|
201
|
-
}
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
fabric.context.on('files:processing:end', async (files) => {
|
|
205
|
-
await events.emit('files:processing:end', files)
|
|
206
|
-
await events.emit('debug', {
|
|
207
|
-
date: new Date(),
|
|
208
|
-
logs: [`✓ File write process completed for ${files.length} files`],
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
await events.emit('debug', {
|
|
213
|
-
date: new Date(),
|
|
214
|
-
logs: ['✓ Fabric initialized', ` • Storage: ${storage ? storage.name : 'disabled (dry-run)'}`, ` • Barrel type: ${config.output.barrelType || 'none'}`],
|
|
215
|
-
})
|
|
216
|
-
|
|
217
156
|
const driver = new PluginDriver(config, {
|
|
218
|
-
fabric,
|
|
219
157
|
events,
|
|
220
158
|
concurrency: DEFAULT_CONCURRENCY,
|
|
221
159
|
})
|
|
222
160
|
|
|
223
|
-
// Run the adapter to produce the universal
|
|
161
|
+
// Run the adapter to produce the universal InputNode.
|
|
224
162
|
|
|
225
163
|
const adapter = config.adapter
|
|
226
164
|
if (!adapter) {
|
|
@@ -234,23 +172,23 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
234
172
|
})
|
|
235
173
|
|
|
236
174
|
driver.adapter = adapter
|
|
237
|
-
driver.
|
|
175
|
+
driver.inputNode = await adapter.parse(source)
|
|
238
176
|
|
|
239
177
|
await events.emit('debug', {
|
|
240
178
|
date: new Date(),
|
|
241
179
|
logs: [
|
|
242
|
-
`✓ Adapter '${adapter.name}' resolved
|
|
243
|
-
` • Schemas: ${driver.
|
|
244
|
-
` • Operations: ${driver.
|
|
180
|
+
`✓ Adapter '${adapter.name}' resolved InputNode`,
|
|
181
|
+
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
182
|
+
` • Operations: ${driver.inputNode.operations.length}`,
|
|
245
183
|
],
|
|
246
184
|
})
|
|
247
185
|
|
|
248
186
|
return {
|
|
249
187
|
config,
|
|
250
188
|
events,
|
|
251
|
-
fabric,
|
|
252
189
|
driver,
|
|
253
190
|
sources,
|
|
191
|
+
storage,
|
|
254
192
|
}
|
|
255
193
|
}
|
|
256
194
|
|
|
@@ -261,7 +199,7 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
261
199
|
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
262
200
|
*/
|
|
263
201
|
export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
264
|
-
const {
|
|
202
|
+
const { files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
|
|
265
203
|
|
|
266
204
|
if (error) {
|
|
267
205
|
throw error
|
|
@@ -275,7 +213,6 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
|
|
|
275
213
|
|
|
276
214
|
return {
|
|
277
215
|
failedPlugins,
|
|
278
|
-
fabric,
|
|
279
216
|
files,
|
|
280
217
|
driver,
|
|
281
218
|
pluginTimings,
|
|
@@ -291,20 +228,20 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
|
|
|
291
228
|
* - Each hook accepts a single handler **or an array** — all entries are called in sequence.
|
|
292
229
|
* - Nodes that are excluded by `exclude`/`include` plugin options are skipped automatically.
|
|
293
230
|
* - Return values are handled via `applyHookResult`: React elements are rendered,
|
|
294
|
-
* `
|
|
231
|
+
* `FileNode[]` are written via upsert, and `void` is a no-op (manual handling).
|
|
295
232
|
* - Barrel files are generated automatically when `output.barrelType` is set.
|
|
296
233
|
*/
|
|
297
234
|
async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promise<void> {
|
|
298
|
-
const { adapter,
|
|
235
|
+
const { adapter, inputNode, resolver, driver } = context
|
|
299
236
|
const { exclude, include, override } = plugin.options
|
|
300
237
|
|
|
301
|
-
if (!adapter || !
|
|
238
|
+
if (!adapter || !inputNode) {
|
|
302
239
|
throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. pluginOas()) before this plugin in your Kubb config.`)
|
|
303
240
|
}
|
|
304
241
|
|
|
305
242
|
const collectedOperations: Array<OperationNode> = []
|
|
306
243
|
|
|
307
|
-
await walk(
|
|
244
|
+
await walk(inputNode, {
|
|
308
245
|
depth: 'shallow',
|
|
309
246
|
async schema(node) {
|
|
310
247
|
if (!plugin.schema) return
|
|
@@ -313,7 +250,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
|
|
|
313
250
|
if (options === null) return
|
|
314
251
|
const result = await plugin.schema.call(context, transformedNode, options)
|
|
315
252
|
|
|
316
|
-
await applyHookResult(result,
|
|
253
|
+
await applyHookResult(result, driver)
|
|
317
254
|
},
|
|
318
255
|
async operation(node) {
|
|
319
256
|
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
@@ -322,7 +259,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
|
|
|
322
259
|
collectedOperations.push(transformedNode)
|
|
323
260
|
if (plugin.operation) {
|
|
324
261
|
const result = await plugin.operation.call(context, transformedNode, options)
|
|
325
|
-
await applyHookResult(result,
|
|
262
|
+
await applyHookResult(result, driver)
|
|
326
263
|
}
|
|
327
264
|
}
|
|
328
265
|
},
|
|
@@ -331,7 +268,7 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
|
|
|
331
268
|
if (plugin.operations && collectedOperations.length > 0) {
|
|
332
269
|
const result = await plugin.operations.call(context, collectedOperations, plugin.options)
|
|
333
270
|
|
|
334
|
-
await applyHookResult(result,
|
|
271
|
+
await applyHookResult(result, driver)
|
|
335
272
|
}
|
|
336
273
|
}
|
|
337
274
|
|
|
@@ -340,13 +277,14 @@ async function runPluginAstHooks(plugin: Plugin, context: PluginContext): Promis
|
|
|
340
277
|
*
|
|
341
278
|
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
342
279
|
* - Generates the root barrel file when `output.barrelType` is set.
|
|
343
|
-
* - Writes all files through
|
|
280
|
+
* - Writes all files through the driver's FileManager and FileProcessor.
|
|
344
281
|
*
|
|
345
282
|
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
346
283
|
* `failedPlugins` to determine whether the build succeeded.
|
|
347
284
|
*/
|
|
348
285
|
export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
349
|
-
const
|
|
286
|
+
const setupResult = overrides ? overrides : await setup(options)
|
|
287
|
+
const { driver, events, sources, storage } = setupResult
|
|
350
288
|
|
|
351
289
|
const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
|
|
352
290
|
// in ms
|
|
@@ -379,7 +317,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
379
317
|
}
|
|
380
318
|
|
|
381
319
|
if (output) {
|
|
382
|
-
const barrelFiles = await getBarrelFiles(
|
|
320
|
+
const barrelFiles = await getBarrelFiles(driver.fileManager.files, {
|
|
383
321
|
type: output.barrelType ?? 'named',
|
|
384
322
|
root,
|
|
385
323
|
output,
|
|
@@ -433,7 +371,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
433
371
|
logs: ['Generating barrel file', ` • Type: ${config.output.barrelType}`, ` • Path: ${rootPath}`],
|
|
434
372
|
})
|
|
435
373
|
|
|
436
|
-
const barrelFiles =
|
|
374
|
+
const barrelFiles = driver.fileManager.files.filter((file) => {
|
|
437
375
|
return file.sources.some((source) => source.isIndexable)
|
|
438
376
|
})
|
|
439
377
|
|
|
@@ -442,21 +380,21 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
442
380
|
logs: [`Found ${barrelFiles.length} indexable files for barrel export`],
|
|
443
381
|
})
|
|
444
382
|
|
|
445
|
-
const existingBarrel =
|
|
383
|
+
const existingBarrel = driver.fileManager.files.find((f) => f.path === rootPath)
|
|
446
384
|
const existingExports = new Set(
|
|
447
385
|
existingBarrel?.exports?.flatMap((e) => (Array.isArray(e.name) ? e.name : [e.name])).filter((n): n is string => Boolean(n)) ?? [],
|
|
448
386
|
)
|
|
449
387
|
|
|
450
|
-
const rootFile
|
|
388
|
+
const rootFile = createFile<object>({
|
|
451
389
|
path: rootPath,
|
|
452
390
|
baseName: BARREL_FILENAME,
|
|
453
|
-
exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
|
|
391
|
+
exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }).map((e) => createExport(e)),
|
|
454
392
|
sources: [],
|
|
455
393
|
imports: [],
|
|
456
394
|
meta: {},
|
|
457
|
-
}
|
|
395
|
+
})
|
|
458
396
|
|
|
459
|
-
|
|
397
|
+
driver.fileManager.upsert(rootFile)
|
|
460
398
|
|
|
461
399
|
await events.emit('debug', {
|
|
462
400
|
date: new Date(),
|
|
@@ -464,9 +402,55 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
464
402
|
})
|
|
465
403
|
}
|
|
466
404
|
|
|
467
|
-
const files =
|
|
405
|
+
const files = driver.fileManager.files
|
|
406
|
+
|
|
407
|
+
// Build a parsers map from config.parsers
|
|
408
|
+
const parsersMap = new Map<FileNode['extname'], Parser>()
|
|
409
|
+
for (const parser of config.parsers) {
|
|
410
|
+
if (parser.extNames) {
|
|
411
|
+
for (const extname of parser.extNames) {
|
|
412
|
+
parsersMap.set(extname, parser)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const fileProcessor = new FileProcessor()
|
|
418
|
+
|
|
419
|
+
await events.emit('debug', {
|
|
420
|
+
date: new Date(),
|
|
421
|
+
logs: [`Writing ${files.length} files...`],
|
|
422
|
+
})
|
|
468
423
|
|
|
469
|
-
await
|
|
424
|
+
await fileProcessor.run(files, {
|
|
425
|
+
parsers: parsersMap,
|
|
426
|
+
extension: config.output.extension,
|
|
427
|
+
onStart: async (processingFiles) => {
|
|
428
|
+
await events.emit('files:processing:start', processingFiles)
|
|
429
|
+
},
|
|
430
|
+
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
431
|
+
await events.emit('file:processing:update', {
|
|
432
|
+
file,
|
|
433
|
+
source,
|
|
434
|
+
processed,
|
|
435
|
+
total,
|
|
436
|
+
percentage,
|
|
437
|
+
config,
|
|
438
|
+
})
|
|
439
|
+
if (source) {
|
|
440
|
+
// Use the absolute file.path as the storage key so fsStorage resolves
|
|
441
|
+
// it correctly regardless of the current working directory.
|
|
442
|
+
await storage?.setItem(file.path, source)
|
|
443
|
+
sources.set(file.path, source)
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
onEnd: async (processedFiles) => {
|
|
447
|
+
await events.emit('files:processing:end', processedFiles)
|
|
448
|
+
await events.emit('debug', {
|
|
449
|
+
date: new Date(),
|
|
450
|
+
logs: [`✓ File write process completed for ${processedFiles.length} files`],
|
|
451
|
+
})
|
|
452
|
+
},
|
|
453
|
+
})
|
|
470
454
|
|
|
471
455
|
// Call buildEnd() on each plugin after all files are written
|
|
472
456
|
for (const plugin of driver.plugins.values()) {
|
|
@@ -478,7 +462,6 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
478
462
|
|
|
479
463
|
return {
|
|
480
464
|
failedPlugins,
|
|
481
|
-
fabric,
|
|
482
465
|
files,
|
|
483
466
|
driver,
|
|
484
467
|
pluginTimings,
|
|
@@ -487,7 +470,6 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
487
470
|
} catch (error) {
|
|
488
471
|
return {
|
|
489
472
|
failedPlugins,
|
|
490
|
-
fabric,
|
|
491
473
|
files: [],
|
|
492
474
|
driver,
|
|
493
475
|
pluginTimings,
|
|
@@ -498,14 +480,14 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
498
480
|
}
|
|
499
481
|
|
|
500
482
|
type BuildBarrelExportsParams = {
|
|
501
|
-
barrelFiles:
|
|
483
|
+
barrelFiles: FileNode[]
|
|
502
484
|
rootDir: string
|
|
503
485
|
existingExports: Set<string>
|
|
504
486
|
config: Config
|
|
505
487
|
driver: PluginDriver
|
|
506
488
|
}
|
|
507
489
|
|
|
508
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams):
|
|
490
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): ExportNode[] {
|
|
509
491
|
const pluginNameMap = new Map<string, Plugin>()
|
|
510
492
|
for (const plugin of driver.plugins.values()) {
|
|
511
493
|
pluginNameMap.set(plugin.name, plugin)
|
|
@@ -533,11 +515,11 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, dri
|
|
|
533
515
|
}
|
|
534
516
|
|
|
535
517
|
return [
|
|
536
|
-
{
|
|
518
|
+
createExport({
|
|
537
519
|
name: exportName,
|
|
538
520
|
path: getRelativePath(rootDir, file.path),
|
|
539
521
|
isTypeOnly: config.output.barrelType === 'all' ? containsOnlyTypes : source.isTypeOnly,
|
|
540
|
-
}
|
|
522
|
+
}),
|
|
541
523
|
]
|
|
542
524
|
})
|
|
543
525
|
})
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { FileNode } from '@kubb/ast/types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Base URL for the Kubb Studio web app.
|
|
@@ -10,6 +10,11 @@ export const DEFAULT_STUDIO_URL = 'https://studio.kubb.dev' as const
|
|
|
10
10
|
*/
|
|
11
11
|
export const DEFAULT_CONCURRENCY = 15
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Maximum number of files processed in parallel by FileProcessor.
|
|
15
|
+
*/
|
|
16
|
+
export const PARALLEL_CONCURRENCY_LIMIT = 100
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* File name used for generated barrel (index) files.
|
|
15
20
|
*/
|
|
@@ -23,7 +28,7 @@ export const DEFAULT_BANNER = 'simple' as const
|
|
|
23
28
|
/**
|
|
24
29
|
* Default file-extension mapping used when no explicit mapping is configured.
|
|
25
30
|
*/
|
|
26
|
-
export const DEFAULT_EXTENSION: Record<
|
|
31
|
+
export const DEFAULT_EXTENSION: Record<FileNode['extname'], FileNode['extname'] | ''> = { '.ts': '.ts' }
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
34
|
* Characters recognized as path separators on both POSIX and Windows.
|
package/src/defineGenerator.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PossiblePromise } from '@internals/utils'
|
|
2
|
-
import type { OperationNode, SchemaNode } from '@kubb/ast/types'
|
|
2
|
+
import type { FileNode, OperationNode, SchemaNode } from '@kubb/ast/types'
|
|
3
3
|
import type { FabricReactNode } from '@kubb/react-fabric/types'
|
|
4
|
-
import type * as KubbFile from './KubbFile.ts'
|
|
5
4
|
import { applyHookResult } from './renderNode.tsx'
|
|
6
5
|
import type { GeneratorContext, PluginFactoryOptions } from './types.ts'
|
|
7
6
|
|
|
@@ -10,10 +9,10 @@ export type { GeneratorContext } from './types.ts'
|
|
|
10
9
|
/**
|
|
11
10
|
* A generator is a named object with optional `schema`, `operation`, and `operations`
|
|
12
11
|
* methods. Each method is called with `this = PluginContext` of the parent plugin,
|
|
13
|
-
* giving full access to `this.config`, `this.resolver`, `this.adapter`,
|
|
12
|
+
* giving full access to `this.config`, `this.resolver`, `this.adapter`,
|
|
14
13
|
* `this.driver`, etc.
|
|
15
14
|
*
|
|
16
|
-
* Return a React element, an array of `
|
|
15
|
+
* Return a React element, an array of `FileNode`, or `void` to handle file
|
|
17
16
|
* writing manually via `this.upsertFile`. Both React and core (non-React) generators
|
|
18
17
|
* use the same method signatures — the return type determines how output is handled.
|
|
19
18
|
*
|
|
@@ -36,32 +35,32 @@ export type Generator<TOptions extends PluginFactoryOptions = PluginFactoryOptio
|
|
|
36
35
|
name: string
|
|
37
36
|
/**
|
|
38
37
|
* Called for each schema node in the AST walk.
|
|
39
|
-
* `this` is the parent plugin's context with `adapter` and `
|
|
38
|
+
* `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
|
|
40
39
|
* `options` contains the per-node resolved options (after exclude/include/override).
|
|
41
40
|
*/
|
|
42
41
|
schema?: (
|
|
43
42
|
this: GeneratorContext<TOptions>,
|
|
44
43
|
node: SchemaNode,
|
|
45
44
|
options: TOptions['resolvedOptions'],
|
|
46
|
-
) => PossiblePromise<FabricReactNode | Array<
|
|
45
|
+
) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
|
|
47
46
|
/**
|
|
48
47
|
* Called for each operation node in the AST walk.
|
|
49
|
-
* `this` is the parent plugin's context with `adapter` and `
|
|
48
|
+
* `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
|
|
50
49
|
*/
|
|
51
50
|
operation?: (
|
|
52
51
|
this: GeneratorContext<TOptions>,
|
|
53
52
|
node: OperationNode,
|
|
54
53
|
options: TOptions['resolvedOptions'],
|
|
55
|
-
) => PossiblePromise<FabricReactNode | Array<
|
|
54
|
+
) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
|
|
56
55
|
/**
|
|
57
56
|
* Called once after all operations have been walked.
|
|
58
|
-
* `this` is the parent plugin's context with `adapter` and `
|
|
57
|
+
* `this` is the parent plugin's context with `adapter` and `inputNode` guaranteed present.
|
|
59
58
|
*/
|
|
60
59
|
operations?: (
|
|
61
60
|
this: GeneratorContext<TOptions>,
|
|
62
61
|
nodes: Array<OperationNode>,
|
|
63
62
|
options: TOptions['resolvedOptions'],
|
|
64
|
-
) => PossiblePromise<FabricReactNode | Array<
|
|
63
|
+
) => PossiblePromise<FabricReactNode | Array<FileNode> | void>
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
/**
|
|
@@ -103,7 +102,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
|
|
|
103
102
|
if (!gen.schema) continue
|
|
104
103
|
const result = await gen.schema.call(this, node, options)
|
|
105
104
|
|
|
106
|
-
await applyHookResult(result, this.
|
|
105
|
+
await applyHookResult(result, this.driver)
|
|
107
106
|
}
|
|
108
107
|
},
|
|
109
108
|
async operation(node, options) {
|
|
@@ -111,7 +110,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
|
|
|
111
110
|
if (!gen.operation) continue
|
|
112
111
|
const result = await gen.operation.call(this, node, options)
|
|
113
112
|
|
|
114
|
-
await applyHookResult(result, this.
|
|
113
|
+
await applyHookResult(result, this.driver)
|
|
115
114
|
}
|
|
116
115
|
},
|
|
117
116
|
async operations(nodes, options) {
|
|
@@ -119,7 +118,7 @@ export function mergeGenerators<TOptions extends PluginFactoryOptions = PluginFa
|
|
|
119
118
|
if (!gen.operations) continue
|
|
120
119
|
const result = await gen.operations.call(this, nodes, options)
|
|
121
120
|
|
|
122
|
-
await applyHookResult(result, this.
|
|
121
|
+
await applyHookResult(result, this.driver)
|
|
123
122
|
}
|
|
124
123
|
},
|
|
125
124
|
}
|
package/src/defineParser.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { FileNode } from '@kubb/ast/types'
|
|
2
2
|
|
|
3
3
|
type PrintOptions = {
|
|
4
|
-
extname?:
|
|
4
|
+
extname?: FileNode['extname']
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type Parser<TMeta extends object = any> = {
|
|
@@ -13,7 +13,7 @@ export type Parser<TMeta extends object = any> = {
|
|
|
13
13
|
*
|
|
14
14
|
* @example ['.ts', '.js']
|
|
15
15
|
*/
|
|
16
|
-
extNames: Array<
|
|
16
|
+
extNames: Array<FileNode['extname']> | undefined
|
|
17
17
|
/**
|
|
18
18
|
* @deprecated Will be removed once Fabric no longer requires it.
|
|
19
19
|
* @default () => {}
|
|
@@ -22,7 +22,7 @@ export type Parser<TMeta extends object = any> = {
|
|
|
22
22
|
/**
|
|
23
23
|
* Convert a resolved file to a string.
|
|
24
24
|
*/
|
|
25
|
-
parse(file:
|
|
25
|
+
parse(file: FileNode<TMeta>, options?: PrintOptions): Promise<string> | string
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export type UserParser<TMeta extends object = any> = Omit<Parser<TMeta>, 'type' | 'install'> & {
|
package/src/defineResolver.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { camelCase, pascalCase } from '@internals/utils'
|
|
3
|
-
import { isOperationNode, isSchemaNode } from '@kubb/ast'
|
|
4
|
-
import type { Node, OperationNode,
|
|
5
|
-
import type * as KubbFile from './KubbFile.ts'
|
|
3
|
+
import { createFile, isOperationNode, isSchemaNode } from '@kubb/ast'
|
|
4
|
+
import type { FileNode, InputNode, Node, OperationNode, SchemaNode } from '@kubb/ast/types'
|
|
6
5
|
import { getMode } from './PluginDriver.ts'
|
|
7
6
|
import type {
|
|
8
7
|
Config,
|
|
@@ -195,24 +194,24 @@ export function defaultResolveOptions<TOptions>(
|
|
|
195
194
|
* // → '/src/types'
|
|
196
195
|
* ```
|
|
197
196
|
*/
|
|
198
|
-
export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }: ResolverPathParams, { root, output, group }: ResolverContext):
|
|
197
|
+
export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }: ResolverPathParams, { root, output, group }: ResolverContext): string {
|
|
199
198
|
const mode = pathMode ?? getMode(path.resolve(root, output.path))
|
|
200
199
|
|
|
201
200
|
if (mode === 'single') {
|
|
202
|
-
return path.resolve(root, output.path)
|
|
201
|
+
return path.resolve(root, output.path)
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
if (group && (groupPath || tag)) {
|
|
206
|
-
return path.resolve(root, output.path, group.name({ group: group.type === 'path' ? groupPath! : tag! }), baseName)
|
|
205
|
+
return path.resolve(root, output.path, group.name({ group: group.type === 'path' ? groupPath! : tag! }), baseName)
|
|
207
206
|
}
|
|
208
207
|
|
|
209
|
-
return path.resolve(root, output.path, baseName)
|
|
208
|
+
return path.resolve(root, output.path, baseName)
|
|
210
209
|
}
|
|
211
210
|
|
|
212
211
|
/**
|
|
213
212
|
* Default file resolver used by `defineResolver`.
|
|
214
213
|
*
|
|
215
|
-
* Resolves a `
|
|
214
|
+
* Resolves a `FileNode` by combining name resolution (`resolver.default`) with
|
|
216
215
|
* path resolution (`resolver.resolvePath`). The resolved file always has empty
|
|
217
216
|
* `sources`, `imports`, and `exports` arrays — consumers populate those separately.
|
|
218
217
|
*
|
|
@@ -236,22 +235,22 @@ export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }:
|
|
|
236
235
|
* // → { baseName: 'listPets.ts', path: '/src/types/petsController/listPets.ts', ... }
|
|
237
236
|
* ```
|
|
238
237
|
*/
|
|
239
|
-
export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext):
|
|
238
|
+
export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext): FileNode {
|
|
240
239
|
const pathMode = getMode(path.resolve(context.root, context.output.path))
|
|
241
240
|
const resolvedName = pathMode === 'single' ? '' : this.default(name, 'file')
|
|
242
|
-
const baseName = `${resolvedName}${extname}` as
|
|
241
|
+
const baseName = `${resolvedName}${extname}` as FileNode['baseName']
|
|
243
242
|
const filePath = this.resolvePath({ baseName, pathMode, tag, path: groupPath }, context)
|
|
244
243
|
|
|
245
|
-
return {
|
|
244
|
+
return createFile({
|
|
246
245
|
path: filePath,
|
|
247
|
-
baseName: path.basename(filePath) as
|
|
246
|
+
baseName: path.basename(filePath) as `${string}.${string}`,
|
|
248
247
|
meta: {
|
|
249
248
|
pluginName: this.pluginName,
|
|
250
249
|
},
|
|
251
250
|
sources: [],
|
|
252
251
|
imports: [],
|
|
253
252
|
exports: [],
|
|
254
|
-
}
|
|
253
|
+
})
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
/**
|
|
@@ -333,13 +332,13 @@ export function buildDefaultBanner({
|
|
|
333
332
|
*
|
|
334
333
|
* @example Function banner with node
|
|
335
334
|
* ```ts
|
|
336
|
-
* defaultResolveBanner(
|
|
335
|
+
* defaultResolveBanner(inputNode, { output: { banner: (node) => `// v${node.version}` }, config })
|
|
337
336
|
* // → '// v3.0.0'
|
|
338
337
|
* ```
|
|
339
338
|
*
|
|
340
339
|
* @example No user banner — Kubb notice with OAS metadata
|
|
341
340
|
* ```ts
|
|
342
|
-
* defaultResolveBanner(
|
|
341
|
+
* defaultResolveBanner(inputNode, { config })
|
|
343
342
|
* // → '/** Generated by Kubb ... Title: Pet Store ... *\/'
|
|
344
343
|
* ```
|
|
345
344
|
*
|
|
@@ -349,7 +348,7 @@ export function buildDefaultBanner({
|
|
|
349
348
|
* // → undefined
|
|
350
349
|
* ```
|
|
351
350
|
*/
|
|
352
|
-
export function defaultResolveBanner(node:
|
|
351
|
+
export function defaultResolveBanner(node: InputNode | undefined, { output, config }: ResolveBannerContext): string | undefined {
|
|
353
352
|
if (typeof output?.banner === 'function') {
|
|
354
353
|
return output.banner(node)
|
|
355
354
|
}
|
|
@@ -381,11 +380,11 @@ export function defaultResolveBanner(node: RootNode | undefined, { output, confi
|
|
|
381
380
|
*
|
|
382
381
|
* @example Function footer with node
|
|
383
382
|
* ```ts
|
|
384
|
-
* defaultResolveFooter(
|
|
383
|
+
* defaultResolveFooter(inputNode, { output: { footer: (node) => `// ${node.title}` }, config })
|
|
385
384
|
* // → '// Pet Store'
|
|
386
385
|
* ```
|
|
387
386
|
*/
|
|
388
|
-
export function defaultResolveFooter(node:
|
|
387
|
+
export function defaultResolveFooter(node: InputNode | undefined, { output }: ResolveBannerContext): string | undefined {
|
|
389
388
|
if (typeof output?.footer === 'function') {
|
|
390
389
|
return node ? output.footer(node) : undefined
|
|
391
390
|
}
|
|
@@ -403,7 +402,7 @@ export function defaultResolveFooter(node: RootNode | undefined, { output }: Res
|
|
|
403
402
|
* - `default` — name casing strategy (camelCase / PascalCase)
|
|
404
403
|
* - `resolveOptions` — include/exclude/override filtering
|
|
405
404
|
* - `resolvePath` — output path computation
|
|
406
|
-
* - `resolveFile` — full `
|
|
405
|
+
* - `resolveFile` — full `FileNode` construction
|
|
407
406
|
*
|
|
408
407
|
* Methods in the builder have access to `this` (the full resolver object), so they
|
|
409
408
|
* can call other resolver methods without circular imports.
|