@kubb/core 5.0.0-alpha.21 → 5.0.0-alpha.22
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-CEQPafXV.d.ts → PluginDriver-DZdEyCoa.d.ts} +87 -33
- package/dist/hooks.cjs +5 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +8 -3
- package/dist/hooks.js +5 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +37 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +20 -12
- package/dist/index.js +37 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/Kubb.ts +5 -5
- package/src/PluginDriver.ts +29 -9
- package/src/build.ts +9 -9
- package/src/constants.ts +2 -2
- package/src/defineGenerator.ts +22 -7
- package/src/defineResolver.ts +13 -10
- package/src/hooks/useDriver.ts +5 -0
- package/src/hooks/useMode.ts +3 -3
- package/src/types.ts +39 -18
- package/src/utils/TreeNode.ts +22 -7
- package/src/utils/getBarrelFiles.ts +9 -6
- package/src/utils/mergeResolvers.ts +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/core",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.22",
|
|
4
4
|
"description": "Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"remeda": "^2.33.6",
|
|
72
72
|
"semver": "^7.7.4",
|
|
73
73
|
"tinyexec": "^1.0.4",
|
|
74
|
-
"@kubb/ast": "5.0.0-alpha.
|
|
74
|
+
"@kubb/ast": "5.0.0-alpha.22"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@types/semver": "^7.7.1",
|
package/src/Kubb.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
2
2
|
import type { Strategy } from './PluginDriver.ts'
|
|
3
3
|
import type { Config, Plugin, PluginLifecycleHooks } from './types'
|
|
4
4
|
|
|
@@ -76,7 +76,7 @@ export interface KubbEvents {
|
|
|
76
76
|
/**
|
|
77
77
|
* Emitted when code generation phase completes.
|
|
78
78
|
*/
|
|
79
|
-
'generation:end': [config: Config, files: Array<
|
|
79
|
+
'generation:end': [config: Config, files: Array<FabricFile.ResolvedFile>, sources: Map<FabricFile.Path, string>]
|
|
80
80
|
/**
|
|
81
81
|
* Emitted with a summary of the generation results.
|
|
82
82
|
* Contains summary lines, title, and success status.
|
|
@@ -160,7 +160,7 @@ export interface KubbEvents {
|
|
|
160
160
|
* Emitted when file processing starts.
|
|
161
161
|
* Contains the list of files to be processed.
|
|
162
162
|
*/
|
|
163
|
-
'files:processing:start': [files: Array<
|
|
163
|
+
'files:processing:start': [files: Array<FabricFile.ResolvedFile>]
|
|
164
164
|
/**
|
|
165
165
|
* Emitted for each file being processed, providing progress updates.
|
|
166
166
|
* Contains processed count, total count, percentage, and file details.
|
|
@@ -186,7 +186,7 @@ export interface KubbEvents {
|
|
|
186
186
|
/**
|
|
187
187
|
* The file being processed.
|
|
188
188
|
*/
|
|
189
|
-
file:
|
|
189
|
+
file: FabricFile.ResolvedFile
|
|
190
190
|
/**
|
|
191
191
|
* Kubb configuration (not present in Fabric).
|
|
192
192
|
* Provides access to the current config during file processing.
|
|
@@ -198,7 +198,7 @@ export interface KubbEvents {
|
|
|
198
198
|
* Emitted when file processing completes.
|
|
199
199
|
* Contains the list of processed files.
|
|
200
200
|
*/
|
|
201
|
-
'files:processing:end': [files: Array<
|
|
201
|
+
'files:processing:end': [files: Array<FabricFile.ResolvedFile>]
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* Emitted when a plugin starts executing.
|
package/src/PluginDriver.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { performance } from 'node:perf_hooks'
|
|
|
3
3
|
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
4
|
import { isPromiseRejectedResult, setUniqueName, transformReservedWord, ValidationPluginError } from '@internals/utils'
|
|
5
5
|
import type { RootNode } from '@kubb/ast/types'
|
|
6
|
-
import type { Fabric as FabricType
|
|
6
|
+
import type { FabricFile, Fabric as FabricType } from '@kubb/fabric-core/types'
|
|
7
7
|
import { CORE_PLUGIN_NAME, DEFAULT_STUDIO_URL } from './constants.ts'
|
|
8
8
|
import { openInStudio as openInStudioFn } from './devtools.ts'
|
|
9
9
|
|
|
@@ -27,6 +27,14 @@ import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
|
|
|
27
27
|
|
|
28
28
|
type RequiredPluginLifecycle = Required<PluginLifecycle>
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Hook dispatch strategy used by the `PluginDriver`.
|
|
32
|
+
*
|
|
33
|
+
* - `hookFirst` — stops at the first non-null result.
|
|
34
|
+
* - `hookForPlugin` — calls only the matching plugin.
|
|
35
|
+
* - `hookParallel` — calls all plugins concurrently.
|
|
36
|
+
* - `hookSeq` — calls all plugins in order, threading the result.
|
|
37
|
+
*/
|
|
30
38
|
export type Strategy = 'hookFirst' | 'hookForPlugin' | 'hookParallel' | 'hookSeq'
|
|
31
39
|
|
|
32
40
|
type ParseResult<H extends PluginLifecycleHooks> = RequiredPluginLifecycle[H]
|
|
@@ -47,15 +55,27 @@ type Options = {
|
|
|
47
55
|
concurrency?: number
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Parameters accepted by `PluginDriver.getFile` to resolve a generated file descriptor.
|
|
60
|
+
*/
|
|
50
61
|
export type GetFileOptions<TOptions = object> = {
|
|
51
62
|
name: string
|
|
52
|
-
mode?:
|
|
53
|
-
extname:
|
|
63
|
+
mode?: FabricFile.Mode
|
|
64
|
+
extname: FabricFile.Extname
|
|
54
65
|
pluginName: string
|
|
55
66
|
options?: TOptions
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Returns `'single'` when `fileOrFolder` has a file extension, `'split'` otherwise.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* getMode('src/gen/types.ts') // 'single'
|
|
75
|
+
* getMode('src/gen/types') // 'split'
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function getMode(fileOrFolder: string | undefined | null): FabricFile.Mode {
|
|
59
79
|
if (!fileOrFolder) {
|
|
60
80
|
return 'split'
|
|
61
81
|
}
|
|
@@ -104,10 +124,10 @@ export class PluginDriver {
|
|
|
104
124
|
events: this.options.events,
|
|
105
125
|
driver: this,
|
|
106
126
|
mode: getMode(resolve(this.config.root, this.config.output.path)),
|
|
107
|
-
addFile: async (...files: Array<
|
|
127
|
+
addFile: async (...files: Array<FabricFile.File>) => {
|
|
108
128
|
await this.options.fabric.addFile(...files)
|
|
109
129
|
},
|
|
110
|
-
upsertFile: async (...files: Array<
|
|
130
|
+
upsertFile: async (...files: Array<FabricFile.File>) => {
|
|
111
131
|
await this.options.fabric.upsertFile(...files)
|
|
112
132
|
},
|
|
113
133
|
get rootNode(): RootNode | undefined {
|
|
@@ -160,7 +180,7 @@ export class PluginDriver {
|
|
|
160
180
|
return this.#getSortedPlugins()
|
|
161
181
|
}
|
|
162
182
|
|
|
163
|
-
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileOptions<TOptions>):
|
|
183
|
+
getFile<TOptions = object>({ name, mode, extname, pluginName, options }: GetFileOptions<TOptions>): FabricFile.File<{ pluginName: string }> {
|
|
164
184
|
const resolvedName = mode ? (mode === 'single' ? '' : this.resolveName({ name, pluginName, type: 'file' })) : name
|
|
165
185
|
|
|
166
186
|
const path = this.resolvePath({
|
|
@@ -176,7 +196,7 @@ export class PluginDriver {
|
|
|
176
196
|
|
|
177
197
|
return {
|
|
178
198
|
path,
|
|
179
|
-
baseName: basename(path) as
|
|
199
|
+
baseName: basename(path) as FabricFile.File['baseName'],
|
|
180
200
|
meta: {
|
|
181
201
|
pluginName,
|
|
182
202
|
},
|
|
@@ -186,7 +206,7 @@ export class PluginDriver {
|
|
|
186
206
|
}
|
|
187
207
|
}
|
|
188
208
|
|
|
189
|
-
resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>):
|
|
209
|
+
resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): FabricFile.Path => {
|
|
190
210
|
const root = resolve(this.config.root, this.config.output.path)
|
|
191
211
|
const defaultPath = resolve(root, params.baseName)
|
|
192
212
|
|
package/src/build.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname, relative, resolve } from 'node:path'
|
|
2
2
|
import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, getRelativePath, URLPath } from '@internals/utils'
|
|
3
|
-
import type { Fabric as FabricType
|
|
3
|
+
import type { FabricFile, Fabric as FabricType } from '@kubb/fabric-core/types'
|
|
4
4
|
import { createFabric } from '@kubb/react-fabric'
|
|
5
5
|
import { typescriptParser } from '@kubb/react-fabric/parsers'
|
|
6
6
|
import { fsPlugin } from '@kubb/react-fabric/plugins'
|
|
@@ -26,7 +26,7 @@ type BuildOutput = {
|
|
|
26
26
|
*/
|
|
27
27
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
28
28
|
fabric: FabricType
|
|
29
|
-
files: Array<
|
|
29
|
+
files: Array<FabricFile.ResolvedFile>
|
|
30
30
|
driver: PluginDriver
|
|
31
31
|
/**
|
|
32
32
|
* Elapsed time in milliseconds for each plugin, keyed by plugin name.
|
|
@@ -36,7 +36,7 @@ type BuildOutput = {
|
|
|
36
36
|
/**
|
|
37
37
|
* Raw generated source, keyed by absolute file path.
|
|
38
38
|
*/
|
|
39
|
-
sources: Map<
|
|
39
|
+
sources: Map<FabricFile.Path, string>
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -46,7 +46,7 @@ type SetupResult = {
|
|
|
46
46
|
events: AsyncEventEmitter<KubbEvents>
|
|
47
47
|
fabric: FabricType
|
|
48
48
|
driver: PluginDriver
|
|
49
|
-
sources: Map<
|
|
49
|
+
sources: Map<FabricFile.Path, string>
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -63,7 +63,7 @@ type SetupResult = {
|
|
|
63
63
|
export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
64
64
|
const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
|
|
65
65
|
|
|
66
|
-
const sources: Map<
|
|
66
|
+
const sources: Map<FabricFile.Path, string> = new Map<FabricFile.Path, string>()
|
|
67
67
|
const diagnosticInfo = getDiagnosticInfo()
|
|
68
68
|
|
|
69
69
|
if (Array.isArray(userConfig.input)) {
|
|
@@ -351,7 +351,7 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
351
351
|
existingBarrel?.exports?.flatMap((e) => (Array.isArray(e.name) ? e.name : [e.name])).filter((n): n is string => Boolean(n)) ?? [],
|
|
352
352
|
)
|
|
353
353
|
|
|
354
|
-
const rootFile:
|
|
354
|
+
const rootFile: FabricFile.File = {
|
|
355
355
|
path: rootPath,
|
|
356
356
|
baseName: BARREL_FILENAME,
|
|
357
357
|
exports: buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }),
|
|
@@ -394,14 +394,14 @@ export async function safeBuild(options: BuildOptions, overrides?: SetupResult):
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
type BuildBarrelExportsParams = {
|
|
397
|
-
barrelFiles:
|
|
397
|
+
barrelFiles: FabricFile.ResolvedFile[]
|
|
398
398
|
rootDir: string
|
|
399
399
|
existingExports: Set<string>
|
|
400
400
|
config: Config
|
|
401
401
|
driver: PluginDriver
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
-
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams):
|
|
404
|
+
function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, driver }: BuildBarrelExportsParams): FabricFile.Export[] {
|
|
405
405
|
const pluginNameMap = new Map<string, Plugin>()
|
|
406
406
|
for (const plugin of driver.plugins) {
|
|
407
407
|
pluginNameMap.set(plugin.name, plugin)
|
|
@@ -433,7 +433,7 @@ function buildBarrelExports({ barrelFiles, rootDir, existingExports, config, dri
|
|
|
433
433
|
name: exportName,
|
|
434
434
|
path: getRelativePath(rootDir, file.path),
|
|
435
435
|
isTypeOnly: config.output.barrelType === 'all' ? containsOnlyTypes : source.isTypeOnly,
|
|
436
|
-
} satisfies
|
|
436
|
+
} satisfies FabricFile.Export,
|
|
437
437
|
]
|
|
438
438
|
})
|
|
439
439
|
})
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Base URL for the Kubb Studio web app.
|
|
@@ -33,7 +33,7 @@ export const DEFAULT_BANNER = 'simple' as const
|
|
|
33
33
|
/**
|
|
34
34
|
* Default file-extension mapping used when no explicit mapping is configured.
|
|
35
35
|
*/
|
|
36
|
-
export const DEFAULT_EXTENSION: Record<
|
|
36
|
+
export const DEFAULT_EXTENSION: Record<FabricFile.Extname, FabricFile.Extname | ''> = { '.ts': '.ts' }
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Characters recognized as path separators on both POSIX and Windows.
|
package/src/defineGenerator.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OperationNode, SchemaNode } from '@kubb/ast/types'
|
|
2
|
-
import type {
|
|
2
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
3
3
|
import type { FabricReactNode } from '@kubb/react-fabric/types'
|
|
4
4
|
import type { Adapter, Config, Plugin, PluginFactoryOptions } from './types.ts'
|
|
5
5
|
|
|
@@ -38,15 +38,21 @@ export type SchemaV2Props<TPlugin extends PluginFactoryOptions = PluginFactoryOp
|
|
|
38
38
|
node: SchemaNode
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Input shape for a core v2 async generator — lifecycle methods are optional.
|
|
43
|
+
*/
|
|
41
44
|
type UserCoreGeneratorV2<TPlugin extends PluginFactoryOptions> = {
|
|
42
45
|
name: string
|
|
43
46
|
type: 'core'
|
|
44
47
|
version?: '2'
|
|
45
|
-
operations?(props: OperationsV2Props<TPlugin>): Promise<Array<
|
|
46
|
-
operation?(props: OperationV2Props<TPlugin>): Promise<Array<
|
|
47
|
-
schema?(props: SchemaV2Props<TPlugin>): Promise<Array<
|
|
48
|
+
operations?(props: OperationsV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
49
|
+
operation?(props: OperationV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
50
|
+
schema?(props: SchemaV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Input shape for a React v2 generator — component methods are optional.
|
|
55
|
+
*/
|
|
50
56
|
type UserReactGeneratorV2<TPlugin extends PluginFactoryOptions> = {
|
|
51
57
|
name: string
|
|
52
58
|
type: 'react'
|
|
@@ -56,15 +62,21 @@ type UserReactGeneratorV2<TPlugin extends PluginFactoryOptions> = {
|
|
|
56
62
|
Schema?(props: SchemaV2Props<TPlugin>): FabricReactNode
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
/**
|
|
66
|
+
* A fully resolved core v2 generator with `version: '2'` and guaranteed async lifecycle methods.
|
|
67
|
+
*/
|
|
59
68
|
export type CoreGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
|
|
60
69
|
name: string
|
|
61
70
|
type: 'core'
|
|
62
71
|
version: '2'
|
|
63
|
-
operations(props: OperationsV2Props<TPlugin>): Promise<Array<
|
|
64
|
-
operation(props: OperationV2Props<TPlugin>): Promise<Array<
|
|
65
|
-
schema(props: SchemaV2Props<TPlugin>): Promise<Array<
|
|
72
|
+
operations(props: OperationsV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
73
|
+
operation(props: OperationV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
74
|
+
schema(props: SchemaV2Props<TPlugin>): Promise<Array<FabricFile.File>>
|
|
66
75
|
}
|
|
67
76
|
|
|
77
|
+
/**
|
|
78
|
+
* A fully resolved React v2 generator with `version: '2'` and guaranteed component methods.
|
|
79
|
+
*/
|
|
68
80
|
export type ReactGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = {
|
|
69
81
|
name: string
|
|
70
82
|
type: 'react'
|
|
@@ -74,6 +86,9 @@ export type ReactGeneratorV2<TPlugin extends PluginFactoryOptions = PluginFactor
|
|
|
74
86
|
Schema(props: SchemaV2Props<TPlugin>): FabricReactNode
|
|
75
87
|
}
|
|
76
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Union of all v2 generator shapes accepted by the plugin system.
|
|
91
|
+
*/
|
|
77
92
|
export type Generator<TPlugin extends PluginFactoryOptions = PluginFactoryOptions> = UserCoreGeneratorV2<TPlugin> | UserReactGeneratorV2<TPlugin>
|
|
78
93
|
|
|
79
94
|
/**
|
package/src/defineResolver.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path'
|
|
|
2
2
|
import { camelCase, pascalCase } from '@internals/utils'
|
|
3
3
|
import { isOperationNode, isSchemaNode } from '@kubb/ast'
|
|
4
4
|
import type { Node, OperationNode, RootNode, SchemaNode } from '@kubb/ast/types'
|
|
5
|
-
import type {
|
|
5
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
6
6
|
import { getMode } from './PluginDriver.ts'
|
|
7
7
|
import type {
|
|
8
8
|
Config,
|
|
@@ -193,11 +193,14 @@ export function defaultResolveOptions<TOptions>(
|
|
|
193
193
|
* // → '/src/types'
|
|
194
194
|
* ```
|
|
195
195
|
*/
|
|
196
|
-
export function defaultResolvePath(
|
|
196
|
+
export function defaultResolvePath(
|
|
197
|
+
{ baseName, pathMode, tag, path: groupPath }: ResolverPathParams,
|
|
198
|
+
{ root, output, group }: ResolverContext,
|
|
199
|
+
): FabricFile.Path {
|
|
197
200
|
const mode = pathMode ?? getMode(path.resolve(root, output.path))
|
|
198
201
|
|
|
199
202
|
if (mode === 'single') {
|
|
200
|
-
return path.resolve(root, output.path) as
|
|
203
|
+
return path.resolve(root, output.path) as FabricFile.Path
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
if (group && (groupPath || tag)) {
|
|
@@ -210,16 +213,16 @@ export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }:
|
|
|
210
213
|
return `${camelCase(ctx.group)}Controller`
|
|
211
214
|
}
|
|
212
215
|
|
|
213
|
-
return path.resolve(root, output.path, groupName({ group: group.type === 'path' ? groupPath! : tag! }), baseName) as
|
|
216
|
+
return path.resolve(root, output.path, groupName({ group: group.type === 'path' ? groupPath! : tag! }), baseName) as FabricFile.Path
|
|
214
217
|
}
|
|
215
218
|
|
|
216
|
-
return path.resolve(root, output.path, baseName) as
|
|
219
|
+
return path.resolve(root, output.path, baseName) as FabricFile.Path
|
|
217
220
|
}
|
|
218
221
|
|
|
219
222
|
/**
|
|
220
223
|
* Default file resolver used by `defineResolver`.
|
|
221
224
|
*
|
|
222
|
-
* Resolves a `
|
|
225
|
+
* Resolves a `FabricFile.File` by combining name resolution (`resolver.default`) with
|
|
223
226
|
* path resolution (`resolver.resolvePath`). The resolved file always has empty
|
|
224
227
|
* `sources`, `imports`, and `exports` arrays — consumers populate those separately.
|
|
225
228
|
*
|
|
@@ -243,15 +246,15 @@ export function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }:
|
|
|
243
246
|
* // → { baseName: 'listPets.ts', path: '/src/types/petsController/listPets.ts', ... }
|
|
244
247
|
* ```
|
|
245
248
|
*/
|
|
246
|
-
export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext):
|
|
249
|
+
export function defaultResolveFile(this: Resolver, { name, extname, tag, path: groupPath }: ResolverFileParams, context: ResolverContext): FabricFile.File {
|
|
247
250
|
const pathMode = getMode(path.resolve(context.root, context.output.path))
|
|
248
251
|
const resolvedName = pathMode === 'single' ? '' : this.default(name, 'file')
|
|
249
|
-
const baseName = `${resolvedName}${extname}` as
|
|
252
|
+
const baseName = `${resolvedName}${extname}` as FabricFile.BaseName
|
|
250
253
|
const filePath = this.resolvePath({ baseName, pathMode, tag, path: groupPath }, context)
|
|
251
254
|
|
|
252
255
|
return {
|
|
253
256
|
path: filePath,
|
|
254
|
-
baseName: path.basename(filePath) as
|
|
257
|
+
baseName: path.basename(filePath) as FabricFile.BaseName,
|
|
255
258
|
meta: {
|
|
256
259
|
pluginName: this.pluginName,
|
|
257
260
|
},
|
|
@@ -397,7 +400,7 @@ export function defaultResolveFooter(node: RootNode | undefined, { output }: Res
|
|
|
397
400
|
* - `default` — name casing strategy (camelCase / PascalCase)
|
|
398
401
|
* - `resolveOptions` — include/exclude/override filtering
|
|
399
402
|
* - `resolvePath` — output path computation
|
|
400
|
-
* - `resolveFile` — full `
|
|
403
|
+
* - `resolveFile` — full `FabricFile.File` construction
|
|
401
404
|
*
|
|
402
405
|
* Methods in the builder have access to `this` (the full resolver object), so they
|
|
403
406
|
* can call other resolver methods without circular imports.
|
package/src/hooks/useDriver.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { useFabric } from '@kubb/react-fabric'
|
|
2
2
|
import type { PluginDriver } from '../PluginDriver.ts'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Returns the `PluginDriver` instance from Fabric context.
|
|
6
|
+
*
|
|
7
|
+
* Use this inside React generator components to access the driver, config, and adapter.
|
|
8
|
+
*/
|
|
4
9
|
export function useDriver(): PluginDriver {
|
|
5
10
|
const { meta } = useFabric<{ driver: PluginDriver }>()
|
|
6
11
|
|
package/src/hooks/useMode.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
2
2
|
import { useFabric } from '@kubb/react-fabric'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @deprecated use `mode` from the generator component props instead
|
|
6
6
|
*/
|
|
7
|
-
export function useMode():
|
|
8
|
-
const { meta } = useFabric<{ mode:
|
|
7
|
+
export function useMode(): FabricFile.Mode {
|
|
8
|
+
const { meta } = useFabric<{ mode: FabricFile.Mode }>()
|
|
9
9
|
|
|
10
10
|
return meta.mode
|
|
11
11
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AsyncEventEmitter, PossiblePromise } from '@internals/utils'
|
|
2
2
|
import type { Node, RootNode, SchemaNode, Visitor } from '@kubb/ast/types'
|
|
3
|
-
import type { Fabric as FabricType
|
|
3
|
+
import type { FabricFile, Fabric as FabricType } from '@kubb/fabric-core/types'
|
|
4
4
|
import type { HttpMethod } from '@kubb/oas'
|
|
5
5
|
import type { DEFAULT_STUDIO_URL, logLevel } from './constants.ts'
|
|
6
6
|
import type { Storage } from './createStorage.ts'
|
|
@@ -119,13 +119,13 @@ export type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptio
|
|
|
119
119
|
*/
|
|
120
120
|
parse: (source: AdapterSource) => PossiblePromise<RootNode>
|
|
121
121
|
/**
|
|
122
|
-
* Extracts `
|
|
122
|
+
* Extracts `FabricFile.Import` entries needed by a `SchemaNode` tree.
|
|
123
123
|
* Populated after the first `parse()` call. Returns an empty array before that.
|
|
124
124
|
*
|
|
125
125
|
* The `resolve` callback receives the collision-corrected schema name and must
|
|
126
126
|
* return the `{ name, path }` pair for the import, or `undefined` to skip it.
|
|
127
127
|
*/
|
|
128
|
-
getImports: (node: SchemaNode, resolve: (schemaName: string) => { name: string; path: string }) => Array<
|
|
128
|
+
getImports: (node: SchemaNode, resolve: (schemaName: string) => { name: string; path: string }) => Array<FabricFile.Import>
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
export type BarrelType = 'all' | 'named' | 'propagate'
|
|
@@ -227,7 +227,7 @@ export type Config<TInput = Input> = {
|
|
|
227
227
|
* Overrides the extension for generated imports and exports. By default, each plugin adds an extension.
|
|
228
228
|
* @default { '.ts': '.ts'}
|
|
229
229
|
*/
|
|
230
|
-
extension?: Record<
|
|
230
|
+
extension?: Record<FabricFile.Extname, FabricFile.Extname | ''>
|
|
231
231
|
/**
|
|
232
232
|
* Configures how `index.ts` files are created, including disabling barrel file generation. Each plugin has its own `barrelType` option; this setting controls the root barrel file (e.g., `src/gen/index.ts`).
|
|
233
233
|
* @default 'named'
|
|
@@ -281,15 +281,25 @@ export type Config<TInput = Input> = {
|
|
|
281
281
|
|
|
282
282
|
// plugin
|
|
283
283
|
|
|
284
|
+
/**
|
|
285
|
+
* A type/string-pattern filter used for `include`, `exclude`, and `override` matching.
|
|
286
|
+
*/
|
|
284
287
|
type PatternFilter = {
|
|
285
288
|
type: string
|
|
286
289
|
pattern: string | RegExp
|
|
287
290
|
}
|
|
288
291
|
|
|
292
|
+
/**
|
|
293
|
+
* A pattern filter paired with partial option overrides to apply when the pattern matches.
|
|
294
|
+
*/
|
|
289
295
|
type PatternOverride<TOptions> = PatternFilter & {
|
|
290
296
|
options: Omit<Partial<TOptions>, 'override'>
|
|
291
297
|
}
|
|
292
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Context passed to `resolver.resolveOptions` to apply include/exclude/override filtering
|
|
301
|
+
* for a given operation or schema node.
|
|
302
|
+
*/
|
|
293
303
|
export type ResolveOptionsContext<TOptions> = {
|
|
294
304
|
options: TOptions
|
|
295
305
|
exclude?: Array<PatternFilter>
|
|
@@ -317,8 +327,8 @@ export type Resolver = {
|
|
|
317
327
|
pluginName: Plugin['name']
|
|
318
328
|
default(name: ResolveNameParams['name'], type?: ResolveNameParams['type']): string
|
|
319
329
|
resolveOptions<TOptions>(node: Node, context: ResolveOptionsContext<TOptions>): TOptions | null
|
|
320
|
-
resolvePath(params: ResolverPathParams, context: ResolverContext):
|
|
321
|
-
resolveFile(params: ResolverFileParams, context: ResolverContext):
|
|
330
|
+
resolvePath(params: ResolverPathParams, context: ResolverContext): FabricFile.Path
|
|
331
|
+
resolveFile(params: ResolverFileParams, context: ResolverContext): FabricFile.File
|
|
322
332
|
resolveBanner(node: RootNode | null, context: ResolveBannerContext): string | undefined
|
|
323
333
|
resolveFooter(node: RootNode | null, context: ResolveBannerContext): string | undefined
|
|
324
334
|
}
|
|
@@ -458,7 +468,12 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
|
|
|
458
468
|
* @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts'
|
|
459
469
|
* @deprecated this will be replaced by resolvers
|
|
460
470
|
*/
|
|
461
|
-
resolvePath?: (
|
|
471
|
+
resolvePath?: (
|
|
472
|
+
this: PluginContext<TOptions>,
|
|
473
|
+
baseName: FabricFile.BaseName,
|
|
474
|
+
mode?: FabricFile.Mode,
|
|
475
|
+
options?: TOptions['resolvePathOptions'],
|
|
476
|
+
) => FabricFile.Path
|
|
462
477
|
/**
|
|
463
478
|
* Resolve to a name based on a string.
|
|
464
479
|
* Useful when converting to PascalCase or camelCase.
|
|
@@ -475,8 +490,8 @@ export type PluginParameter<H extends PluginLifecycleHooks> = Parameters<Require
|
|
|
475
490
|
|
|
476
491
|
export type ResolvePathParams<TOptions = object> = {
|
|
477
492
|
pluginName?: string
|
|
478
|
-
baseName:
|
|
479
|
-
mode?:
|
|
493
|
+
baseName: FabricFile.BaseName
|
|
494
|
+
mode?: FabricFile.Mode
|
|
480
495
|
/**
|
|
481
496
|
* Options to be passed to 'resolvePath' 3th parameter
|
|
482
497
|
*/
|
|
@@ -504,13 +519,13 @@ export type PluginContext<TOptions extends PluginFactoryOptions = PluginFactoryO
|
|
|
504
519
|
/**
|
|
505
520
|
* Only add when the file does not exist yet
|
|
506
521
|
*/
|
|
507
|
-
addFile: (...file: Array<
|
|
522
|
+
addFile: (...file: Array<FabricFile.File>) => Promise<void>
|
|
508
523
|
/**
|
|
509
524
|
* merging multiple sources into the same output file
|
|
510
525
|
*/
|
|
511
|
-
upsertFile: (...file: Array<
|
|
526
|
+
upsertFile: (...file: Array<FabricFile.File>) => Promise<void>
|
|
512
527
|
events: AsyncEventEmitter<KubbEvents>
|
|
513
|
-
mode:
|
|
528
|
+
mode: FabricFile.Mode
|
|
514
529
|
/**
|
|
515
530
|
* Current plugin
|
|
516
531
|
*/
|
|
@@ -714,8 +729,8 @@ export type ResolvePathOptions = {
|
|
|
714
729
|
* ```
|
|
715
730
|
*/
|
|
716
731
|
export type ResolverPathParams = {
|
|
717
|
-
baseName:
|
|
718
|
-
pathMode?:
|
|
732
|
+
baseName: FabricFile.BaseName
|
|
733
|
+
pathMode?: FabricFile.Mode
|
|
719
734
|
/**
|
|
720
735
|
* Tag value used when `group.type === 'tag'`.
|
|
721
736
|
*/
|
|
@@ -745,7 +760,9 @@ export type ResolverContext = {
|
|
|
745
760
|
root: string
|
|
746
761
|
output: Output
|
|
747
762
|
group?: Group
|
|
748
|
-
/**
|
|
763
|
+
/**
|
|
764
|
+
* Plugin name used to populate `meta.pluginName` on the resolved file.
|
|
765
|
+
*/
|
|
749
766
|
pluginName?: string
|
|
750
767
|
}
|
|
751
768
|
|
|
@@ -766,10 +783,14 @@ export type ResolverContext = {
|
|
|
766
783
|
*/
|
|
767
784
|
export type ResolverFileParams = {
|
|
768
785
|
name: string
|
|
769
|
-
extname:
|
|
770
|
-
/**
|
|
786
|
+
extname: FabricFile.Extname
|
|
787
|
+
/**
|
|
788
|
+
* Tag value used when `group.type === 'tag'`.
|
|
789
|
+
*/
|
|
771
790
|
tag?: string
|
|
772
|
-
/**
|
|
791
|
+
/**
|
|
792
|
+
* Path value used when `group.type === 'path'`.
|
|
793
|
+
*/
|
|
773
794
|
path?: string
|
|
774
795
|
}
|
|
775
796
|
|
package/src/utils/TreeNode.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import type {
|
|
2
|
+
import type { FabricFile } from '@kubb/fabric-core/types'
|
|
3
3
|
import { getMode } from '../PluginDriver.ts'
|
|
4
4
|
|
|
5
5
|
type BarrelData = {
|
|
6
|
-
file?:
|
|
6
|
+
file?: FabricFile.File
|
|
7
7
|
/**
|
|
8
8
|
* @deprecated use file instead
|
|
9
9
|
*/
|
|
10
|
-
type:
|
|
10
|
+
type: FabricFile.Mode
|
|
11
11
|
path: string
|
|
12
12
|
name: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
17
|
-
* flat list of generated {@link
|
|
17
|
+
* flat list of generated {@link FabricFile.File} entries.
|
|
18
18
|
*
|
|
19
19
|
* Each node represents either a directory or a file within the output tree.
|
|
20
20
|
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
@@ -76,6 +76,9 @@ export class TreeNode {
|
|
|
76
76
|
return leaves
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Visits this node and every descendant in depth-first order.
|
|
81
|
+
*/
|
|
79
82
|
forEach(callback: (treeNode: TreeNode) => void): this {
|
|
80
83
|
if (typeof callback !== 'function') {
|
|
81
84
|
throw new TypeError('forEach() callback must be a function')
|
|
@@ -90,6 +93,9 @@ export class TreeNode {
|
|
|
90
93
|
return this
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
|
|
98
|
+
*/
|
|
93
99
|
findDeep(predicate?: (value: TreeNode, index: number, obj: TreeNode[]) => boolean): TreeNode | undefined {
|
|
94
100
|
if (typeof predicate !== 'function') {
|
|
95
101
|
throw new TypeError('find() predicate must be a function')
|
|
@@ -98,6 +104,9 @@ export class TreeNode {
|
|
|
98
104
|
return this.leaves.find(predicate)
|
|
99
105
|
}
|
|
100
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Calls `callback` for every leaf of this node.
|
|
109
|
+
*/
|
|
101
110
|
forEachDeep(callback: (treeNode: TreeNode) => void): void {
|
|
102
111
|
if (typeof callback !== 'function') {
|
|
103
112
|
throw new TypeError('forEach() callback must be a function')
|
|
@@ -106,6 +115,9 @@ export class TreeNode {
|
|
|
106
115
|
this.leaves.forEach(callback)
|
|
107
116
|
}
|
|
108
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Returns all leaves that satisfy `callback`.
|
|
120
|
+
*/
|
|
109
121
|
filterDeep(callback: (treeNode: TreeNode) => boolean): Array<TreeNode> {
|
|
110
122
|
if (typeof callback !== 'function') {
|
|
111
123
|
throw new TypeError('filter() callback must be a function')
|
|
@@ -114,6 +126,9 @@ export class TreeNode {
|
|
|
114
126
|
return this.leaves.filter(callback)
|
|
115
127
|
}
|
|
116
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Maps every leaf through `callback` and returns the resulting array.
|
|
131
|
+
*/
|
|
117
132
|
mapDeep<T>(callback: (treeNode: TreeNode) => T): Array<T> {
|
|
118
133
|
if (typeof callback !== 'function') {
|
|
119
134
|
throw new TypeError('map() callback must be a function')
|
|
@@ -128,7 +143,7 @@ export class TreeNode {
|
|
|
128
143
|
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
129
144
|
* - Returns `null` when no files match.
|
|
130
145
|
*/
|
|
131
|
-
public static build(files:
|
|
146
|
+
public static build(files: FabricFile.File[], root?: string): TreeNode | null {
|
|
132
147
|
try {
|
|
133
148
|
const filteredTree = buildDirectoryTree(files, root)
|
|
134
149
|
|
|
@@ -172,13 +187,13 @@ export class TreeNode {
|
|
|
172
187
|
type DirectoryTree = {
|
|
173
188
|
name: string
|
|
174
189
|
path: string
|
|
175
|
-
file?:
|
|
190
|
+
file?: FabricFile.File
|
|
176
191
|
children: Array<DirectoryTree>
|
|
177
192
|
}
|
|
178
193
|
|
|
179
194
|
const normalizePath = (p: string): string => p.replaceAll('\\', '/')
|
|
180
195
|
|
|
181
|
-
function buildDirectoryTree(files: Array<
|
|
196
|
+
function buildDirectoryTree(files: Array<FabricFile.File>, rootFolder = ''): DirectoryTree | null {
|
|
182
197
|
const normalizedRootFolder = normalizePath(rootFolder)
|
|
183
198
|
const rootPrefix = normalizedRootFolder.endsWith('/') ? normalizedRootFolder : `${normalizedRootFolder}/`
|
|
184
199
|
|