@kubb/core 4.36.1 → 5.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/{types-D30QAz2y.d.ts → PluginDriver-BkFepPdm.d.ts} +354 -291
  2. package/dist/hooks.cjs +85 -8
  3. package/dist/hooks.cjs.map +1 -1
  4. package/dist/hooks.d.ts +66 -4
  5. package/dist/hooks.js +83 -8
  6. package/dist/hooks.js.map +1 -1
  7. package/dist/index.cjs +346 -315
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +91 -77
  10. package/dist/index.js +338 -305
  11. package/dist/index.js.map +1 -1
  12. package/package.json +7 -7
  13. package/src/Kubb.ts +27 -55
  14. package/src/{PluginManager.ts → PluginDriver.ts} +69 -82
  15. package/src/build.ts +32 -33
  16. package/src/constants.ts +1 -1
  17. package/src/createAdapter.ts +25 -0
  18. package/src/createPlugin.ts +28 -0
  19. package/src/createStorage.ts +58 -0
  20. package/src/defineGenerator.ts +134 -0
  21. package/src/defineLogger.ts +13 -3
  22. package/src/defineResolver.ts +131 -0
  23. package/src/hooks/index.ts +2 -1
  24. package/src/hooks/useKubb.ts +143 -0
  25. package/src/hooks/useMode.ts +5 -2
  26. package/src/hooks/usePlugin.ts +5 -2
  27. package/src/hooks/usePluginDriver.ts +11 -0
  28. package/src/index.ts +7 -7
  29. package/src/storages/fsStorage.ts +2 -2
  30. package/src/storages/memoryStorage.ts +2 -2
  31. package/src/types.ts +94 -48
  32. package/src/utils/FunctionParams.ts +2 -2
  33. package/src/utils/TreeNode.ts +1 -1
  34. package/src/utils/formatters.ts +1 -1
  35. package/src/utils/getBarrelFiles.ts +73 -11
  36. package/src/utils/getConfigs.ts +3 -21
  37. package/src/utils/linters.ts +1 -1
  38. package/src/utils/packageJSON.ts +61 -0
  39. package/src/BarrelManager.ts +0 -74
  40. package/src/PackageManager.ts +0 -180
  41. package/src/PromiseManager.ts +0 -40
  42. package/src/defineAdapter.ts +0 -22
  43. package/src/definePlugin.ts +0 -12
  44. package/src/defineStorage.ts +0 -56
  45. package/src/errors.ts +0 -1
  46. package/src/hooks/usePluginManager.ts +0 -8
  47. package/src/utils/getPlugins.ts +0 -23
@@ -1,30 +1,12 @@
1
1
  import type { CLIOptions, ConfigInput } from '../config.ts'
2
2
  import type { Config, UserConfig } from '../types.ts'
3
- import { getPlugins } from './getPlugins.ts'
4
3
 
5
4
  /**
6
5
  * Converting UserConfig to Config Array without a change in the object beside the JSON convert.
7
6
  */
8
7
  export async function getConfigs(config: ConfigInput | UserConfig, args: CLIOptions): Promise<Array<Config>> {
9
- const resolvedConfig: Promise<UserConfig | Array<UserConfig>> =
10
- typeof config === 'function' ? Promise.resolve(config(args as CLIOptions)) : Promise.resolve(config)
8
+ const resolved = await (typeof config === 'function' ? config(args as CLIOptions) : config)
9
+ const userConfigs = Array.isArray(resolved) ? resolved : [resolved]
11
10
 
12
- let userConfigs = await resolvedConfig
13
-
14
- if (!Array.isArray(userConfigs)) {
15
- userConfigs = [userConfigs]
16
- }
17
-
18
- const results: Array<Config> = []
19
-
20
- for (const item of userConfigs) {
21
- const plugins = item.plugins ? await getPlugins(item.plugins) : undefined
22
-
23
- results.push({
24
- ...item,
25
- plugins,
26
- } as Config)
27
- }
28
-
29
- return results
11
+ return userConfigs.map((item) => ({ ...item }) as Config)
30
12
  }
@@ -13,7 +13,7 @@ async function isLinterAvailable(linter: Linter): Promise<boolean> {
13
13
  }
14
14
 
