@kubb/core 5.0.0-beta.62 → 5.0.0-beta.64
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/{diagnostics-D0G07LHG.d.ts → diagnostics-BqiNAWVS.d.ts} +47 -40
- package/dist/index.cjs +47 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.js +49 -54
- package/dist/index.js.map +1 -1
- package/dist/{memoryStorage-CWFzAz4o.js → memoryStorage-DWnhqUf2.js} +3 -3
- package/dist/memoryStorage-DWnhqUf2.js.map +1 -0
- package/dist/{memoryStorage-CUj1hrxa.cjs → memoryStorage-mojU6pbA.cjs} +2 -2
- package/dist/memoryStorage-mojU6pbA.cjs.map +1 -0
- package/dist/mocks.cjs +2 -2
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +3 -3
- package/dist/mocks.js +3 -3
- package/dist/mocks.js.map +1 -1
- package/package.json +4 -5
- package/dist/memoryStorage-CUj1hrxa.cjs.map +0 -1
- package/dist/memoryStorage-CWFzAz4o.js.map +0 -1
- package/src/FileManager.ts +0 -137
- package/src/FileProcessor.ts +0 -212
- package/src/KubbDriver.ts +0 -893
- package/src/Transform.ts +0 -105
- package/src/constants.ts +0 -126
- package/src/createAdapter.ts +0 -127
- package/src/createKubb.ts +0 -196
- package/src/createRenderer.ts +0 -72
- package/src/createReporter.ts +0 -134
- package/src/createStorage.ts +0 -83
- package/src/defineGenerator.ts +0 -210
- package/src/defineParser.ts +0 -62
- package/src/definePlugin.ts +0 -437
- package/src/defineResolver.ts +0 -711
- package/src/diagnostics.ts +0 -662
- package/src/index.ts +0 -20
- package/src/mocks.ts +0 -249
- package/src/reporters/cliReporter.ts +0 -89
- package/src/reporters/fileReporter.ts +0 -103
- package/src/reporters/jsonReporter.ts +0 -20
- package/src/reporters/report.ts +0 -85
- package/src/storages/fsStorage.ts +0 -82
- package/src/storages/memoryStorage.ts +0 -55
- package/src/types.ts +0 -829
- /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/src/diagnostics.ts
DELETED
|
@@ -1,662 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
-
import { styleText } from 'node:util'
|
|
3
|
-
import type { AsyncEventEmitter } from '@internals/utils'
|
|
4
|
-
import { getErrorMessage } from '@internals/utils'
|
|
5
|
-
import { version } from '../package.json'
|
|
6
|
-
import { type DiagnosticCode, diagnosticCode } from './constants.ts'
|
|
7
|
-
import type { KubbHooks } from './types.ts'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Docs major version, derived from the package version so the link tracks the published major.
|
|
11
|
-
*/
|
|
12
|
-
const docsMajor = version.split('.')[0] ?? '5'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* How serious a diagnostic is. `error` fails the build, `warning` and `info`
|
|
16
|
-
* are reported but do not.
|
|
17
|
-
*/
|
|
18
|
-
export type DiagnosticSeverity = 'error' | 'warning' | 'info'
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A human-readable explanation of a diagnostic code: a short title, what triggers it, and how
|
|
22
|
-
* to resolve it. This is the source of truth the kubb.dev `/diagnostics/<slug>` pages mirror, so
|
|
23
|
-
* every code stays documented in one place. Adding a code without documenting it fails the build.
|
|
24
|
-
*/
|
|
25
|
-
export type DiagnosticDoc = {
|
|
26
|
-
/**
|
|
27
|
-
* Short title shown as the docs heading.
|
|
28
|
-
*/
|
|
29
|
-
title: string
|
|
30
|
-
/**
|
|
31
|
-
* What triggers the diagnostic.
|
|
32
|
-
*/
|
|
33
|
-
cause: string
|
|
34
|
-
/**
|
|
35
|
-
* The action that resolves it.
|
|
36
|
-
*/
|
|
37
|
-
fix: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Points a diagnostic back into the source document. Inputs are parsed into an
|
|
42
|
-
* object model with no line/column, so locations carry a JSON pointer the adapter
|
|
43
|
-
* builds (the OAS adapter emits `#/components/schemas/Pet`). A `config` diagnostic
|
|
44
|
-
* points at the Kubb config itself and so has no pointer.
|
|
45
|
-
*/
|
|
46
|
-
export type DiagnosticLocation =
|
|
47
|
-
| {
|
|
48
|
-
kind: 'schema'
|
|
49
|
-
/**
|
|
50
|
-
* RFC 6901 JSON pointer into the source document.
|
|
51
|
-
*/
|
|
52
|
-
pointer: string
|
|
53
|
-
/**
|
|
54
|
-
* The original reference when the diagnostic stems from an unresolved one.
|
|
55
|
-
*/
|
|
56
|
-
ref?: string
|
|
57
|
-
}
|
|
58
|
-
| {
|
|
59
|
-
kind: 'operation' | 'document'
|
|
60
|
-
/**
|
|
61
|
-
* RFC 6901 JSON pointer into the source document.
|
|
62
|
-
*/
|
|
63
|
-
pointer: string
|
|
64
|
-
}
|
|
65
|
-
| {
|
|
66
|
-
kind: 'config'
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* What a diagnostic carries.
|
|
71
|
-
* - `problem` is a build issue shown to the user, and the only kind rendered as a problem.
|
|
72
|
-
* - `performance` records a plugin's elapsed time.
|
|
73
|
-
* - `update` is a version notice.
|
|
74
|
-
*/
|
|
75
|
-
export type DiagnosticKind = 'problem' | 'performance' | 'update'
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Codes that describe a build problem: every {@link DiagnosticCode} except the
|
|
79
|
-
* `performance` and `updateAvailable` codes, which ride on their own variants.
|
|
80
|
-
*/
|
|
81
|
-
export type ProblemCode = Exclude<DiagnosticCode, typeof diagnosticCode.performance | typeof diagnosticCode.updateAvailable>
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* A build problem collected during a run, gathered into the result instead of
|
|
85
|
-
* aborting on the first failure.
|
|
86
|
-
*/
|
|
87
|
-
export type ProblemDiagnostic = {
|
|
88
|
-
/**
|
|
89
|
-
* @default 'problem'
|
|
90
|
-
*/
|
|
91
|
-
kind?: 'problem'
|
|
92
|
-
/**
|
|
93
|
-
* Stable identifier for the problem, from the {@link diagnosticCode} catalog.
|
|
94
|
-
*/
|
|
95
|
-
code: ProblemCode
|
|
96
|
-
severity: DiagnosticSeverity
|
|
97
|
-
message: string
|
|
98
|
-
location?: DiagnosticLocation
|
|
99
|
-
/**
|
|
100
|
-
* A suggested fix, phrased as an action the user can take.
|
|
101
|
-
*/
|
|
102
|
-
help?: string
|
|
103
|
-
/**
|
|
104
|
-
* Name of the plugin or subsystem that produced the diagnostic.
|
|
105
|
-
*/
|
|
106
|
-
plugin?: string
|
|
107
|
-
/**
|
|
108
|
-
* The underlying error, when the diagnostic wraps a thrown one.
|
|
109
|
-
*/
|
|
110
|
-
cause?: Error
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* A per-plugin performance record, built with {@link Diagnostics.performance}. The `performance`
|
|
115
|
-
* kind keeps it out of the problem list. It feeds the per-plugin timing bars, and reporters sum
|
|
116
|
-
* these into the run total.
|
|
117
|
-
*/
|
|
118
|
-
export type PerformanceDiagnostic = {
|
|
119
|
-
kind: 'performance'
|
|
120
|
-
code: typeof diagnosticCode.performance
|
|
121
|
-
severity: 'info'
|
|
122
|
-
message: string
|
|
123
|
-
/**
|
|
124
|
-
* The plugin this measurement belongs to.
|
|
125
|
-
*/
|
|
126
|
-
plugin: string
|
|
127
|
-
/**
|
|
128
|
-
* Elapsed milliseconds.
|
|
129
|
-
*/
|
|
130
|
-
duration: number
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* A notice that a newer Kubb version is available on npm, built with {@link Diagnostics.update}.
|
|
135
|
-
* It renders like any info diagnostic.
|
|
136
|
-
*/
|
|
137
|
-
export type UpdateDiagnostic = {
|
|
138
|
-
kind: 'update'
|
|
139
|
-
code: typeof diagnosticCode.updateAvailable
|
|
140
|
-
severity: 'info'
|
|
141
|
-
message: string
|
|
142
|
-
/**
|
|
143
|
-
* The running Kubb version.
|
|
144
|
-
*/
|
|
145
|
-
currentVersion: string
|
|
146
|
-
/**
|
|
147
|
-
* The newest version published on npm.
|
|
148
|
-
*/
|
|
149
|
-
latestVersion: string
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* A structured record collected during a build, discriminated on `kind`: a
|
|
154
|
-
* {@link ProblemDiagnostic} for an issue, a {@link PerformanceDiagnostic} for a per-plugin
|
|
155
|
-
* timing, or an {@link UpdateDiagnostic} for a version notice.
|
|
156
|
-
*/
|
|
157
|
-
export type Diagnostic = ProblemDiagnostic | PerformanceDiagnostic | UpdateDiagnostic
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Maps each {@link DiagnosticCode} to the variant it selects, for {@link narrow}.
|
|
161
|
-
* Every {@link ProblemCode} selects a {@link ProblemDiagnostic}, and the two bookkeeping codes
|
|
162
|
-
* select their own variant.
|
|
163
|
-
*/
|
|
164
|
-
export type DiagnosticByCode = Record<ProblemCode, ProblemDiagnostic> &
|
|
165
|
-
Record<typeof diagnosticCode.performance, PerformanceDiagnostic> &
|
|
166
|
-
Record<typeof diagnosticCode.updateAvailable, UpdateDiagnostic>
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Narrows a {@link Diagnostic} to the variant for `code`, or `null` when it does not match.
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* ```ts
|
|
173
|
-
* const update = narrow(diagnostic, diagnosticCode.updateAvailable)
|
|
174
|
-
* if (update) {
|
|
175
|
-
* console.log(update.latestVersion)
|
|
176
|
-
* }
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
function narrow<C extends DiagnosticCode>(diagnostic: Diagnostic, code: C): DiagnosticByCode[C] | null {
|
|
180
|
-
return diagnostic.code === code ? (diagnostic as DiagnosticByCode[C]) : null
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
185
|
-
* with no `kind` is treated as a `problem`.
|
|
186
|
-
*/
|
|
187
|
-
function isKind<T extends Diagnostic>(kind: DiagnosticKind) {
|
|
188
|
-
return (diagnostic: Diagnostic): diagnostic is T => (diagnostic.kind ?? 'problem') === kind
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
193
|
-
*
|
|
194
|
-
* @example
|
|
195
|
-
* ```ts
|
|
196
|
-
* if (isProblem(diagnostic)) {
|
|
197
|
-
* console.log(diagnostic.location)
|
|
198
|
-
* }
|
|
199
|
-
* ```
|
|
200
|
-
*/
|
|
201
|
-
const isProblem = isKind<ProblemDiagnostic>('problem')
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```ts
|
|
208
|
-
* const timings = diagnostics.filter(isPerformance)
|
|
209
|
-
* ```
|
|
210
|
-
*/
|
|
211
|
-
const isPerformance = isKind<PerformanceDiagnostic>('performance')
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* ```ts
|
|
218
|
-
* if (isUpdate(diagnostic)) {
|
|
219
|
-
* console.log(diagnostic.latestVersion)
|
|
220
|
-
* }
|
|
221
|
-
* ```
|
|
222
|
-
*/
|
|
223
|
-
const isUpdate = isKind<UpdateDiagnostic>('update')
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Glyph and accent color per severity, matching the miette/oxlint convention
|
|
227
|
-
* (`×` error, `⚠` warning, `ℹ` advice).
|
|
228
|
-
*/
|
|
229
|
-
const severityStyle: Record<DiagnosticSeverity, { glyph: string; color: 'red' | 'yellow' | 'blue' }> = {
|
|
230
|
-
error: { glyph: '×', color: 'red' },
|
|
231
|
-
warning: { glyph: '⚠', color: 'yellow' },
|
|
232
|
-
info: { glyph: 'ℹ', color: 'blue' },
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* A {@link Diagnostic} reduced to its JSON-safe fields plus a `docsUrl`, for
|
|
237
|
-
* machine-readable output (the `--reporter json` report, the MCP tools). Drops the
|
|
238
|
-
* non-serializable `cause` and the `timing`/`duration` bookkeeping.
|
|
239
|
-
*/
|
|
240
|
-
export type SerializedDiagnostic = {
|
|
241
|
-
code: DiagnosticCode
|
|
242
|
-
severity: DiagnosticSeverity
|
|
243
|
-
message: string
|
|
244
|
-
location?: DiagnosticLocation
|
|
245
|
-
help?: string
|
|
246
|
-
plugin?: string
|
|
247
|
-
/**
|
|
248
|
-
* The kubb.dev docs link for the code, omitted for the unknown fallback.
|
|
249
|
-
*/
|
|
250
|
-
docsUrl?: string
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Explanation for every {@link diagnosticCode}. Use {@link Diagnostics.explain} to look one up
|
|
255
|
-
* and `Diagnostics.docsUrl` for the matching kubb.dev page.
|
|
256
|
-
*/
|
|
257
|
-
const diagnosticCatalog: Record<DiagnosticCode, DiagnosticDoc> = {
|
|
258
|
-
[diagnosticCode.unknown]: {
|
|
259
|
-
title: 'Unknown error',
|
|
260
|
-
cause: 'An error was thrown without a stable Kubb code, so it is reported as-is.',
|
|
261
|
-
fix: 'Read the underlying message and stack. If it comes from a plugin or adapter, check its configuration; otherwise report it as a possible Kubb bug.',
|
|
262
|
-
},
|
|
263
|
-
[diagnosticCode.inputNotFound]: {
|
|
264
|
-
title: 'Input not found',
|
|
265
|
-
cause: 'The file or URL set in `input.path` (or passed as `kubb generate PATH`) could not be read.',
|
|
266
|
-
fix: 'Check that the path or URL exists and is readable, then set it in `input.path` or pass it on the CLI.',
|
|
267
|
-
},
|
|
268
|
-
[diagnosticCode.inputRequired]: {
|
|
269
|
-
title: 'Input required',
|
|
270
|
-
cause: 'An adapter is configured but no `input` was provided.',
|
|
271
|
-
fix: 'Set `input.path` (a file or URL) or `input.data` (an inline spec) in your Kubb config.',
|
|
272
|
-
},
|
|
273
|
-
[diagnosticCode.refNotFound]: {
|
|
274
|
-
title: 'Reference not found',
|
|
275
|
-
cause: 'A `$ref` could not be resolved in the source document.',
|
|
276
|
-
fix: 'Add the missing definition (for example under `components.schemas`) or fix the `$ref`. Run `kubb validate` to check the spec.',
|
|
277
|
-
},
|
|
278
|
-
[diagnosticCode.invalidServerVariable]: {
|
|
279
|
-
title: 'Invalid server variable',
|
|
280
|
-
cause: 'A server variable value is not allowed by its `enum`.',
|
|
281
|
-
fix: 'Use one of the values listed in the server variable `enum`, or update the spec.',
|
|
282
|
-
},
|
|
283
|
-
[diagnosticCode.pluginNotFound]: {
|
|
284
|
-
title: 'Plugin not found',
|
|
285
|
-
cause: 'A plugin that another plugin depends on is missing from the config.',
|
|
286
|
-
fix: 'Add the required plugin to the `plugins` array in kubb.config.ts, or remove the dependency on it.',
|
|
287
|
-
},
|
|
288
|
-
[diagnosticCode.pluginFailed]: {
|
|
289
|
-
title: 'Plugin failed',
|
|
290
|
-
cause: 'A plugin threw while generating, or reported an error through `ctx.error`.',
|
|
291
|
-
fix: 'Read the underlying error and check the plugin options and the schema or operation it failed on.',
|
|
292
|
-
},
|
|
293
|
-
[diagnosticCode.pluginWarning]: {
|
|
294
|
-
title: 'Plugin warning',
|
|
295
|
-
cause: 'A plugin reported a non-fatal warning through `ctx.warn`.',
|
|
296
|
-
fix: 'Review the message. It does not fail the build; adjust the plugin options or input if the warning is unwanted.',
|
|
297
|
-
},
|
|
298
|
-
[diagnosticCode.pluginInfo]: {
|
|
299
|
-
title: 'Plugin info',
|
|
300
|
-
cause: 'A plugin reported an informational message through `ctx.info`.',
|
|
301
|
-
fix: 'Informational only. No action is required.',
|
|
302
|
-
},
|
|
303
|
-
[diagnosticCode.unsupportedFormat]: {
|
|
304
|
-
title: 'Unsupported format',
|
|
305
|
-
cause: 'A schema uses a `format` Kubb does not map to a specific type, so it falls back to the base type.',
|
|
306
|
-
fix: 'Use a format Kubb supports, or handle the custom format with a parser or plugin.',
|
|
307
|
-
},
|
|
308
|
-
[diagnosticCode.deprecated]: {
|
|
309
|
-
title: 'Deprecated',
|
|
310
|
-
cause: 'A referenced schema or operation is marked `deprecated`.',
|
|
311
|
-
fix: 'Migrate off the deprecated definition if the warning is unwanted.',
|
|
312
|
-
},
|
|
313
|
-
[diagnosticCode.adapterRequired]: {
|
|
314
|
-
title: 'Adapter required',
|
|
315
|
-
cause: 'An action needs an adapter but none is configured.',
|
|
316
|
-
fix: 'Set `adapter` in kubb.config.ts, for example `adapterOas()`.',
|
|
317
|
-
},
|
|
318
|
-
[diagnosticCode.pathTraversal]: {
|
|
319
|
-
title: 'Path traversal',
|
|
320
|
-
cause: 'A resolved output path escaped the output directory, which can stem from a path traversal in the spec or a misconfigured `group.name`.',
|
|
321
|
-
fix: 'Keep generated paths within the output directory. Review the `group.name` function and the names coming from the spec.',
|
|
322
|
-
},
|
|
323
|
-
[diagnosticCode.invalidPluginOptions]: {
|
|
324
|
-
title: 'Invalid plugin options',
|
|
325
|
-
cause: "A plugin was configured with options that cannot be honored, for example `output.mode: 'file'` paired with a `group` option.",
|
|
326
|
-
fix: "Fix the plugin options. A single-file output has nothing to group, so remove the `group` option or use `output.mode: 'directory'`.",
|
|
327
|
-
},
|
|
328
|
-
[diagnosticCode.hookFailed]: {
|
|
329
|
-
title: 'Hook failed',
|
|
330
|
-
cause: 'A post-generate shell hook (`hooks.done`) exited with a non-zero status.',
|
|
331
|
-
fix: 'Check the command is installed and correct, and run it manually to see the error.',
|
|
332
|
-
},
|
|
333
|
-
[diagnosticCode.formatFailed]: {
|
|
334
|
-
title: 'Format failed',
|
|
335
|
-
cause: 'The formatter pass over the generated files failed.',
|
|
336
|
-
fix: 'Check the formatter (oxfmt, biome, or prettier) is installed and its config is valid, then run it manually on the output.',
|
|
337
|
-
},
|
|
338
|
-
[diagnosticCode.lintFailed]: {
|
|
339
|
-
title: 'Lint failed',
|
|
340
|
-
cause: 'The linter pass over the generated files failed.',
|
|
341
|
-
fix: 'Check the linter (oxlint, biome, or eslint) is installed and its config is valid, then run it manually on the output.',
|
|
342
|
-
},
|
|
343
|
-
[diagnosticCode.performance]: {
|
|
344
|
-
title: 'Performance',
|
|
345
|
-
cause: 'Not a failure. Records a plugin’s elapsed time, summed into the run total.',
|
|
346
|
-
fix: 'No action. This is an informational metric.',
|
|
347
|
-
},
|
|
348
|
-
[diagnosticCode.updateAvailable]: {
|
|
349
|
-
title: 'Update available',
|
|
350
|
-
cause: 'A newer Kubb version is published on npm than the one running.',
|
|
351
|
-
fix: 'Update the `@kubb/*` packages, for example `npm install -g @kubb/cli`, to get the latest fixes.',
|
|
352
|
-
},
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
357
|
-
* that lets deep code report a diagnostic without threading a callback.
|
|
358
|
-
*
|
|
359
|
-
* The sink lives in a single `AsyncLocalStorage` in the `@kubb/core` bundle.
|
|
360
|
-
* `Diagnostics.scope` activates it for a run, so anything inside that run (the
|
|
361
|
-
* adapter parse, a lazily consumed stream, a generator) reports through
|
|
362
|
-
* `Diagnostics.report` and lands in the same run.
|
|
363
|
-
*/
|
|
364
|
-
export class Diagnostics {
|
|
365
|
-
static #reporterStorage = new AsyncLocalStorage<(diagnostic: Diagnostic) => void>()
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* The diagnostic code catalog, exposed as `Diagnostics.code` (e.g. `Diagnostics.code.refNotFound`).
|
|
369
|
-
*/
|
|
370
|
-
static code = diagnosticCode
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Type guard for a build {@link ProblemDiagnostic}.
|
|
374
|
-
*/
|
|
375
|
-
static isProblem = isProblem
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Type guard for a version-update {@link UpdateDiagnostic}.
|
|
379
|
-
*/
|
|
380
|
-
static isUpdate = isUpdate
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Type guard for a per-plugin {@link PerformanceDiagnostic}.
|
|
384
|
-
*/
|
|
385
|
-
static isPerformance = isPerformance
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Narrows a {@link Diagnostic} to the variant for `code`, or `null` when it does not match.
|
|
389
|
-
*/
|
|
390
|
-
static narrow = narrow
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* An `Error` that carries a {@link Diagnostic}, so structured problems can flow
|
|
394
|
-
* through the existing throw/catch paths while keeping their code and location.
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* ```ts
|
|
398
|
-
* throw new Diagnostics.Error({ code: diagnosticCode.refNotFound, severity: 'error', message: `Could not find ${ref}`, location: { kind: 'schema', pointer: ref, ref } })
|
|
399
|
-
* ```
|
|
400
|
-
*/
|
|
401
|
-
static Error = class DiagnosticError extends Error {
|
|
402
|
-
diagnostic: ProblemDiagnostic
|
|
403
|
-
|
|
404
|
-
constructor(diagnostic: ProblemDiagnostic) {
|
|
405
|
-
super(diagnostic.message, { cause: diagnostic.cause })
|
|
406
|
-
this.name = 'DiagnosticError'
|
|
407
|
-
this.diagnostic = diagnostic
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Structural check for a {@link Diagnostics.Error}, including one thrown from a duplicated
|
|
413
|
-
* `@kubb/core` copy where `instanceof` fails. Matches on the `name` and a `diagnostic`
|
|
414
|
-
* that carries a `code`.
|
|
415
|
-
*/
|
|
416
|
-
static isError(error: unknown): error is InstanceType<typeof Diagnostics.Error> {
|
|
417
|
-
if (error instanceof Diagnostics.Error) {
|
|
418
|
-
return true
|
|
419
|
-
}
|
|
420
|
-
return (
|
|
421
|
-
error instanceof Error &&
|
|
422
|
-
error.name === 'DiagnosticError' &&
|
|
423
|
-
'diagnostic' in error &&
|
|
424
|
-
typeof (error as { diagnostic?: unknown }).diagnostic === 'object' &&
|
|
425
|
-
(error as { diagnostic?: Diagnostic }).diagnostic !== null &&
|
|
426
|
-
typeof (error as { diagnostic?: { code?: unknown } }).diagnostic?.code === 'string'
|
|
427
|
-
)
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Runs `fn` with `sink` as the active diagnostic sink for the whole async
|
|
432
|
-
* subtree, so {@link Diagnostics.report} reaches it from anywhere inside.
|
|
433
|
-
*/
|
|
434
|
-
static scope<T>(sink: (diagnostic: Diagnostic) => void, fn: () => T): T {
|
|
435
|
-
return Diagnostics.#reporterStorage.run(sink, fn)
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Collects a diagnostic into the active build via the run-scoped sink, without throwing.
|
|
440
|
-
* Returns `true` when a run consumed it, `false` when called outside a {@link Diagnostics.scope}
|
|
441
|
-
* (so callers can fall back to throwing). Use a `warning`/`info` severity for non-fatal issues.
|
|
442
|
-
* For rendering a diagnostic live on the hook bus, use {@link Diagnostics.emit} instead.
|
|
443
|
-
*/
|
|
444
|
-
static report(diagnostic: Diagnostic): boolean {
|
|
445
|
-
const sink = Diagnostics.#reporterStorage.getStore()
|
|
446
|
-
if (!sink) {
|
|
447
|
-
return false
|
|
448
|
-
}
|
|
449
|
-
sink(diagnostic)
|
|
450
|
-
return true
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Emits a diagnostic on the run's `kubb:diagnostic` event so the loggers render it live.
|
|
455
|
-
* Use it instead of calling `hooks.emit('kubb:diagnostic', ...)` directly. To collect a
|
|
456
|
-
* diagnostic into the build result from deep in a run, use {@link Diagnostics.report} instead.
|
|
457
|
-
*/
|
|
458
|
-
static async emit(hooks: AsyncEventEmitter<KubbHooks>, diagnostic: ProblemDiagnostic | UpdateDiagnostic): Promise<void> {
|
|
459
|
-
await hooks.emit('kubb:diagnostic', { diagnostic })
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link Diagnostics.Error}
|
|
464
|
-
* keeps its structured data, and anything else becomes a `KUBB_UNKNOWN` error.
|
|
465
|
-
*/
|
|
466
|
-
static from(error: unknown): ProblemDiagnostic {
|
|
467
|
-
// The event emitter and BuildError wrap the original, so walk the cause chain to
|
|
468
|
-
// recover a Diagnostics.Error thrown deeper down. `root` tracks the deepest error so
|
|
469
|
-
// the unknown diagnostic reports the original message and stack, not the wrapper's.
|
|
470
|
-
const seen = new Set<unknown>()
|
|
471
|
-
let current: unknown = error
|
|
472
|
-
let root: Error | undefined
|
|
473
|
-
while (current instanceof Error && !seen.has(current)) {
|
|
474
|
-
// Match structurally, not just by `instanceof`: a `Diagnostics.Error` thrown from a
|
|
475
|
-
// duplicated `@kubb/core` copy (bundled into an adapter or plugin) is a different
|
|
476
|
-
// class, but still carries the same `diagnostic`, so its code must survive.
|
|
477
|
-
if (Diagnostics.isError(current)) {
|
|
478
|
-
return current.diagnostic
|
|
479
|
-
}
|
|
480
|
-
seen.add(current)
|
|
481
|
-
root = current
|
|
482
|
-
current = current.cause
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
return {
|
|
486
|
-
code: diagnosticCode.unknown,
|
|
487
|
-
severity: 'error',
|
|
488
|
-
message: root ? root.message : getErrorMessage(error),
|
|
489
|
-
cause: root,
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Builds a per-plugin performance record. Reporters sum these into the run total.
|
|
495
|
-
*/
|
|
496
|
-
static performance({ plugin, duration }: { plugin: string; duration: number }): PerformanceDiagnostic {
|
|
497
|
-
return {
|
|
498
|
-
kind: 'performance',
|
|
499
|
-
code: diagnosticCode.performance,
|
|
500
|
-
severity: 'info',
|
|
501
|
-
message: `${plugin} generated in ${Math.round(duration)}ms`,
|
|
502
|
-
plugin,
|
|
503
|
-
duration,
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Builds the version-update notice shown when a newer Kubb is published on npm.
|
|
509
|
-
*/
|
|
510
|
-
static update({ currentVersion, latestVersion }: { currentVersion: string; latestVersion: string }): UpdateDiagnostic {
|
|
511
|
-
return {
|
|
512
|
-
kind: 'update',
|
|
513
|
-
code: diagnosticCode.updateAvailable,
|
|
514
|
-
severity: 'info',
|
|
515
|
-
message: `Update available: v${currentVersion} → v${latestVersion}. Run \`npm install -g @kubb/cli\` to update.`,
|
|
516
|
-
currentVersion,
|
|
517
|
-
latestVersion,
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* True when any diagnostic is an error, the severity that fails a build. Non-error
|
|
523
|
-
* diagnostics are ignored.
|
|
524
|
-
*/
|
|
525
|
-
static hasError(diagnostics: ReadonlyArray<Diagnostic>): boolean {
|
|
526
|
-
return diagnostics.some((diagnostic) => diagnostic.severity === 'error')
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Names of the plugins that failed, deduped, derived from the error diagnostics
|
|
531
|
-
* that carry a `plugin`.
|
|
532
|
-
*/
|
|
533
|
-
static failedPlugins(diagnostics: ReadonlyArray<Diagnostic>): Array<string> {
|
|
534
|
-
const names = new Set<string>()
|
|
535
|
-
for (const diagnostic of diagnostics) {
|
|
536
|
-
if (diagnostic.severity === 'error' && diagnostic.plugin) {
|
|
537
|
-
names.add(diagnostic.plugin)
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return [...names]
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Counts `problem` diagnostics by severity for the run summary. `timing`
|
|
545
|
-
* diagnostics are ignored.
|
|
546
|
-
*/
|
|
547
|
-
static count(diagnostics: ReadonlyArray<Diagnostic>): { errors: number; warnings: number; infos: number } {
|
|
548
|
-
let errors = 0
|
|
549
|
-
let warnings = 0
|
|
550
|
-
let infos = 0
|
|
551
|
-
for (const diagnostic of diagnostics) {
|
|
552
|
-
if (!isProblem(diagnostic)) {
|
|
553
|
-
continue
|
|
554
|
-
}
|
|
555
|
-
if (diagnostic.severity === 'error') {
|
|
556
|
-
errors += 1
|
|
557
|
-
} else if (diagnostic.severity === 'warning') {
|
|
558
|
-
warnings += 1
|
|
559
|
-
} else {
|
|
560
|
-
infos += 1
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return { errors, warnings, infos }
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Drops duplicate `problem` diagnostics that share a code, location pointer, and
|
|
568
|
-
* plugin, so the same issue reported across several passes is shown once. Non-problem
|
|
569
|
-
* diagnostics are always kept.
|
|
570
|
-
*/
|
|
571
|
-
static dedupe(diagnostics: ReadonlyArray<Diagnostic>): Array<Diagnostic> {
|
|
572
|
-
const seen = new Set<string>()
|
|
573
|
-
const result: Array<Diagnostic> = []
|
|
574
|
-
for (const diagnostic of diagnostics) {
|
|
575
|
-
if (!isProblem(diagnostic)) {
|
|
576
|
-
result.push(diagnostic)
|
|
577
|
-
continue
|
|
578
|
-
}
|
|
579
|
-
const pointer = diagnostic.location && 'pointer' in diagnostic.location ? diagnostic.location.pointer : ''
|
|
580
|
-
const key = `${diagnostic.code} ${pointer} ${diagnostic.plugin ?? ''}`
|
|
581
|
-
if (seen.has(key)) {
|
|
582
|
-
continue
|
|
583
|
-
}
|
|
584
|
-
seen.add(key)
|
|
585
|
-
result.push(diagnostic)
|
|
586
|
-
}
|
|
587
|
-
return result
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* Builds the kubb.dev docs URL for a diagnostic code, e.g.
|
|
592
|
-
* `KUBB_REF_NOT_FOUND` → `https://kubb.dev/docs/5.x/reference/diagnostics/kubb-ref-not-found`.
|
|
593
|
-
*/
|
|
594
|
-
static docsUrl(code: string): string {
|
|
595
|
-
const slug = code.toLowerCase().replaceAll('_', '-')
|
|
596
|
-
return `https://kubb.dev/docs/${docsMajor}.x/reference/diagnostics/${slug}`
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* The catalog entry for a code: its title, cause, and fix. Mirrors the kubb.dev
|
|
601
|
-
* `/diagnostics/<slug>` page.
|
|
602
|
-
*/
|
|
603
|
-
static explain(code: DiagnosticCode): DiagnosticDoc {
|
|
604
|
-
return diagnosticCatalog[code]
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
609
|
-
* consumers. The `cause`, `kind`, and `duration` are dropped, and absent optional
|
|
610
|
-
* fields are omitted rather than set to `undefined`.
|
|
611
|
-
*/
|
|
612
|
-
static serialize(diagnostic: Diagnostic): SerializedDiagnostic {
|
|
613
|
-
const problem = isProblem(diagnostic) ? diagnostic : undefined
|
|
614
|
-
return {
|
|
615
|
-
code: diagnostic.code,
|
|
616
|
-
severity: diagnostic.severity,
|
|
617
|
-
message: diagnostic.message,
|
|
618
|
-
...(problem?.location ? { location: problem.location } : {}),
|
|
619
|
-
...(problem?.help ? { help: problem.help } : {}),
|
|
620
|
-
...(problem?.plugin ? { plugin: problem.plugin } : {}),
|
|
621
|
-
...(diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }),
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* Renders a {@link Diagnostic} for terminal output as its parts: the colored severity `symbol`
|
|
627
|
-
* (the gutter glyph), the `plugin(CODE): message` `headline`, and the `details` lines (optional
|
|
628
|
-
* `at <pointer>`, `help:`, and `docs:`).
|
|
629
|
-
*
|
|
630
|
-
* Hosts compose these to fit their gutter: a clack logger passes `symbol` as its own gutter and
|
|
631
|
-
* `[headline, ...details]` as the message, while plain text outputs use {@link Diagnostics.formatLines}.
|
|
632
|
-
*/
|
|
633
|
-
static format(diagnostic: Diagnostic): { symbol: string; headline: string; details: Array<string> } {
|
|
634
|
-
const { code, severity, message } = diagnostic
|
|
635
|
-
const { glyph, color } = severityStyle[severity]
|
|
636
|
-
const problem = isProblem(diagnostic) ? diagnostic : undefined
|
|
637
|
-
|
|
638
|
-
const rule = styleText(color, styleText('bold', problem?.plugin ? `${problem.plugin}(${code})` : code))
|
|
639
|
-
const details: Array<string> = []
|
|
640
|
-
|
|
641
|
-
if (problem?.location && 'pointer' in problem.location) {
|
|
642
|
-
details.push(` ${styleText('dim', 'at')} ${styleText('cyan', problem.location.pointer)}`)
|
|
643
|
-
}
|
|
644
|
-
if (problem?.help) {
|
|
645
|
-
details.push(` ${styleText('cyan', 'help:')} ${problem.help}`)
|
|
646
|
-
}
|
|
647
|
-
if (code !== diagnosticCode.unknown) {
|
|
648
|
-
details.push(` ${styleText('dim', 'docs:')} ${styleText('cyan', Diagnostics.docsUrl(code))}`)
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
return { symbol: styleText(color, styleText('bold', glyph)), headline: `${rule}: ${message}`, details }
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* The self-contained block form of {@link Diagnostics.format}: `${symbol} ${headline}` followed by
|
|
656
|
-
* the detail lines. Used where there is no gutter to own the symbol (plain and file output).
|
|
657
|
-
*/
|
|
658
|
-
static formatLines(diagnostic: Diagnostic): Array<string> {
|
|
659
|
-
const { symbol, headline, details } = Diagnostics.format(diagnostic)
|
|
660
|
-
return [`${symbol} ${headline}`, ...details]
|
|
661
|
-
}
|
|
662
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export { AsyncEventEmitter, Url } from '@internals/utils'
|
|
2
|
-
// `@kubb/ast` re-exported as the `ast` namespace. Node constructors are reached as `ast.factory.createX`.
|
|
3
|
-
export * as ast from '@kubb/ast'
|
|
4
|
-
export { createAdapter } from './createAdapter.ts'
|
|
5
|
-
export { Diagnostics } from './diagnostics.ts'
|
|
6
|
-
export { createKubb } from './createKubb.ts'
|
|
7
|
-
export { createReporter, logLevel } from './createReporter.ts'
|
|
8
|
-
export { cliReporter } from './reporters/cliReporter.ts'
|
|
9
|
-
export { fileReporter } from './reporters/fileReporter.ts'
|
|
10
|
-
export { jsonReporter } from './reporters/jsonReporter.ts'
|
|
11
|
-
export { createRenderer } from './createRenderer.ts'
|
|
12
|
-
export { createStorage } from './createStorage.ts'
|
|
13
|
-
export { defineGenerator } from './defineGenerator.ts'
|
|
14
|
-
export { defineParser } from './defineParser.ts'
|
|
15
|
-
export { definePlugin } from './definePlugin.ts'
|
|
16
|
-
export { defineResolver } from './defineResolver.ts'
|
|
17
|
-
export { KubbDriver } from './KubbDriver.ts'
|
|
18
|
-
export { fsStorage } from './storages/fsStorage.ts'
|
|
19
|
-
export { memoryStorage } from './storages/memoryStorage.ts'
|
|
20
|
-
export * from './types.ts'
|