@kubb/core 5.11.4 → 5.11.6
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-C2bwJUE5.js → Hookable-CvStaYYq.js} +95 -73
- package/dist/Hookable-CvStaYYq.js.map +1 -0
- package/dist/{createAdapter-D6osJ_0O.d.ts → createAdapter-DtX_MKL-.d.ts} +77 -14
- package/dist/index.cjs +148 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -16
- package/dist/index.d.ts +3 -5
- package/dist/index.js +56 -38
- package/dist/index.js.map +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/package.json +2 -2
- package/dist/Hookable-C2bwJUE5.js.map +0 -1
|
@@ -320,6 +320,16 @@ type DiagnosticFormatOptions = {
|
|
|
320
320
|
* @default 0
|
|
321
321
|
*/
|
|
322
322
|
width?: number;
|
|
323
|
+
/**
|
|
324
|
+
* Where the diagnostic's pointer sits in the input file, when the host resolved it. Replaces the
|
|
325
|
+
* pointer on the `at` row, and an error's `human` layout draws `frame` under it.
|
|
326
|
+
*/
|
|
327
|
+
position?: {
|
|
328
|
+
file: string;
|
|
329
|
+
line: number;
|
|
330
|
+
column: number;
|
|
331
|
+
frame?: Array<string>;
|
|
332
|
+
};
|
|
323
333
|
};
|
|
324
334
|
/**
|
|
325
335
|
* A {@link Diagnostic} reduced to its JSON-safe fields plus a `docsUrl`, for
|
|
@@ -371,7 +381,7 @@ declare function serialize(diagnostic: Diagnostic): SerializedDiagnostic;
|
|
|
371
381
|
* with `at`, `fix`, the first cause, and `Learn more` for an error, and one row for a warning.
|
|
372
382
|
* `verbose` adds the full stack.
|
|
373
383
|
*/
|
|
374
|
-
declare function format(diagnostic: Diagnostic, { audience, verbose, width }?: DiagnosticFormatOptions): Array<string>;
|
|
384
|
+
declare function format(diagnostic: Diagnostic, { audience, verbose, width, position }?: DiagnosticFormatOptions): Array<string>;
|
|
375
385
|
//#endregion
|
|
376
386
|
//#region src/createReporter.d.ts
|
|
377
387
|
/**
|
|
@@ -414,9 +424,28 @@ type GenerationResult = {
|
|
|
414
424
|
*/
|
|
415
425
|
diagnostics: Array<Diagnostic>;
|
|
416
426
|
/**
|
|
417
|
-
* Number of files
|
|
427
|
+
* Number of generated files for this config, including unchanged files.
|
|
418
428
|
*/
|
|
419
429
|
filesCreated: number;
|
|
430
|
+
/**
|
|
431
|
+
* Number of generated files whose content matches the previous output.
|
|
432
|
+
*/
|
|
433
|
+
filesUnchanged?: number;
|
|
434
|
+
/**
|
|
435
|
+
* Changes compared with the output before this run, when the host collected them.
|
|
436
|
+
* Line totals include added and deleted files.
|
|
437
|
+
*/
|
|
438
|
+
fileChanges?: {
|
|
439
|
+
added: number;
|
|
440
|
+
modified: number;
|
|
441
|
+
deleted: number;
|
|
442
|
+
linesAdded: number;
|
|
443
|
+
linesRemoved: number;
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* The result previews generation without writing output.
|
|
447
|
+
*/
|
|
448
|
+
dryRun?: boolean;
|
|
420
449
|
status: 'success' | 'failed';
|
|
421
450
|
/**
|
|
422
451
|
* `process.hrtime()` snapshot taken when this config started generating.
|
|
@@ -453,10 +482,10 @@ type Reporter = {
|
|
|
453
482
|
*/
|
|
454
483
|
report: (result: GenerationResult, context: ReporterContext) => void | Promise<void>;
|
|
455
484
|
/**
|
|
456
|
-
*
|
|
457
|
-
*
|
|
485
|
+
* Runs once after the run's last config, on `kubb:lifecycle:end`. Only a reporter that collects
|
|
486
|
+
* its configs' reports into one document, such as `json`, has one.
|
|
458
487
|
*/
|
|
459
|
-
drain
|
|
488
|
+
drain?: (context: ReporterContext) => void | Promise<void>;
|
|
460
489
|
/**
|
|
461
490
|
* Whether the host should collect files from `kubb:plugin:end` hooks.
|
|
462
491
|
*/
|
|
@@ -675,6 +704,19 @@ type OutputManifest = {
|
|
|
675
704
|
*/
|
|
676
705
|
commit(): Promise<void>;
|
|
677
706
|
};
|
|
707
|
+
/**
|
|
708
|
+
* Loads the stored manifest, starting empty when it is missing, unreadable, or from an older
|
|
709
|
+
* version. `storage` holds the generated files, `cache` holds the manifest itself.
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* ```ts
|
|
713
|
+
* const manifest = await createOutputManifest({ storage: config.storage, cache: cacheStorage({ root: config.root }) })
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
declare function createOutputManifest({ storage, cache }: {
|
|
717
|
+
storage: Storage;
|
|
718
|
+
cache: Storage;
|
|
719
|
+
}): Promise<OutputManifest>;
|
|
678
720
|
//#endregion
|
|
679
721
|
//#region src/Hookable.d.ts
|
|
680
722
|
/**
|
|
@@ -774,7 +816,7 @@ type WriteOptions = ParseOptions & {
|
|
|
774
816
|
*/
|
|
775
817
|
manifest?: OutputManifest;
|
|
776
818
|
/**
|
|
777
|
-
* Receives `kubb:files:processing:start`, `:update` (
|
|
819
|
+
* Receives `kubb:files:processing:start`, `:update` (in input order, as files finish) and `:end` for the write. `config` fills the update rows.
|
|
778
820
|
*/
|
|
779
821
|
hooks?: Hookable<KubbHooks>;
|
|
780
822
|
config?: Config;
|
|
@@ -819,11 +861,12 @@ declare class FileManager {
|
|
|
819
861
|
* Parses and writes every file through a bounded pool of workers. A small spec runs all its files
|
|
820
862
|
* at once; a spec with thousands of files keeps at most {@link FILE_CONCURRENCY} parsed sources in
|
|
821
863
|
* memory rather than holding every source, while still overlapping each file's write with the
|
|
822
|
-
* next file's parse. Files finish in whatever order they parse, so
|
|
823
|
-
*
|
|
864
|
+
* next file's parse. Files finish in whatever order they parse, so each finished file flushes the
|
|
865
|
+
* run of finished files ahead of it as one `kubb:files:processing:update`, keeping rows in input order.
|
|
824
866
|
*
|
|
825
867
|
* A file the storage already holds is skipped, so a rebuild that generates identical output
|
|
826
|
-
* writes nothing and leaves every mtime where it was.
|
|
868
|
+
* writes nothing and leaves every mtime where it was. `kubb:files:processing:end` lists the
|
|
869
|
+
* skipped paths as `unchanged`.
|
|
827
870
|
*/
|
|
828
871
|
write(files: Array<FileNode>, { storage, parsers, manifest, signal, hooks, config }: WriteOptions): Promise<void>;
|
|
829
872
|
}
|
|
@@ -1368,6 +1411,13 @@ type KubbBuildStartContext = FilesContext & {
|
|
|
1368
1411
|
* To observe individual schemas and operations use the `kubb:generate:schema` / `kubb:generate:operation` hooks.
|
|
1369
1412
|
*/
|
|
1370
1413
|
meta: InputMeta | undefined;
|
|
1414
|
+
/**
|
|
1415
|
+
* How many schemas and operations the parsed document holds, the totals for a progress display.
|
|
1416
|
+
*/
|
|
1417
|
+
nodeCounts: {
|
|
1418
|
+
schemas: number;
|
|
1419
|
+
operations: number;
|
|
1420
|
+
};
|
|
1371
1421
|
/**
|
|
1372
1422
|
* Looks up a registered plugin by name, typed by the plugin registry.
|
|
1373
1423
|
*/
|
|
@@ -1519,6 +1569,10 @@ type KubbFilesProcessingEndContext = {
|
|
|
1519
1569
|
* All files that were serialized in this batch.
|
|
1520
1570
|
*/
|
|
1521
1571
|
files: Array<FileNode>;
|
|
1572
|
+
/**
|
|
1573
|
+
* Paths of the files the storage already held with the same content, so they were not written.
|
|
1574
|
+
*/
|
|
1575
|
+
unchanged: Array<string>;
|
|
1522
1576
|
};
|
|
1523
1577
|
type KubbHookStartContext = {
|
|
1524
1578
|
/**
|
|
@@ -1756,7 +1810,7 @@ type Diagnostic = ProblemDiagnostic | PerformanceDiagnostic | UpdateDiagnostic;
|
|
|
1756
1810
|
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
1757
1811
|
* that lets deep code report a diagnostic without threading a callback.
|
|
1758
1812
|
*
|
|
1759
|
-
* The sink lives in a single `AsyncLocalStorage`
|
|
1813
|
+
* The sink lives in a single `AsyncLocalStorage` on `globalThis`, shared by every copy of `@kubb/core`.
|
|
1760
1814
|
* `Diagnostics.scope` activates it for a run, so anything inside that run (the
|
|
1761
1815
|
* adapter parse, a generator) reports through `Diagnostics.report` and lands
|
|
1762
1816
|
* in the same run.
|
|
@@ -3096,9 +3150,11 @@ type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
|
|
|
3096
3150
|
name: TOptions['name'];
|
|
3097
3151
|
meta?: ExtensionMeta<TOptions['name']>;
|
|
3098
3152
|
/**
|
|
3099
|
-
*
|
|
3153
|
+
* The options the adapter was created with, before `setup` applied defaults, as a plugin keeps its
|
|
3154
|
+
* own. Kubb Studio merges its overrides onto these, so a default derived from another option, such
|
|
3155
|
+
* as `emptySchemaType` from `unknownType`, follows a change to it.
|
|
3100
3156
|
*/
|
|
3101
|
-
options: TOptions['
|
|
3157
|
+
options: TOptions['options'];
|
|
3102
3158
|
/**
|
|
3103
3159
|
* Parsed source document after the first `parse()` call. `null` before parsing.
|
|
3104
3160
|
*/
|
|
@@ -3120,6 +3176,13 @@ type Adapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions> = {
|
|
|
3120
3176
|
validate: (input: string, options?: {
|
|
3121
3177
|
throwOnError?: boolean;
|
|
3122
3178
|
}) => Promise<void>;
|
|
3179
|
+
/**
|
|
3180
|
+
* Zero-based UTF-16 offsets of a diagnostic in `source` for a code frame, `end` exclusive; `undefined` when it cannot be found.
|
|
3181
|
+
*/
|
|
3182
|
+
locate?: (source: string, location: DiagnosticLocation) => {
|
|
3183
|
+
start: number;
|
|
3184
|
+
end: number;
|
|
3185
|
+
} | undefined;
|
|
3123
3186
|
};
|
|
3124
3187
|
/**
|
|
3125
3188
|
* The object passed to `defineExtension` with `kind: 'adapter'`.
|
|
@@ -3152,5 +3215,5 @@ type AdapterFactory<T extends AdapterFactoryOptions = AdapterFactoryOptions> = E
|
|
|
3152
3215
|
*/
|
|
3153
3216
|
declare function createAdapter<T extends AdapterFactoryOptions = AdapterFactoryOptions>(definition: AdapterDefinition<T>): AdapterFactory<T>;
|
|
3154
3217
|
//#endregion
|
|
3155
|
-
export { NodeCache as $,
|
|
3156
|
-
//# sourceMappingURL=createAdapter-
|
|
3218
|
+
export { NodeCache as $, ReporterContext as $t, PluginFactory as A, KubbInfoContext as At, ResolveImportsOptions as B, RunHookOptions as Bt, NormalizedPlugin as C, KubbGenerationEndContext as Ct, Override as D, KubbHookLineContext as Dt, OutputOptions as E, KubbHookEndContext as Et, definePlugin as F, KubbWarnContext as Ft, ResolverFile as G, createOutputManifest as Gt, ResolvePathOptions as H, KubbDriver as Ht, BannerMeta as I, PossibleConfig as It, ResolverPatch as J, createRenderer as Jt, ResolverFileParams as K, Renderer as Kt, ResolveBannerContext as L, PostGenerateCommand as Lt, PluginType as M, KubbMessageContext as Mt, ResolvePluginOptions as N, KubbPluginsEndContext as Nt, Plugin as O, KubbHookStartContext as Ot, assertPluginOption as P, KubbSuccessContext as Pt, defineGenerator as Q, Reporter as Qt, ResolveBannerFile as R, UserConfig as Rt, KubbPluginStartContext as S, KubbFilesProcessingUpdateContext as St, OutputMode as T, KubbGenerationSummaryContext as Tt, Resolver as U, Hookable as Ut, ResolveOptionsContext as V, runHook as Vt, ResolverDefault as W, OutputManifest as Wt, Generator as X, createStorage as Xt, ResolverPathParams as Y, Storage as Yt, GeneratorContext as Z, GenerationResult as Zt, Filter as _, KubbDiagnosticContext as _t, AdapterSource as a, DiagnosticFormatOptions as an, Diagnostics as at, KubbPluginEndContext as b, KubbFilesProcessingEndContext as bt, ExtensionDependency as c, ParserDefinition as cn, ProblemDiagnostic as ct, ExtensionKind as d, CLIOptions as dt, ReporterName as en, Diagnostic as et, ExtensionMeta as f, Config as ft, Exclude$1 as g, KubbBuildStartContext as gt, resolveMeta as h, KubbBuildEndContext as ht, AdapterFactoryOptions as i, logLevel as in, DiagnosticSeverity as it, PluginName as j, KubbLifecycleStartContext as jt, PluginDefinition as k, KubbHooks as kt, ExtensionDependencyRecord as l, ParserFactory as ln, UpdateDiagnostic as lt, defineExtension as m, Input as mt, AdapterDefinition as n, UserReporter as nn, DiagnosticKind as nt, createAdapter as o, SerializedDiagnostic as on, PerformanceDiagnostic as ot, checkCompatibility as p, FilesContext as pt, ResolverFilePathParams as q, RendererFactory as qt, AdapterFactory as r, createReporter as rn, DiagnosticLocation as rt, ExtensionDependencies as s, Parser as sn, ProblemCode as st, Adapter as t, ReporterPluginFiles as tn, DiagnosticDoc as tt, ExtensionFactory as u, defineParser as un, BuildOutput as ut, Group as v, KubbErrorContext as vt, Output as w, KubbGenerationStartContext as wt, KubbPluginSetupContext as x, KubbFilesProcessingStartContext as xt, Include as y, KubbFileProcessingUpdate as yt, ResolveFileOptions as z, HookResult as zt };
|
|
3219
|
+
//# sourceMappingURL=createAdapter-DtX_MKL-.d.ts.map
|
package/dist/index.cjs
CHANGED
|
@@ -41,7 +41,7 @@ unstorage_drivers_fs = __toESM(unstorage_drivers_fs, 1);
|
|
|
41
41
|
let node_url = require("node:url");
|
|
42
42
|
let node_module = require("node:module");
|
|
43
43
|
//#region package.json
|
|
44
|
-
var version = "5.11.
|
|
44
|
+
var version = "5.11.6";
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region ../../internals/utils/src/casing.ts
|
|
47
47
|
/**
|
|
@@ -779,13 +779,15 @@ function serialize(diagnostic) {
|
|
|
779
779
|
* with `at`, `fix`, the first cause, and `Learn more` for an error, and one row for a warning.
|
|
780
780
|
* `verbose` adds the full stack.
|
|
781
781
|
*/
|
|
782
|
-
function format(diagnostic, { audience = "agent", verbose = false, width = 0 } = {}) {
|
|
782
|
+
function format(diagnostic, { audience = "agent", verbose = false, width = 0, position } = {}) {
|
|
783
783
|
const { code, severity, message } = diagnostic;
|
|
784
784
|
const problem = Diagnostics.isProblem(diagnostic) ? diagnostic : void 0;
|
|
785
785
|
const human = audience === "human";
|
|
786
786
|
const paint = human ? node_util.styleText : (_format, text) => text;
|
|
787
787
|
const [firstLine = "", ...rest] = message.split("\n");
|
|
788
788
|
const pointer = problem?.location && "pointer" in problem.location ? problem.location.pointer : void 0;
|
|
789
|
+
const at = position ? `${position.file}:${position.line}:${position.column}` : void 0;
|
|
790
|
+
const where = at ?? pointer;
|
|
789
791
|
const help = problem ? getHelp(problem) : void 0;
|
|
790
792
|
const causes = problem ? getCauses(problem) : [];
|
|
791
793
|
const crash = problem ? crashCodes.has(problem.code) : false;
|
|
@@ -795,7 +797,7 @@ function format(diagnostic, { audience = "agent", verbose = false, width = 0 } =
|
|
|
795
797
|
const row = (label, value) => {
|
|
796
798
|
if (value) details.push(` ${label}: ${value}`);
|
|
797
799
|
};
|
|
798
|
-
row("at", pointer);
|
|
800
|
+
row("at", at && pointer ? `${at} (${pointer})` : pointer);
|
|
799
801
|
if (problem && severity !== "info") row("why", Diagnostics.explain(problem.code).cause);
|
|
800
802
|
row("fix", help);
|
|
801
803
|
for (const cause of causes) row("caused by", cause);
|
|
@@ -805,24 +807,25 @@ function format(diagnostic, { audience = "agent", verbose = false, width = 0 } =
|
|
|
805
807
|
return [`${severity} ${code}${problem?.plugin ? ` [${problem.plugin}]` : ""}: ${firstLine}`, ...details];
|
|
806
808
|
}
|
|
807
809
|
const left = `${paint(severityStyle[severity], severityIcon[severity])} ${paint("bold", firstLine)}`;
|
|
808
|
-
const gap =
|
|
809
|
-
const headline = `${left}${" ".repeat(gap)}${paint("dim", code)}
|
|
810
|
+
const gap = width - (0, node_util.stripVTControlCharacters)(left).length - code.length;
|
|
811
|
+
const headline = width && gap < 2 ? [left, `${" ".repeat(Math.max(0, width - code.length))}${paint("dim", code)}`] : [`${left}${" ".repeat(Math.max(2, gap))}${paint("dim", code)}`];
|
|
810
812
|
const frame = rest.filter((line) => line.trim()).map((line) => ` ${paint("dim", line)}`);
|
|
811
813
|
const label = (name) => paint("dim", name.length < 4 ? name.padEnd(4) : `${name} `);
|
|
812
|
-
const rows = [["at",
|
|
814
|
+
const rows = [["at", where && paint("cyan", where)], ["fix", help]];
|
|
813
815
|
if (severity !== "error") {
|
|
814
816
|
const first = rows.find(([, value]) => value);
|
|
815
817
|
return [
|
|
816
|
-
headline,
|
|
818
|
+
...headline,
|
|
817
819
|
...frame,
|
|
818
820
|
...first ? [` ${label(first[0])} ${first[1]}`] : []
|
|
819
821
|
];
|
|
820
822
|
}
|
|
821
823
|
rows.push(["caused by", causes[0]], ["report", problem?.issues && paint("cyan", problem.issues)]);
|
|
822
|
-
const
|
|
824
|
+
const codeFrame = (position?.frame ?? []).map((line) => ` ${line}`);
|
|
825
|
+
const details = [...frame, ...rows.flatMap(([name, value]) => value ? [` ${label(name)} ${value}`, ...name === "at" ? codeFrame : []] : [])];
|
|
823
826
|
if (frames?.length) details.push(` ${paint("dim", "stack")}`, ...frames);
|
|
824
827
|
if (code !== diagnosticCode.unknown) details.push(` ${paint("dim", "↳ Learn more")} ${paint("cyan", Diagnostics.docsUrl(code).replace(/^https:\/\//, ""))}`);
|
|
825
|
-
return [headline, ...details];
|
|
828
|
+
return [...headline, ...details];
|
|
826
829
|
}
|
|
827
830
|
//#endregion
|
|
828
831
|
//#region src/Diagnostics.ts
|
|
@@ -874,6 +877,7 @@ const failedCode = {
|
|
|
874
877
|
parser: diagnosticCode.parserFailed
|
|
875
878
|
};
|
|
876
879
|
const docsByCode = new Map(Object.values(diagnosticCatalog).map((doc) => [doc.code, doc]));
|
|
880
|
+
const reporterKey = Symbol.for("kubb.diagnostics.reporter");
|
|
877
881
|
const countKey = {
|
|
878
882
|
error: "errors",
|
|
879
883
|
warning: "warnings",
|
|
@@ -883,13 +887,13 @@ const countKey = {
|
|
|
883
887
|
* Static helpers for working with {@link Diagnostic}s, plus the run-scoped sink
|
|
884
888
|
* that lets deep code report a diagnostic without threading a callback.
|
|
885
889
|
*
|
|
886
|
-
* The sink lives in a single `AsyncLocalStorage`
|
|
890
|
+
* The sink lives in a single `AsyncLocalStorage` on `globalThis`, shared by every copy of `@kubb/core`.
|
|
887
891
|
* `Diagnostics.scope` activates it for a run, so anything inside that run (the
|
|
888
892
|
* adapter parse, a generator) reports through `Diagnostics.report` and lands
|
|
889
893
|
* in the same run.
|
|
890
894
|
*/
|
|
891
895
|
var Diagnostics = class Diagnostics {
|
|
892
|
-
static #reporterStorage = new node_async_hooks.AsyncLocalStorage();
|
|
896
|
+
static #reporterStorage = globalThis[reporterKey] ??= new node_async_hooks.AsyncLocalStorage();
|
|
893
897
|
/**
|
|
894
898
|
* The diagnostic code catalog, exposed as `Diagnostics.code` (e.g. `Diagnostics.code.refNotFound`).
|
|
895
899
|
*/
|
|
@@ -1232,7 +1236,7 @@ function defineExtension(definition) {
|
|
|
1232
1236
|
return Object.assign(build(options), {
|
|
1233
1237
|
name: meta.name,
|
|
1234
1238
|
meta,
|
|
1235
|
-
options
|
|
1239
|
+
options: raw
|
|
1236
1240
|
});
|
|
1237
1241
|
}
|
|
1238
1242
|
});
|
|
@@ -1745,7 +1749,8 @@ var Resolver = class Resolver {
|
|
|
1745
1749
|
#resolvePath({ baseName, tag, path: groupPath, root, output, group }) {
|
|
1746
1750
|
if (output.mode !== "directory") return node_path.default.resolve(root, output.path);
|
|
1747
1751
|
const outputDir = node_path.default.resolve(root, output.path);
|
|
1748
|
-
const
|
|
1752
|
+
const groupValue = group?.type === "path" ? groupPath : tag;
|
|
1753
|
+
const result = group && groupValue ? node_path.default.resolve(outputDir, Resolver.#resolveGroupDir(group, groupValue), baseName) : node_path.default.resolve(outputDir, baseName);
|
|
1749
1754
|
assertInside({
|
|
1750
1755
|
target: result,
|
|
1751
1756
|
parent: outputDir,
|
|
@@ -1986,10 +1991,7 @@ function runParser(parser, print) {
|
|
|
1986
1991
|
* Reads a `copy` file and prints it through the parser's `copy` hook, or verbatim with `banner`/`footer` when there is none.
|
|
1987
1992
|
*/
|
|
1988
1993
|
async function parseCopy(file, parser) {
|
|
1989
|
-
|
|
1990
|
-
try {
|
|
1991
|
-
content = await (0, node_fs_promises.readFile)(file.copy, "utf8");
|
|
1992
|
-
} catch (err) {
|
|
1994
|
+
const content = await (0, node_fs_promises.readFile)(file.copy, "utf8").catch((err) => {
|
|
1993
1995
|
throw new Diagnostics.Error({
|
|
1994
1996
|
code: Diagnostics.code.pluginFailed,
|
|
1995
1997
|
severity: "error",
|
|
@@ -1997,7 +1999,7 @@ async function parseCopy(file, parser) {
|
|
|
1997
1999
|
help: "Check that the file a plugin copies into the output exists and is readable.",
|
|
1998
2000
|
cause: toError(err)
|
|
1999
2001
|
});
|
|
2000
|
-
}
|
|
2002
|
+
});
|
|
2001
2003
|
if (parser?.copy) return runParser(parser, () => parser.parse(_kubb_ast.ast.factory.createFile(parser.copy(file, content))));
|
|
2002
2004
|
return [
|
|
2003
2005
|
file.banner,
|
|
@@ -2011,9 +2013,9 @@ function mergeFile(a, b) {
|
|
|
2011
2013
|
banner: b.banner,
|
|
2012
2014
|
footer: b.footer,
|
|
2013
2015
|
copy: b.copy ?? a.copy,
|
|
2014
|
-
sources:
|
|
2015
|
-
imports:
|
|
2016
|
-
exports:
|
|
2016
|
+
sources: [...a.sources, ...b.sources],
|
|
2017
|
+
imports: [...a.imports, ...b.imports],
|
|
2018
|
+
exports: [...a.exports, ...b.exports]
|
|
2017
2019
|
};
|
|
2018
2020
|
}
|
|
2019
2021
|
function isIndexPath(path) {
|
|
@@ -2022,17 +2024,49 @@ function isIndexPath(path) {
|
|
|
2022
2024
|
function compareFiles(a, b) {
|
|
2023
2025
|
return a.path.length - b.path.length || Number(isIndexPath(a.path)) - Number(isIndexPath(b.path));
|
|
2024
2026
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2027
|
+
/**
|
|
2028
|
+
* Writes `source` to `file.path` unless the storage already holds it, either verbatim or, per the
|
|
2029
|
+
* manifest, as what the output passes make of it. Checking here rather than in a driver gives a
|
|
2030
|
+
* storage writing to S3 or a database the same guarantee `fsStorage` has. `false` when skipped.
|
|
2031
|
+
*/
|
|
2032
|
+
async function writeChanged({ storage, manifest, file, source, signal }) {
|
|
2033
|
+
signal?.throwIfAborted();
|
|
2034
|
+
const stored = await storage.readItem(file.path);
|
|
2035
|
+
if (stored !== null && (matchesStored({
|
|
2028
2036
|
stored,
|
|
2029
2037
|
source
|
|
2030
|
-
})
|
|
2031
|
-
|
|
2032
|
-
key,
|
|
2038
|
+
}) || manifest?.isUpToDate({
|
|
2039
|
+
key: file.path,
|
|
2033
2040
|
source,
|
|
2034
2041
|
disk: stored
|
|
2035
|
-
})
|
|
2042
|
+
}))) return false;
|
|
2043
|
+
signal?.throwIfAborted();
|
|
2044
|
+
await storage.writeItem(file.path, source, { stored });
|
|
2045
|
+
manifest?.track({
|
|
2046
|
+
key: file.path,
|
|
2047
|
+
source
|
|
2048
|
+
});
|
|
2049
|
+
return true;
|
|
2050
|
+
}
|
|
2051
|
+
/** Reports finished files in input order: each finished file flushes the run of finished files ahead of it as one update. */
|
|
2052
|
+
function createProgress(files, config, hooks) {
|
|
2053
|
+
const finished = [];
|
|
2054
|
+
let flushed = 0;
|
|
2055
|
+
return async (index) => {
|
|
2056
|
+
finished[index] = true;
|
|
2057
|
+
const updates = [];
|
|
2058
|
+
while (finished[flushed]) {
|
|
2059
|
+
flushed++;
|
|
2060
|
+
updates.push({
|
|
2061
|
+
file: files[flushed - 1],
|
|
2062
|
+
processed: flushed,
|
|
2063
|
+
total: files.length,
|
|
2064
|
+
percentage: flushed / files.length * 100,
|
|
2065
|
+
config
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
if (updates.length) await hooks.callHook("kubb:files:processing:update", { files: updates });
|
|
2069
|
+
};
|
|
2036
2070
|
}
|
|
2037
2071
|
/**
|
|
2038
2072
|
* In-memory file store for generated files, and the writer that turns them into source
|
|
@@ -2077,25 +2111,17 @@ var FileManager = class {
|
|
|
2077
2111
|
}
|
|
2078
2112
|
}
|
|
2079
2113
|
#store(files, mergeExisting) {
|
|
2080
|
-
const batch =
|
|
2081
|
-
const resolved =
|
|
2082
|
-
for (const file of batch) {
|
|
2114
|
+
const batch = /* @__PURE__ */ new Set();
|
|
2115
|
+
const resolved = files.map((file) => {
|
|
2083
2116
|
const existing = this.#cache.get(file.path);
|
|
2084
|
-
const merged = existing && mergeExisting
|
|
2117
|
+
const merged = _kubb_ast.ast.factory.createFile(existing && (mergeExisting || batch.has(file.path)) ? mergeFile(existing, file) : file);
|
|
2085
2118
|
this.#cache.set(merged.path, merged);
|
|
2086
|
-
|
|
2087
|
-
|
|
2119
|
+
batch.add(merged.path);
|
|
2120
|
+
return merged;
|
|
2121
|
+
});
|
|
2088
2122
|
if (resolved.length > 0) this.#sorted = null;
|
|
2089
2123
|
return resolved;
|
|
2090
2124
|
}
|
|
2091
|
-
#dedupe(files) {
|
|
2092
|
-
const seen = /* @__PURE__ */ new Map();
|
|
2093
|
-
for (const file of files) {
|
|
2094
|
-
const prev = seen.get(file.path);
|
|
2095
|
-
seen.set(file.path, prev ? mergeFile(prev, file) : file);
|
|
2096
|
-
}
|
|
2097
|
-
return [...seen.values()];
|
|
2098
|
-
}
|
|
2099
2125
|
/**
|
|
2100
2126
|
* Releases all stored files. Called by the core after `kubb:build:end`.
|
|
2101
2127
|
*/
|
|
@@ -2123,50 +2149,38 @@ var FileManager = class {
|
|
|
2123
2149
|
* Parses and writes every file through a bounded pool of workers. A small spec runs all its files
|
|
2124
2150
|
* at once; a spec with thousands of files keeps at most {@link FILE_CONCURRENCY} parsed sources in
|
|
2125
2151
|
* memory rather than holding every source, while still overlapping each file's write with the
|
|
2126
|
-
* next file's parse. Files finish in whatever order they parse, so
|
|
2127
|
-
*
|
|
2152
|
+
* next file's parse. Files finish in whatever order they parse, so each finished file flushes the
|
|
2153
|
+
* run of finished files ahead of it as one `kubb:files:processing:update`, keeping rows in input order.
|
|
2128
2154
|
*
|
|
2129
2155
|
* A file the storage already holds is skipped, so a rebuild that generates identical output
|
|
2130
|
-
* writes nothing and leaves every mtime where it was.
|
|
2156
|
+
* writes nothing and leaves every mtime where it was. `kubb:files:processing:end` lists the
|
|
2157
|
+
* skipped paths as `unchanged`.
|
|
2131
2158
|
*/
|
|
2132
2159
|
async write(files, { storage, parsers, manifest, signal, hooks, config }) {
|
|
2133
2160
|
if (files.length === 0) return;
|
|
2134
2161
|
await hooks?.callHook("kubb:files:processing:start", { files });
|
|
2135
|
-
const
|
|
2136
|
-
const
|
|
2162
|
+
const unchanged = [];
|
|
2163
|
+
const progress = hooks && config ? createProgress(files, config, hooks) : void 0;
|
|
2137
2164
|
await inParallel({
|
|
2138
2165
|
items: files,
|
|
2139
2166
|
limit: 50,
|
|
2140
2167
|
run: async (file, index) => {
|
|
2141
2168
|
signal?.throwIfAborted();
|
|
2142
2169
|
const source = await this.parse(file, { parsers });
|
|
2143
|
-
if (
|
|
2170
|
+
if (source && !await writeChanged({
|
|
2171
|
+
storage,
|
|
2172
|
+
manifest,
|
|
2144
2173
|
file,
|
|
2145
|
-
processed: index + 1,
|
|
2146
|
-
total,
|
|
2147
|
-
percentage: (index + 1) / total * 100,
|
|
2148
|
-
config
|
|
2149
|
-
});
|
|
2150
|
-
if (!source) return;
|
|
2151
|
-
signal?.throwIfAborted();
|
|
2152
|
-
const stored = await storage.readItem(file.path);
|
|
2153
|
-
if (isUnchanged({
|
|
2154
|
-
stored,
|
|
2155
2174
|
source,
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
signal?.throwIfAborted();
|
|
2160
|
-
await storage.writeItem(file.path, source, { stored });
|
|
2161
|
-
manifest?.track({
|
|
2162
|
-
key: file.path,
|
|
2163
|
-
source
|
|
2164
|
-
});
|
|
2175
|
+
signal
|
|
2176
|
+
})) unchanged.push(file.path);
|
|
2177
|
+
await progress?.(index);
|
|
2165
2178
|
}
|
|
2166
2179
|
});
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2180
|
+
await hooks?.callHook("kubb:files:processing:end", {
|
|
2181
|
+
files,
|
|
2182
|
+
unchanged
|
|
2183
|
+
});
|
|
2170
2184
|
}
|
|
2171
2185
|
};
|
|
2172
2186
|
//#endregion
|
|
@@ -2494,10 +2508,15 @@ var KubbDriver = class {
|
|
|
2494
2508
|
plugin.options = normalizePluginOptions(plugin.options, plugin.name);
|
|
2495
2509
|
}
|
|
2496
2510
|
if (this.adapter && this.inputNode) {
|
|
2511
|
+
const { meta, schemas, operations } = this.inputNode;
|
|
2497
2512
|
const buildStartContext = this.#withFiles({
|
|
2498
2513
|
config,
|
|
2499
2514
|
adapter: this.adapter,
|
|
2500
|
-
meta
|
|
2515
|
+
meta,
|
|
2516
|
+
nodeCounts: {
|
|
2517
|
+
schemas: schemas.length,
|
|
2518
|
+
operations: operations.length
|
|
2519
|
+
},
|
|
2501
2520
|
getPlugin: this.getPlugin.bind(this)
|
|
2502
2521
|
});
|
|
2503
2522
|
await hooks.callHook("kubb:build:start", buildStartContext);
|
|
@@ -2782,7 +2801,10 @@ var KubbDriver = class {
|
|
|
2782
2801
|
}
|
|
2783
2802
|
return diagnostics;
|
|
2784
2803
|
}
|
|
2785
|
-
/**
|
|
2804
|
+
/**
|
|
2805
|
+
* Runs `node` through the generators whose `match` accepts it; `generate` picks the method for the
|
|
2806
|
+
* node kind.
|
|
2807
|
+
*/
|
|
2786
2808
|
async #dispatchNode({ generators, node, ctx, generate }) {
|
|
2787
2809
|
for (const generator of generators) {
|
|
2788
2810
|
if (!(generator.match ? await generator.match(node, ctx) : true)) continue;
|
|
@@ -3692,17 +3714,23 @@ const logLevel = {
|
|
|
3692
3714
|
* ```
|
|
3693
3715
|
*/
|
|
3694
3716
|
function createReporter(reporter) {
|
|
3717
|
+
const { name, needsPluginFiles, drain } = reporter;
|
|
3718
|
+
if (!drain) return {
|
|
3719
|
+
name,
|
|
3720
|
+
needsPluginFiles,
|
|
3721
|
+
async report(result, context) {
|
|
3722
|
+
await reporter.report(result, context);
|
|
3723
|
+
}
|
|
3724
|
+
};
|
|
3695
3725
|
const reports = [];
|
|
3696
3726
|
return {
|
|
3697
|
-
name
|
|
3698
|
-
needsPluginFiles
|
|
3727
|
+
name,
|
|
3728
|
+
needsPluginFiles,
|
|
3699
3729
|
async report(result, context) {
|
|
3700
|
-
|
|
3701
|
-
if (reporter.drain) reports.push(report);
|
|
3730
|
+
reports.push(await reporter.report(result, context));
|
|
3702
3731
|
},
|
|
3703
3732
|
async drain(context) {
|
|
3704
|
-
await
|
|
3705
|
-
reports.length = 0;
|
|
3733
|
+
await drain(context, reports.splice(0));
|
|
3706
3734
|
}
|
|
3707
3735
|
};
|
|
3708
3736
|
}
|
|
@@ -3714,7 +3742,7 @@ function createReporter(reporter) {
|
|
|
3714
3742
|
* counts, so every reporter renders the same data.
|
|
3715
3743
|
*/
|
|
3716
3744
|
function buildReport(result) {
|
|
3717
|
-
const { config, diagnostics, filesCreated, status, hrStart } = result;
|
|
3745
|
+
const { config, diagnostics, filesCreated, filesUnchanged = 0, status, hrStart } = result;
|
|
3718
3746
|
const failed = Diagnostics.failedPlugins(diagnostics);
|
|
3719
3747
|
const total = config.plugins?.length ?? 0;
|
|
3720
3748
|
const counts = Diagnostics.count(diagnostics);
|
|
@@ -3733,6 +3761,9 @@ function buildReport(result) {
|
|
|
3733
3761
|
},
|
|
3734
3762
|
counts,
|
|
3735
3763
|
filesCreated,
|
|
3764
|
+
filesUnchanged,
|
|
3765
|
+
fileChanges: result.fileChanges,
|
|
3766
|
+
dryRun: result.dryRun,
|
|
3736
3767
|
durationMs: getElapsedMs(hrStart),
|
|
3737
3768
|
output: (0, node_path.resolve)(config.root, config.output.path),
|
|
3738
3769
|
timings,
|
|
@@ -3742,39 +3773,48 @@ function buildReport(result) {
|
|
|
3742
3773
|
//#endregion
|
|
3743
3774
|
//#region src/reporters/summary.ts
|
|
3744
3775
|
/**
|
|
3745
|
-
*
|
|
3746
|
-
*
|
|
3747
|
-
* can render the same lines through whatever output it already owns.
|
|
3776
|
+
* Puts the outcome first, followed by file counts, labeled plugin results, and the output path.
|
|
3777
|
+
* Hosts that collect file changes get a compact diff section. Verbose output adds plugin timings.
|
|
3748
3778
|
*/
|
|
3749
3779
|
function formatSummary(report, { showTimings }) {
|
|
3750
|
-
const { status, plugins, counts, filesCreated, durationMs, output, timings } = report;
|
|
3751
|
-
const
|
|
3780
|
+
const { status, plugins, counts, filesCreated, filesUnchanged, fileChanges, dryRun, durationMs, output, timings } = report;
|
|
3781
|
+
const failed = status === "failed";
|
|
3782
|
+
const successTone = counts.warnings ? "yellow" : "green";
|
|
3783
|
+
const tone = failed ? "red" : successTone;
|
|
3784
|
+
const title = (() => {
|
|
3785
|
+
if (failed) return "✗ Generation failed";
|
|
3786
|
+
if (dryRun) return "◇ Dry run complete";
|
|
3787
|
+
if (counts.warnings) return "✓ Generation complete with warnings";
|
|
3788
|
+
if (filesCreated > 0 && filesCreated === filesUnchanged) return "✓ Already up to date";
|
|
3789
|
+
return "✓ Generation complete";
|
|
3790
|
+
})();
|
|
3791
|
+
const separator = (0, node_util.styleText)("dim", " · ");
|
|
3792
|
+
const generated = Math.max(0, filesCreated - filesUnchanged);
|
|
3793
|
+
const fileLabel = `${generated} ${generated === 1 ? "file" : "files"} ${dryRun ? "planned" : "generated"}`;
|
|
3794
|
+
const lines = [
|
|
3795
|
+
`${(0, node_util.styleText)(tone, title)} ${(0, node_util.styleText)("dim", formatMs(durationMs))}`,
|
|
3796
|
+
"",
|
|
3797
|
+
` ${(0, node_util.styleText)(generated ? "bold" : "dim", fileLabel)}${filesUnchanged ? separator + (0, node_util.styleText)("dim", `${filesUnchanged} unchanged`) : ""}`
|
|
3798
|
+
];
|
|
3752
3799
|
const notRun = plugins.total - plugins.passed - plugins.failed.length;
|
|
3753
|
-
|
|
3754
|
-
(0, node_util.styleText)("green", `${plugins.passed} passed`),
|
|
3755
|
-
(0, node_util.styleText)("red", `${plugins.failed.length} failed`),
|
|
3800
|
+
const pluginCounts = [
|
|
3801
|
+
plugins.passed > 0 ? (0, node_util.styleText)("green", `${plugins.passed} passed`) : void 0,
|
|
3802
|
+
plugins.failed.length > 0 ? (0, node_util.styleText)("red", `${plugins.failed.length} failed`) : void 0,
|
|
3756
3803
|
notRun > 0 ? (0, node_util.styleText)("dim", `${notRun} not run`) : void 0
|
|
3757
|
-
].filter(Boolean)
|
|
3758
|
-
|
|
3759
|
-
if (
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
const lines = rows.map(([label, value]) => `${(0, node_util.styleText)("dim", label.padStart(labelWidth))} ${value}`);
|
|
3804
|
+
].filter(Boolean);
|
|
3805
|
+
lines.push(` ${(0, node_util.styleText)("dim", "Plugins")} ${pluginCounts.join(separator) || (0, node_util.styleText)("dim", "none configured")}`);
|
|
3806
|
+
if (plugins.failed.length) lines.push(` ${(0, node_util.styleText)("dim", "Failed plugins")} ${(0, node_util.styleText)("red", plugins.failed.join(", "))}`);
|
|
3807
|
+
const issues = [counts.errors > 0 ? (0, node_util.styleText)("red", `${counts.errors} ${counts.errors === 1 ? "error" : "errors"}`) : void 0, counts.warnings > 0 ? (0, node_util.styleText)("yellow", `${counts.warnings} ${counts.warnings === 1 ? "warning" : "warnings"}`) : void 0].filter(Boolean);
|
|
3808
|
+
if (issues.length) lines.push(` ${(0, node_util.styleText)("dim", "Issues")} ${issues.join(separator)}`);
|
|
3809
|
+
const destination = (0, node_path.relative)(process.cwd(), output);
|
|
3810
|
+
const displayPath = destination.startsWith("..") || (0, node_path.isAbsolute)(destination) ? output : `./${destination}`;
|
|
3811
|
+
lines.push(` ${(0, node_util.styleText)("dim", failed || dryRun ? "Target" : "Output")} ${(0, node_util.styleText)(failed ? "dim" : "cyan", displayPath)}`);
|
|
3812
|
+
if (dryRun) lines.push(` ${(0, node_util.styleText)("dim", "Nothing was written.")}`);
|
|
3813
|
+
if (fileChanges) lines.push("", (0, node_util.styleText)("bold", dryRun ? "Planned file changes" : "File changes"), ` ${(0, node_util.styleText)(fileChanges.added ? "green" : "dim", `${fileChanges.added} added`)}${separator}${(0, node_util.styleText)(fileChanges.modified ? "yellow" : "dim", `${fileChanges.modified} modified`)}${separator}${(0, node_util.styleText)(fileChanges.deleted ? "red" : "dim", `${fileChanges.deleted} deleted`)}`, ` ${(0, node_util.styleText)("dim", "Lines")} ${(0, node_util.styleText)(fileChanges.linesAdded ? "green" : "dim", `+${fileChanges.linesAdded}`)} ${(0, node_util.styleText)(fileChanges.linesRemoved ? "red" : "dim", `−${fileChanges.linesRemoved}`)}`);
|
|
3768
3814
|
if (showTimings && timings.length > 0) {
|
|
3769
|
-
const nameWidth = Math.max(
|
|
3770
|
-
|
|
3771
|
-
lines.push((0, node_util.styleText)("dim",
|
|
3772
|
-
for (const timing of timings) {
|
|
3773
|
-
const timeStr = formatMs(timing.durationMs);
|
|
3774
|
-
const barLength = Math.min(Math.ceil(timing.durationMs / 100), 10);
|
|
3775
|
-
const bar = (0, node_util.styleText)("dim", "█".repeat(barLength));
|
|
3776
|
-
lines.push(`${indent}${(0, node_util.styleText)("dim", "•")} ${timing.plugin.padEnd(nameWidth)} ${bar} ${timeStr}`);
|
|
3777
|
-
}
|
|
3815
|
+
const nameWidth = Math.max(...timings.map((timing) => timing.plugin.length));
|
|
3816
|
+
lines.push("", (0, node_util.styleText)("dim", "Plugin timings"));
|
|
3817
|
+
for (const timing of timings) lines.push(` ${timing.plugin.padEnd(nameWidth)} ${(0, node_util.styleText)("dim", formatMs(timing.durationMs))}`);
|
|
3778
3818
|
}
|
|
3779
3819
|
return lines;
|
|
3780
3820
|
}
|
|
@@ -4226,6 +4266,7 @@ exports.cliReporter = cliReporter;
|
|
|
4226
4266
|
exports.createAdapter = createAdapter;
|
|
4227
4267
|
exports.createCliReporter = createCliReporter;
|
|
4228
4268
|
exports.createKubb = createKubb;
|
|
4269
|
+
exports.createOutputManifest = createOutputManifest;
|
|
4229
4270
|
exports.createRenderer = createRenderer;
|
|
4230
4271
|
exports.createReporter = createReporter;
|
|
4231
4272
|
exports.createResolver = createResolver;
|