@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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "./rolldown-runtime-CRm0XQPb.js";
|
|
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
8
|
//#region package.json
|
|
9
|
-
var version = "5.10.
|
|
9
|
+
var version = "5.10.2";
|
|
10
10
|
//#endregion
|
|
11
11
|
//#region ../../internals/utils/src/casing.ts
|
|
12
12
|
/**
|
|
@@ -62,6 +62,35 @@ function toError(value) {
|
|
|
62
62
|
function getErrorMessage(value) {
|
|
63
63
|
return value instanceof Error ? value.message : String(value);
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns `error` followed by every error in its `cause` chain, outermost first. Stops at a
|
|
67
|
+
* non-`Error` cause or a cycle.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* getErrorChain(new Error('Config failed loading', { cause: new Error('boom') })).map((error) => error.message)
|
|
72
|
+
* // ['Config failed loading', 'boom']
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
function getErrorChain(error) {
|
|
76
|
+
const chain = [];
|
|
77
|
+
for (let current = error; current instanceof Error && !chain.includes(current); current = current.cause) chain.push(current);
|
|
78
|
+
return chain;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region ../../internals/utils/src/jsonPointer.ts
|
|
82
|
+
/**
|
|
83
|
+
* Escapes a single JSON pointer reference token per RFC 6901 (`~` → `~0`, `/` → `~1`), so a
|
|
84
|
+
* name holding those characters maps to a distinct pointer.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* escapePointerToken('/pets/{id}') // '~1pets~1{id}'
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
function escapePointerToken(token) {
|
|
92
|
+
return token.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
93
|
+
}
|
|
65
94
|
//#endregion
|
|
66
95
|
//#region ../../internals/utils/src/runtime.ts
|
|
67
96
|
const isBun = typeof Bun !== "undefined";
|
|
@@ -262,375 +291,388 @@ function formatMs(ms) {
|
|
|
262
291
|
return `${Math.round(ms)}ms`;
|
|
263
292
|
}
|
|
264
293
|
//#endregion
|
|
265
|
-
//#region src/
|
|
294
|
+
//#region src/diagnosticCatalog.ts
|
|
266
295
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
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");
|
|
448
|
-
/**
|
|
449
|
-
* Accent color per severity. The color tints the `[CODE]` tag (red error, yellow warning,
|
|
450
|
-
* blue info).
|
|
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.
|
|
296
|
+
* Every diagnostic code with its docs entry: a short title, what triggers it, and how to resolve
|
|
297
|
+
* it. The source of truth for {@link diagnosticCode}, and the kubb.dev `/reference/diagnostics`
|
|
298
|
+
* sections mirror it.
|
|
470
299
|
*/
|
|
471
300
|
const diagnosticCatalog = {
|
|
472
|
-
|
|
301
|
+
unknown: {
|
|
302
|
+
code: "KUBB_UNKNOWN",
|
|
473
303
|
title: "Unknown error",
|
|
474
304
|
cause: "An error was thrown without a stable Kubb code, so it is reported as-is.",
|
|
475
305
|
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
306
|
},
|
|
477
|
-
|
|
307
|
+
inputNotFound: {
|
|
308
|
+
code: "KUBB_INPUT_NOT_FOUND",
|
|
478
309
|
title: "Input not found",
|
|
479
310
|
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
311
|
fix: "Check that the path exists and is readable, then set it as `input` or pass it on the CLI."
|
|
481
312
|
},
|
|
482
|
-
|
|
313
|
+
inputRequestFailed: {
|
|
314
|
+
code: "KUBB_INPUT_REQUEST_FAILED",
|
|
483
315
|
title: "Input request failed",
|
|
484
316
|
cause: "A URL set as `input` (or reached through a `$ref`) answered with a 4xx or 5xx status instead of the document.",
|
|
485
317
|
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
318
|
},
|
|
487
|
-
|
|
319
|
+
inputUnreachable: {
|
|
320
|
+
code: "KUBB_INPUT_UNREACHABLE",
|
|
488
321
|
title: "Input unreachable",
|
|
489
322
|
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
323
|
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
324
|
},
|
|
492
|
-
|
|
325
|
+
inputRequired: {
|
|
326
|
+
code: "KUBB_INPUT_REQUIRED",
|
|
493
327
|
title: "Input required",
|
|
494
328
|
cause: "An adapter is configured but no `input` was provided.",
|
|
495
329
|
fix: "Set `input` to a file path, a URL, an inline spec (JSON/YAML string), or a parsed object in your Kubb config."
|
|
496
330
|
},
|
|
497
|
-
|
|
331
|
+
legacyInput: {
|
|
332
|
+
code: "KUBB_LEGACY_INPUT",
|
|
498
333
|
title: "Legacy input shape",
|
|
499
334
|
cause: "`input` is a `{ path }` or `{ data }` wrapper, which v4 used to point at a document and v5 reads as the document itself.",
|
|
500
335
|
fix: "Unwrap it: `input: { path: \"./petStore.yaml\" }` becomes `input: \"./petStore.yaml\"`, and `input: { data: spec }` becomes `input: spec`."
|
|
501
336
|
},
|
|
502
|
-
|
|
337
|
+
invalidDocument: {
|
|
338
|
+
code: "KUBB_INVALID_DOCUMENT",
|
|
503
339
|
title: "Invalid document",
|
|
504
|
-
cause: "The
|
|
505
|
-
fix: "
|
|
340
|
+
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.",
|
|
341
|
+
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
342
|
},
|
|
507
|
-
|
|
343
|
+
invalidSpec: {
|
|
344
|
+
code: "KUBB_INVALID_SPEC",
|
|
508
345
|
title: "Invalid spec",
|
|
509
346
|
cause: "The document does not pass OpenAPI schema validation. Kubb still generates from it, but the output can be incomplete or wrong.",
|
|
510
347
|
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
348
|
},
|
|
512
|
-
|
|
349
|
+
refNotFound: {
|
|
350
|
+
code: "KUBB_REF_NOT_FOUND",
|
|
513
351
|
title: "Reference not found",
|
|
514
352
|
cause: "A `$ref` could not be resolved in the source document.",
|
|
515
353
|
fix: "Add the missing definition (for example under `components.schemas`) or fix the `$ref`. Run `kubb validate` to check the spec."
|
|
516
354
|
},
|
|
517
|
-
|
|
355
|
+
invalidServerVariable: {
|
|
356
|
+
code: "KUBB_INVALID_SERVER_VARIABLE",
|
|
518
357
|
title: "Invalid server variable",
|
|
519
358
|
cause: "A server variable value is not allowed by its `enum`.",
|
|
520
359
|
fix: "Use one of the values listed in the server variable `enum`, or update the spec."
|
|
521
360
|
},
|
|
522
|
-
|
|
361
|
+
pluginNotFound: {
|
|
362
|
+
code: "KUBB_PLUGIN_NOT_FOUND",
|
|
523
363
|
title: "Plugin not found",
|
|
524
364
|
cause: "A plugin that another plugin depends on is missing from the config.",
|
|
525
365
|
fix: "Add the required plugin to the `plugins` array in kubb.config.ts, or remove the dependency on it."
|
|
526
366
|
},
|
|
527
|
-
|
|
367
|
+
incompatibleExtension: {
|
|
368
|
+
code: "KUBB_INCOMPATIBLE_EXTENSION",
|
|
528
369
|
title: "Incompatible extension",
|
|
529
370
|
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
371
|
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
372
|
},
|
|
532
|
-
|
|
373
|
+
missingDependency: {
|
|
374
|
+
code: "KUBB_MISSING_DEPENDENCY",
|
|
533
375
|
title: "Missing dependency",
|
|
534
376
|
cause: "A plugin lists another plugin in its `dependencies` record without `optional: true`, and the config does not include it.",
|
|
535
377
|
fix: "Add the missing plugin to the `plugins` array in kubb.config.ts."
|
|
536
378
|
},
|
|
537
|
-
|
|
379
|
+
incompatibleDependency: {
|
|
380
|
+
code: "KUBB_INCOMPATIBLE_DEPENDENCY",
|
|
538
381
|
title: "Incompatible dependency",
|
|
539
382
|
cause: "A plugin's `dependencies` record sets a `version` range, and the configured plugin's `meta.version` falls outside it.",
|
|
540
383
|
fix: "Install a version of the dependency inside the range, or a version of the dependent plugin that supports the installed dependency."
|
|
541
384
|
},
|
|
542
|
-
|
|
385
|
+
pluginFailed: {
|
|
386
|
+
code: "KUBB_PLUGIN_FAILED",
|
|
543
387
|
title: "Plugin failed",
|
|
544
388
|
cause: "A plugin threw while generating, or reported an error through `ctx.error`.",
|
|
545
|
-
fix: "
|
|
389
|
+
fix: "Check the plugin options and the node at `at:`. If it is a bug, report it with the stack at `report:`."
|
|
546
390
|
},
|
|
547
|
-
|
|
391
|
+
adapterFailed: {
|
|
392
|
+
code: "KUBB_ADAPTER_FAILED",
|
|
548
393
|
title: "Adapter failed",
|
|
549
394
|
cause: "The adapter threw an unexpected error while parsing the input.",
|
|
550
|
-
fix: "
|
|
395
|
+
fix: "Check the input and the adapter options. If it is a bug, report it with the stack at `report:`."
|
|
551
396
|
},
|
|
552
|
-
|
|
397
|
+
parserFailed: {
|
|
398
|
+
code: "KUBB_PARSER_FAILED",
|
|
553
399
|
title: "Parser failed",
|
|
554
400
|
cause: "A parser threw an unexpected error while printing a generated file.",
|
|
555
|
-
fix: "
|
|
401
|
+
fix: "Check the parser options. If it is a bug, report it with the stack at `report:`."
|
|
556
402
|
},
|
|
557
|
-
|
|
403
|
+
pluginWarning: {
|
|
404
|
+
code: "KUBB_PLUGIN_WARNING",
|
|
558
405
|
title: "Plugin warning",
|
|
559
406
|
cause: "A plugin reported a non-fatal warning through `ctx.warn`.",
|
|
560
407
|
fix: "Review the message. It does not fail the build; adjust the plugin options or input if the warning is unwanted."
|
|
561
408
|
},
|
|
562
|
-
|
|
409
|
+
pluginInfo: {
|
|
410
|
+
code: "KUBB_PLUGIN_INFO",
|
|
563
411
|
title: "Plugin info",
|
|
564
412
|
cause: "A plugin reported an informational message through `ctx.info`.",
|
|
565
413
|
fix: "Informational only. No action is required."
|
|
566
414
|
},
|
|
567
|
-
|
|
415
|
+
noContext: {
|
|
416
|
+
code: "KUBB_NO_CONTEXT",
|
|
568
417
|
title: "No plugin context",
|
|
569
418
|
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
419
|
fix: "Call the composable inside `setup` or `kubb:plugin:setup`, including in code that setup awaits."
|
|
571
420
|
},
|
|
572
|
-
|
|
421
|
+
unsupportedFormat: {
|
|
422
|
+
code: "KUBB_UNSUPPORTED_FORMAT",
|
|
573
423
|
title: "Unsupported format",
|
|
574
424
|
cause: "A schema uses a `format` Kubb does not map to a specific type, so it falls back to the base type.",
|
|
575
425
|
fix: "Use a format Kubb supports, or handle the custom format with a parser or plugin."
|
|
576
426
|
},
|
|
577
|
-
|
|
427
|
+
deprecated: {
|
|
428
|
+
code: "KUBB_DEPRECATED",
|
|
578
429
|
title: "Deprecated",
|
|
579
430
|
cause: "A referenced schema or operation is marked `deprecated`.",
|
|
580
431
|
fix: "Migrate off the deprecated definition if the warning is unwanted."
|
|
581
432
|
},
|
|
582
|
-
|
|
433
|
+
adapterRequired: {
|
|
434
|
+
code: "KUBB_ADAPTER_REQUIRED",
|
|
583
435
|
title: "Adapter required",
|
|
584
436
|
cause: "An action needs an adapter but none is configured.",
|
|
585
437
|
fix: "Set `adapter` in kubb.config.ts, for example `adapterOas()`."
|
|
586
438
|
},
|
|
587
|
-
|
|
439
|
+
pathTraversal: {
|
|
440
|
+
code: "KUBB_PATH_TRAVERSAL",
|
|
588
441
|
title: "Path traversal",
|
|
589
442
|
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
443
|
fix: "Keep generated paths within the output directory. Review the `group.name` function and the names coming from the spec."
|
|
591
444
|
},
|
|
592
|
-
|
|
445
|
+
cleanRoot: {
|
|
446
|
+
code: "KUBB_CLEAN_ROOT",
|
|
593
447
|
title: "Clean targets the project root",
|
|
594
448
|
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
449
|
fix: "Point `output.path` at a subdirectory such as `./src/gen` so clean only removes generated code, or disable `output.clean`."
|
|
596
450
|
},
|
|
597
|
-
|
|
451
|
+
invalidPluginOptions: {
|
|
452
|
+
code: "KUBB_INVALID_PLUGIN_OPTIONS",
|
|
598
453
|
title: "Invalid plugin options",
|
|
599
454
|
cause: "A plugin was configured with options that cannot be honored, for example `output.mode: 'file'` paired with a `group` option.",
|
|
600
455
|
fix: "Fix the plugin options. A single-file output has nothing to group, so remove the `group` option or use `output.mode: 'directory'`."
|
|
601
456
|
},
|
|
602
|
-
|
|
457
|
+
postGenerateFailed: {
|
|
458
|
+
code: "KUBB_POST_GENERATE_FAILED",
|
|
603
459
|
title: "Post-generate command failed",
|
|
604
460
|
cause: "A post-generate command (`output.postGenerate`) exited with a non-zero status.",
|
|
605
461
|
fix: "Check the command is installed and correct, and run it manually to see the error."
|
|
606
462
|
},
|
|
607
|
-
|
|
463
|
+
formatFailed: {
|
|
464
|
+
code: "KUBB_FORMAT_FAILED",
|
|
608
465
|
title: "Format failed",
|
|
609
466
|
cause: "The formatter pass over the generated files failed.",
|
|
610
467
|
fix: "Check the formatter (oxfmt, biome, or prettier) is installed and its config is valid, then run it manually on the output."
|
|
611
468
|
},
|
|
612
|
-
|
|
469
|
+
lintFailed: {
|
|
470
|
+
code: "KUBB_LINT_FAILED",
|
|
613
471
|
title: "Lint failed",
|
|
614
472
|
cause: "The linter pass over the generated files failed.",
|
|
615
473
|
fix: "Check the linter (oxlint, biome, or eslint) is installed and its config is valid, then run it manually on the output."
|
|
616
474
|
},
|
|
617
|
-
|
|
475
|
+
barrelDuplicateExport: {
|
|
476
|
+
code: "KUBB_BARREL_DUPLICATE_EXPORT",
|
|
618
477
|
title: "Duplicate barrel export",
|
|
619
478
|
cause: "Two generated modules export the same name, so the barrel cannot re-export both.",
|
|
620
479
|
fix: "Rename one of the colliding declarations, or configure the plugin resolver to produce distinct names."
|
|
621
480
|
},
|
|
622
|
-
|
|
481
|
+
internalError: {
|
|
482
|
+
code: "KUBB_INTERNAL_ERROR",
|
|
483
|
+
title: "Internal error",
|
|
484
|
+
cause: "Kubb hit a state it does not expect. This is a bug in Kubb, not in your config.",
|
|
485
|
+
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."
|
|
486
|
+
},
|
|
487
|
+
performance: {
|
|
488
|
+
code: "KUBB_PERFORMANCE",
|
|
623
489
|
title: "Performance",
|
|
624
490
|
cause: "Not a failure. Records a plugin’s elapsed time, summed into the run total.",
|
|
625
491
|
fix: "No action. This is an informational metric."
|
|
626
492
|
},
|
|
627
|
-
|
|
493
|
+
updateAvailable: {
|
|
494
|
+
code: "KUBB_UPDATE_AVAILABLE",
|
|
628
495
|
title: "Update available",
|
|
629
496
|
cause: "A newer Kubb version is published on npm than the one running.",
|
|
630
497
|
fix: "Update the `@kubb/*` packages, for example `npm install -g @kubb/cli`, to get the latest fixes."
|
|
631
498
|
}
|
|
632
499
|
};
|
|
633
500
|
/**
|
|
501
|
+
* Stable codes Kubb attaches to a `Diagnostic`, derived from {@link diagnosticCatalog}. Reference
|
|
502
|
+
* these instead of inlining the string at a throw site.
|
|
503
|
+
*/
|
|
504
|
+
const diagnosticCode = Object.fromEntries(Object.entries(diagnosticCatalog).map(([key, { code }]) => [key, code]));
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/formatDiagnostic.ts
|
|
507
|
+
/** A problem's `help`, or the catalog fix. `info` gets none: its catalog fix says no action is needed. */
|
|
508
|
+
function getHelp(problem) {
|
|
509
|
+
if (problem.help || problem.severity === "info") return problem.help;
|
|
510
|
+
return Diagnostics.explain(problem.code).fix;
|
|
511
|
+
}
|
|
512
|
+
/** The `cause` chain's messages, outermost first, minus those `message` already contains. */
|
|
513
|
+
function getCauses(problem) {
|
|
514
|
+
return getErrorChain(problem.cause).filter((error) => !problem.message.includes(error.message)).map((error) => error.name === "Error" ? error.message : `${error.name}: ${error.message}`);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Accent color per severity, which paints the icon in the `human` layout (red error, yellow
|
|
518
|
+
* warning, blue info). The code itself stays dim.
|
|
519
|
+
*/
|
|
520
|
+
const severityStyle = {
|
|
521
|
+
error: "red",
|
|
522
|
+
warning: "yellow",
|
|
523
|
+
info: "blue"
|
|
524
|
+
};
|
|
525
|
+
const severityIcon = {
|
|
526
|
+
error: "✖",
|
|
527
|
+
warning: "⚠",
|
|
528
|
+
info: "ℹ"
|
|
529
|
+
};
|
|
530
|
+
/**
|
|
531
|
+
* Codes for a crash, which carry the root cause's stack.
|
|
532
|
+
*/
|
|
533
|
+
const crashCodes = /* @__PURE__ */ new Set([
|
|
534
|
+
diagnosticCode.unknown,
|
|
535
|
+
diagnosticCode.internalError,
|
|
536
|
+
diagnosticCode.pluginFailed,
|
|
537
|
+
diagnosticCode.adapterFailed,
|
|
538
|
+
diagnosticCode.parserFailed
|
|
539
|
+
]);
|
|
540
|
+
/**
|
|
541
|
+
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
542
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
543
|
+
* `stack`. Absent optional fields are omitted.
|
|
544
|
+
*/
|
|
545
|
+
function serialize(diagnostic) {
|
|
546
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
547
|
+
const doc = problem ? Diagnostics.explain(problem.code) : void 0;
|
|
548
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
549
|
+
const causes = problem ? getCauses(problem) : [];
|
|
550
|
+
const stack = problem && crashCodes.has(problem.code) ? getErrorChain(problem.cause).at(-1)?.stack : void 0;
|
|
551
|
+
return {
|
|
552
|
+
code: diagnostic.code,
|
|
553
|
+
severity: diagnostic.severity,
|
|
554
|
+
message: diagnostic.message,
|
|
555
|
+
...doc ? {
|
|
556
|
+
title: doc.title,
|
|
557
|
+
cause: doc.cause
|
|
558
|
+
} : {},
|
|
559
|
+
...problem?.location ? { location: problem.location } : {},
|
|
560
|
+
...help ? { help } : {},
|
|
561
|
+
...problem?.plugin ? { plugin: problem.plugin } : {},
|
|
562
|
+
...problem?.issues ? { issues: problem.issues } : {},
|
|
563
|
+
...causes.length ? { causes } : {},
|
|
564
|
+
...stack ? { stack } : {},
|
|
565
|
+
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Renders a {@link Diagnostic} as lines: a headline, then indented detail rows. `agent` (the default)
|
|
570
|
+
* prints uncolored labeled rows (`at:`, `why:`, `fix:`, `caused by:`, `stack:` for a crash,
|
|
571
|
+
* `docs:`, `report:`). `human` prints an icon, the bold message, and the code aligned to `width`,
|
|
572
|
+
* with `at`, `fix`, the first cause, and `Learn more` for an error, and one row for a warning.
|
|
573
|
+
* `verbose` adds the full stack.
|
|
574
|
+
*/
|
|
575
|
+
function format(diagnostic, { audience = "agent", verbose = false, width = 0 } = {}) {
|
|
576
|
+
const { code, severity, message } = diagnostic;
|
|
577
|
+
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
578
|
+
const human = audience === "human";
|
|
579
|
+
const paint = human ? styleText : (_format, text) => text;
|
|
580
|
+
const [firstLine = "", ...rest] = message.split("\n");
|
|
581
|
+
const pointer = problem?.location && "pointer" in problem.location ? problem.location.pointer : void 0;
|
|
582
|
+
const help = problem ? getHelp(problem) : void 0;
|
|
583
|
+
const causes = problem ? getCauses(problem) : [];
|
|
584
|
+
const crash = problem ? crashCodes.has(problem.code) : false;
|
|
585
|
+
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())}`);
|
|
586
|
+
if (!human) {
|
|
587
|
+
const details = rest.filter((line) => line.trim()).map((line) => ` ${line}`);
|
|
588
|
+
const row = (label, value) => {
|
|
589
|
+
if (value) details.push(` ${label}: ${value}`);
|
|
590
|
+
};
|
|
591
|
+
row("at", pointer);
|
|
592
|
+
if (problem && severity !== "info") row("why", Diagnostics.explain(problem.code).cause);
|
|
593
|
+
row("fix", help);
|
|
594
|
+
for (const cause of causes) row("caused by", cause);
|
|
595
|
+
if (frames?.length) details.push(" stack:", ...frames);
|
|
596
|
+
if (code !== diagnosticCode.unknown) row("docs", Diagnostics.docsUrl(code));
|
|
597
|
+
row("report", problem?.issues);
|
|
598
|
+
return [`${severity} ${code}${problem?.plugin ? ` [${problem.plugin}]` : ""}: ${firstLine}`, ...details];
|
|
599
|
+
}
|
|
600
|
+
const left = `${paint(severityStyle[severity], severityIcon[severity])} ${paint("bold", firstLine)}`;
|
|
601
|
+
const gap = Math.max(2, width - stripVTControlCharacters(left).length - code.length);
|
|
602
|
+
const headline = `${left}${" ".repeat(gap)}${paint("dim", code)}`;
|
|
603
|
+
const frame = rest.filter((line) => line.trim()).map((line) => ` ${paint("dim", line)}`);
|
|
604
|
+
const label = (name) => paint("dim", name.length < 4 ? name.padEnd(4) : `${name} `);
|
|
605
|
+
const rows = [["at", pointer && paint("cyan", pointer)], ["fix", help]];
|
|
606
|
+
if (severity !== "error") {
|
|
607
|
+
const first = rows.find(([, value]) => value);
|
|
608
|
+
return [
|
|
609
|
+
headline,
|
|
610
|
+
...frame,
|
|
611
|
+
...first ? [` ${label(first[0])} ${first[1]}`] : []
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
rows.push(["caused by", causes[0]], ["report", problem?.issues && paint("cyan", problem.issues)]);
|
|
615
|
+
const details = [...frame, ...rows.flatMap(([name, value]) => value ? [` ${label(name)} ${value}`] : [])];
|
|
616
|
+
if (frames?.length) details.push(` ${paint("dim", "stack")}`, ...frames);
|
|
617
|
+
if (code !== diagnosticCode.unknown) details.push(` ${paint("dim", "↳ Learn more")} ${paint("cyan", Diagnostics.docsUrl(code).replace(/^https:\/\//, ""))}`);
|
|
618
|
+
return [headline, ...details];
|
|
619
|
+
}
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/Diagnostics.ts
|
|
622
|
+
/**
|
|
623
|
+
* Docs major version, derived from the package version so the link tracks the published major.
|
|
624
|
+
*/
|
|
625
|
+
const docsMajor = version.split(".")[0] ?? "5";
|
|
626
|
+
/**
|
|
627
|
+
* Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
|
|
628
|
+
* with no `kind` is treated as a `problem`.
|
|
629
|
+
*/
|
|
630
|
+
function isKind(kind) {
|
|
631
|
+
return (diagnostic) => (diagnostic.kind ?? "problem") === kind;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Returns `true` when the diagnostic is a build {@link ProblemDiagnostic}.
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* ```ts
|
|
638
|
+
* if (isProblem(diagnostic)) {
|
|
639
|
+
* console.log(diagnostic.location)
|
|
640
|
+
* }
|
|
641
|
+
* ```
|
|
642
|
+
*/
|
|
643
|
+
const isProblem = isKind("problem");
|
|
644
|
+
/**
|
|
645
|
+
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```ts
|
|
649
|
+
* const timings = diagnostics.filter(isPerformance)
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
const isPerformance = isKind("performance");
|
|
653
|
+
/**
|
|
654
|
+
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```ts
|
|
658
|
+
* if (isUpdate(diagnostic)) {
|
|
659
|
+
* console.log(diagnostic.latestVersion)
|
|
660
|
+
* }
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
663
|
+
const isUpdate = isKind("update");
|
|
664
|
+
const failedCode = {
|
|
665
|
+
plugin: diagnosticCode.pluginFailed,
|
|
666
|
+
adapter: diagnosticCode.adapterFailed,
|
|
667
|
+
parser: diagnosticCode.parserFailed
|
|
668
|
+
};
|
|
669
|
+
const docsByCode = new Map(Object.values(diagnosticCatalog).map((doc) => [doc.code, doc]));
|
|
670
|
+
const countKey = {
|
|
671
|
+
error: "errors",
|
|
672
|
+
warning: "warnings",
|
|
673
|
+
info: "infos"
|
|
674
|
+
};
|
|
675
|
+
/**
|
|
634
676
|
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
635
677
|
* that lets deep code report a diagnostic without threading a callback.
|
|
636
678
|
*
|
|
@@ -705,27 +747,37 @@ var Diagnostics = class Diagnostics {
|
|
|
705
747
|
return true;
|
|
706
748
|
}
|
|
707
749
|
/**
|
|
708
|
-
* Emits a diagnostic on the run's
|
|
709
|
-
*
|
|
710
|
-
*
|
|
750
|
+
* Emits a diagnostic, or any thrown value, on the run's hooks so the loggers render it live.
|
|
751
|
+
* A coded problem or an update goes to `kubb:diagnostic`. An uncoded one goes to `kubb:error`
|
|
752
|
+
* whole, so every wrapper's message, its help, and the stack survive. To collect a diagnostic into
|
|
753
|
+
* the build result from deep in a run, use {@link Diagnostics.report} instead.
|
|
711
754
|
*/
|
|
712
|
-
static async emit(hooks,
|
|
713
|
-
|
|
755
|
+
static async emit(hooks, value) {
|
|
756
|
+
const thrown = value instanceof Error || typeof value?.code !== "string";
|
|
757
|
+
const diagnostic = thrown ? Diagnostics.from(value) : value;
|
|
758
|
+
if (!isProblem(diagnostic) || diagnostic.code !== diagnosticCode.unknown) {
|
|
759
|
+
await hooks.callHook("kubb:diagnostic", { diagnostic });
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const { cause, help, message } = diagnostic;
|
|
763
|
+
const bare = cause && !help && cause.message === message;
|
|
764
|
+
await hooks.callHook("kubb:error", { error: thrown ? toError(value) : bare ? cause : new Diagnostics.Error(diagnostic) });
|
|
714
765
|
}
|
|
715
766
|
/**
|
|
716
767
|
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link Diagnostics.Error}
|
|
717
768
|
* keeps its structured data, and anything else becomes a `KUBB_UNKNOWN` error.
|
|
718
769
|
*/
|
|
719
770
|
static from(error) {
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
771
|
+
const chain = getErrorChain(error);
|
|
772
|
+
const coded = chain.findLast((current) => Diagnostics.isError(current));
|
|
773
|
+
if (coded) {
|
|
774
|
+
const { diagnostic } = coded;
|
|
775
|
+
return diagnostic.cause || !crashCodes.has(diagnostic.code) ? diagnostic : {
|
|
776
|
+
...diagnostic,
|
|
777
|
+
cause: coded
|
|
778
|
+
};
|
|
728
779
|
}
|
|
780
|
+
const root = chain.at(-1);
|
|
729
781
|
return {
|
|
730
782
|
code: diagnosticCode.unknown,
|
|
731
783
|
severity: "error",
|
|
@@ -743,7 +795,9 @@ var Diagnostics = class Diagnostics {
|
|
|
743
795
|
const diagnostic = Diagnostics.from(error);
|
|
744
796
|
const name = extension.meta?.name ?? extension.name;
|
|
745
797
|
const owner = kind === "plugin" ? { plugin: diagnostic.plugin ?? name } : {};
|
|
746
|
-
|
|
798
|
+
const crashed = diagnostic.code === diagnosticCode.unknown && diagnostic.cause?.name !== "AbortError";
|
|
799
|
+
const failed = Object.values(failedCode).includes(diagnostic.code);
|
|
800
|
+
if (!crashed && !failed) return {
|
|
747
801
|
...diagnostic,
|
|
748
802
|
...owner
|
|
749
803
|
};
|
|
@@ -751,8 +805,8 @@ var Diagnostics = class Diagnostics {
|
|
|
751
805
|
return {
|
|
752
806
|
...diagnostic,
|
|
753
807
|
...owner,
|
|
754
|
-
code: failedCode[kind],
|
|
755
|
-
message: kind
|
|
808
|
+
code: crashed ? failedCode[kind] : diagnostic.code,
|
|
809
|
+
message: crashed && kind !== "plugin" ? `${kind} "${name}" crashed: ${diagnostic.message}` : diagnostic.message,
|
|
756
810
|
...issues ? { issues } : {}
|
|
757
811
|
};
|
|
758
812
|
}
|
|
@@ -843,71 +897,33 @@ var Diagnostics = class Diagnostics {
|
|
|
843
897
|
* `/reference/diagnostics#<slug>` section.
|
|
844
898
|
*/
|
|
845
899
|
static explain(code) {
|
|
846
|
-
return
|
|
900
|
+
return docsByCode.get(code);
|
|
847
901
|
}
|
|
848
902
|
/**
|
|
849
903
|
* Reduces a diagnostic to its JSON-safe fields plus a `docsUrl`, for machine-readable
|
|
850
|
-
* consumers
|
|
851
|
-
* fields are omitted
|
|
904
|
+
* consumers, with the catalog `title`, `cause`, and fix, and the `cause` error as `causes` and
|
|
905
|
+
* `stack`. Absent optional fields are omitted.
|
|
852
906
|
*/
|
|
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
|
-
}
|
|
907
|
+
static serialize = serialize;
|
|
890
908
|
/**
|
|
891
|
-
*
|
|
892
|
-
* indented detail rows. Used where there is no gutter (plain and file output).
|
|
909
|
+
* Renders a diagnostic as lines for an `agent` (the default) or a `human`. See {@link format}.
|
|
893
910
|
*/
|
|
894
|
-
static
|
|
895
|
-
const { headline, details } = Diagnostics.format(diagnostic);
|
|
896
|
-
return [headline, ...details];
|
|
897
|
-
}
|
|
911
|
+
static format = format;
|
|
898
912
|
};
|
|
899
913
|
//#endregion
|
|
900
914
|
//#region src/defineExtension.ts
|
|
901
915
|
/**
|
|
902
916
|
* Returns `meta`, or `{ name }` for an extension that only sets the deprecated `name`. Throws `KUBB_INVALID_PLUGIN_OPTIONS` when it has neither.
|
|
903
917
|
*/
|
|
904
|
-
function resolveMeta(
|
|
918
|
+
function resolveMeta(extension) {
|
|
919
|
+
const { name, meta } = extension;
|
|
905
920
|
if (meta) return meta;
|
|
906
921
|
if (name) return { name };
|
|
922
|
+
const keys = Object.keys(extension);
|
|
907
923
|
throw new Diagnostics.Error({
|
|
908
924
|
code: Diagnostics.code.invalidPluginOptions,
|
|
909
925
|
severity: "error",
|
|
910
|
-
message:
|
|
926
|
+
message: `An extension has neither \`meta.name\` nor \`name\`. Received keys: ${keys.length ? keys.join(", ") : "none"}.`,
|
|
911
927
|
help: "Set `meta: { name }` on the plugin or parser.",
|
|
912
928
|
location: { kind: "config" }
|
|
913
929
|
});
|
|
@@ -1744,7 +1760,13 @@ async function parseCopy(file, parser) {
|
|
|
1744
1760
|
try {
|
|
1745
1761
|
content = await readFile(file.copy, "utf8");
|
|
1746
1762
|
} catch (err) {
|
|
1747
|
-
throw new Error(
|
|
1763
|
+
throw new Diagnostics.Error({
|
|
1764
|
+
code: Diagnostics.code.pluginFailed,
|
|
1765
|
+
severity: "error",
|
|
1766
|
+
message: `Could not copy file into output: ${file.copy}`,
|
|
1767
|
+
help: "Check that the file a plugin copies into the output exists and is readable.",
|
|
1768
|
+
cause: toError(err)
|
|
1769
|
+
});
|
|
1748
1770
|
}
|
|
1749
1771
|
if (parser?.copy) return runParser(parser, () => parser.parse(ast.factory.createFile(parser.copy(file, content))));
|
|
1750
1772
|
return [
|
|
@@ -1986,6 +2008,50 @@ function normalizePluginOptions(rawOptions, pluginName) {
|
|
|
1986
2008
|
});
|
|
1987
2009
|
return options;
|
|
1988
2010
|
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Throws `KUBB_INVALID_PLUGIN_OPTIONS` when a `plugins` entry is not a plugin object, such as a
|
|
2013
|
+
* factory passed without calling it.
|
|
2014
|
+
*/
|
|
2015
|
+
function assertPluginEntry(entry, index) {
|
|
2016
|
+
if (typeof entry === "function") {
|
|
2017
|
+
const name = entry.name || "pluginName";
|
|
2018
|
+
throw new Diagnostics.Error({
|
|
2019
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2020
|
+
severity: "error",
|
|
2021
|
+
message: `plugins[${index}] is a function ("${name}"), not a plugin.`,
|
|
2022
|
+
help: `Call it: ${name}()`,
|
|
2023
|
+
location: { kind: "config" }
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
if (typeof entry !== "object" || entry === null) throw new Diagnostics.Error({
|
|
2027
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2028
|
+
severity: "error",
|
|
2029
|
+
message: `plugins[${index}] is ${entry === null ? "null" : typeof entry}, not a plugin.`,
|
|
2030
|
+
help: "Pass the result of a plugin factory, for example `pluginTs()`, or remove the entry.",
|
|
2031
|
+
location: { kind: "config" }
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Where a generator crashed: a label naming the node and, when the node has one, a JSON pointer
|
|
2036
|
+
* to it. Built from the node fields so core stays adapter-agnostic.
|
|
2037
|
+
*/
|
|
2038
|
+
function getCrashSite(node) {
|
|
2039
|
+
if (node.kind === "Schema") return node.name ? {
|
|
2040
|
+
label: `schema "${node.name}"`,
|
|
2041
|
+
location: {
|
|
2042
|
+
kind: "schema",
|
|
2043
|
+
pointer: `#/components/schemas/${escapePointerToken(node.name)}`
|
|
2044
|
+
}
|
|
2045
|
+
} : { label: "an inline schema" };
|
|
2046
|
+
if (!node.method || !node.path) return { label: `operation "${node.operationId}"` };
|
|
2047
|
+
return {
|
|
2048
|
+
label: `operation "${node.operationId}" (${node.method.toUpperCase()} ${node.path})`,
|
|
2049
|
+
location: {
|
|
2050
|
+
kind: "operation",
|
|
2051
|
+
pointer: `#/paths/${escapePointerToken(node.path)}/${node.method.toLowerCase()}`
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
1989
2055
|
var KubbDriver = class {
|
|
1990
2056
|
config;
|
|
1991
2057
|
options;
|
|
@@ -2025,9 +2091,19 @@ var KubbDriver = class {
|
|
|
2025
2091
|
async setup() {
|
|
2026
2092
|
this.options.signal?.throwIfAborted();
|
|
2027
2093
|
for (const extension of [this.config.adapter, ...this.config.parsers]) if (extension?.meta) checkCompatibility(extension.meta);
|
|
2028
|
-
const
|
|
2094
|
+
const names = /* @__PURE__ */ new Set();
|
|
2095
|
+
const normalized = this.#sortPlugins(this.config.plugins.map((rawPlugin, index) => {
|
|
2096
|
+
assertPluginEntry(rawPlugin, index);
|
|
2029
2097
|
if (rawPlugin.meta) checkCompatibility(rawPlugin.meta);
|
|
2030
2098
|
const meta = resolveMeta(rawPlugin);
|
|
2099
|
+
if (names.has(meta.name)) throw new Diagnostics.Error({
|
|
2100
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
2101
|
+
severity: "error",
|
|
2102
|
+
message: `Plugin "${meta.name}" appears twice in \`plugins\`.`,
|
|
2103
|
+
help: "Remove the duplicate, or merge its options.",
|
|
2104
|
+
location: { kind: "config" }
|
|
2105
|
+
});
|
|
2106
|
+
names.add(meta.name);
|
|
2031
2107
|
return {
|
|
2032
2108
|
name: meta.name,
|
|
2033
2109
|
meta,
|
|
@@ -2353,7 +2429,8 @@ var KubbDriver = class {
|
|
|
2353
2429
|
operationGenerators: generators.filter((generator) => generator.operation),
|
|
2354
2430
|
operationsGenerators: generators.filter((generator) => generator.operations),
|
|
2355
2431
|
pluginOperations: [],
|
|
2356
|
-
error: null
|
|
2432
|
+
error: null,
|
|
2433
|
+
crashSite: void 0
|
|
2357
2434
|
};
|
|
2358
2435
|
});
|
|
2359
2436
|
const resolveForPlugin = (state, node) => {
|
|
@@ -2397,6 +2474,7 @@ var KubbDriver = class {
|
|
|
2397
2474
|
await this.hooks.callHook("kubb:generate:schema", transformedNode, ctx);
|
|
2398
2475
|
} catch (caughtError) {
|
|
2399
2476
|
state.error = toError(caughtError);
|
|
2477
|
+
state.crashSite = getCrashSite(node);
|
|
2400
2478
|
}
|
|
2401
2479
|
}
|
|
2402
2480
|
}
|
|
@@ -2425,6 +2503,7 @@ var KubbDriver = class {
|
|
|
2425
2503
|
}
|
|
2426
2504
|
} catch (caughtError) {
|
|
2427
2505
|
state.error = toError(caughtError);
|
|
2506
|
+
state.crashSite = getCrashSite(node);
|
|
2428
2507
|
}
|
|
2429
2508
|
}
|
|
2430
2509
|
}
|
|
@@ -2453,10 +2532,18 @@ var KubbDriver = class {
|
|
|
2453
2532
|
success: !state.error,
|
|
2454
2533
|
error: state.error ?? void 0
|
|
2455
2534
|
});
|
|
2456
|
-
if (state.error)
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2535
|
+
if (state.error) {
|
|
2536
|
+
const diagnostic = Diagnostics.fromExtension(state.error, {
|
|
2537
|
+
kind: "plugin",
|
|
2538
|
+
extension: state.plugin
|
|
2539
|
+
});
|
|
2540
|
+
const site = diagnostic.code === Diagnostics.code.pluginFailed && !diagnostic.location ? state.crashSite : void 0;
|
|
2541
|
+
diagnostics.push(site ? {
|
|
2542
|
+
...diagnostic,
|
|
2543
|
+
message: `While generating ${site.label}: ${diagnostic.message}`,
|
|
2544
|
+
...site.location ? { location: site.location } : {}
|
|
2545
|
+
} : diagnostic);
|
|
2546
|
+
}
|
|
2460
2547
|
diagnostics.push(Diagnostics.performance({
|
|
2461
2548
|
plugin: state.plugin.name,
|
|
2462
2549
|
duration
|
|
@@ -2531,27 +2618,37 @@ var KubbDriver = class {
|
|
|
2531
2618
|
get resolver() {
|
|
2532
2619
|
return driver.getResolver(plugin.name);
|
|
2533
2620
|
},
|
|
2534
|
-
warn(message) {
|
|
2621
|
+
warn(message, options) {
|
|
2535
2622
|
report({
|
|
2536
2623
|
code: Diagnostics.code.pluginWarning,
|
|
2537
2624
|
severity: "warning",
|
|
2538
|
-
message
|
|
2625
|
+
message,
|
|
2626
|
+
...options
|
|
2539
2627
|
});
|
|
2540
2628
|
},
|
|
2541
|
-
error(error) {
|
|
2629
|
+
error(error, options) {
|
|
2630
|
+
if (Diagnostics.isError(error)) {
|
|
2631
|
+
report({
|
|
2632
|
+
...options,
|
|
2633
|
+
...error.diagnostic
|
|
2634
|
+
});
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2542
2637
|
const cause = typeof error === "string" ? void 0 : error;
|
|
2543
2638
|
report({
|
|
2544
2639
|
code: Diagnostics.code.pluginFailed,
|
|
2545
2640
|
severity: "error",
|
|
2546
2641
|
message: typeof error === "string" ? error : error.message,
|
|
2547
|
-
cause
|
|
2642
|
+
cause,
|
|
2643
|
+
...options
|
|
2548
2644
|
});
|
|
2549
2645
|
},
|
|
2550
|
-
info(message) {
|
|
2646
|
+
info(message, options) {
|
|
2551
2647
|
report({
|
|
2552
2648
|
code: Diagnostics.code.pluginInfo,
|
|
2553
2649
|
severity: "info",
|
|
2554
|
-
message
|
|
2650
|
+
message,
|
|
2651
|
+
...options
|
|
2555
2652
|
});
|
|
2556
2653
|
}
|
|
2557
2654
|
};
|
|
@@ -2691,4 +2788,4 @@ var Hookable = class {
|
|
|
2691
2788
|
//#endregion
|
|
2692
2789
|
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 };
|
|
2693
2790
|
|
|
2694
|
-
//# sourceMappingURL=Hookable-
|
|
2791
|
+
//# sourceMappingURL=Hookable-Bjfoua7Y.js.map
|