@kubb/core 5.10.1 → 5.10.3
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/{Hookable-ZX2OqaPh.js → Hookable-BsWZ8pHO.js} +427 -323
- package/dist/Hookable-BsWZ8pHO.js.map +1 -0
- package/dist/Hookable-C4S0gBso.cjs +3030 -0
- package/dist/Hookable-C4S0gBso.cjs.map +1 -0
- package/dist/{Hookable-BnYDebfN.cjs → Hookable-pvEb-nN9.cjs} +418 -321
- package/dist/Hookable-pvEb-nN9.cjs.map +1 -0
- package/dist/{createAdapter-D55JE9lX.d.ts → createAdapter-B5kKMsWV.d.cts} +851 -732
- package/dist/createAdapter-B5kKMsWV.d.ts +3147 -0
- package/dist/index.cjs +31 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +620 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +31 -19
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.d.cts +107 -0
- package/dist/mocks.d.ts +1 -2
- package/dist/mocks.js +1 -2
- package/dist/mocks.js.map +1 -1
- package/package.json +26 -8
- package/dist/Hookable-BnYDebfN.cjs.map +0 -1
- package/dist/Hookable-ZX2OqaPh.js.map +0 -1
- package/dist/rolldown-runtime-CRm0XQPb.js +0 -9
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import "node:module";
|
|
2
2
|
import { satisfies } from "verkit";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
import { styleText } from "node:util";
|
|
5
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
5
|
import path, { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
6
|
+
import { stripVTControlCharacters, styleText } from "node:util";
|
|
7
7
|
import { ast, collectImportedRefNames, collectUsedSchemaNames, extractStringsFromNodes } from "@kubb/ast";
|
|
8
|
+
//#region \0rolldown/runtime.js
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __name = (target, value) => __defProp(target, "name", {
|
|
11
|
+
value,
|
|
12
|
+
configurable: true
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
8
15
|
//#region package.json
|
|
9
|
-
var version = "5.10.
|
|
16
|
+
var version = "5.10.3";
|
|
10
17
|
//#endregion
|
|
11
18
|
//#region ../../internals/utils/src/casing.ts
|
|
12
19
|
/**
|
|
@@ -62,6 +69,35 @@ function toError(value) {
|
|
|
62
69
|
function getErrorMessage(value) {
|
|
63
70
|
return value instanceof Error ? value.message : String(value);
|
|
64
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns `error` followed by every error in its `cause` chain, outermost first. Stops at a
|
|
74
|
+
* non-`Error` cause or a cycle.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* getErrorChain(new Error('Config failed loading', { cause: new Error('boom') })).map((error) => error.message)
|
|
79
|
+
* // ['Config failed loading', 'boom']
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
function getErrorChain(error) {
|
|
83
|
+
const chain = [];
|
|
84
|
+
for (let current = error; current instanceof Error && !chain.includes(current); current = current.cause) chain.push(current);
|
|
85
|
+
return chain;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region ../../internals/utils/src/jsonPointer.ts
|
|
89
|
+
/**
|
|
90
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
91
|
+
* name holding those characters maps to a distinct pointer.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* escapePointerToken('/pets/{id}') // '~1pets~1{id}'
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
function escapePointerToken(token) {
|
|
99
|
+
return token.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
100
|
+
}
|
|
65
101
|
//#endregion
|
|
66
102
|
//#region ../../internals/utils/src/runtime.ts
|
|
67
103
|
const isBun = typeof Bun !== "undefined";
|
|
@@ -262,375 +298,388 @@ function formatMs(ms) {
|
|
|
262
298
|
return `${Math.round(ms)}ms`;
|
|
263
299
|
}
|
|
264
300
|
//#endregion
|
|
265
|
-
//#region src/
|
|
266
|
-
/**
|
|
267
|
-
* Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
|
|
268
|
-
* and stays stable so it can be referenced in tooling and (later) docs. Reference
|
|
269
|
-
* these instead of inlining the string at a throw site.
|
|
270
|
-
*/
|
|
271
|
-
const diagnosticCode = {
|
|
272
|
-
/**
|
|
273
|
-
* Fallback for an unstructured error with no specific code.
|
|
274
|
-
*/
|
|
275
|
-
unknown: "KUBB_UNKNOWN",
|
|
276
|
-
/**
|
|
277
|
-
* The file or URL set as `input` could not be read.
|
|
278
|
-
*/
|
|
279
|
-
inputNotFound: "KUBB_INPUT_NOT_FOUND",
|
|
280
|
-
/**
|
|
281
|
-
* A URL set as `input` (or referenced by a `$ref`) answered with a 4xx or 5xx status
|
|
282
|
-
* instead of the document.
|
|
283
|
-
*/
|
|
284
|
-
inputRequestFailed: "KUBB_INPUT_REQUEST_FAILED",
|
|
285
|
-
/**
|
|
286
|
-
* A URL set as `input` (or referenced by a `$ref`) never answered, so the request failed
|
|
287
|
-
* before a status was returned.
|
|
288
|
-
*/
|
|
289
|
-
inputUnreachable: "KUBB_INPUT_UNREACHABLE",
|
|
290
|
-
/**
|
|
291
|
-
* An adapter was configured without an `input`.
|
|
292
|
-
*/
|
|
293
|
-
inputRequired: "KUBB_INPUT_REQUIRED",
|
|
294
|
-
/**
|
|
295
|
-
* `input` uses the v4 `{ path }` / `{ data }` wrapper, which v5 reads as a parsed
|
|
296
|
-
* document instead of a pointer to one.
|
|
297
|
-
*/
|
|
298
|
-
legacyInput: "KUBB_LEGACY_INPUT",
|
|
299
|
-
/**
|
|
300
|
-
* The parsed `input` carries no `openapi` or `swagger` version, so it is not a
|
|
301
|
-
* document the adapter can read.
|
|
302
|
-
*/
|
|
303
|
-
invalidDocument: "KUBB_INVALID_DOCUMENT",
|
|
304
|
-
/** The document fails OpenAPI schema validation. Reported as a warning. */
|
|
305
|
-
invalidSpec: "KUBB_INVALID_SPEC",
|
|
306
|
-
/**
|
|
307
|
-
* A `$ref` (or equivalent reference) could not be resolved in the source document.
|
|
308
|
-
*/
|
|
309
|
-
refNotFound: "KUBB_REF_NOT_FOUND",
|
|
310
|
-
/**
|
|
311
|
-
* A server variable value is not allowed by its `enum`.
|
|
312
|
-
*/
|
|
313
|
-
invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
|
|
314
|
-
/**
|
|
315
|
-
* A required plugin is missing from the config.
|
|
316
|
-
*/
|
|
317
|
-
pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
|
|
318
|
-
/**
|
|
319
|
-
* The running Kubb or Node.js version is outside a plugin's `meta.compatibility`.
|
|
320
|
-
*/
|
|
321
|
-
incompatibleExtension: "KUBB_INCOMPATIBLE_EXTENSION",
|
|
322
|
-
/**
|
|
323
|
-
* A required entry in a plugin's `dependencies` record is missing from the config.
|
|
324
|
-
*/
|
|
325
|
-
missingDependency: "KUBB_MISSING_DEPENDENCY",
|
|
326
|
-
/**
|
|
327
|
-
* A dependency's `meta.version` is outside the range a plugin's `dependencies` record declares.
|
|
328
|
-
*/
|
|
329
|
-
incompatibleDependency: "KUBB_INCOMPATIBLE_DEPENDENCY",
|
|
330
|
-
/**
|
|
331
|
-
* A plugin threw while generating.
|
|
332
|
-
*/
|
|
333
|
-
pluginFailed: "KUBB_PLUGIN_FAILED",
|
|
334
|
-
/** An adapter threw while parsing the input. */
|
|
335
|
-
adapterFailed: "KUBB_ADAPTER_FAILED",
|
|
336
|
-
/** A parser threw while printing a generated file. */
|
|
337
|
-
parserFailed: "KUBB_PARSER_FAILED",
|
|
338
|
-
/**
|
|
339
|
-
* A plugin reported a non-fatal warning through `ctx.warn`.
|
|
340
|
-
*/
|
|
341
|
-
pluginWarning: "KUBB_PLUGIN_WARNING",
|
|
342
|
-
/**
|
|
343
|
-
* A plugin reported an informational message through `ctx.info`.
|
|
344
|
-
*/
|
|
345
|
-
pluginInfo: "KUBB_PLUGIN_INFO",
|
|
346
|
-
/**
|
|
347
|
-
* A `kubb/kit` composable was called outside a plugin's `setup` and `kubb:plugin:setup`.
|
|
348
|
-
*/
|
|
349
|
-
noContext: "KUBB_NO_CONTEXT",
|
|
350
|
-
/**
|
|
351
|
-
* A schema uses a `format` Kubb does not map to a specific type. Reserved for
|
|
352
|
-
* adapters to emit as a `warning`.
|
|
353
|
-
*/
|
|
354
|
-
unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
|
|
355
|
-
/**
|
|
356
|
-
* A referenced schema or operation is marked `deprecated`. Reserved for adapters
|
|
357
|
-
* to emit as an `info`.
|
|
358
|
-
*/
|
|
359
|
-
deprecated: "KUBB_DEPRECATED",
|
|
360
|
-
/**
|
|
361
|
-
* An adapter is required but the config has none. The build cannot read the input
|
|
362
|
-
* without one.
|
|
363
|
-
*/
|
|
364
|
-
adapterRequired: "KUBB_ADAPTER_REQUIRED",
|
|
365
|
-
/**
|
|
366
|
-
* A resolved output path escapes the output directory, which can stem from a path
|
|
367
|
-
* traversal in the spec or a misconfigured `group.name`.
|
|
368
|
-
*/
|
|
369
|
-
pathTraversal: "KUBB_PATH_TRAVERSAL",
|
|
370
|
-
/**
|
|
371
|
-
* `output.clean` is enabled but `output.path` resolves to the project root or a parent of it,
|
|
372
|
-
* so cleaning would delete kubb.config and every source file.
|
|
373
|
-
*/
|
|
374
|
-
cleanRoot: "KUBB_CLEAN_ROOT",
|
|
375
|
-
/**
|
|
376
|
-
* A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
|
|
377
|
-
*/
|
|
378
|
-
invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
|
|
379
|
-
/**
|
|
380
|
-
* A post-generate command (`output.postGenerate`) exited with a failure.
|
|
381
|
-
*/
|
|
382
|
-
postGenerateFailed: "KUBB_POST_GENERATE_FAILED",
|
|
383
|
-
/**
|
|
384
|
-
* The formatter pass over the generated files failed.
|
|
385
|
-
*/
|
|
386
|
-
formatFailed: "KUBB_FORMAT_FAILED",
|
|
387
|
-
/**
|
|
388
|
-
* The linter pass over the generated files failed.
|
|
389
|
-
*/
|
|
390
|
-
lintFailed: "KUBB_LINT_FAILED",
|
|
391
|
-
/**
|
|
392
|
-
* A barrel tries to re-export the same name from more than one module.
|
|
393
|
-
*/
|
|
394
|
-
barrelDuplicateExport: "KUBB_BARREL_DUPLICATE_EXPORT",
|
|
395
|
-
/**
|
|
396
|
-
* Not a failure. Carries a plugin's elapsed time, summed into the run total.
|
|
397
|
-
*/
|
|
398
|
-
performance: "KUBB_PERFORMANCE",
|
|
399
|
-
/**
|
|
400
|
-
* Not a failure. A newer Kubb version is available on npm.
|
|
401
|
-
*/
|
|
402
|
-
updateAvailable: "KUBB_UPDATE_AVAILABLE"
|
|
403
|
-
};
|
|
404
|
-
//#endregion
|
|
405
|
-
//#region src/Diagnostics.ts
|
|
406
|
-
/**
|
|
407
|
-
* Docs major version, derived from the package version so the link tracks the published major.
|
|
408
|
-
*/
|
|
409
|
-
const docsMajor = version.split(".")[0] ?? "5";
|
|
410
|
-
/**
|
|
411
|
-
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
412
|
-
* with no `kind` is treated as a `problem`.
|
|
413
|
-
*/
|
|
414
|
-
function isKind(kind) {
|
|
415
|
-
return (diagnostic) => (diagnostic.kind ?? "problem") === kind;
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
419
|
-
*
|
|
420
|
-
* @example
|
|
421
|
-
* ```ts
|
|
422
|
-
* if (isProblem(diagnostic)) {
|
|
423
|
-
* console.log(diagnostic.location)
|
|
424
|
-
* }
|
|
425
|
-
* ```
|
|
426
|
-
*/
|
|
427
|
-
const isProblem = isKind("problem");
|
|
428
|
-
/**
|
|
429
|
-
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
430
|
-
*
|
|
431
|
-
* @example
|
|
432
|
-
* ```ts
|
|
433
|
-
* const timings = diagnostics.filter(isPerformance)
|
|
434
|
-
* ```
|
|
435
|
-
*/
|
|
436
|
-
const isPerformance = isKind("performance");
|
|
437
|
-
/**
|
|
438
|
-
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
* ```ts
|
|
442
|
-
* if (isUpdate(diagnostic)) {
|
|
443
|
-
* console.log(diagnostic.latestVersion)
|
|
444
|
-
* }
|
|
445
|
-
* ```
|
|
446
|
-
*/
|
|
447
|
-
const isUpdate = isKind("update");
|
|
301
|
+
//#region src/diagnosticCatalog.ts
|
|
448
302
|
/**
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
|
|
452
|
-
const severityStyle = {
|
|
453
|
-
error: "red",
|
|
454
|
-
warning: "yellow",
|
|
455
|
-
info: "blue"
|
|
456
|
-
};
|
|
457
|
-
const failedCode = {
|
|
458
|
-
plugin: diagnosticCode.pluginFailed,
|
|
459
|
-
adapter: diagnosticCode.adapterFailed,
|
|
460
|
-
parser: diagnosticCode.parserFailed
|
|
461
|
-
};
|
|
462
|
-
const countKey = {
|
|
463
|
-
error: "errors",
|
|
464
|
-
warning: "warnings",
|
|
465
|
-
info: "infos"
|
|
466
|
-
};
|
|
467
|
-
/**
|
|
468
|
-
* Explanation for every {@link diagnosticCode}. Use {@link Diagnostics.explain} to look one up
|
|
469
|
-
* and `Diagnostics.docsUrl` for the matching kubb.dev page.
|
|
303
|
+
* Every diagnostic code with its docs entry: a short title, what triggers it, and how to resolve
|
|
304
|
+
* it. The source of truth for {@link diagnosticCode}, and the kubb.dev `/reference/diagnostics`
|
|
305
|
+
* sections mirror it.
|
|
470
306
|
*/
|
|
471
307
|
const diagnosticCatalog = {
|
|
472
|
-
|
|
308
|
+
unknown: {
|
|
309
|
+
code: "KUBB_UNKNOWN",
|
|
473
310
|
title: "Unknown error",
|
|
474
311
|
cause: "An error was thrown without a stable Kubb code, so it is reported as-is.",
|
|
475
312
|
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."
|
|
476
313
|
},
|
|
477
|
-
|
|
314
|
+
inputNotFound: {
|
|
315
|
+
code: "KUBB_INPUT_NOT_FOUND",
|
|
478
316
|
title: "Input not found",
|
|
479
317
|
cause: "The file set as `input` (or passed as `kubb generate PATH`) could not be read. A URL reports `KUBB_INPUT_REQUEST_FAILED` or `KUBB_INPUT_UNREACHABLE` instead.",
|
|
480
318
|
fix: "Check that the path exists and is readable, then set it as `input` or pass it on the CLI."
|
|
481
319
|
},
|
|
482
|
-
|
|
320
|
+
inputRequestFailed: {
|
|
321
|
+
code: "KUBB_INPUT_REQUEST_FAILED",
|
|
483
322
|
title: "Input request failed",
|
|
484
323
|
cause: "A URL set as `input` (or reached through a `$ref`) answered with a 4xx or 5xx status instead of the document.",
|
|
485
324
|
fix: "Open the URL to see what the server returns. A 401 or 403 needs credentials Kubb does not send, so download the document and point `input` at the local file. A 404 means the path is wrong, and a 5xx means the server itself failed."
|
|
486
325
|
},
|
|
487
|
-
|
|
326
|
+
inputUnreachable: {
|
|
327
|
+
code: "KUBB_INPUT_UNREACHABLE",
|
|
488
328
|
title: "Input unreachable",
|
|
489
329
|
cause: "A URL set as `input` (or reached through a `$ref`) never answered, so the request failed before a status came back. A refused connection, an unknown host, an expired certificate, and a timeout all land here.",
|
|
490
330
|
fix: "Check that the host is running and reachable from this machine. For a local server, start it and confirm the port matches the one in `input`."
|
|
491
331
|
},
|
|
492
|
-
|
|
332
|
+
inputRequired: {
|
|
333
|
+
code: "KUBB_INPUT_REQUIRED",
|
|
493
334
|
title: "Input required",
|
|
494
335
|
cause: "An adapter is configured but no `input` was provided.",
|
|
495
336
|
fix: "Set `input` to a file path, a URL, an inline spec (JSON/YAML string), or a parsed object in your Kubb config."
|
|
496
337
|
},
|
|
497
|
-
|
|
338
|
+
legacyInput: {
|
|
339
|
+
code: "KUBB_LEGACY_INPUT",
|
|
498
340
|
title: "Legacy input shape",
|
|
499
341
|
cause: "`input` is a `{ path }` or `{ data }` wrapper, which v4 used to point at a document and v5 reads as the document itself.",
|
|
500
342
|
fix: "Unwrap it: `input: { path: \"./petStore.yaml\" }` becomes `input: \"./petStore.yaml\"`, and `input: { data: spec }` becomes `input: spec`."
|
|
501
343
|
},
|
|
502
|
-
|
|
344
|
+
invalidDocument: {
|
|
345
|
+
code: "KUBB_INVALID_DOCUMENT",
|
|
503
346
|
title: "Invalid document",
|
|
504
|
-
cause: "The
|
|
505
|
-
fix: "
|
|
347
|
+
cause: "The `input` cannot be read as an OpenAPI or Swagger document: its JSON or YAML has a syntax error, it declares no `openapi` or `swagger` version field, or its version cannot be upgraded to OpenAPI 3.1.",
|
|
348
|
+
fix: "Fix the syntax at the reported line, and check that `input` points at the spec and not an HTML page. Point `input` at a Swagger 2.0, OpenAPI 3.0, or OpenAPI 3.1 document, and check that a passed object is the spec itself rather than a wrapper around it."
|
|
506
349
|
},
|
|
507
|
-
|
|
350
|
+
invalidSpec: {
|
|
351
|
+
code: "KUBB_INVALID_SPEC",
|
|
508
352
|
title: "Invalid spec",
|
|
509
353
|
cause: "The document does not pass OpenAPI schema validation. Kubb still generates from it, but the output can be incomplete or wrong.",
|
|
510
354
|
fix: "Fix the reported problem in the spec. Run `kubb validate` to list every error, or set `validate: false` on the adapter to skip the check."
|
|
511
355
|
},
|
|
512
|
-
|
|
356
|
+
refNotFound: {
|
|
357
|
+
code: "KUBB_REF_NOT_FOUND",
|
|
513
358
|
title: "Reference not found",
|
|
514
359
|
cause: "A `$ref` could not be resolved in the source document.",
|
|
515
360
|
fix: "Add the missing definition (for example under `components.schemas`) or fix the `$ref`. Run `kubb validate` to check the spec."
|
|
516
361
|
},
|
|
517
|
-
|
|
362
|
+
invalidServerVariable: {
|
|
363
|
+
code: "KUBB_INVALID_SERVER_VARIABLE",
|
|
518
364
|
title: "Invalid server variable",
|
|
519
365
|
cause: "A server variable value is not allowed by its `enum`.",
|
|
520
366
|
fix: "Use one of the values listed in the server variable `enum`, or update the spec."
|
|
521
367
|
},
|
|
522
|
-
|
|
368
|
+
pluginNotFound: {
|
|
369
|
+
code: "KUBB_PLUGIN_NOT_FOUND",
|
|
523
370
|
title: "Plugin not found",
|
|
524
371
|
cause: "A plugin that another plugin depends on is missing from the config.",
|
|
525
372
|
fix: "Add the required plugin to the `plugins` array in kubb.config.ts, or remove the dependency on it."
|
|
526
373
|
},
|
|
527
|
-
|
|
374
|
+
incompatibleExtension: {
|
|
375
|
+
code: "KUBB_INCOMPATIBLE_EXTENSION",
|
|
528
376
|
title: "Incompatible extension",
|
|
529
377
|
cause: "A plugin sets `meta.compatibility.kubb` or `meta.compatibility.node` to a range the running Kubb or Node.js version does not satisfy.",
|
|
530
378
|
fix: "Upgrade Kubb or Node.js to a version in the range, or install a version of the plugin that supports your current Kubb and Node.js."
|
|
531
379
|
},
|
|
532
|
-
|
|
380
|
+
missingDependency: {
|
|
381
|
+
code: "KUBB_MISSING_DEPENDENCY",
|
|
533
382
|
title: "Missing dependency",
|
|
534
383
|
cause: "A plugin lists another plugin in its `dependencies` record without `optional: true`, and the config does not include it.",
|
|
535
384
|
fix: "Add the missing plugin to the `plugins` array in kubb.config.ts."
|
|
536
385
|
},
|
|
537
|
-
|
|
386
|
+
incompatibleDependency: {
|
|
387
|
+
code: "KUBB_INCOMPATIBLE_DEPENDENCY",
|
|
538
388
|
title: "Incompatible dependency",
|
|
539
389
|
cause: "A plugin's `dependencies` record sets a `version` range, and the configured plugin's `meta.version` falls outside it.",
|
|
540
390
|
fix: "Install a version of the dependency inside the range, or a version of the dependent plugin that supports the installed dependency."
|
|
541
391
|
},
|
|
542
|
-
|
|
392
|
+
pluginFailed: {
|
|
393
|
+
code: "KUBB_PLUGIN_FAILED",
|
|
543
394
|
title: "Plugin failed",
|
|
544
395
|
cause: "A plugin threw while generating, or reported an error through `ctx.error`.",
|
|
545
|
-
fix: "
|
|
396
|
+
fix: "Check the plugin options and the node at `at:`. If it is a bug, report it with the stack at `report:`."
|
|
546
397
|
},
|
|
547
|
-
|
|
398
|
+
adapterFailed: {
|
|
399
|
+
code: "KUBB_ADAPTER_FAILED",
|
|
548
400
|
title: "Adapter failed",
|
|
549
401
|
cause: "The adapter threw an unexpected error while parsing the input.",
|
|
550
|
-
fix: "
|
|
402
|
+
fix: "Check the input and the adapter options. If it is a bug, report it with the stack at `report:`."
|
|
551
403
|
},
|
|
552
|
-
|
|
404
|
+
parserFailed: {
|
|
405
|
+
code: "KUBB_PARSER_FAILED",
|
|
553
406
|
title: "Parser failed",
|
|
554
407
|
cause: "A parser threw an unexpected error while printing a generated file.",
|
|
555
|
-
fix: "
|
|
408
|
+
fix: "Check the parser options. If it is a bug, report it with the stack at `report:`."
|
|
556
409
|
},
|
|
557
|
-
|
|
410
|
+
pluginWarning: {
|
|
411
|
+
code: "KUBB_PLUGIN_WARNING",
|
|
558
412
|
title: "Plugin warning",
|
|
559
413
|
cause: "A plugin reported a non-fatal warning through `ctx.warn`.",
|
|
560
414
|
fix: "Review the message. It does not fail the build; adjust the plugin options or input if the warning is unwanted."
|
|
561
415
|
},
|
|
562
|
-
|
|
416
|
+
pluginInfo: {
|
|
417
|
+
code: "KUBB_PLUGIN_INFO",
|
|
563
418
|
title: "Plugin info",
|
|
564
419
|
cause: "A plugin reported an informational message through `ctx.info`.",
|
|
565
420
|
fix: "Informational only. No action is required."
|
|
566
421
|
},
|
|
567
|
-
|
|
422
|
+
noContext: {
|
|
423
|
+
code: "KUBB_NO_CONTEXT",
|
|
568
424
|
title: "No plugin context",
|
|
569
425
|
cause: "A `kubb/kit` composable such as `useKubb()` or `addGenerator()` ran outside the `setup` and `kubb:plugin:setup` of a plugin, so there is no plugin to apply it to.",
|
|
570
426
|
fix: "Call the composable inside `setup` or `kubb:plugin:setup`, including in code that setup awaits."
|
|
571
427
|
},
|
|
572
|
-
|
|
428
|
+
unsupportedFormat: {
|
|
429
|
+
code: "KUBB_UNSUPPORTED_FORMAT",
|
|
573
430
|
title: "Unsupported format",
|
|
574
431
|
cause: "A schema uses a `format` Kubb does not map to a specific type, so it falls back to the base type.",
|
|
575
432
|
fix: "Use a format Kubb supports, or handle the custom format with a parser or plugin."
|
|
576
433
|
},
|
|
577
|
-
|
|
434
|
+
deprecated: {
|
|
435
|
+
code: "KUBB_DEPRECATED",
|
|
578
436
|
title: "Deprecated",
|
|
579
437
|
cause: "A referenced schema or operation is marked `deprecated`.",
|
|
580
438
|
fix: "Migrate off the deprecated definition if the warning is unwanted."
|
|
581
439
|
},
|
|
582
|
-
|
|
440
|
+
adapterRequired: {
|
|
441
|
+
code: "KUBB_ADAPTER_REQUIRED",
|
|
583
442
|
title: "Adapter required",
|
|
584
443
|
cause: "An action needs an adapter but none is configured.",
|
|
585
444
|
fix: "Set `adapter` in kubb.config.ts, for example `adapterOas()`."
|
|
586
445
|
},
|
|
587
|
-
|
|
446
|
+
pathTraversal: {
|
|
447
|
+
code: "KUBB_PATH_TRAVERSAL",
|
|
588
448
|
title: "Path traversal",
|
|
589
449
|
cause: "A resolved output path escaped the output directory, which can stem from a path traversal in the spec or a misconfigured `group.name`.",
|
|
590
450
|
fix: "Keep generated paths within the output directory. Review the `group.name` function and the names coming from the spec."
|
|
591
451
|
},
|
|
592
|
-
|
|
452
|
+
cleanRoot: {
|
|
453
|
+
code: "KUBB_CLEAN_ROOT",
|
|
593
454
|
title: "Clean targets the project root",
|
|
594
455
|
cause: "`output.clean` is enabled and `output.path` resolves to the project root or a parent of it, so cleaning would delete `kubb.config` and every source file.",
|
|
595
456
|
fix: "Point `output.path` at a subdirectory such as `./src/gen` so clean only removes generated code, or disable `output.clean`."
|
|
596
457
|
},
|
|
597
|
-
|
|
458
|
+
invalidPluginOptions: {
|
|
459
|
+
code: "KUBB_INVALID_PLUGIN_OPTIONS",
|
|
598
460
|
title: "Invalid plugin options",
|
|
599
461
|
cause: "A plugin was configured with options that cannot be honored, for example `output.mode: 'file'` paired with a `group` option.",
|
|
600
462
|
fix: "Fix the plugin options. A single-file output has nothing to group, so remove the `group` option or use `output.mode: 'directory'`."
|
|
601
463
|
},
|
|
602
|
-
|
|
464
|
+
postGenerateFailed: {
|
|
465
|
+
code: "KUBB_POST_GENERATE_FAILED",
|
|
603
466
|
title: "Post-generate command failed",
|
|
604
467
|
cause: "A post-generate command (`output.postGenerate`) exited with a non-zero status.",
|
|
605
468
|
fix: "Check the command is installed and correct, and run it manually to see the error."
|
|
606
469
|
},
|
|
607
|
-
|
|
470
|
+
formatFailed: {
|
|
471
|
+
code: "KUBB_FORMAT_FAILED",
|
|
608
472
|
title: "Format failed",
|
|
609
473
|
cause: "The formatter pass over the generated files failed.",
|
|
610
474
|
fix: "Check the formatter (oxfmt, biome, or prettier) is installed and its config is valid, then run it manually on the output."
|
|
611
475
|
},
|
|
612
|
-
|
|
476
|
+
lintFailed: {
|
|
477
|
+
code: "KUBB_LINT_FAILED",
|
|
613
478
|
title: "Lint failed",
|
|
614
479
|
cause: "The linter pass over the generated files failed.",
|
|
615
480
|
fix: "Check the linter (oxlint, biome, or eslint) is installed and its config is valid, then run it manually on the output."
|
|
616
481
|
},
|
|
617
|
-
|
|
482
|
+
barrelDuplicateExport: {
|
|
483
|
+
code: "KUBB_BARREL_DUPLICATE_EXPORT",
|
|
618
484
|
title: "Duplicate barrel export",
|
|
619
485
|
cause: "Two generated modules export the same name, so the barrel cannot re-export both.",
|
|
620
486
|
fix: "Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names."
|
|
621
487
|
},
|
|
622
|
-
|
|
488
|
+
internalError: {
|
|
489
|
+
code: "KUBB_INTERNAL_ERROR",
|
|
490
|
+
title: "Internal error",
|
|
491
|
+
cause: "Kubb hit a state it does not expect. This is a bug in Kubb, not in your config.",
|
|
492
|
+
fix: "Rerun with `--reporter json` or `--verbose` to get the stack. Then report it at https://github.com/kubb-labs/kubb/issues with the spec and config that trigger it."
|
|
493
|
+
},
|
|
494
|
+
performance: {
|
|
495
|
+
code: "KUBB_PERFORMANCE",
|
|
623
496
|
title: "Performance",
|
|
624
497
|
cause: "Not a failure. Records a plugin’s elapsed time, summed into the run total.",
|
|
625
498
|
fix: "No action. This is an informational metric."
|
|
626
499
|
},
|
|
627
|
-
|
|
500
|
+
updateAvailable: {
|
|
501
|
+
code: "KUBB_UPDATE_AVAILABLE",
|
|
628
502
|
title: "Update available",
|
|
629
503
|
cause: "A newer Kubb version is published on npm than the one running.",
|
|
630
504
|
fix: "Update the `@kubb/*` packages, for example `npm install -g @kubb/cli`, to get the latest fixes."
|
|
631
505
|
}
|
|
632
506
|
};
|
|
633
507
|
/**
|
|
508
|
+
* Stable codes Kubb attaches to a `Diagnostic`, derived from {@link diagnosticCatalog}. Reference
|
|
509
|
+
* these instead of inlining the string at a throw site.
|
|
510
|
+
*/
|
|
511
|
+
const diagnosticCode = Object.fromEntries(Object.entries(diagnosticCatalog).map(([key, { code }]) => [key, code]));
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/formatDiagnostic.ts
|
|
514
|
+
/** A problem's `help`, or the catalog fix. `info` gets none: its catalog fix says no action is needed. */
|
|
515
|
+
function getHelp(problem) {
|
|
516
|
+
if (problem.help || problem.severity === "info") return problem.help;
|
|
517
|
+
return Diagnostics.explain(problem.code).fix;
|
|
518
|
+
}
|
|
519
|
+
/** The `cause` chain's messages, outermost first, minus those `message` already contains. */
|
|
520
|
+
function getCauses(problem) {
|
|
521
|
+
return getErrorChain(problem.cause).filter((error) => !problem.message.includes(error.message)).map((error) => error.name === "Error" ? error.message : `${error.name}: ${error.message}`);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Accent color per severity, which paints the icon in the `human` layout (red error, yellow
|
|
525
|
+
* warning, blue info). The code itself stays dim.
|
|
526
|
+
*/
|
|
527
|
+
const severityStyle = {
|
|
528
|
+
error: "red",
|
|
529
|
+
warning: "yellow",
|
|
530
|
+
info: "blue"
|
|
531
|
+
};
|
|
532
|
+
const severityIcon = {
|
|
533
|
+
error: "✖",
|
|
534
|
+
warning: "⚠",
|
|
535
|
+
info: "ℹ"
|
|
536
|
+
};
|
|
537
|
+
/**
|
|
538
|
+
* Codes for a crash, which carry the root cause's stack.
|
|
539
|
+
*/
|
|
540
|
+
const crashCodes = /* @__PURE__ */ new Set([
|
|
541
|
+
diagnosticCode.unknown,
|
|
542
|
+
diagnosticCode.internalError,
|
|
543
|
+
diagnosticCode.pluginFailed,
|
|
544
|
+
diagnosticCode.adapterFailed,
|
|
545
|
+
diagnosticCode.parserFailed
|
|
546
|
+
]);
|
|
547
|
+
/**
|
|
548
|
+
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
549
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
550
|
+
* `stack`. Absent optional fields are omitted.
|
|
551
|
+
*/
|
|
552
|
+
function serialize(diagnostic) {
|
|
553
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
554
|
+
const doc = problem ? Diagnostics.explain(problem.code) : void 0;
|
|
555
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
556
|
+
const causes = problem ? getCauses(problem) : [];
|
|
557
|
+
const stack = problem && crashCodes.has(problem.code) ? getErrorChain(problem.cause).at(-1)?.stack : void 0;
|
|
558
|
+
return {
|
|
559
|
+
code: diagnostic.code,
|
|
560
|
+
severity: diagnostic.severity,
|
|
561
|
+
message: diagnostic.message,
|
|
562
|
+
...doc ? {
|
|
563
|
+
title: doc.title,
|
|
564
|
+
cause: doc.cause
|
|
565
|
+
} : {},
|
|
566
|
+
...problem?.location ? { location: problem.location } : {},
|
|
567
|
+
...help ? { help } : {},
|
|
568
|
+
...problem?.plugin ? { plugin: problem.plugin } : {},
|
|
569
|
+
...problem?.issues ? { issues: problem.issues } : {},
|
|
570
|
+
...causes.length ? { causes } : {},
|
|
571
|
+
...stack ? { stack } : {},
|
|
572
|
+
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Renders a {@link Diagnostic} as lines: a headline, then indented detail rows. `agent` (the default)
|
|
577
|
+
* prints uncolored labeled rows (`at:`, `why:`, `fix:`, `caused by:`, `stack:` for a crash,
|
|
578
|
+
* `docs:`, `report:`). `human` prints an icon, the bold message, and the code aligned to `width`,
|
|
579
|
+
* with `at`, `fix`, the first cause, and `Learn more` for an error, and one row for a warning.
|
|
580
|
+
* `verbose` adds the full stack.
|
|
581
|
+
*/
|
|
582
|
+
function format(diagnostic, { audience = "agent", verbose = false, width = 0 } = {}) {
|
|
583
|
+
const { code, severity, message } = diagnostic;
|
|
584
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
585
|
+
const human = audience === "human";
|
|
586
|
+
const paint = human ? styleText : (_format, text) => text;
|
|
587
|
+
const [firstLine = "", ...rest] = message.split("\n");
|
|
588
|
+
const pointer = problem?.location && "pointer" in problem.location ? problem.location.pointer : void 0;
|
|
589
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
590
|
+
const causes = problem ? getCauses(problem) : [];
|
|
591
|
+
const crash = problem ? crashCodes.has(problem.code) : false;
|
|
592
|
+
const frames = (problem && (verbose || !human && crash) ? getErrorChain(problem.cause).at(-1)?.stack : void 0)?.split("\n").slice(1, verbose ? void 0 : 4).map((frame) => ` ${paint("dim", frame.trim())}`);
|
|
593
|
+
if (!human) {
|
|
594
|
+
const details = rest.filter((line) => line.trim()).map((line) => ` ${line}`);
|
|
595
|
+
const row = (label, value) => {
|
|
596
|
+
if (value) details.push(` ${label}: ${value}`);
|
|
597
|
+
};
|
|
598
|
+
row("at", pointer);
|
|
599
|
+
if (problem && severity !== "info") row("why", Diagnostics.explain(problem.code).cause);
|
|
600
|
+
row("fix", help);
|
|
601
|
+
for (const cause of causes) row("caused by", cause);
|
|
602
|
+
if (frames?.length) details.push(" stack:", ...frames);
|
|
603
|
+
if (code !== diagnosticCode.unknown) row("docs", Diagnostics.docsUrl(code));
|
|
604
|
+
row("report", problem?.issues);
|
|
605
|
+
return [`${severity} ${code}${problem?.plugin ? ` [${problem.plugin}]` : ""}: ${firstLine}`, ...details];
|
|
606
|
+
}
|
|
607
|
+
const left = `${paint(severityStyle[severity], severityIcon[severity])} ${paint("bold", firstLine)}`;
|
|
608
|
+
const gap = Math.max(2, width - stripVTControlCharacters(left).length - code.length);
|
|
609
|
+
const headline = `${left}${" ".repeat(gap)}${paint("dim", code)}`;
|
|
610
|
+
const frame = rest.filter((line) => line.trim()).map((line) => ` ${paint("dim", line)}`);
|
|
611
|
+
const label = (name) => paint("dim", name.length < 4 ? name.padEnd(4) : `${name} `);
|
|
612
|
+
const rows = [["at", pointer && paint("cyan", pointer)], ["fix", help]];
|
|
613
|
+
if (severity !== "error") {
|
|
614
|
+
const first = rows.find(([, value]) => value);
|
|
615
|
+
return [
|
|
616
|
+
headline,
|
|
617
|
+
...frame,
|
|
618
|
+
...first ? [` ${label(first[0])} ${first[1]}`] : []
|
|
619
|
+
];
|
|
620
|
+
}
|
|
621
|
+
rows.push(["caused by", causes[0]], ["report", problem?.issues && paint("cyan", problem.issues)]);
|
|
622
|
+
const details = [...frame, ...rows.flatMap(([name, value]) => value ? [` ${label(name)} ${value}`] : [])];
|
|
623
|
+
if (frames?.length) details.push(` ${paint("dim", "stack")}`, ...frames);
|
|
624
|
+
if (code !== diagnosticCode.unknown) details.push(` ${paint("dim", "↳ Learn more")} ${paint("cyan", Diagnostics.docsUrl(code).replace(/^https:\/\//, ""))}`);
|
|
625
|
+
return [headline, ...details];
|
|
626
|
+
}
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/Diagnostics.ts
|
|
629
|
+
/**
|
|
630
|
+
* Docs major version, derived from the package version so the link tracks the published major.
|
|
631
|
+
*/
|
|
632
|
+
const docsMajor = version.split(".")[0] ?? "5";
|
|
633
|
+
/**
|
|
634
|
+
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
635
|
+
* with no `kind` is treated as a `problem`.
|
|
636
|
+
*/
|
|
637
|
+
function isKind(kind) {
|
|
638
|
+
return (diagnostic) => (diagnostic.kind ?? "problem") === kind;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* if (isProblem(diagnostic)) {
|
|
646
|
+
* console.log(diagnostic.location)
|
|
647
|
+
* }
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
const isProblem = isKind("problem");
|
|
651
|
+
/**
|
|
652
|
+
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```ts
|
|
656
|
+
* const timings = diagnostics.filter(isPerformance)
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
659
|
+
const isPerformance = isKind("performance");
|
|
660
|
+
/**
|
|
661
|
+
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```ts
|
|
665
|
+
* if (isUpdate(diagnostic)) {
|
|
666
|
+
* console.log(diagnostic.latestVersion)
|
|
667
|
+
* }
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
const isUpdate = isKind("update");
|
|
671
|
+
const failedCode = {
|
|
672
|
+
plugin: diagnosticCode.pluginFailed,
|
|
673
|
+
adapter: diagnosticCode.adapterFailed,
|
|
674
|
+
parser: diagnosticCode.parserFailed
|
|
675
|
+
};
|
|
676
|
+
const docsByCode = new Map(Object.values(diagnosticCatalog).map((doc) => [doc.code, doc]));
|
|
677
|
+
const countKey = {
|
|
678
|
+
error: "errors",
|
|
679
|
+
warning: "warnings",
|
|
680
|
+
info: "infos"
|
|
681
|
+
};
|
|
682
|
+
/**
|
|
634
683
|
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
635
684
|
* that lets deep code report a diagnostic without threading a callback.
|
|
636
685
|
*
|
|
@@ -705,27 +754,37 @@ var Diagnostics = class Diagnostics {
|
|
|
705
754
|
return true;
|
|
706
755
|
}
|
|
707
756
|
/**
|
|
708
|
-
* Emits a diagnostic on the run's
|
|
709
|
-
*
|
|
710
|
-
*
|
|
757
|
+
* Emits a diagnostic, or any thrown value, on the run's hooks so the loggers render it live.
|
|
758
|
+
* A coded problem or an update goes to `kubb:diagnostic`. An uncoded one goes to `kubb:error`
|
|
759
|
+
* whole, so every wrapper's message, its help, and the stack survive. To collect a diagnostic into
|
|
760
|
+
* the build result from deep in a run, use {@link Diagnostics.report} instead.
|
|
711
761
|
*/
|
|
712
|
-
static async emit(hooks,
|
|
713
|
-
|
|
762
|
+
static async emit(hooks, value) {
|
|
763
|
+
const thrown = value instanceof Error || typeof value?.code !== "string";
|
|
764
|
+
const diagnostic = thrown ? Diagnostics.from(value) : value;
|
|
765
|
+
if (!isProblem(diagnostic) || diagnostic.code !== diagnosticCode.unknown) {
|
|
766
|
+
await hooks.callHook("kubb:diagnostic", { diagnostic });
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const { cause, help, message } = diagnostic;
|
|
770
|
+
const bare = cause && !help && cause.message === message;
|
|
771
|
+
await hooks.callHook("kubb:error", { error: thrown ? toError(value) : bare ? cause : new Diagnostics.Error(diagnostic) });
|
|
714
772
|
}
|
|
715
773
|
/**
|
|
716
774
|
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link Diagnostics.Error}
|
|
717
775
|
* keeps its structured data, and anything else becomes a `KUBB_UNKNOWN` error.
|
|
718
776
|
*/
|
|
719
777
|
static from(error) {
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
778
|
+
const chain = getErrorChain(error);
|
|
779
|
+
const coded = chain.findLast((current) => Diagnostics.isError(current));
|
|
780
|
+
if (coded) {
|
|
781
|
+
const { diagnostic } = coded;
|
|
782
|
+
return diagnostic.cause || !crashCodes.has(diagnostic.code) ? diagnostic : {
|
|
783
|
+
...diagnostic,
|
|
784
|
+
cause: coded
|
|
785
|
+
};
|
|
728
786
|
}
|
|
787
|
+
const root = chain.at(-1);
|
|
729
788
|
return {
|
|
730
789
|
code: diagnosticCode.unknown,
|
|
731
790
|
severity: "error",
|
|
@@ -743,7 +802,9 @@ var Diagnostics = class Diagnostics {
|
|
|
743
802
|
const diagnostic = Diagnostics.from(error);
|
|
744
803
|
const name = extension.meta?.name ?? extension.name;
|
|
745
804
|
const owner = kind === "plugin" ? { plugin: diagnostic.plugin ?? name } : {};
|
|
746
|
-
|
|
805
|
+
const crashed = diagnostic.code === diagnosticCode.unknown && diagnostic.cause?.name !== "AbortError";
|
|
806
|
+
const failed = Object.values(failedCode).includes(diagnostic.code);
|
|
807
|
+
if (!crashed && !failed) return {
|
|
747
808
|
...diagnostic,
|
|
748
809
|
...owner
|
|
749
810
|
};
|
|
@@ -751,8 +812,8 @@ var Diagnostics = class Diagnostics {
|
|
|
751
812
|
return {
|
|
752
813
|
...diagnostic,
|
|
753
814
|
...owner,
|
|
754
|
-
code: failedCode[kind],
|
|
755
|
-
message: kind
|
|
815
|
+
code: crashed ? failedCode[kind] : diagnostic.code,
|
|
816
|
+
message: crashed && kind !== "plugin" ? `${kind} "${name}" crashed: ${diagnostic.message}` : diagnostic.message,
|
|
756
817
|
...issues ? { issues } : {}
|
|
757
818
|
};
|
|
758
819
|
}
|
|
@@ -843,71 +904,33 @@ var Diagnostics = class Diagnostics {
|
|
|
843
904
|
* `/reference/diagnostics#<slug>` section.
|
|
844
905
|
*/
|
|
845
906
|
static explain(code) {
|
|
846
|
-
return
|
|
907
|
+
return docsByCode.get(code);
|
|
847
908
|
}
|
|
848
909
|
/**
|
|
849
910
|
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
850
|
-
* consumers
|
|
851
|
-
* fields are omitted
|
|
911
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
912
|
+
* `stack`. Absent optional fields are omitted.
|
|
852
913
|
*/
|
|
853
|
-
static serialize
|
|
854
|
-
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
855
|
-
return {
|
|
856
|
-
code: diagnostic.code,
|
|
857
|
-
severity: diagnostic.severity,
|
|
858
|
-
message: diagnostic.message,
|
|
859
|
-
...problem?.location ? { location: problem.location } : {},
|
|
860
|
-
...problem?.help ? { help: problem.help } : {},
|
|
861
|
-
...problem?.plugin ? { plugin: problem.plugin } : {},
|
|
862
|
-
...problem?.issues ? { issues: problem.issues } : {},
|
|
863
|
-
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Renders a {@link Diagnostic} for terminal output as its parts: the `headline`
|
|
868
|
-
* (`[CODE] plugin: message`, with the code in the severity color) and the indented `details`
|
|
869
|
-
* rows (`at:` pointer, `fix:` help, `see:` docs link, `report:` issue tracker).
|
|
870
|
-
*
|
|
871
|
-
* Hosts compose these to fit their gutter: a clack logger passes `[headline, ...details]` as the
|
|
872
|
-
* message with no gutter symbol, while plain text outputs use {@link Diagnostics.formatLines}.
|
|
873
|
-
*/
|
|
874
|
-
static format(diagnostic) {
|
|
875
|
-
const { code, severity, message } = diagnostic;
|
|
876
|
-
const color = severityStyle[severity];
|
|
877
|
-
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
878
|
-
const tag = styleText(color, styleText("bold", `[${code}]`));
|
|
879
|
-
const headline = problem?.plugin ? `${tag} ${problem.plugin}: ${message}` : `${tag}: ${message}`;
|
|
880
|
-
const details = [];
|
|
881
|
-
if (problem?.location && "pointer" in problem.location) details.push(` ${styleText("dim", "at:")} ${styleText("cyan", problem.location.pointer)}`);
|
|
882
|
-
if (problem?.help) details.push(` ${styleText("cyan", "fix:")} ${problem.help}`);
|
|
883
|
-
if (code !== diagnosticCode.unknown) details.push(` ${styleText("dim", "see:")} ${styleText("cyan", Diagnostics.docsUrl(code))}`);
|
|
884
|
-
if (problem?.issues) details.push(` ${styleText("dim", "report:")} ${styleText("cyan", problem.issues)}`);
|
|
885
|
-
return {
|
|
886
|
-
headline,
|
|
887
|
-
details
|
|
888
|
-
};
|
|
889
|
-
}
|
|
914
|
+
static serialize = serialize;
|
|
890
915
|
/**
|
|
891
|
-
*
|
|
892
|
-
* indented detail rows. Used where there is no gutter (plain and file output).
|
|
916
|
+
* Renders a diagnostic as lines for an `agent` (the default) or a `human`. See {@link format}.
|
|
893
917
|
*/
|
|
894
|
-
static
|
|
895
|
-
const { headline, details } = Diagnostics.format(diagnostic);
|
|
896
|
-
return [headline, ...details];
|
|
897
|
-
}
|
|
918
|
+
static format = format;
|
|
898
919
|
};
|
|
899
920
|
//#endregion
|
|
900
921
|
//#region src/defineExtension.ts
|
|
901
922
|
/**
|
|
902
923
|
* Returns `meta`, or `{ name }` for an extension that only sets the deprecated `name`. Throws `KUBB_INVALID_PLUGIN_OPTIONS` when it has neither.
|
|
903
924
|
*/
|
|
904
|
-
function resolveMeta(
|
|
925
|
+
function resolveMeta(extension) {
|
|
926
|
+
const { name, meta } = extension;
|
|
905
927
|
if (meta) return meta;
|
|
906
928
|
if (name) return { name };
|
|
929
|
+
const keys = Object.keys(extension);
|
|
907
930
|
throw new Diagnostics.Error({
|
|
908
931
|
code: Diagnostics.code.invalidPluginOptions,
|
|
909
932
|
severity: "error",
|
|
910
|
-
message:
|
|
933
|
+
message: `An extension has neither \`meta.name\` nor \`name\`. Received keys: ${keys.length ? keys.join(", ") : "none"}.`,
|
|
911
934
|
help: "Set `meta: { name }` on the plugin or parser.",
|
|
912
935
|
location: { kind: "config" }
|
|
913
936
|
});
|
|
@@ -1744,7 +1767,13 @@ async function parseCopy(file, parser) {
|
|
|
1744
1767
|
try {
|
|
1745
1768
|
content = await readFile(file.copy, "utf8");
|
|
1746
1769
|
} catch (err) {
|
|
1747
|
-
throw new Error(
|
|
1770
|
+
throw new Diagnostics.Error({
|
|
1771
|
+
code: Diagnostics.code.pluginFailed,
|
|
1772
|
+
severity: "error",
|
|
1773
|
+
message: `Could not copy file into output: ${file.copy}`,
|
|
1774
|
+
help: "Check that the file a plugin copies into the output exists and is readable.",
|
|
1775
|
+
cause: toError(err)
|
|
1776
|
+
});
|
|
1748
1777
|
}
|
|
1749
1778
|
if (parser?.copy) return runParser(parser, () => parser.parse(ast.factory.createFile(parser.copy(file, content))));
|
|
1750
1779
|
return [
|
|
@@ -1986,6 +2015,50 @@ function normalizePluginOptions(rawOptions, pluginName) {
|
|
|
1986
2015
|
});
|
|
1987
2016
|
return options;
|
|
1988
2017
|
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Throws `KUBB_INVALID_PLUGIN_OPTIONS` when a `plugins` entry is not a plugin object, such as a
|
|
2020
|
+
* factory passed without calling it.
|
|
2021
|
+
*/
|
|
2022
|
+
function assertPluginEntry(entry, index) {
|
|
2023
|
+
if (typeof entry === "function") {
|
|
2024
|
+
const name = entry.name || "pluginName";
|
|
2025
|
+
throw new Diagnostics.Error({
|
|
2026
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2027
|
+
severity: "error",
|
|
2028
|
+
message: `plugins[${index}] is a function ("${name}"), not a plugin.`,
|
|
2029
|
+
help: `Call it: ${name}()`,
|
|
2030
|
+
location: { kind: "config" }
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
if (typeof entry !== "object" || entry === null) throw new Diagnostics.Error({
|
|
2034
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2035
|
+
severity: "error",
|
|
2036
|
+
message: `plugins[${index}] is ${entry === null ? "null" : typeof entry}, not a plugin.`,
|
|
2037
|
+
help: "Pass the result of a plugin factory, for example `pluginTs()`, or remove the entry.",
|
|
2038
|
+
location: { kind: "config" }
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Where a generator crashed: a label naming the node and, when the node has one, a JSON pointer
|
|
2043
|
+
* to it. Built from the node fields so core stays adapter-agnostic.
|
|
2044
|
+
*/
|
|
2045
|
+
function getCrashSite(node) {
|
|
2046
|
+
if (node.kind === "Schema") return node.name ? {
|
|
2047
|
+
label: `schema "${node.name}"`,
|
|
2048
|
+
location: {
|
|
2049
|
+
kind: "schema",
|
|
2050
|
+
pointer: `#/components/schemas/${escapePointerToken(node.name)}`
|
|
2051
|
+
}
|
|
2052
|
+
} : { label: "an inline schema" };
|
|
2053
|
+
if (!node.method || !node.path) return { label: `operation "${node.operationId}"` };
|
|
2054
|
+
return {
|
|
2055
|
+
label: `operation "${node.operationId}" (${node.method.toUpperCase()} ${node.path})`,
|
|
2056
|
+
location: {
|
|
2057
|
+
kind: "operation",
|
|
2058
|
+
pointer: `#/paths/${escapePointerToken(node.path)}/${node.method.toLowerCase()}`
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
1989
2062
|
var KubbDriver = class {
|
|
1990
2063
|
config;
|
|
1991
2064
|
options;
|
|
@@ -2025,9 +2098,19 @@ var KubbDriver = class {
|
|
|
2025
2098
|
async setup() {
|
|
2026
2099
|
this.options.signal?.throwIfAborted();
|
|
2027
2100
|
for (const extension of [this.config.adapter, ...this.config.parsers]) if (extension?.meta) checkCompatibility(extension.meta);
|
|
2028
|
-
const
|
|
2101
|
+
const names = /* @__PURE__ */ new Set();
|
|
2102
|
+
const normalized = this.#sortPlugins(this.config.plugins.map((rawPlugin, index) => {
|
|
2103
|
+
assertPluginEntry(rawPlugin, index);
|
|
2029
2104
|
if (rawPlugin.meta) checkCompatibility(rawPlugin.meta);
|
|
2030
2105
|
const meta = resolveMeta(rawPlugin);
|
|
2106
|
+
if (names.has(meta.name)) throw new Diagnostics.Error({
|
|
2107
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2108
|
+
severity: "error",
|
|
2109
|
+
message: `Plugin "${meta.name}" appears twice in \`plugins\`.`,
|
|
2110
|
+
help: "Remove the duplicate, or merge its options.",
|
|
2111
|
+
location: { kind: "config" }
|
|
2112
|
+
});
|
|
2113
|
+
names.add(meta.name);
|
|
2031
2114
|
return {
|
|
2032
2115
|
name: meta.name,
|
|
2033
2116
|
meta,
|
|
@@ -2353,7 +2436,8 @@ var KubbDriver = class {
|
|
|
2353
2436
|
operationGenerators: generators.filter((generator) => generator.operation),
|
|
2354
2437
|
operationsGenerators: generators.filter((generator) => generator.operations),
|
|
2355
2438
|
pluginOperations: [],
|
|
2356
|
-
error: null
|
|
2439
|
+
error: null,
|
|
2440
|
+
crashSite: void 0
|
|
2357
2441
|
};
|
|
2358
2442
|
});
|
|
2359
2443
|
const resolveForPlugin = (state, node) => {
|
|
@@ -2397,6 +2481,7 @@ var KubbDriver = class {
|
|
|
2397
2481
|
await this.hooks.callHook("kubb:generate:schema", transformedNode, ctx);
|
|
2398
2482
|
} catch (caughtError) {
|
|
2399
2483
|
state.error = toError(caughtError);
|
|
2484
|
+
state.crashSite = getCrashSite(node);
|
|
2400
2485
|
}
|
|
2401
2486
|
}
|
|
2402
2487
|
}
|
|
@@ -2425,6 +2510,7 @@ var KubbDriver = class {
|
|
|
2425
2510
|
}
|
|
2426
2511
|
} catch (caughtError) {
|
|
2427
2512
|
state.error = toError(caughtError);
|
|
2513
|
+
state.crashSite = getCrashSite(node);
|
|
2428
2514
|
}
|
|
2429
2515
|
}
|
|
2430
2516
|
}
|
|
@@ -2453,10 +2539,18 @@ var KubbDriver = class {
|
|
|
2453
2539
|
success: !state.error,
|
|
2454
2540
|
error: state.error ?? void 0
|
|
2455
2541
|
});
|
|
2456
|
-
if (state.error)
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2542
|
+
if (state.error) {
|
|
2543
|
+
const diagnostic = Diagnostics.fromExtension(state.error, {
|
|
2544
|
+
kind: "plugin",
|
|
2545
|
+
extension: state.plugin
|
|
2546
|
+
});
|
|
2547
|
+
const site = diagnostic.code === Diagnostics.code.pluginFailed && !diagnostic.location ? state.crashSite : void 0;
|
|
2548
|
+
diagnostics.push(site ? {
|
|
2549
|
+
...diagnostic,
|
|
2550
|
+
message: `While generating ${site.label}: ${diagnostic.message}`,
|
|
2551
|
+
...site.location ? { location: site.location } : {}
|
|
2552
|
+
} : diagnostic);
|
|
2553
|
+
}
|
|
2460
2554
|
diagnostics.push(Diagnostics.performance({
|
|
2461
2555
|
plugin: state.plugin.name,
|
|
2462
2556
|
duration
|
|
@@ -2531,27 +2625,37 @@ var KubbDriver = class {
|
|
|
2531
2625
|
get resolver() {
|
|
2532
2626
|
return driver.getResolver(plugin.name);
|
|
2533
2627
|
},
|
|
2534
|
-
warn(message) {
|
|
2628
|
+
warn(message, options) {
|
|
2535
2629
|
report({
|
|
2536
2630
|
code: Diagnostics.code.pluginWarning,
|
|
2537
2631
|
severity: "warning",
|
|
2538
|
-
message
|
|
2632
|
+
message,
|
|
2633
|
+
...options
|
|
2539
2634
|
});
|
|
2540
2635
|
},
|
|
2541
|
-
error(error) {
|
|
2636
|
+
error(error, options) {
|
|
2637
|
+
if (Diagnostics.isError(error)) {
|
|
2638
|
+
report({
|
|
2639
|
+
...options,
|
|
2640
|
+
...error.diagnostic
|
|
2641
|
+
});
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2542
2644
|
const cause = typeof error === "string" ? void 0 : error;
|
|
2543
2645
|
report({
|
|
2544
2646
|
code: Diagnostics.code.pluginFailed,
|
|
2545
2647
|
severity: "error",
|
|
2546
2648
|
message: typeof error === "string" ? error : error.message,
|
|
2547
|
-
cause
|
|
2649
|
+
cause,
|
|
2650
|
+
...options
|
|
2548
2651
|
});
|
|
2549
2652
|
},
|
|
2550
|
-
info(message) {
|
|
2653
|
+
info(message, options) {
|
|
2551
2654
|
report({
|
|
2552
2655
|
code: Diagnostics.code.pluginInfo,
|
|
2553
2656
|
severity: "info",
|
|
2554
|
-
message
|
|
2657
|
+
message,
|
|
2658
|
+
...options
|
|
2555
2659
|
});
|
|
2556
2660
|
}
|
|
2557
2661
|
};
|
|
@@ -2689,6 +2793,6 @@ var Hookable = class {
|
|
|
2689
2793
|
}
|
|
2690
2794
|
};
|
|
2691
2795
|
//#endregion
|
|
2692
|
-
export { inParallel as A, useOptions as C, Diagnostics as D, resolveMeta as E, toError as F, camelCase as I, read as M, toPosixPath as N, formatMs as O, write as P, useKubb as S, defineExtension as T, setMacros as _, FileManager as a, tryUseKubb as b, Resolver as c, definePlugin as d, addGenerator as f, requirePlugin as g, kubbContext as h, createNodeCache as i, isPathInside as j, getElapsedMs as k, getInputKind as l, injectFile as m, KubbDriver as n, _usingCtx as o, addMacro as p, pluginNotFoundError as r, createResolver as s, Hookable as t, assertPluginOption as u, setOptions as v, checkCompatibility as w, useConfig as x, setResolver as y };
|
|
2796
|
+
export { inParallel as A, useOptions as C, Diagnostics as D, resolveMeta as E, toError as F, camelCase as I, __name as L, read as M, toPosixPath as N, formatMs as O, write as P, useKubb as S, defineExtension as T, setMacros as _, FileManager as a, tryUseKubb as b, Resolver as c, definePlugin as d, addGenerator as f, requirePlugin as g, kubbContext as h, createNodeCache as i, isPathInside as j, getElapsedMs as k, getInputKind as l, injectFile as m, KubbDriver as n, _usingCtx as o, addMacro as p, pluginNotFoundError as r, createResolver as s, Hookable as t, assertPluginOption as u, setOptions as v, checkCompatibility as w, useConfig as x, setResolver as y };
|
|
2693
2797
|
|
|
2694
|
-
//# sourceMappingURL=Hookable-
|
|
2798
|
+
//# sourceMappingURL=Hookable-BsWZ8pHO.js.map
|