@kubb/core 5.10.0 → 5.10.2
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-DBB42ADP.js → Hookable-Bjfoua7Y.js} +418 -321
- package/dist/Hookable-Bjfoua7Y.js.map +1 -0
- package/dist/{Hookable-XjiJoKGb.cjs → Hookable-RTuEiBd9.cjs} +418 -321
- package/dist/Hookable-RTuEiBd9.cjs.map +1 -0
- package/dist/{createAdapter-D55JE9lX.d.ts → createAdapter-CMnLz31B.d.ts} +851 -731
- package/dist/index.cjs +31 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +31 -18
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/package.json +2 -2
- package/dist/Hookable-DBB42ADP.js.map +0 -1
- package/dist/Hookable-XjiJoKGb.cjs.map +0 -1
|
@@ -26,13 +26,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
//#endregion
|
|
27
27
|
let verkit = require("verkit");
|
|
28
28
|
let node_async_hooks = require("node:async_hooks");
|
|
29
|
-
let node_util = require("node:util");
|
|
30
29
|
let node_fs_promises = require("node:fs/promises");
|
|
31
30
|
let node_path = require("node:path");
|
|
32
31
|
node_path = __toESM(node_path, 1);
|
|
32
|
+
let node_util = require("node:util");
|
|
33
33
|
let _kubb_ast = require("@kubb/ast");
|
|
34
34
|
//#region package.json
|
|
35
|
-
var version = "5.10.
|
|
35
|
+
var version = "5.10.2";
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region ../../internals/utils/src/casing.ts
|
|
38
38
|
/**
|
|
@@ -88,6 +88,35 @@ function toError(value) {
|
|
|
88
88
|
function getErrorMessage(value) {
|
|
89
89
|
return value instanceof Error ? value.message : String(value);
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns `error` followed by every error in its `cause` chain, outermost first. Stops at a
|
|
93
|
+
* non-`Error` cause or a cycle.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* getErrorChain(new Error('Config failed loading', { cause: new Error('boom') })).map((error) => error.message)
|
|
98
|
+
* // ['Config failed loading', 'boom']
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
function getErrorChain(error) {
|
|
102
|
+
const chain = [];
|
|
103
|
+
for (let current = error; current instanceof Error && !chain.includes(current); current = current.cause) chain.push(current);
|
|
104
|
+
return chain;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region ../../internals/utils/src/jsonPointer.ts
|
|
108
|
+
/**
|
|
109
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
110
|
+
* name holding those characters maps to a distinct pointer.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* escapePointerToken('/pets/{id}') // '~1pets~1{id}'
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
function escapePointerToken(token) {
|
|
118
|
+
return token.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
119
|
+
}
|
|
91
120
|
//#endregion
|
|
92
121
|
//#region ../../internals/utils/src/runtime.ts
|
|
93
122
|
const isBun = typeof Bun !== "undefined";
|
|
@@ -288,375 +317,388 @@ function formatMs(ms) {
|
|
|
288
317
|
return `${Math.round(ms)}ms`;
|
|
289
318
|
}
|
|
290
319
|
//#endregion
|
|
291
|
-
//#region src/
|
|
292
|
-
/**
|
|
293
|
-
* Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
|
|
294
|
-
* and stays stable so it can be referenced in tooling and (later) docs. Reference
|
|
295
|
-
* these instead of inlining the string at a throw site.
|
|
296
|
-
*/
|
|
297
|
-
const diagnosticCode = {
|
|
298
|
-
/**
|
|
299
|
-
* Fallback for an unstructured error with no specific code.
|
|
300
|
-
*/
|
|
301
|
-
unknown: "KUBB_UNKNOWN",
|
|
302
|
-
/**
|
|
303
|
-
* The file or URL set as `input` could not be read.
|
|
304
|
-
*/
|
|
305
|
-
inputNotFound: "KUBB_INPUT_NOT_FOUND",
|
|
306
|
-
/**
|
|
307
|
-
* A URL set as `input` (or referenced by a `$ref`) answered with a 4xx or 5xx status
|
|
308
|
-
* instead of the document.
|
|
309
|
-
*/
|
|
310
|
-
inputRequestFailed: "KUBB_INPUT_REQUEST_FAILED",
|
|
311
|
-
/**
|
|
312
|
-
* A URL set as `input` (or referenced by a `$ref`) never answered, so the request failed
|
|
313
|
-
* before a status was returned.
|
|
314
|
-
*/
|
|
315
|
-
inputUnreachable: "KUBB_INPUT_UNREACHABLE",
|
|
316
|
-
/**
|
|
317
|
-
* An adapter was configured without an `input`.
|
|
318
|
-
*/
|
|
319
|
-
inputRequired: "KUBB_INPUT_REQUIRED",
|
|
320
|
-
/**
|
|
321
|
-
* `input` uses the v4 `{ path }` / `{ data }` wrapper, which v5 reads as a parsed
|
|
322
|
-
* document instead of a pointer to one.
|
|
323
|
-
*/
|
|
324
|
-
legacyInput: "KUBB_LEGACY_INPUT",
|
|
325
|
-
/**
|
|
326
|
-
* The parsed `input` carries no `openapi` or `swagger` version, so it is not a
|
|
327
|
-
* document the adapter can read.
|
|
328
|
-
*/
|
|
329
|
-
invalidDocument: "KUBB_INVALID_DOCUMENT",
|
|
330
|
-
/** The document fails OpenAPI schema validation. Reported as a warning. */
|
|
331
|
-
invalidSpec: "KUBB_INVALID_SPEC",
|
|
332
|
-
/**
|
|
333
|
-
* A `$ref` (or equivalent reference) could not be resolved in the source document.
|
|
334
|
-
*/
|
|
335
|
-
refNotFound: "KUBB_REF_NOT_FOUND",
|
|
336
|
-
/**
|
|
337
|
-
* A server variable value is not allowed by its `enum`.
|
|
338
|
-
*/
|
|
339
|
-
invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
|
|
340
|
-
/**
|
|
341
|
-
* A required plugin is missing from the config.
|
|
342
|
-
*/
|
|
343
|
-
pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
|
|
344
|
-
/**
|
|
345
|
-
* The running Kubb or Node.js version is outside a plugin's `meta.compatibility`.
|
|
346
|
-
*/
|
|
347
|
-
incompatibleExtension: "KUBB_INCOMPATIBLE_EXTENSION",
|
|
348
|
-
/**
|
|
349
|
-
* A required entry in a plugin's `dependencies` record is missing from the config.
|
|
350
|
-
*/
|
|
351
|
-
missingDependency: "KUBB_MISSING_DEPENDENCY",
|
|
352
|
-
/**
|
|
353
|
-
* A dependency's `meta.version` is outside the range a plugin's `dependencies` record declares.
|
|
354
|
-
*/
|
|
355
|
-
incompatibleDependency: "KUBB_INCOMPATIBLE_DEPENDENCY",
|
|
356
|
-
/**
|
|
357
|
-
* A plugin threw while generating.
|
|
358
|
-
*/
|
|
359
|
-
pluginFailed: "KUBB_PLUGIN_FAILED",
|
|
360
|
-
/** An adapter threw while parsing the input. */
|
|
361
|
-
adapterFailed: "KUBB_ADAPTER_FAILED",
|
|
362
|
-
/** A parser threw while printing a generated file. */
|
|
363
|
-
parserFailed: "KUBB_PARSER_FAILED",
|
|
364
|
-
/**
|
|
365
|
-
* A plugin reported a non-fatal warning through `ctx.warn`.
|
|
366
|
-
*/
|
|
367
|
-
pluginWarning: "KUBB_PLUGIN_WARNING",
|
|
368
|
-
/**
|
|
369
|
-
* A plugin reported an informational message through `ctx.info`.
|
|
370
|
-
*/
|
|
371
|
-
pluginInfo: "KUBB_PLUGIN_INFO",
|
|
372
|
-
/**
|
|
373
|
-
* A `kubb/kit` composable was called outside a plugin's `setup` and `kubb:plugin:setup`.
|
|
374
|
-
*/
|
|
375
|
-
noContext: "KUBB_NO_CONTEXT",
|
|
376
|
-
/**
|
|
377
|
-
* A schema uses a `format` Kubb does not map to a specific type. Reserved for
|
|
378
|
-
* adapters to emit as a `warning`.
|
|
379
|
-
*/
|
|
380
|
-
unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
|
|
381
|
-
/**
|
|
382
|
-
* A referenced schema or operation is marked `deprecated`. Reserved for adapters
|
|
383
|
-
* to emit as an `info`.
|
|
384
|
-
*/
|
|
385
|
-
deprecated: "KUBB_DEPRECATED",
|
|
386
|
-
/**
|
|
387
|
-
* An adapter is required but the config has none. The build cannot read the input
|
|
388
|
-
* without one.
|
|
389
|
-
*/
|
|
390
|
-
adapterRequired: "KUBB_ADAPTER_REQUIRED",
|
|
391
|
-
/**
|
|
392
|
-
* A resolved output path escapes the output directory, which can stem from a path
|
|
393
|
-
* traversal in the spec or a misconfigured `group.name`.
|
|
394
|
-
*/
|
|
395
|
-
pathTraversal: "KUBB_PATH_TRAVERSAL",
|
|
396
|
-
/**
|
|
397
|
-
* `output.clean` is enabled but `output.path` resolves to the project root or a parent of it,
|
|
398
|
-
* so cleaning would delete kubb.config and every source file.
|
|
399
|
-
*/
|
|
400
|
-
cleanRoot: "KUBB_CLEAN_ROOT",
|
|
401
|
-
/**
|
|
402
|
-
* A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
|
|
403
|
-
*/
|
|
404
|
-
invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
|
|
405
|
-
/**
|
|
406
|
-
* A post-generate command (`output.postGenerate`) exited with a failure.
|
|
407
|
-
*/
|
|
408
|
-
postGenerateFailed: "KUBB_POST_GENERATE_FAILED",
|
|
409
|
-
/**
|
|
410
|
-
* The formatter pass over the generated files failed.
|
|
411
|
-
*/
|
|
412
|
-
formatFailed: "KUBB_FORMAT_FAILED",
|
|
413
|
-
/**
|
|
414
|
-
* The linter pass over the generated files failed.
|
|
415
|
-
*/
|
|
416
|
-
lintFailed: "KUBB_LINT_FAILED",
|
|
417
|
-
/**
|
|
418
|
-
* A barrel tries to re-export the same name from more than one module.
|
|
419
|
-
*/
|
|
420
|
-
barrelDuplicateExport: "KUBB_BARREL_DUPLICATE_EXPORT",
|
|
421
|
-
/**
|
|
422
|
-
* Not a failure. Carries a plugin's elapsed time, summed into the run total.
|
|
423
|
-
*/
|
|
424
|
-
performance: "KUBB_PERFORMANCE",
|
|
425
|
-
/**
|
|
426
|
-
* Not a failure. A newer Kubb version is available on npm.
|
|
427
|
-
*/
|
|
428
|
-
updateAvailable: "KUBB_UPDATE_AVAILABLE"
|
|
429
|
-
};
|
|
430
|
-
//#endregion
|
|
431
|
-
//#region src/Diagnostics.ts
|
|
432
|
-
/**
|
|
433
|
-
* Docs major version, derived from the package version so the link tracks the published major.
|
|
434
|
-
*/
|
|
435
|
-
const docsMajor = version.split(".")[0] ?? "5";
|
|
436
|
-
/**
|
|
437
|
-
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
438
|
-
* with no `kind` is treated as a `problem`.
|
|
439
|
-
*/
|
|
440
|
-
function isKind(kind) {
|
|
441
|
-
return (diagnostic) => (diagnostic.kind ?? "problem") === kind;
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
445
|
-
*
|
|
446
|
-
* @example
|
|
447
|
-
* ```ts
|
|
448
|
-
* if (isProblem(diagnostic)) {
|
|
449
|
-
* console.log(diagnostic.location)
|
|
450
|
-
* }
|
|
451
|
-
* ```
|
|
452
|
-
*/
|
|
453
|
-
const isProblem = isKind("problem");
|
|
454
|
-
/**
|
|
455
|
-
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
456
|
-
*
|
|
457
|
-
* @example
|
|
458
|
-
* ```ts
|
|
459
|
-
* const timings = diagnostics.filter(isPerformance)
|
|
460
|
-
* ```
|
|
461
|
-
*/
|
|
462
|
-
const isPerformance = isKind("performance");
|
|
463
|
-
/**
|
|
464
|
-
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
465
|
-
*
|
|
466
|
-
* @example
|
|
467
|
-
* ```ts
|
|
468
|
-
* if (isUpdate(diagnostic)) {
|
|
469
|
-
* console.log(diagnostic.latestVersion)
|
|
470
|
-
* }
|
|
471
|
-
* ```
|
|
472
|
-
*/
|
|
473
|
-
const isUpdate = isKind("update");
|
|
320
|
+
//#region src/diagnosticCatalog.ts
|
|
474
321
|
/**
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
|
|
478
|
-
const severityStyle = {
|
|
479
|
-
error: "red",
|
|
480
|
-
warning: "yellow",
|
|
481
|
-
info: "blue"
|
|
482
|
-
};
|
|
483
|
-
const failedCode = {
|
|
484
|
-
plugin: diagnosticCode.pluginFailed,
|
|
485
|
-
adapter: diagnosticCode.adapterFailed,
|
|
486
|
-
parser: diagnosticCode.parserFailed
|
|
487
|
-
};
|
|
488
|
-
const countKey = {
|
|
489
|
-
error: "errors",
|
|
490
|
-
warning: "warnings",
|
|
491
|
-
info: "infos"
|
|
492
|
-
};
|
|
493
|
-
/**
|
|
494
|
-
* Explanation for every {@link diagnosticCode}. Use {@link Diagnostics.explain} to look one up
|
|
495
|
-
* and `Diagnostics.docsUrl` for the matching kubb.dev page.
|
|
322
|
+
* Every diagnostic code with its docs entry: a short title, what triggers it, and how to resolve
|
|
323
|
+
* it. The source of truth for {@link diagnosticCode}, and the kubb.dev `/reference/diagnostics`
|
|
324
|
+
* sections mirror it.
|
|
496
325
|
*/
|
|
497
326
|
const diagnosticCatalog = {
|
|
498
|
-
|
|
327
|
+
unknown: {
|
|
328
|
+
code: "KUBB_UNKNOWN",
|
|
499
329
|
title: "Unknown error",
|
|
500
330
|
cause: "An error was thrown without a stable Kubb code, so it is reported as-is.",
|
|
501
331
|
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."
|
|
502
332
|
},
|
|
503
|
-
|
|
333
|
+
inputNotFound: {
|
|
334
|
+
code: "KUBB_INPUT_NOT_FOUND",
|
|
504
335
|
title: "Input not found",
|
|
505
336
|
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.",
|
|
506
337
|
fix: "Check that the path exists and is readable, then set it as `input` or pass it on the CLI."
|
|
507
338
|
},
|
|
508
|
-
|
|
339
|
+
inputRequestFailed: {
|
|
340
|
+
code: "KUBB_INPUT_REQUEST_FAILED",
|
|
509
341
|
title: "Input request failed",
|
|
510
342
|
cause: "A URL set as `input` (or reached through a `$ref`) answered with a 4xx or 5xx status instead of the document.",
|
|
511
343
|
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."
|
|
512
344
|
},
|
|
513
|
-
|
|
345
|
+
inputUnreachable: {
|
|
346
|
+
code: "KUBB_INPUT_UNREACHABLE",
|
|
514
347
|
title: "Input unreachable",
|
|
515
348
|
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.",
|
|
516
349
|
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`."
|
|
517
350
|
},
|
|
518
|
-
|
|
351
|
+
inputRequired: {
|
|
352
|
+
code: "KUBB_INPUT_REQUIRED",
|
|
519
353
|
title: "Input required",
|
|
520
354
|
cause: "An adapter is configured but no `input` was provided.",
|
|
521
355
|
fix: "Set `input` to a file path, a URL, an inline spec (JSON/YAML string), or a parsed object in your Kubb config."
|
|
522
356
|
},
|
|
523
|
-
|
|
357
|
+
legacyInput: {
|
|
358
|
+
code: "KUBB_LEGACY_INPUT",
|
|
524
359
|
title: "Legacy input shape",
|
|
525
360
|
cause: "`input` is a `{ path }` or `{ data }` wrapper, which v4 used to point at a document and v5 reads as the document itself.",
|
|
526
361
|
fix: "Unwrap it: `input: { path: \"./petStore.yaml\" }` becomes `input: \"./petStore.yaml\"`, and `input: { data: spec }` becomes `input: spec`."
|
|
527
362
|
},
|
|
528
|
-
|
|
363
|
+
invalidDocument: {
|
|
364
|
+
code: "KUBB_INVALID_DOCUMENT",
|
|
529
365
|
title: "Invalid document",
|
|
530
|
-
cause: "The
|
|
531
|
-
fix: "
|
|
366
|
+
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.",
|
|
367
|
+
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."
|
|
532
368
|
},
|
|
533
|
-
|
|
369
|
+
invalidSpec: {
|
|
370
|
+
code: "KUBB_INVALID_SPEC",
|
|
534
371
|
title: "Invalid spec",
|
|
535
372
|
cause: "The document does not pass OpenAPI schema validation. Kubb still generates from it, but the output can be incomplete or wrong.",
|
|
536
373
|
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."
|
|
537
374
|
},
|
|
538
|
-
|
|
375
|
+
refNotFound: {
|
|
376
|
+
code: "KUBB_REF_NOT_FOUND",
|
|
539
377
|
title: "Reference not found",
|
|
540
378
|
cause: "A `$ref` could not be resolved in the source document.",
|
|
541
379
|
fix: "Add the missing definition (for example under `components.schemas`) or fix the `$ref`. Run `kubb validate` to check the spec."
|
|
542
380
|
},
|
|
543
|
-
|
|
381
|
+
invalidServerVariable: {
|
|
382
|
+
code: "KUBB_INVALID_SERVER_VARIABLE",
|
|
544
383
|
title: "Invalid server variable",
|
|
545
384
|
cause: "A server variable value is not allowed by its `enum`.",
|
|
546
385
|
fix: "Use one of the values listed in the server variable `enum`, or update the spec."
|
|
547
386
|
},
|
|
548
|
-
|
|
387
|
+
pluginNotFound: {
|
|
388
|
+
code: "KUBB_PLUGIN_NOT_FOUND",
|
|
549
389
|
title: "Plugin not found",
|
|
550
390
|
cause: "A plugin that another plugin depends on is missing from the config.",
|
|
551
391
|
fix: "Add the required plugin to the `plugins` array in kubb.config.ts, or remove the dependency on it."
|
|
552
392
|
},
|
|
553
|
-
|
|
393
|
+
incompatibleExtension: {
|
|
394
|
+
code: "KUBB_INCOMPATIBLE_EXTENSION",
|
|
554
395
|
title: "Incompatible extension",
|
|
555
396
|
cause: "A plugin sets `meta.compatibility.kubb` or `meta.compatibility.node` to a range the running Kubb or Node.js version does not satisfy.",
|
|
556
397
|
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."
|
|
557
398
|
},
|
|
558
|
-
|
|
399
|
+
missingDependency: {
|
|
400
|
+
code: "KUBB_MISSING_DEPENDENCY",
|
|
559
401
|
title: "Missing dependency",
|
|
560
402
|
cause: "A plugin lists another plugin in its `dependencies` record without `optional: true`, and the config does not include it.",
|
|
561
403
|
fix: "Add the missing plugin to the `plugins` array in kubb.config.ts."
|
|
562
404
|
},
|
|
563
|
-
|
|
405
|
+
incompatibleDependency: {
|
|
406
|
+
code: "KUBB_INCOMPATIBLE_DEPENDENCY",
|
|
564
407
|
title: "Incompatible dependency",
|
|
565
408
|
cause: "A plugin's `dependencies` record sets a `version` range, and the configured plugin's `meta.version` falls outside it.",
|
|
566
409
|
fix: "Install a version of the dependency inside the range, or a version of the dependent plugin that supports the installed dependency."
|
|
567
410
|
},
|
|
568
|
-
|
|
411
|
+
pluginFailed: {
|
|
412
|
+
code: "KUBB_PLUGIN_FAILED",
|
|
569
413
|
title: "Plugin failed",
|
|
570
414
|
cause: "A plugin threw while generating, or reported an error through `ctx.error`.",
|
|
571
|
-
fix: "
|
|
415
|
+
fix: "Check the plugin options and the node at `at:`. If it is a bug, report it with the stack at `report:`."
|
|
572
416
|
},
|
|
573
|
-
|
|
417
|
+
adapterFailed: {
|
|
418
|
+
code: "KUBB_ADAPTER_FAILED",
|
|
574
419
|
title: "Adapter failed",
|
|
575
420
|
cause: "The adapter threw an unexpected error while parsing the input.",
|
|
576
|
-
fix: "
|
|
421
|
+
fix: "Check the input and the adapter options. If it is a bug, report it with the stack at `report:`."
|
|
577
422
|
},
|
|
578
|
-
|
|
423
|
+
parserFailed: {
|
|
424
|
+
code: "KUBB_PARSER_FAILED",
|
|
579
425
|
title: "Parser failed",
|
|
580
426
|
cause: "A parser threw an unexpected error while printing a generated file.",
|
|
581
|
-
fix: "
|
|
427
|
+
fix: "Check the parser options. If it is a bug, report it with the stack at `report:`."
|
|
582
428
|
},
|
|
583
|
-
|
|
429
|
+
pluginWarning: {
|
|
430
|
+
code: "KUBB_PLUGIN_WARNING",
|
|
584
431
|
title: "Plugin warning",
|
|
585
432
|
cause: "A plugin reported a non-fatal warning through `ctx.warn`.",
|
|
586
433
|
fix: "Review the message. It does not fail the build; adjust the plugin options or input if the warning is unwanted."
|
|
587
434
|
},
|
|
588
|
-
|
|
435
|
+
pluginInfo: {
|
|
436
|
+
code: "KUBB_PLUGIN_INFO",
|
|
589
437
|
title: "Plugin info",
|
|
590
438
|
cause: "A plugin reported an informational message through `ctx.info`.",
|
|
591
439
|
fix: "Informational only. No action is required."
|
|
592
440
|
},
|
|
593
|
-
|
|
441
|
+
noContext: {
|
|
442
|
+
code: "KUBB_NO_CONTEXT",
|
|
594
443
|
title: "No plugin context",
|
|
595
444
|
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.",
|
|
596
445
|
fix: "Call the composable inside `setup` or `kubb:plugin:setup`, including in code that setup awaits."
|
|
597
446
|
},
|
|
598
|
-
|
|
447
|
+
unsupportedFormat: {
|
|
448
|
+
code: "KUBB_UNSUPPORTED_FORMAT",
|
|
599
449
|
title: "Unsupported format",
|
|
600
450
|
cause: "A schema uses a `format` Kubb does not map to a specific type, so it falls back to the base type.",
|
|
601
451
|
fix: "Use a format Kubb supports, or handle the custom format with a parser or plugin."
|
|
602
452
|
},
|
|
603
|
-
|
|
453
|
+
deprecated: {
|
|
454
|
+
code: "KUBB_DEPRECATED",
|
|
604
455
|
title: "Deprecated",
|
|
605
456
|
cause: "A referenced schema or operation is marked `deprecated`.",
|
|
606
457
|
fix: "Migrate off the deprecated definition if the warning is unwanted."
|
|
607
458
|
},
|
|
608
|
-
|
|
459
|
+
adapterRequired: {
|
|
460
|
+
code: "KUBB_ADAPTER_REQUIRED",
|
|
609
461
|
title: "Adapter required",
|
|
610
462
|
cause: "An action needs an adapter but none is configured.",
|
|
611
463
|
fix: "Set `adapter` in kubb.config.ts, for example `adapterOas()`."
|
|
612
464
|
},
|
|
613
|
-
|
|
465
|
+
pathTraversal: {
|
|
466
|
+
code: "KUBB_PATH_TRAVERSAL",
|
|
614
467
|
title: "Path traversal",
|
|
615
468
|
cause: "A resolved output path escaped the output directory, which can stem from a path traversal in the spec or a misconfigured `group.name`.",
|
|
616
469
|
fix: "Keep generated paths within the output directory. Review the `group.name` function and the names coming from the spec."
|
|
617
470
|
},
|
|
618
|
-
|
|
471
|
+
cleanRoot: {
|
|
472
|
+
code: "KUBB_CLEAN_ROOT",
|
|
619
473
|
title: "Clean targets the project root",
|
|
620
474
|
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.",
|
|
621
475
|
fix: "Point `output.path` at a subdirectory such as `./src/gen` so clean only removes generated code, or disable `output.clean`."
|
|
622
476
|
},
|
|
623
|
-
|
|
477
|
+
invalidPluginOptions: {
|
|
478
|
+
code: "KUBB_INVALID_PLUGIN_OPTIONS",
|
|
624
479
|
title: "Invalid plugin options",
|
|
625
480
|
cause: "A plugin was configured with options that cannot be honored, for example `output.mode: 'file'` paired with a `group` option.",
|
|
626
481
|
fix: "Fix the plugin options. A single-file output has nothing to group, so remove the `group` option or use `output.mode: 'directory'`."
|
|
627
482
|
},
|
|
628
|
-
|
|
483
|
+
postGenerateFailed: {
|
|
484
|
+
code: "KUBB_POST_GENERATE_FAILED",
|
|
629
485
|
title: "Post-generate command failed",
|
|
630
486
|
cause: "A post-generate command (`output.postGenerate`) exited with a non-zero status.",
|
|
631
487
|
fix: "Check the command is installed and correct, and run it manually to see the error."
|
|
632
488
|
},
|
|
633
|
-
|
|
489
|
+
formatFailed: {
|
|
490
|
+
code: "KUBB_FORMAT_FAILED",
|
|
634
491
|
title: "Format failed",
|
|
635
492
|
cause: "The formatter pass over the generated files failed.",
|
|
636
493
|
fix: "Check the formatter (oxfmt, biome, or prettier) is installed and its config is valid, then run it manually on the output."
|
|
637
494
|
},
|
|
638
|
-
|
|
495
|
+
lintFailed: {
|
|
496
|
+
code: "KUBB_LINT_FAILED",
|
|
639
497
|
title: "Lint failed",
|
|
640
498
|
cause: "The linter pass over the generated files failed.",
|
|
641
499
|
fix: "Check the linter (oxlint, biome, or eslint) is installed and its config is valid, then run it manually on the output."
|
|
642
500
|
},
|
|
643
|
-
|
|
501
|
+
barrelDuplicateExport: {
|
|
502
|
+
code: "KUBB_BARREL_DUPLICATE_EXPORT",
|
|
644
503
|
title: "Duplicate barrel export",
|
|
645
504
|
cause: "Two generated modules export the same name, so the barrel cannot re-export both.",
|
|
646
505
|
fix: "Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names."
|
|
647
506
|
},
|
|
648
|
-
|
|
507
|
+
internalError: {
|
|
508
|
+
code: "KUBB_INTERNAL_ERROR",
|
|
509
|
+
title: "Internal error",
|
|
510
|
+
cause: "Kubb hit a state it does not expect. This is a bug in Kubb, not in your config.",
|
|
511
|
+
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."
|
|
512
|
+
},
|
|
513
|
+
performance: {
|
|
514
|
+
code: "KUBB_PERFORMANCE",
|
|
649
515
|
title: "Performance",
|
|
650
516
|
cause: "Not a failure. Records a plugin’s elapsed time, summed into the run total.",
|
|
651
517
|
fix: "No action. This is an informational metric."
|
|
652
518
|
},
|
|
653
|
-
|
|
519
|
+
updateAvailable: {
|
|
520
|
+
code: "KUBB_UPDATE_AVAILABLE",
|
|
654
521
|
title: "Update available",
|
|
655
522
|
cause: "A newer Kubb version is published on npm than the one running.",
|
|
656
523
|
fix: "Update the `@kubb/*` packages, for example `npm install -g @kubb/cli`, to get the latest fixes."
|
|
657
524
|
}
|
|
658
525
|
};
|
|
659
526
|
/**
|
|
527
|
+
* Stable codes Kubb attaches to a `Diagnostic`, derived from {@link diagnosticCatalog}. Reference
|
|
528
|
+
* these instead of inlining the string at a throw site.
|
|
529
|
+
*/
|
|
530
|
+
const diagnosticCode = Object.fromEntries(Object.entries(diagnosticCatalog).map(([key, { code }]) => [key, code]));
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/formatDiagnostic.ts
|
|
533
|
+
/** A problem's `help`, or the catalog fix. `info` gets none: its catalog fix says no action is needed. */
|
|
534
|
+
function getHelp(problem) {
|
|
535
|
+
if (problem.help || problem.severity === "info") return problem.help;
|
|
536
|
+
return Diagnostics.explain(problem.code).fix;
|
|
537
|
+
}
|
|
538
|
+
/** The `cause` chain's messages, outermost first, minus those `message` already contains. */
|
|
539
|
+
function getCauses(problem) {
|
|
540
|
+
return getErrorChain(problem.cause).filter((error) => !problem.message.includes(error.message)).map((error) => error.name === "Error" ? error.message : `${error.name}: ${error.message}`);
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Accent color per severity, which paints the icon in the `human` layout (red error, yellow
|
|
544
|
+
* warning, blue info). The code itself stays dim.
|
|
545
|
+
*/
|
|
546
|
+
const severityStyle = {
|
|
547
|
+
error: "red",
|
|
548
|
+
warning: "yellow",
|
|
549
|
+
info: "blue"
|
|
550
|
+
};
|
|
551
|
+
const severityIcon = {
|
|
552
|
+
error: "✖",
|
|
553
|
+
warning: "⚠",
|
|
554
|
+
info: "ℹ"
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* Codes for a crash, which carry the root cause's stack.
|
|
558
|
+
*/
|
|
559
|
+
const crashCodes = /* @__PURE__ */ new Set([
|
|
560
|
+
diagnosticCode.unknown,
|
|
561
|
+
diagnosticCode.internalError,
|
|
562
|
+
diagnosticCode.pluginFailed,
|
|
563
|
+
diagnosticCode.adapterFailed,
|
|
564
|
+
diagnosticCode.parserFailed
|
|
565
|
+
]);
|
|
566
|
+
/**
|
|
567
|
+
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
568
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
569
|
+
* `stack`. Absent optional fields are omitted.
|
|
570
|
+
*/
|
|
571
|
+
function serialize(diagnostic) {
|
|
572
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
573
|
+
const doc = problem ? Diagnostics.explain(problem.code) : void 0;
|
|
574
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
575
|
+
const causes = problem ? getCauses(problem) : [];
|
|
576
|
+
const stack = problem && crashCodes.has(problem.code) ? getErrorChain(problem.cause).at(-1)?.stack : void 0;
|
|
577
|
+
return {
|
|
578
|
+
code: diagnostic.code,
|
|
579
|
+
severity: diagnostic.severity,
|
|
580
|
+
message: diagnostic.message,
|
|
581
|
+
...doc ? {
|
|
582
|
+
title: doc.title,
|
|
583
|
+
cause: doc.cause
|
|
584
|
+
} : {},
|
|
585
|
+
...problem?.location ? { location: problem.location } : {},
|
|
586
|
+
...help ? { help } : {},
|
|
587
|
+
...problem?.plugin ? { plugin: problem.plugin } : {},
|
|
588
|
+
...problem?.issues ? { issues: problem.issues } : {},
|
|
589
|
+
...causes.length ? { causes } : {},
|
|
590
|
+
...stack ? { stack } : {},
|
|
591
|
+
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Renders a {@link Diagnostic} as lines: a headline, then indented detail rows. `agent` (the default)
|
|
596
|
+
* prints uncolored labeled rows (`at:`, `why:`, `fix:`, `caused by:`, `stack:` for a crash,
|
|
597
|
+
* `docs:`, `report:`). `human` prints an icon, the bold message, and the code aligned to `width`,
|
|
598
|
+
* with `at`, `fix`, the first cause, and `Learn more` for an error, and one row for a warning.
|
|
599
|
+
* `verbose` adds the full stack.
|
|
600
|
+
*/
|
|
601
|
+
function format(diagnostic, { audience = "agent", verbose = false, width = 0 } = {}) {
|
|
602
|
+
const { code, severity, message } = diagnostic;
|
|
603
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
604
|
+
const human = audience === "human";
|
|
605
|
+
const paint = human ? node_util.styleText : (_format, text) => text;
|
|
606
|
+
const [firstLine = "", ...rest] = message.split("\n");
|
|
607
|
+
const pointer = problem?.location && "pointer" in problem.location ? problem.location.pointer : void 0;
|
|
608
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
609
|
+
const causes = problem ? getCauses(problem) : [];
|
|
610
|
+
const crash = problem ? crashCodes.has(problem.code) : false;
|
|
611
|
+
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())}`);
|
|
612
|
+
if (!human) {
|
|
613
|
+
const details = rest.filter((line) => line.trim()).map((line) => ` ${line}`);
|
|
614
|
+
const row = (label, value) => {
|
|
615
|
+
if (value) details.push(` ${label}: ${value}`);
|
|
616
|
+
};
|
|
617
|
+
row("at", pointer);
|
|
618
|
+
if (problem && severity !== "info") row("why", Diagnostics.explain(problem.code).cause);
|
|
619
|
+
row("fix", help);
|
|
620
|
+
for (const cause of causes) row("caused by", cause);
|
|
621
|
+
if (frames?.length) details.push(" stack:", ...frames);
|
|
622
|
+
if (code !== diagnosticCode.unknown) row("docs", Diagnostics.docsUrl(code));
|
|
623
|
+
row("report", problem?.issues);
|
|
624
|
+
return [`${severity} ${code}${problem?.plugin ? ` [${problem.plugin}]` : ""}: ${firstLine}`, ...details];
|
|
625
|
+
}
|
|
626
|
+
const left = `${paint(severityStyle[severity], severityIcon[severity])} ${paint("bold", firstLine)}`;
|
|
627
|
+
const gap = Math.max(2, width - (0, node_util.stripVTControlCharacters)(left).length - code.length);
|
|
628
|
+
const headline = `${left}${" ".repeat(gap)}${paint("dim", code)}`;
|
|
629
|
+
const frame = rest.filter((line) => line.trim()).map((line) => ` ${paint("dim", line)}`);
|
|
630
|
+
const label = (name) => paint("dim", name.length < 4 ? name.padEnd(4) : `${name} `);
|
|
631
|
+
const rows = [["at", pointer && paint("cyan", pointer)], ["fix", help]];
|
|
632
|
+
if (severity !== "error") {
|
|
633
|
+
const first = rows.find(([, value]) => value);
|
|
634
|
+
return [
|
|
635
|
+
headline,
|
|
636
|
+
...frame,
|
|
637
|
+
...first ? [` ${label(first[0])} ${first[1]}`] : []
|
|
638
|
+
];
|
|
639
|
+
}
|
|
640
|
+
rows.push(["caused by", causes[0]], ["report", problem?.issues && paint("cyan", problem.issues)]);
|
|
641
|
+
const details = [...frame, ...rows.flatMap(([name, value]) => value ? [` ${label(name)} ${value}`] : [])];
|
|
642
|
+
if (frames?.length) details.push(` ${paint("dim", "stack")}`, ...frames);
|
|
643
|
+
if (code !== diagnosticCode.unknown) details.push(` ${paint("dim", "↳ Learn more")} ${paint("cyan", Diagnostics.docsUrl(code).replace(/^https:\/\//, ""))}`);
|
|
644
|
+
return [headline, ...details];
|
|
645
|
+
}
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/Diagnostics.ts
|
|
648
|
+
/**
|
|
649
|
+
* Docs major version, derived from the package version so the link tracks the published major.
|
|
650
|
+
*/
|
|
651
|
+
const docsMajor = version.split(".")[0] ?? "5";
|
|
652
|
+
/**
|
|
653
|
+
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
654
|
+
* with no `kind` is treated as a `problem`.
|
|
655
|
+
*/
|
|
656
|
+
function isKind(kind) {
|
|
657
|
+
return (diagnostic) => (diagnostic.kind ?? "problem") === kind;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```ts
|
|
664
|
+
* if (isProblem(diagnostic)) {
|
|
665
|
+
* console.log(diagnostic.location)
|
|
666
|
+
* }
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
const isProblem = isKind("problem");
|
|
670
|
+
/**
|
|
671
|
+
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* const timings = diagnostics.filter(isPerformance)
|
|
676
|
+
* ```
|
|
677
|
+
*/
|
|
678
|
+
const isPerformance = isKind("performance");
|
|
679
|
+
/**
|
|
680
|
+
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* if (isUpdate(diagnostic)) {
|
|
685
|
+
* console.log(diagnostic.latestVersion)
|
|
686
|
+
* }
|
|
687
|
+
* ```
|
|
688
|
+
*/
|
|
689
|
+
const isUpdate = isKind("update");
|
|
690
|
+
const failedCode = {
|
|
691
|
+
plugin: diagnosticCode.pluginFailed,
|
|
692
|
+
adapter: diagnosticCode.adapterFailed,
|
|
693
|
+
parser: diagnosticCode.parserFailed
|
|
694
|
+
};
|
|
695
|
+
const docsByCode = new Map(Object.values(diagnosticCatalog).map((doc) => [doc.code, doc]));
|
|
696
|
+
const countKey = {
|
|
697
|
+
error: "errors",
|
|
698
|
+
warning: "warnings",
|
|
699
|
+
info: "infos"
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
660
702
|
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
661
703
|
* that lets deep code report a diagnostic without threading a callback.
|
|
662
704
|
*
|
|
@@ -731,27 +773,37 @@ var Diagnostics = class Diagnostics {
|
|
|
731
773
|
return true;
|
|
732
774
|
}
|
|
733
775
|
/**
|
|
734
|
-
* Emits a diagnostic on the run's
|
|
735
|
-
*
|
|
736
|
-
*
|
|
776
|
+
* Emits a diagnostic, or any thrown value, on the run's hooks so the loggers render it live.
|
|
777
|
+
* A coded problem or an update goes to `kubb:diagnostic`. An uncoded one goes to `kubb:error`
|
|
778
|
+
* whole, so every wrapper's message, its help, and the stack survive. To collect a diagnostic into
|
|
779
|
+
* the build result from deep in a run, use {@link Diagnostics.report} instead.
|
|
737
780
|
*/
|
|
738
|
-
static async emit(hooks,
|
|
739
|
-
|
|
781
|
+
static async emit(hooks, value) {
|
|
782
|
+
const thrown = value instanceof Error || typeof value?.code !== "string";
|
|
783
|
+
const diagnostic = thrown ? Diagnostics.from(value) : value;
|
|
784
|
+
if (!isProblem(diagnostic) || diagnostic.code !== diagnosticCode.unknown) {
|
|
785
|
+
await hooks.callHook("kubb:diagnostic", { diagnostic });
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const { cause, help, message } = diagnostic;
|
|
789
|
+
const bare = cause && !help && cause.message === message;
|
|
790
|
+
await hooks.callHook("kubb:error", { error: thrown ? toError(value) : bare ? cause : new Diagnostics.Error(diagnostic) });
|
|
740
791
|
}
|
|
741
792
|
/**
|
|
742
793
|
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link Diagnostics.Error}
|
|
743
794
|
* keeps its structured data, and anything else becomes a `KUBB_UNKNOWN` error.
|
|
744
795
|
*/
|
|
745
796
|
static from(error) {
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
797
|
+
const chain = getErrorChain(error);
|
|
798
|
+
const coded = chain.findLast((current) => Diagnostics.isError(current));
|
|
799
|
+
if (coded) {
|
|
800
|
+
const { diagnostic } = coded;
|
|
801
|
+
return diagnostic.cause || !crashCodes.has(diagnostic.code) ? diagnostic : {
|
|
802
|
+
...diagnostic,
|
|
803
|
+
cause: coded
|
|
804
|
+
};
|
|
754
805
|
}
|
|
806
|
+
const root = chain.at(-1);
|
|
755
807
|
return {
|
|
756
808
|
code: diagnosticCode.unknown,
|
|
757
809
|
severity: "error",
|
|
@@ -769,7 +821,9 @@ var Diagnostics = class Diagnostics {
|
|
|
769
821
|
const diagnostic = Diagnostics.from(error);
|
|
770
822
|
const name = extension.meta?.name ?? extension.name;
|
|
771
823
|
const owner = kind === "plugin" ? { plugin: diagnostic.plugin ?? name } : {};
|
|
772
|
-
|
|
824
|
+
const crashed = diagnostic.code === diagnosticCode.unknown && diagnostic.cause?.name !== "AbortError";
|
|
825
|
+
const failed = Object.values(failedCode).includes(diagnostic.code);
|
|
826
|
+
if (!crashed && !failed) return {
|
|
773
827
|
...diagnostic,
|
|
774
828
|
...owner
|
|
775
829
|
};
|
|
@@ -777,8 +831,8 @@ var Diagnostics = class Diagnostics {
|
|
|
777
831
|
return {
|
|
778
832
|
...diagnostic,
|
|
779
833
|
...owner,
|
|
780
|
-
code: failedCode[kind],
|
|
781
|
-
message: kind
|
|
834
|
+
code: crashed ? failedCode[kind] : diagnostic.code,
|
|
835
|
+
message: crashed && kind !== "plugin" ? `${kind} "${name}" crashed: ${diagnostic.message}` : diagnostic.message,
|
|
782
836
|
...issues ? { issues } : {}
|
|
783
837
|
};
|
|
784
838
|
}
|
|
@@ -869,71 +923,33 @@ var Diagnostics = class Diagnostics {
|
|
|
869
923
|
* `/reference/diagnostics#<slug>` section.
|
|
870
924
|
*/
|
|
871
925
|
static explain(code) {
|
|
872
|
-
return
|
|
926
|
+
return docsByCode.get(code);
|
|
873
927
|
}
|
|
874
928
|
/**
|
|
875
929
|
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
876
|
-
* consumers
|
|
877
|
-
* fields are omitted
|
|
930
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
931
|
+
* `stack`. Absent optional fields are omitted.
|
|
878
932
|
*/
|
|
879
|
-
static serialize
|
|
880
|
-
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
881
|
-
return {
|
|
882
|
-
code: diagnostic.code,
|
|
883
|
-
severity: diagnostic.severity,
|
|
884
|
-
message: diagnostic.message,
|
|
885
|
-
...problem?.location ? { location: problem.location } : {},
|
|
886
|
-
...problem?.help ? { help: problem.help } : {},
|
|
887
|
-
...problem?.plugin ? { plugin: problem.plugin } : {},
|
|
888
|
-
...problem?.issues ? { issues: problem.issues } : {},
|
|
889
|
-
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Renders a {@link Diagnostic} for terminal output as its parts: the `headline`
|
|
894
|
-
* (`[CODE] plugin: message`, with the code in the severity color) and the indented `details`
|
|
895
|
-
* rows (`at:` pointer, `fix:` help, `see:` docs link, `report:` issue tracker).
|
|
896
|
-
*
|
|
897
|
-
* Hosts compose these to fit their gutter: a clack logger passes `[headline, ...details]` as the
|
|
898
|
-
* message with no gutter symbol, while plain text outputs use {@link Diagnostics.formatLines}.
|
|
899
|
-
*/
|
|
900
|
-
static format(diagnostic) {
|
|
901
|
-
const { code, severity, message } = diagnostic;
|
|
902
|
-
const color = severityStyle[severity];
|
|
903
|
-
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
904
|
-
const tag = (0, node_util.styleText)(color, (0, node_util.styleText)("bold", `[${code}]`));
|
|
905
|
-
const headline = problem?.plugin ? `${tag} ${problem.plugin}: ${message}` : `${tag}: ${message}`;
|
|
906
|
-
const details = [];
|
|
907
|
-
if (problem?.location && "pointer" in problem.location) details.push(` ${(0, node_util.styleText)("dim", "at:")} ${(0, node_util.styleText)("cyan", problem.location.pointer)}`);
|
|
908
|
-
if (problem?.help) details.push(` ${(0, node_util.styleText)("cyan", "fix:")} ${problem.help}`);
|
|
909
|
-
if (code !== diagnosticCode.unknown) details.push(` ${(0, node_util.styleText)("dim", "see:")} ${(0, node_util.styleText)("cyan", Diagnostics.docsUrl(code))}`);
|
|
910
|
-
if (problem?.issues) details.push(` ${(0, node_util.styleText)("dim", "report:")} ${(0, node_util.styleText)("cyan", problem.issues)}`);
|
|
911
|
-
return {
|
|
912
|
-
headline,
|
|
913
|
-
details
|
|
914
|
-
};
|
|
915
|
-
}
|
|
933
|
+
static serialize = serialize;
|
|
916
934
|
/**
|
|
917
|
-
*
|
|
918
|
-
* indented detail rows. Used where there is no gutter (plain and file output).
|
|
935
|
+
* Renders a diagnostic as lines for an `agent` (the default) or a `human`. See {@link format}.
|
|
919
936
|
*/
|
|
920
|
-
static
|
|
921
|
-
const { headline, details } = Diagnostics.format(diagnostic);
|
|
922
|
-
return [headline, ...details];
|
|
923
|
-
}
|
|
937
|
+
static format = format;
|
|
924
938
|
};
|
|
925
939
|
//#endregion
|
|
926
940
|
//#region src/defineExtension.ts
|
|
927
941
|
/**
|
|
928
942
|
* Returns `meta`, or `{ name }` for an extension that only sets the deprecated `name`. Throws `KUBB_INVALID_PLUGIN_OPTIONS` when it has neither.
|
|
929
943
|
*/
|
|
930
|
-
function resolveMeta(
|
|
944
|
+
function resolveMeta(extension) {
|
|
945
|
+
const { name, meta } = extension;
|
|
931
946
|
if (meta) return meta;
|
|
932
947
|
if (name) return { name };
|
|
948
|
+
const keys = Object.keys(extension);
|
|
933
949
|
throw new Diagnostics.Error({
|
|
934
950
|
code: Diagnostics.code.invalidPluginOptions,
|
|
935
951
|
severity: "error",
|
|
936
|
-
message:
|
|
952
|
+
message: `An extension has neither \`meta.name\` nor \`name\`. Received keys: ${keys.length ? keys.join(", ") : "none"}.`,
|
|
937
953
|
help: "Set `meta: { name }` on the plugin or parser.",
|
|
938
954
|
location: { kind: "config" }
|
|
939
955
|
});
|
|
@@ -1770,7 +1786,13 @@ async function parseCopy(file, parser) {
|
|
|
1770
1786
|
try {
|
|
1771
1787
|
content = await (0, node_fs_promises.readFile)(file.copy, "utf8");
|
|
1772
1788
|
} catch (err) {
|
|
1773
|
-
throw new Error(
|
|
1789
|
+
throw new Diagnostics.Error({
|
|
1790
|
+
code: Diagnostics.code.pluginFailed,
|
|
1791
|
+
severity: "error",
|
|
1792
|
+
message: `Could not copy file into output: ${file.copy}`,
|
|
1793
|
+
help: "Check that the file a plugin copies into the output exists and is readable.",
|
|
1794
|
+
cause: toError(err)
|
|
1795
|
+
});
|
|
1774
1796
|
}
|
|
1775
1797
|
if (parser?.copy) return runParser(parser, () => parser.parse(_kubb_ast.ast.factory.createFile(parser.copy(file, content))));
|
|
1776
1798
|
return [
|
|
@@ -2012,6 +2034,50 @@ function normalizePluginOptions(rawOptions, pluginName) {
|
|
|
2012
2034
|
});
|
|
2013
2035
|
return options;
|
|
2014
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Throws `KUBB_INVALID_PLUGIN_OPTIONS` when a `plugins` entry is not a plugin object, such as a
|
|
2039
|
+
* factory passed without calling it.
|
|
2040
|
+
*/
|
|
2041
|
+
function assertPluginEntry(entry, index) {
|
|
2042
|
+
if (typeof entry === "function") {
|
|
2043
|
+
const name = entry.name || "pluginName";
|
|
2044
|
+
throw new Diagnostics.Error({
|
|
2045
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2046
|
+
severity: "error",
|
|
2047
|
+
message: `plugins[${index}] is a function ("${name}"), not a plugin.`,
|
|
2048
|
+
help: `Call it: ${name}()`,
|
|
2049
|
+
location: { kind: "config" }
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
if (typeof entry !== "object" || entry === null) throw new Diagnostics.Error({
|
|
2053
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2054
|
+
severity: "error",
|
|
2055
|
+
message: `plugins[${index}] is ${entry === null ? "null" : typeof entry}, not a plugin.`,
|
|
2056
|
+
help: "Pass the result of a plugin factory, for example `pluginTs()`, or remove the entry.",
|
|
2057
|
+
location: { kind: "config" }
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Where a generator crashed: a label naming the node and, when the node has one, a JSON pointer
|
|
2062
|
+
* to it. Built from the node fields so core stays adapter-agnostic.
|
|
2063
|
+
*/
|
|
2064
|
+
function getCrashSite(node) {
|
|
2065
|
+
if (node.kind === "Schema") return node.name ? {
|
|
2066
|
+
label: `schema "${node.name}"`,
|
|
2067
|
+
location: {
|
|
2068
|
+
kind: "schema",
|
|
2069
|
+
pointer: `#/components/schemas/${escapePointerToken(node.name)}`
|
|
2070
|
+
}
|
|
2071
|
+
} : { label: "an inline schema" };
|
|
2072
|
+
if (!node.method || !node.path) return { label: `operation "${node.operationId}"` };
|
|
2073
|
+
return {
|
|
2074
|
+
label: `operation "${node.operationId}" (${node.method.toUpperCase()} ${node.path})`,
|
|
2075
|
+
location: {
|
|
2076
|
+
kind: "operation",
|
|
2077
|
+
pointer: `#/paths/${escapePointerToken(node.path)}/${node.method.toLowerCase()}`
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2015
2081
|
var KubbDriver = class {
|
|
2016
2082
|
config;
|
|
2017
2083
|
options;
|
|
@@ -2051,9 +2117,19 @@ var KubbDriver = class {
|
|
|
2051
2117
|
async setup() {
|
|
2052
2118
|
this.options.signal?.throwIfAborted();
|
|
2053
2119
|
for (const extension of [this.config.adapter, ...this.config.parsers]) if (extension?.meta) checkCompatibility(extension.meta);
|
|
2054
|
-
const
|
|
2120
|
+
const names = /* @__PURE__ */ new Set();
|
|
2121
|
+
const normalized = this.#sortPlugins(this.config.plugins.map((rawPlugin, index) => {
|
|
2122
|
+
assertPluginEntry(rawPlugin, index);
|
|
2055
2123
|
if (rawPlugin.meta) checkCompatibility(rawPlugin.meta);
|
|
2056
2124
|
const meta = resolveMeta(rawPlugin);
|
|
2125
|
+
if (names.has(meta.name)) throw new Diagnostics.Error({
|
|
2126
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2127
|
+
severity: "error",
|
|
2128
|
+
message: `Plugin "${meta.name}" appears twice in \`plugins\`.`,
|
|
2129
|
+
help: "Remove the duplicate, or merge its options.",
|
|
2130
|
+
location: { kind: "config" }
|
|
2131
|
+
});
|
|
2132
|
+
names.add(meta.name);
|
|
2057
2133
|
return {
|
|
2058
2134
|
name: meta.name,
|
|
2059
2135
|
meta,
|
|
@@ -2379,7 +2455,8 @@ var KubbDriver = class {
|
|
|
2379
2455
|
operationGenerators: generators.filter((generator) => generator.operation),
|
|
2380
2456
|
operationsGenerators: generators.filter((generator) => generator.operations),
|
|
2381
2457
|
pluginOperations: [],
|
|
2382
|
-
error: null
|
|
2458
|
+
error: null,
|
|
2459
|
+
crashSite: void 0
|
|
2383
2460
|
};
|
|
2384
2461
|
});
|
|
2385
2462
|
const resolveForPlugin = (state, node) => {
|
|
@@ -2423,6 +2500,7 @@ var KubbDriver = class {
|
|
|
2423
2500
|
await this.hooks.callHook("kubb:generate:schema", transformedNode, ctx);
|
|
2424
2501
|
} catch (caughtError) {
|
|
2425
2502
|
state.error = toError(caughtError);
|
|
2503
|
+
state.crashSite = getCrashSite(node);
|
|
2426
2504
|
}
|
|
2427
2505
|
}
|
|
2428
2506
|
}
|
|
@@ -2451,6 +2529,7 @@ var KubbDriver = class {
|
|
|
2451
2529
|
}
|
|
2452
2530
|
} catch (caughtError) {
|
|
2453
2531
|
state.error = toError(caughtError);
|
|
2532
|
+
state.crashSite = getCrashSite(node);
|
|
2454
2533
|
}
|
|
2455
2534
|
}
|
|
2456
2535
|
}
|
|
@@ -2479,10 +2558,18 @@ var KubbDriver = class {
|
|
|
2479
2558
|
success: !state.error,
|
|
2480
2559
|
error: state.error ?? void 0
|
|
2481
2560
|
});
|
|
2482
|
-
if (state.error)
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2561
|
+
if (state.error) {
|
|
2562
|
+
const diagnostic = Diagnostics.fromExtension(state.error, {
|
|
2563
|
+
kind: "plugin",
|
|
2564
|
+
extension: state.plugin
|
|
2565
|
+
});
|
|
2566
|
+
const site = diagnostic.code === Diagnostics.code.pluginFailed && !diagnostic.location ? state.crashSite : void 0;
|
|
2567
|
+
diagnostics.push(site ? {
|
|
2568
|
+
...diagnostic,
|
|
2569
|
+
message: `While generating ${site.label}: ${diagnostic.message}`,
|
|
2570
|
+
...site.location ? { location: site.location } : {}
|
|
2571
|
+
} : diagnostic);
|
|
2572
|
+
}
|
|
2486
2573
|
diagnostics.push(Diagnostics.performance({
|
|
2487
2574
|
plugin: state.plugin.name,
|
|
2488
2575
|
duration
|
|
@@ -2557,27 +2644,37 @@ var KubbDriver = class {
|
|
|
2557
2644
|
get resolver() {
|
|
2558
2645
|
return driver.getResolver(plugin.name);
|
|
2559
2646
|
},
|
|
2560
|
-
warn(message) {
|
|
2647
|
+
warn(message, options) {
|
|
2561
2648
|
report({
|
|
2562
2649
|
code: Diagnostics.code.pluginWarning,
|
|
2563
2650
|
severity: "warning",
|
|
2564
|
-
message
|
|
2651
|
+
message,
|
|
2652
|
+
...options
|
|
2565
2653
|
});
|
|
2566
2654
|
},
|
|
2567
|
-
error(error) {
|
|
2655
|
+
error(error, options) {
|
|
2656
|
+
if (Diagnostics.isError(error)) {
|
|
2657
|
+
report({
|
|
2658
|
+
...options,
|
|
2659
|
+
...error.diagnostic
|
|
2660
|
+
});
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2568
2663
|
const cause = typeof error === "string" ? void 0 : error;
|
|
2569
2664
|
report({
|
|
2570
2665
|
code: Diagnostics.code.pluginFailed,
|
|
2571
2666
|
severity: "error",
|
|
2572
2667
|
message: typeof error === "string" ? error : error.message,
|
|
2573
|
-
cause
|
|
2668
|
+
cause,
|
|
2669
|
+
...options
|
|
2574
2670
|
});
|
|
2575
2671
|
},
|
|
2576
|
-
info(message) {
|
|
2672
|
+
info(message, options) {
|
|
2577
2673
|
report({
|
|
2578
2674
|
code: Diagnostics.code.pluginInfo,
|
|
2579
2675
|
severity: "info",
|
|
2580
|
-
message
|
|
2676
|
+
message,
|
|
2677
|
+
...options
|
|
2581
2678
|
});
|
|
2582
2679
|
}
|
|
2583
2680
|
};
|
|
@@ -2944,4 +3041,4 @@ Object.defineProperty(exports, "write", {
|
|
|
2944
3041
|
}
|
|
2945
3042
|
});
|
|
2946
3043
|
|
|
2947
|
-
//# sourceMappingURL=Hookable-
|
|
3044
|
+
//# sourceMappingURL=Hookable-RTuEiBd9.cjs.map
|