15
15
  export async function detectLinter(): Promise<Linter | undefined> {
16
- const linterNames: Linter[] = ['biome', 'oxlint', 'eslint']
16
+ const linterNames = new Set(['biome', 'oxlint', 'eslint'] as const)
17
17
 
18
18
  for (const linter of linterNames) {
19
19
  if (await isLinterAvailable(linter)) {
@@ -0,0 +1,61 @@
1
+ import { readSync } from '@internals/utils'
2
+ import * as pkg from 'empathic/package'
3
+ import { coerce, satisfies } from 'semver'
4
+
5
+ type PackageJSON = {
6
+ dependencies?: Record<string, string>
7
+ devDependencies?: Record<string, string>
8
+ }
9
+
10
+ type DependencyName = string
11
+ type DependencyVersion = string
12
+
13
+ function getPackageJSONSync(cwd?: string): PackageJSON | undefined {
14
+ const pkgPath = pkg.up({ cwd })
15
+ if (!pkgPath) {
16
+ return undefined
17
+ }
18
+
19
+ return JSON.parse(readSync(pkgPath)) as PackageJSON
20
+ }
21
+
22
+ function match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): string | undefined {
23
+ const dependencies = {
24
+ ...(packageJSON.dependencies || {}),
25
+ ...(packageJSON.devDependencies || {}),
26
+ }
27
+
28
+ if (typeof dependency === 'string' && dependencies[dependency]) {
29
+ return dependencies[dependency]
30
+ }
31
+
32
+ const matched = Object.keys(dependencies).find((dep) => dep.match(dependency))
33
+
34
+ return matched ? dependencies[matched] : undefined
35
+ }
36
+
37
+ function getVersionSync(dependency: DependencyName | RegExp, cwd?: string): DependencyVersion | undefined {
38
+ const packageJSON = getPackageJSONSync(cwd)
39
+
40
+ return packageJSON ? match(packageJSON, dependency) : undefined
41
+ }
42
+
43
+ export function satisfiesDependency(dependency: DependencyName | RegExp, version: DependencyVersion, cwd?: string): boolean {
44
+ const packageVersion = getVersionSync(dependency, cwd)
45
+
46
+ if (!packageVersion) {
47
+ return false
48
+ }
49
+
50
+ if (packageVersion === version) {
51
+ return true
52
+ }
53
+
54
+ const semVer = coerce(packageVersion)
55
+
56
+ if (!semVer) {
57
+ return false
58
+ }
59
+
60
+ return satisfies(semVer, version)
61
+ }
@@ -1,74 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/useIterableCallbackReturn: not needed */
2
- import { join } from 'node:path'
3
- import { getRelativePath } from '@internals/utils'
4
- import type { KubbFile } from '@kubb/fabric-core/types'
5
-
6
- import type { FileMetaBase } from './utils/getBarrelFiles.ts'
7
- import { TreeNode } from './utils/TreeNode.ts'
8
-
9
- export class BarrelManager {
10
- getFiles({ files: generatedFiles, root }: { files: KubbFile.File[]; root?: string; meta?: FileMetaBase | undefined }): Array<KubbFile.File> {
11
- const cachedFiles = new Map<KubbFile.Path, KubbFile.File>()
12
-
13
- TreeNode.build(generatedFiles, root)?.forEach((treeNode) => {
14
- if (!treeNode || !treeNode.children || !treeNode.parent?.data.path) {
15
- return
16
- }
17
-
18
- const barrelFile: KubbFile.File = {
19
- path: join(treeNode.parent?.data.path, 'index.ts') as KubbFile.Path,
20
- baseName: 'index.ts',
21
- exports: [],
22
- imports: [],
23
- sources: [],
24
- }
25
- const previousBarrelFile = cachedFiles.get(barrelFile.path)
26
- const leaves = treeNode.leaves
27
-
28
- leaves.forEach((item) => {
29
- if (!item.data.name) {
30
- return
31
- }
32
-
33
- const sources = item.data.file?.sources || []
34
-
35
- sources.forEach((source) => {
36
- if (!item.data.file?.path || !source.isIndexable || !source.name) {
37
- return
38
- }
39
- const alreadyContainInPreviousBarrelFile = previousBarrelFile?.sources.some(
40
- (item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly,
41
- )
42
-
43
- if (alreadyContainInPreviousBarrelFile) {
44
- return
45
- }
46
-
47
- barrelFile.exports!.push({
48
- name: [source.name],
49
- path: getRelativePath(treeNode.parent?.data.path, item.data.path),
50
- isTypeOnly: source.isTypeOnly,
51
- })
52
-
53
- barrelFile.sources.push({
54
- name: source.name,
55
- isTypeOnly: source.isTypeOnly,
56
- //TODO use parser to generate import
57
- value: '',
58
- isExportable: false,
59
- isIndexable: false,
60
- })
61
- })
62
- })
63
-
64
- if (previousBarrelFile) {
65
- previousBarrelFile.sources.push(...barrelFile.sources)
66
- previousBarrelFile.exports?.push(...(barrelFile.exports || []))
67
- } else {
68
- cachedFiles.set(barrelFile.path, barrelFile)
69
- }
70
- })
71
-
72
- return [...cachedFiles.values()]
73
- }
74
- }
@@ -1,180 +0,0 @@
1
- import mod from 'node:module'
2
- import os from 'node:os'
3
- import { pathToFileURL } from 'node:url'
4
- import { read, readSync } from '@internals/utils'
5
- import * as pkg from 'empathic/package'
6
- import { coerce, satisfies } from 'semver'
7
- import { PATH_SEPARATORS } from './constants.ts'
8
-
9
- type PackageJSON = {
10
- dependencies?: Record<string, string>
11
- devDependencies?: Record<string, string>
12
- }
13
-
14
- type DependencyName = string
15
-
16
- type DependencyVersion = string
17
-
18
- export class PackageManager {
19
- static #cache: Record<DependencyName, DependencyVersion> = {}
20
-
21
- #cwd?: string
22
-
23
- constructor(workspace?: string) {
24
- if (workspace) {
25
- this.#cwd = workspace
26
- }
27
- }
28
-
29
- set workspace(workspace: string) {
30
- this.#cwd = workspace
31
- }
32
-
33
- get workspace(): string | undefined {
34
- return this.#cwd
35
- }
36
-
37
- normalizeDirectory(directory: string): string {
38
- const lastChar = directory[directory.length - 1]
39
- if (lastChar && !(PATH_SEPARATORS as readonly string[]).includes(lastChar)) {
40
- return `${directory}/`
41
- }
42
-
43
- return directory
44
- }
45
-
46
- getLocation(path: string): string {
47
- let location = path
48
-
49
- if (this.#cwd) {
50
- const require = mod.createRequire(this.normalizeDirectory(this.#cwd))
51
- location = require.resolve(path)
52
- }
53
-
54
- return location
55
- }
56
-
57
- async import(path: string): Promise<unknown> {
58
- let location = this.getLocation(path)
59
-
60
- if (os.platform() === 'win32') {
61
- location = pathToFileURL(location).href
62
- }
63
-
64
- const module = await import(location)
65
-
66
- return module?.default ?? module
67
- }
68
-
69
- async getPackageJSON(): Promise<PackageJSON | undefined> {
70
- const pkgPath = pkg.up({
71
- cwd: this.#cwd,
72
- })
73
- if (!pkgPath) {
74
- return undefined
75
- }
76
-
77
- const json = await read(pkgPath)
78
-
79
- return JSON.parse(json) as PackageJSON
80
- }
81
-
82
- getPackageJSONSync(): PackageJSON | undefined {
83
- const pkgPath = pkg.up({
84
- cwd: this.#cwd,
85
- })
86
- if (!pkgPath) {
87
- return undefined
88
- }
89
-
90
- const json = readSync(pkgPath)
91
-
92
- return JSON.parse(json) as PackageJSON
93
- }
94
-
95
- static setVersion(dependency: DependencyName, version: DependencyVersion): void {
96
- PackageManager.#cache[dependency] = version
97
- }
98
-
99
- #match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): string | undefined {
100
- const dependencies = {
101
- ...(packageJSON.dependencies || {}),
102
- ...(packageJSON.devDependencies || {}),
103
- }
104
-
105
- if (typeof dependency === 'string' && dependencies[dependency]) {
106
- return dependencies[dependency]
107
- }
108
-
109
- const matchedDependency = Object.keys(dependencies).find((dep) => dep.match(dependency))
110
-
111
- return matchedDependency ? dependencies[matchedDependency] : undefined
112
- }
113
-
114
- async getVersion(dependency: DependencyName | RegExp): Promise<DependencyVersion | undefined> {
115
- if (typeof dependency === 'string' && PackageManager.#cache[dependency]) {
116
- return PackageManager.#cache[dependency]
117
- }
118
-
119
- const packageJSON = await this.getPackageJSON()
120
-
121
- if (!packageJSON) {
122
- return undefined
123
- }
124
-
125
- return this.#match(packageJSON, dependency)
126
- }
127
-
128
- getVersionSync(dependency: DependencyName | RegExp): DependencyVersion | undefined {
129
- if (typeof dependency === 'string' && PackageManager.#cache[dependency]) {
130
- return PackageManager.#cache[dependency]
131
- }
132
-
133
- const packageJSON = this.getPackageJSONSync()
134
-
135
- if (!packageJSON) {
136
- return undefined
137
- }
138
-
139
- return this.#match(packageJSON, dependency)
140
- }
141
-
142
- async isValid(dependency: DependencyName | RegExp, version: DependencyVersion): Promise<boolean> {
143
- const packageVersion = await this.getVersion(dependency)
144
-
145
- if (!packageVersion) {
146
- return false
147
- }
148
-
149
- if (packageVersion === version) {
150
- return true
151
- }
152
-
153
- const semVer = coerce(packageVersion)
154
-
155
- if (!semVer) {
156
- return false
157
- }
158
-
159
- return satisfies(semVer, version)
160
- }
161
- isValidSync(dependency: DependencyName | RegExp, version: DependencyVersion): boolean {
162
- const packageVersion = this.getVersionSync(dependency)
163
-
164
- if (!packageVersion) {
165
- return false
166
- }
167
-
168
- if (packageVersion === version) {
169
- return true
170
- }
171
-
172
- const semVer = coerce(packageVersion)
173
-
174
- if (!semVer) {
175
- return false
176
- }
177
-
178
- return satisfies(semVer, version)
179
- }
180
- }
@@ -1,40 +0,0 @@
1
- import type { Strategy, StrategySwitch } from './utils/executeStrategies.ts'
2
- import { hookFirst, hookParallel, hookSeq } from './utils/executeStrategies.ts'
3
-
4
- type PromiseFunc<T = unknown, T2 = never> = () => T2 extends never ? Promise<T> : Promise<T> | T2
5
-
6
- type Options<TState = unknown> = {
7
- nullCheck?: (state: TState) => boolean
8
- }
9
-
10
- export class PromiseManager<TState = unknown> {
11
- #options: Options<TState> = {}
12
-
13
- constructor(options: Options<TState> = {}) {
14
- this.#options = options
15
- }
16
-
17
- run<TInput extends Array<PromiseFunc<TValue, null>>, TValue, TStrategy extends Strategy, TOutput = StrategySwitch<TStrategy, TInput, TValue>>(
18
- strategy: TStrategy,
19
- promises: TInput,
20
- { concurrency = Number.POSITIVE_INFINITY }: { concurrency?: number } = {},
21
- ): TOutput {
22
- if (strategy === 'seq') {
23
- return hookSeq<TInput, TValue, TOutput>(promises)
24
- }
25
-
26
- if (strategy === 'first') {
27
- return hookFirst<TInput, TValue, TOutput>(promises, this.#options.nullCheck as ((state: unknown) => boolean) | undefined)
28
- }
29
-
30
- if (strategy === 'parallel') {
31
- return hookParallel<TInput, TValue, TOutput>(promises, concurrency)
32
- }
33
-
34
- throw new Error(`${strategy} not implemented`)
35
- }
36
- }
37
-
38
- export function isPromiseRejectedResult<T>(result: PromiseSettledResult<unknown>): result is Omit<PromiseRejectedResult, 'reason'> & { reason: T } {
39
- return result.status === 'rejected'
40
- }
@@ -1,22 +0,0 @@
1
- import type { Adapter, AdapterFactoryOptions } from './types.ts'
2
-
3
- type AdapterBuilder<T extends AdapterFactoryOptions> = (options: T['options']) => Adapter<T>
4
-
5
- /**
6
- * Wraps an adapter builder to make the options parameter optional.
7
- *
8
- * @example
9
- * ```ts
10
- * export const adapterOas = defineAdapter<OasAdapter>((options) => {
11
- * const { validate = true, dateType = 'string' } = options
12
- * return {
13
- * name: adapterOasName,
14
- * options: { validate, dateType, ... },
15
- * parse(source) { ... },
16
- * }
17
- * })
18
- * ```
19
- */
20
- export function defineAdapter<T extends AdapterFactoryOptions = AdapterFactoryOptions>(build: AdapterBuilder<T>): (options?: T['options']) => Adapter<T> {
21
- return (options) => build(options ?? ({} as T['options']))
22
- }
@@ -1,12 +0,0 @@
1
- import type { PluginFactoryOptions, UserPluginWithLifeCycle } from './types.ts'
2
-
3
- type PluginBuilder<T extends PluginFactoryOptions = PluginFactoryOptions> = (options: T['options']) => UserPluginWithLifeCycle<T>
4
-
5
- /**
6
- * Wraps a plugin builder to make the options parameter optional.
7
- */
8
- export function definePlugin<T extends PluginFactoryOptions = PluginFactoryOptions>(
9
- build: PluginBuilder<T>,
10
- ): (options?: T['options']) => UserPluginWithLifeCycle<T> {
11
- return (options) => build(options ?? ({} as T['options']))
12
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * Storage interface for persisting Kubb output.
3
- *
4
- * Keys are root-relative forward-slash paths (e.g. `src/gen/api/getPets.ts`).
5
- * Implement this interface to route generated files to any backend — filesystem,
6
- * S3, Redis, in-memory, etc.
7
- *
8
- * Use `defineStorage` to create a typed storage driver.
9
- */
10
- export interface DefineStorage {
11
- /** Identifier used for logging and debugging (e.g. `'fs'`, `'s3'`). */
12
- readonly name: string
13
- /** Returns `true` when an entry for `key` exists in storage. */
14
- hasItem(key: string): Promise<boolean>
15
- /** Returns the stored string value, or `null` when `key` does not exist. */
16
- getItem(key: string): Promise<string | null>
17
- /** Persists `value` under `key`, creating any required structure. */
18
- setItem(key: string, value: string): Promise<void>
19
- /** Removes the entry for `key`. No-ops when the key does not exist. */
20
- removeItem(key: string): Promise<void>
21
- /** Returns all keys, optionally filtered to those starting with `base`. */
22
- getKeys(base?: string): Promise<Array<string>>
23
- /** Removes all entries, optionally scoped to those starting with `base`. */
24
- clear(base?: string): Promise<void>
25
- /** Optional teardown hook called after the build completes. */
26
- dispose?(): Promise<void>
27
- }
28
-
29
- /**
30
- * Wraps a storage builder so the `options` argument is optional, following the
31
- * same factory pattern as `definePlugin`, `defineLogger`, and `defineAdapter`.
32
- *
33
- * The builder receives the resolved options object and must return a
34
- * `DefineStorage`-compatible object that includes a `name` string.
35
- *
36
- * @example
37
- * ```ts
38
- * import { defineStorage } from '@kubb/core'
39
- *
40
- * export const memoryStorage = defineStorage((_options) => {
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() { return [...store.keys()] },
49
- * async clear() { store.clear() },
50
- * }
51
- * })
52
- * ```
53
- */
54
- export function defineStorage<TOptions = Record<string, never>>(build: (options: TOptions) => DefineStorage): (options?: TOptions) => DefineStorage {
55
- return (options) => build(options ?? ({} as TOptions))
56
- }
package/src/errors.ts DELETED
@@ -1 +0,0 @@
1
- export { BuildError, ValidationPluginError } from '@internals/utils'
@@ -1,8 +0,0 @@
1
- import { useApp } from '@kubb/react-fabric'
2
- import type { PluginManager } from '../PluginManager.ts'
3
-
4
- export function usePluginManager(): PluginManager {
5
- const { meta } = useApp<{ pluginManager: PluginManager }>()
6
-
7
- return meta.pluginManager
8
- }
@@ -1,23 +0,0 @@
1
- import type { UnknownUserPlugin, UserConfig } from '../types.ts'
2
-
3
- type PluginsArray = Array<Omit<UnknownUserPlugin, 'inject'>>
4
-
5
- function isJSONPlugins(plugins: UserConfig['plugins']): boolean {
6
- return Array.isArray(plugins) && plugins.some((plugin) => Array.isArray(plugin) && typeof (plugin as unknown[])[0] === 'string')
7
- }
8
-
9
- function isObjectPlugins(plugins: UserConfig['plugins']): boolean {
10
- return plugins instanceof Object && !Array.isArray(plugins)
11
- }
12
-
13
- export function getPlugins(plugins: UserConfig['plugins']): Promise<PluginsArray | undefined> {
14
- if (isObjectPlugins(plugins)) {
15
- throw new Error('Object plugins are not supported anymore, best to use http://kubb.dev/getting-started/configure#json')
16
- }
17
-
18
- if (isJSONPlugins(plugins)) {
19
- throw new Error('JSON plugins are not supported anymore, best to use http://kubb.dev/getting-started/configure#json')
20
- }
21
-
22
- return Promise.resolve(plugins)
23
- }