@kubb/core 5.0.0-alpha.16 → 5.0.0-alpha.18
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-CNKhDf-w.d.ts → PluginDriver-BRTrzfiD.d.ts} +135 -27
- package/dist/hooks.cjs +16 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +17 -1
- package/dist/hooks.js +16 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +396 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +246 -19
- package/dist/index.js +394 -273
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/Kubb.ts +15 -5
- package/src/build.ts +42 -0
- package/src/config.ts +9 -8
- package/src/constants.ts +43 -0
- package/src/hooks/useKubb.ts +16 -0
- package/src/index.ts +1 -0
- package/src/types.ts +9 -3
- package/src/utils/TreeNode.ts +23 -0
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/executeStrategies.ts +16 -3
- package/src/utils/formatters.ts +9 -20
- package/src/utils/getBarrelFiles.ts +8 -0
- package/src/utils/getConfigs.ts +5 -1
- package/src/utils/getPreset.ts +7 -0
- package/src/utils/linters.ts +22 -2
- package/src/utils/packageJSON.ts +21 -6
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.18",
|
|
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",
|
|
@@ -64,14 +64,14 @@
|
|
|
64
64
|
}
|
|
65
65
|
],
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@kubb/fabric-core": "0.
|
|
68
|
-
"@kubb/react-fabric": "0.
|
|
67
|
+
"@kubb/fabric-core": "0.15.1",
|
|
68
|
+
"@kubb/react-fabric": "0.15.1",
|
|
69
69
|
"empathic": "^2.0.0",
|
|
70
70
|
"fflate": "^0.8.2",
|
|
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.18"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@types/semver": "^7.7.1",
|
package/src/Kubb.ts
CHANGED
|
@@ -167,15 +167,25 @@ export interface KubbEvents {
|
|
|
167
167
|
*/
|
|
168
168
|
'file:processing:update': [
|
|
169
169
|
{
|
|
170
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Number of files processed so far.
|
|
172
|
+
*/
|
|
171
173
|
processed: number
|
|
172
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Total number of files to process.
|
|
176
|
+
*/
|
|
173
177
|
total: number
|
|
174
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* Processing percentage (0–100).
|
|
180
|
+
*/
|
|
175
181
|
percentage: number
|
|
176
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* Optional source identifier.
|
|
184
|
+
*/
|
|
177
185
|
source?: string
|
|
178
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* The file being processed.
|
|
188
|
+
*/
|
|
179
189
|
file: KubbFile.ResolvedFile
|
|
180
190
|
/**
|
|
181
191
|
* Kubb configuration (not present in Fabric).
|
package/src/build.ts
CHANGED
|
@@ -17,16 +17,31 @@ type BuildOptions = {
|
|
|
17
17
|
events?: AsyncEventEmitter<KubbEvents>
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Full output produced by a successful or failed build.
|
|
22
|
+
*/
|
|
20
23
|
type BuildOutput = {
|
|
24
|
+
/**
|
|
25
|
+
* Plugins that threw during installation, paired with the caught error.
|
|
26
|
+
*/
|
|
21
27
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
22
28
|
fabric: FabricType
|
|
23
29
|
files: Array<KubbFile.ResolvedFile>
|
|
24
30
|
driver: PluginDriver
|
|
31
|
+
/**
|
|
32
|
+
* Elapsed time in milliseconds for each plugin, keyed by plugin name.
|
|
33
|
+
*/
|
|
25
34
|
pluginTimings: Map<string, number>
|
|
26
35
|
error?: Error
|
|
36
|
+
/**
|
|
37
|
+
* Raw generated source, keyed by absolute file path.
|
|
38
|
+
*/
|
|
27
39
|
sources: Map<KubbFile.Path, string>
|
|
28
40
|
}
|
|
29
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Intermediate result returned by {@link setup} and accepted by {@link safeBuild}.
|
|
44
|
+
*/
|
|
30
45
|
type SetupResult = {
|
|
31
46
|
events: AsyncEventEmitter<KubbEvents>
|
|
32
47
|
fabric: FabricType
|
|
@@ -34,6 +49,17 @@ type SetupResult = {
|
|
|
34
49
|
sources: Map<KubbFile.Path, string>
|
|
35
50
|
}
|
|
36
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Initializes all Kubb infrastructure for a build without executing any plugins.
|
|
54
|
+
*
|
|
55
|
+
* - Validates the input path (when applicable).
|
|
56
|
+
* - Applies config defaults (`root`, `output.*`, `devtools`).
|
|
57
|
+
* - Creates the Fabric instance and wires storage, format, and lint hooks.
|
|
58
|
+
* - Runs the adapter (if configured) to produce the universal `RootNode`.
|
|
59
|
+
*
|
|
60
|
+
* Pass the returned {@link SetupResult} directly to {@link safeBuild} or {@link build}
|
|
61
|
+
* via the `overrides` argument to reuse the same infrastructure across multiple runs.
|
|
62
|
+
*/
|
|
37
63
|
export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
38
64
|
const { config: userConfig, events = new AsyncEventEmitter<KubbEvents>() } = options
|
|
39
65
|
|
|
@@ -199,6 +225,12 @@ export async function setup(options: BuildOptions): Promise<SetupResult> {
|
|
|
199
225
|
}
|
|
200
226
|
}
|
|
201
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Runs a full Kubb build and throws on any error or plugin failure.
|
|
230
|
+
*
|
|
231
|
+
* Internally delegates to {@link safeBuild} and rethrows collected errors.
|
|
232
|
+
* Pass an existing {@link SetupResult} via `overrides` to skip the setup phase.
|
|
233
|
+
*/
|
|
202
234
|
export async function build(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
203
235
|
const { fabric, files, driver, failedPlugins, pluginTimings, error, sources } = await safeBuild(options, overrides)
|
|
204
236
|
|
|
@@ -223,6 +255,16 @@ export async function build(options: BuildOptions, overrides?: SetupResult): Pro
|
|
|
223
255
|
}
|
|
224
256
|
}
|
|
225
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Runs a full Kubb build and captures errors instead of throwing.
|
|
260
|
+
*
|
|
261
|
+
* - Installs each plugin in order, recording failures in `failedPlugins`.
|
|
262
|
+
* - Generates the root barrel file when `output.barrelType` is set.
|
|
263
|
+
* - Writes all files through Fabric.
|
|
264
|
+
*
|
|
265
|
+
* Returns a {@link BuildOutput} even on failure — inspect `error` and
|
|
266
|
+
* `failedPlugins` to determine whether the build succeeded.
|
|
267
|
+
*/
|
|
226
268
|
export async function safeBuild(options: BuildOptions, overrides?: SetupResult): Promise<BuildOutput> {
|
|
227
269
|
const { fabric, driver, events, sources } = overrides ? overrides : await setup(options)
|
|
228
270
|
|
package/src/config.ts
CHANGED
|
@@ -5,12 +5,14 @@ import type { InputPath, UserConfig } from './types.ts'
|
|
|
5
5
|
* CLI options derived from command-line flags.
|
|
6
6
|
*/
|
|
7
7
|
export type CLIOptions = {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Path to `kubb.config.js`.
|
|
10
|
+
*/
|
|
9
11
|
config?: string
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Enable watch mode for input files.
|
|
14
|
+
*/
|
|
12
15
|
watch?: boolean
|
|
13
|
-
|
|
14
16
|
/**
|
|
15
17
|
* Logging verbosity for CLI usage.
|
|
16
18
|
*
|
|
@@ -20,12 +22,11 @@ export type CLIOptions = {
|
|
|
20
22
|
* @default 'silent'
|
|
21
23
|
*/
|
|
22
24
|
logLevel?: 'silent' | 'info' | 'debug'
|
|
23
|
-
|
|
24
|
-
/** Run Kubb with Bun */
|
|
25
|
-
bun?: boolean
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* All accepted forms of a Kubb configuration.
|
|
29
|
+
*/
|
|
29
30
|
export type ConfigInput = PossiblePromise<UserConfig | UserConfig[]> | ((cli: CLIOptions) => PossiblePromise<UserConfig | UserConfig[]>)
|
|
30
31
|
|
|
31
32
|
/**
|
package/src/constants.ts
CHANGED
|
@@ -1,21 +1,50 @@
|
|
|
1
1
|
import type { KubbFile } from '@kubb/fabric-core/types'
|
|
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
|
|
|
8
|
+
/**
|
|
9
|
+
* Internal plugin name used to identify the core Kubb runtime.
|
|
10
|
+
*/
|
|
5
11
|
export const CORE_PLUGIN_NAME = 'core' as const
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Maximum number of event-emitter listeners before Node.js emits a warning.
|
|
15
|
+
*/
|
|
7
16
|
export const DEFAULT_MAX_LISTENERS = 100
|
|
8
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Default number of plugins that may run concurrently during a build.
|
|
20
|
+
*/
|
|
9
21
|
export const DEFAULT_CONCURRENCY = 15
|
|
10
22
|
|
|
23
|
+
/**
|
|
24
|
+
* File name used for generated barrel (index) files.
|
|
25
|
+
*/
|
|
11
26
|
export const BARREL_FILENAME = 'index.ts' as const
|
|
12
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Default banner style written at the top of every generated file.
|
|
30
|
+
*/
|
|
13
31
|
export const DEFAULT_BANNER = 'simple' as const
|
|
14
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Default file-extension mapping used when no explicit mapping is configured.
|
|
35
|
+
*/
|
|
15
36
|
export const DEFAULT_EXTENSION: Record<KubbFile.Extname, KubbFile.Extname | ''> = { '.ts': '.ts' }
|
|
16
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Characters recognized as path separators on both POSIX and Windows.
|
|
40
|
+
*/
|
|
17
41
|
export const PATH_SEPARATORS = new Set(['/', '\\'] as const)
|
|
18
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Numeric log-level thresholds used internally to compare verbosity.
|
|
45
|
+
*
|
|
46
|
+
* Higher numbers are more verbose.
|
|
47
|
+
*/
|
|
19
48
|
export const logLevel = {
|
|
20
49
|
silent: Number.NEGATIVE_INFINITY,
|
|
21
50
|
error: 0,
|
|
@@ -25,6 +54,13 @@ export const logLevel = {
|
|
|
25
54
|
debug: 5,
|
|
26
55
|
} as const
|
|
27
56
|
|
|
57
|
+
/**
|
|
58
|
+
* CLI command descriptors for each supported linter.
|
|
59
|
+
*
|
|
60
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
61
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
62
|
+
* the linter is not found.
|
|
63
|
+
*/
|
|
28
64
|
export const linters = {
|
|
29
65
|
eslint: {
|
|
30
66
|
command: 'eslint',
|
|
@@ -43,6 +79,13 @@ export const linters = {
|
|
|
43
79
|
},
|
|
44
80
|
} as const
|
|
45
81
|
|
|
82
|
+
/**
|
|
83
|
+
* CLI command descriptors for each supported code formatter.
|
|
84
|
+
*
|
|
85
|
+
* Each entry contains the executable `command`, an `args` factory that maps an
|
|
86
|
+
* output path to the correct argument list, and an `errorMessage` shown when
|
|
87
|
+
* the formatter is not found.
|
|
88
|
+
*/
|
|
46
89
|
export const formatters = {
|
|
47
90
|
prettier: {
|
|
48
91
|
command: 'prettier',
|
package/src/hooks/useKubb.ts
CHANGED
|
@@ -97,6 +97,22 @@ function buildDefaultBanner({ title, description, version, config }: { title?: s
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* React-Fabric hook that exposes the current plugin context inside a generator component.
|
|
102
|
+
*
|
|
103
|
+
* Returns the active `plugin`, `mode`, `config`, and a set of resolver helpers
|
|
104
|
+
* (`getFile`, `resolveName`, `resolvePath`, `resolveBanner`, `resolveFooter`) that
|
|
105
|
+
* all default to the current plugin when no explicit `pluginName` is provided.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* function Operation({ node }: OperationProps) {
|
|
110
|
+
* const { config, resolvePath } = useKubb()
|
|
111
|
+
* const filePath = resolvePath({ baseName: node.operationId })
|
|
112
|
+
* return <File path={filePath}>...</File>
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
100
116
|
export function useKubb<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(): UseKubbReturn<TOptions> {
|
|
101
117
|
const { meta } = useFabric<{
|
|
102
118
|
plugin: Plugin<TOptions>
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -91,11 +91,17 @@ export type AdapterFactoryOptions<TName extends string = string, TOptions extend
|
|
|
91
91
|
* ```
|
|
92
92
|
*/
|
|
93
93
|
export type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
|
|
94
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Human-readable identifier, e.g. `'oas'`, `'drizzle'`, `'asyncapi'`.
|
|
96
|
+
*/
|
|
95
97
|
name: TOptions['name']
|
|
96
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Resolved options (after defaults have been applied).
|
|
100
|
+
*/
|
|
97
101
|
options: TOptions['resolvedOptions']
|
|
98
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Convert the raw source into a universal `RootNode`.
|
|
104
|
+
*/
|
|
99
105
|
parse: (source: AdapterSource) => PossiblePromise<RootNode>
|
|
100
106
|
/**
|
|
101
107
|
* Extracts `KubbFile.Import` entries needed by a `SchemaNode` tree.
|
package/src/utils/TreeNode.ts
CHANGED
|
@@ -12,6 +12,15 @@ type BarrelData = {
|
|
|
12
12
|
name: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
17
|
+
* flat list of generated {@link KubbFile.File} entries.
|
|
18
|
+
*
|
|
19
|
+
* Each node represents either a directory or a file within the output tree.
|
|
20
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
21
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
22
|
+
* `*Deep` helpers.
|
|
23
|
+
*/
|
|
15
24
|
export class TreeNode {
|
|
16
25
|
data: BarrelData
|
|
17
26
|
parent?: TreeNode
|
|
@@ -32,6 +41,9 @@ export class TreeNode {
|
|
|
32
41
|
return child
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
46
|
+
*/
|
|
35
47
|
get root(): TreeNode {
|
|
36
48
|
if (!this.parent) {
|
|
37
49
|
return this
|
|
@@ -39,6 +51,11 @@ export class TreeNode {
|
|
|
39
51
|
return this.parent.root
|
|
40
52
|
}
|
|
41
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
56
|
+
*
|
|
57
|
+
* Results are cached after the first traversal.
|
|
58
|
+
*/
|
|
42
59
|
get leaves(): Array<TreeNode> {
|
|
43
60
|
if (!this.children || this.children.length === 0) {
|
|
44
61
|
// this is a leaf
|
|
@@ -105,6 +122,12 @@ export class TreeNode {
|
|
|
105
122
|
return this.leaves.map(callback)
|
|
106
123
|
}
|
|
107
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
127
|
+
*
|
|
128
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
129
|
+
* - Returns `null` when no files match.
|
|
130
|
+
*/
|
|
108
131
|
public static build(files: KubbFile.File[], root?: string): TreeNode | null {
|
|
109
132
|
try {
|
|
110
133
|
const filteredTree = buildDirectoryTree(files, root)
|
package/src/utils/diagnostics.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { version as nodeVersion } from 'node:process'
|
|
|
2
2
|
import { version as KubbVersion } from '../../package.json'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Returns a snapshot of the current runtime environment.
|
|
6
|
+
*
|
|
7
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
8
|
+
* issues can be reproduced without manual information gathering.
|
|
6
9
|
*/
|
|
7
10
|
export function getDiagnosticInfo() {
|
|
8
11
|
return {
|
|
@@ -7,7 +7,11 @@ type ValueOfPromiseFuncArray<TInput extends Array<unknown>> = TInput extends Arr
|
|
|
7
7
|
type SeqOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Promise<Array<Awaited<ValueOfPromiseFuncArray<TInput>>>>
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Runs promise functions in sequence, threading each result into the next call.
|
|
11
|
+
*
|
|
12
|
+
* - Each function receives the accumulated state from the previous call.
|
|
13
|
+
* - Skips functions that return a falsy value (acts as a no-op for that step).
|
|
14
|
+
* - Returns an array of all individual results.
|
|
11
15
|
*/
|
|
12
16
|
export function hookSeq<TInput extends Array<PromiseFunc<TValue, null>>, TValue, TOutput = SeqOutput<TInput, TValue>>(promises: TInput): TOutput {
|
|
13
17
|
return promises.filter(Boolean).reduce(
|
|
@@ -33,7 +37,10 @@ export function hookSeq<TInput extends Array<PromiseFunc<TValue, null>>, TValue,
|
|
|
33
37
|
type HookFirstOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown> = ValueOfPromiseFuncArray<TInput>
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
40
|
+
* Runs promise functions in sequence and returns the first non-null result.
|
|
41
|
+
*
|
|
42
|
+
* - Stops as soon as `nullCheck` passes for a result (default: `!== null`).
|
|
43
|
+
* - Subsequent functions are skipped once a match is found.
|
|
37
44
|
*/
|
|
38
45
|
export function hookFirst<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown, TOutput = HookFirstOutput<TInput, TValue>>(
|
|
39
46
|
promises: TInput,
|
|
@@ -57,7 +64,10 @@ export function hookFirst<TInput extends Array<PromiseFunc<TValue, null>>, TValu
|
|
|
57
64
|
type HookParallelOutput<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = Promise<PromiseSettledResult<Awaited<ValueOfPromiseFuncArray<TInput>>>[]>
|
|
58
65
|
|
|
59
66
|
/**
|
|
60
|
-
* Runs
|
|
67
|
+
* Runs promise functions concurrently and returns all settled results.
|
|
68
|
+
*
|
|
69
|
+
* - Limits simultaneous executions to `concurrency` (default: unlimited).
|
|
70
|
+
* - Uses `Promise.allSettled` so individual failures do not cancel other tasks.
|
|
61
71
|
*/
|
|
62
72
|
export function hookParallel<TInput extends Array<PromiseFunc<TValue, null>>, TValue = unknown, TOutput = HookParallelOutput<TInput, TValue>>(
|
|
63
73
|
promises: TInput,
|
|
@@ -70,6 +80,9 @@ export function hookParallel<TInput extends Array<PromiseFunc<TValue, null>>, TV
|
|
|
70
80
|
return Promise.allSettled(tasks) as TOutput
|
|
71
81
|
}
|
|
72
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Execution strategy used when dispatching plugin hook calls.
|
|
85
|
+
*/
|
|
73
86
|
export type Strategy = 'seq' | 'first' | 'parallel'
|
|
74
87
|
|
|
75
88
|
type StrategyOutputMap<TInput extends Array<PromiseFunc<TValue, null>>, TValue> = {
|
package/src/utils/formatters.ts
CHANGED
|
@@ -4,18 +4,13 @@ import type { formatters } from '../constants.ts'
|
|
|
4
4
|
type Formatter = keyof typeof formatters
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Returns `true` when the given formatter is installed and callable.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @remarks
|
|
13
|
-
* This function checks availability by running `<formatter> --version` command.
|
|
14
|
-
* All supported formatters (biome, prettier, oxfmt) implement the --version flag.
|
|
9
|
+
* Availability is detected by running `<formatter> --version` and checking
|
|
10
|
+
* that the process exits without error.
|
|
15
11
|
*/
|
|
16
12
|
async function isFormatterAvailable(formatter: Formatter): Promise<boolean> {
|
|
17
13
|
try {
|
|
18
|
-
// Try to get the version of the formatter to check if it's installed
|
|
19
14
|
await x(formatter, ['--version'], { nodeOptions: { stdio: 'ignore' } })
|
|
20
15
|
return true
|
|
21
16
|
} catch {
|
|
@@ -24,26 +19,20 @@ async function isFormatterAvailable(formatter: Formatter): Promise<boolean> {
|
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* @returns Promise that resolves to the first available formatter or undefined if none are found
|
|
22
|
+
* Detects the first available code formatter on the current system.
|
|
30
23
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* Uses the `--version` flag to detect if each formatter command is available.
|
|
34
|
-
* This is a reliable method as all supported formatters implement this flag.
|
|
24
|
+
* - Checks in preference order: `biome`, `oxfmt`, `prettier`.
|
|
25
|
+
* - Returns `null` when none are found.
|
|
35
26
|
*
|
|
36
27
|
* @example
|
|
37
|
-
* ```
|
|
28
|
+
* ```ts
|
|
38
29
|
* const formatter = await detectFormatter()
|
|
39
30
|
* if (formatter) {
|
|
40
31
|
* console.log(`Using ${formatter} for formatting`)
|
|
41
|
-
* } else {
|
|
42
|
-
* console.log('No formatter found')
|
|
43
32
|
* }
|
|
44
33
|
* ```
|
|
45
34
|
*/
|
|
46
|
-
export async function detectFormatter(): Promise<Formatter |
|
|
35
|
+
export async function detectFormatter(): Promise<Formatter | null> {
|
|
47
36
|
const formatterNames = new Set(['biome', 'oxfmt', 'prettier'] as const)
|
|
48
37
|
|
|
49
38
|
for (const formatter of formatterNames) {
|
|
@@ -52,5 +41,5 @@ export async function detectFormatter(): Promise<Formatter | undefined> {
|
|
|
52
41
|
}
|
|
53
42
|
}
|
|
54
43
|
|
|
55
|
-
return
|
|
44
|
+
return null
|
|
56
45
|
}
|
|
@@ -105,6 +105,14 @@ function trimExtName(text: string): string {
|
|
|
105
105
|
return text
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
110
|
+
*
|
|
111
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
112
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
113
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
114
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
115
|
+
*/
|
|
108
116
|
export async function getBarrelFiles(files: Array<KubbFile.ResolvedFile>, { type, meta = {}, root, output }: AddIndexesProps): Promise<Array<KubbFile.File>> {
|
|
109
117
|
if (!type || type === 'propagate') {
|
|
110
118
|
return []
|
package/src/utils/getConfigs.ts
CHANGED
|
@@ -2,7 +2,11 @@ import type { CLIOptions, ConfigInput } from '../config.ts'
|
|
|
2
2
|
import type { Config, UserConfig } from '../types.ts'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Resolves a {@link ConfigInput} into a normalized array of {@link Config} objects.
|
|
6
|
+
*
|
|
7
|
+
* - Awaits the config when it is a `Promise`.
|
|
8
|
+
* - Calls the factory function with `args` when the config is a function.
|
|
9
|
+
* - Wraps a single config object in an array for uniform downstream handling.
|
|
6
10
|
*/
|
|
7
11
|
export async function getConfigs(config: ConfigInput | UserConfig, args: CLIOptions): Promise<Array<Config>> {
|
|
8
12
|
const resolved = await (typeof config === 'function' ? config(args as CLIOptions) : config)
|
package/src/utils/getPreset.ts
CHANGED
|
@@ -16,6 +16,13 @@ type GetPresetResult<TResolver extends Resolver> = {
|
|
|
16
16
|
preset: Preset<TResolver> | undefined
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Resolves a named preset into merged resolvers and transformers.
|
|
21
|
+
*
|
|
22
|
+
* - Merges the preset's resolvers on top of the first (default) resolver to produce `baseResolver`.
|
|
23
|
+
* - Merges any additional user-supplied resolvers on top of that to produce the final `resolver`.
|
|
24
|
+
* - Concatenates preset transformers before user-supplied transformers.
|
|
25
|
+
*/
|
|
19
26
|
export function getPreset<TResolver extends Resolver = Resolver>(params: GetPresetParams<TResolver>): GetPresetResult<TResolver> {
|
|
20
27
|
const { preset: presetName, presets, resolvers, transformers: userTransformers } = params
|
|
21
28
|
const [defaultResolver, ...userResolvers] = resolvers
|
package/src/utils/linters.ts
CHANGED
|
@@ -3,6 +3,12 @@ import type { linters } from '../constants.ts'
|
|
|
3
3
|
|
|
4
4
|
type Linter = keyof typeof linters
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Returns `true` when the given linter is installed and callable.
|
|
8
|
+
*
|
|
9
|
+
* Availability is detected by running `<linter> --version` and checking
|
|
10
|
+
* that the process exits without error.
|
|
11
|
+
*/
|
|
6
12
|
async function isLinterAvailable(linter: Linter): Promise<boolean> {
|
|
7
13
|
try {
|
|
8
14
|
await x(linter, ['--version'], { nodeOptions: { stdio: 'ignore' } })
|
|
@@ -12,7 +18,21 @@ async function isLinterAvailable(linter: Linter): Promise<boolean> {
|
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Detects the first available linter on the current system.
|
|
23
|
+
*
|
|
24
|
+
* - Checks in preference order: `biome`, `oxlint`, `eslint`.
|
|
25
|
+
* - Returns `null` when none are found.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const linter = await detectLinter()
|
|
30
|
+
* if (linter) {
|
|
31
|
+
* console.log(`Using ${linter} for linting`)
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export async function detectLinter(): Promise<Linter | null> {
|
|
16
36
|
const linterNames = new Set(['biome', 'oxlint', 'eslint'] as const)
|
|
17
37
|
|
|
18
38
|
for (const linter of linterNames) {
|
|
@@ -21,5 +41,5 @@ export async function detectLinter(): Promise<Linter | undefined> {
|
|
|
21
41
|
}
|
|
22
42
|
}
|
|
23
43
|
|
|
24
|
-
return
|
|
44
|
+
return null
|
|
25
45
|
}
|
package/src/utils/packageJSON.ts
CHANGED
|
@@ -10,16 +10,16 @@ type PackageJSON = {
|
|
|
10
10
|
type DependencyName = string
|
|
11
11
|
type DependencyVersion = string
|
|
12
12
|
|
|
13
|
-
function getPackageJSONSync(cwd?: string): PackageJSON |
|
|
13
|
+
function getPackageJSONSync(cwd?: string): PackageJSON | null {
|
|
14
14
|
const pkgPath = pkg.up({ cwd })
|
|
15
15
|
if (!pkgPath) {
|
|
16
|
-
return
|
|
16
|
+
return null
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
return JSON.parse(readSync(pkgPath)) as PackageJSON
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): string |
|
|
22
|
+
function match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): string | null {
|
|
23
23
|
const dependencies = {
|
|
24
24
|
...(packageJSON.dependencies || {}),
|
|
25
25
|
...(packageJSON.devDependencies || {}),
|
|
@@ -31,15 +31,30 @@ function match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): s
|
|
|
31
31
|
|
|
32
32
|
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency))
|
|
33
33
|
|
|
34
|
-
return matched ? dependencies[matched] :
|
|
34
|
+
return matched ? (dependencies[matched] ?? null) : null
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function getVersionSync(dependency: DependencyName | RegExp, cwd?: string): DependencyVersion |
|
|
37
|
+
function getVersionSync(dependency: DependencyName | RegExp, cwd?: string): DependencyVersion | null {
|
|
38
38
|
const packageJSON = getPackageJSONSync(cwd)
|
|
39
39
|
|
|
40
|
-
return packageJSON ? match(packageJSON, dependency) :
|
|
40
|
+
return packageJSON ? match(packageJSON, dependency) : null
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Returns `true` when the nearest `package.json` declares a dependency that
|
|
45
|
+
* satisfies the given semver range.
|
|
46
|
+
*
|
|
47
|
+
* - Searches both `dependencies` and `devDependencies`.
|
|
48
|
+
* - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
|
|
49
|
+
* - Uses `semver.satisfies` for range comparison; returns `false` when the
|
|
50
|
+
* version string cannot be coerced into a valid semver.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* satisfiesDependency('react', '>=18') // true when react@18.x is installed
|
|
55
|
+
* satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
43
58
|
export function satisfiesDependency(dependency: DependencyName | RegExp, version: DependencyVersion, cwd?: string): boolean {
|
|
44
59
|
const packageVersion = getVersionSync(dependency, cwd)
|
|
45
60
|
|