@kubb/core 5.0.0-beta.108 → 5.0.0-beta.109

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { $ as PluginFactoryOptions, A as Kubb, At as DiagnosticDoc, B as Exclude, Bt as Hookable, C as KubbWarnContext, Ct as Reporter, D as CreateKubbOptions, Dt as createReporter, E as UserConfig, Et as UserReporter, F as NodeCache, Ft as PerformanceDiagnostic, G as KubbPluginSetupContext, H as Group, Ht as AdapterFactoryOptions, I as KubbDriver, It as ProblemCode, J as Output, K as KubbPluginStartContext, L as FileManagerHooks, Lt as ProblemDiagnostic, M as Generator, Mt as DiagnosticLocation, N as GeneratorContext, Nt as DiagnosticSeverity, O as GenerateOptions, Ot as logLevel, P as defineGenerator, Pt as Diagnostics, Q as Plugin, R as Parser, Rt as SerializedDiagnostic, S as KubbSuccessContext, St as GenerationResult, T as PostGenerateCommand, Tt as ReporterName, U as Include, Ut as AdapterSource, V as Filter, Vt as Adapter, W as KubbPluginEndContext, Wt as createAdapter, X as OutputOptions, Y as OutputMode, Z as Override, _ as KubbHookStartContext, _t as Renderer, a as KubbBuildEndContext, at as ResolveBannerFile, b as KubbLifecycleStartContext, bt as Storage, c as KubbErrorContext, ct as ResolveOptionsContext, d as KubbFilesProcessingStartContext, dt as ResolverDefault, et as PluginName, f as KubbFilesProcessingUpdateContext, ft as ResolverFile, g as KubbHookLineContext, gt as ResolverPathParams, h as KubbHookEndContext, ht as ResolverPatch, i as Input, it as ResolveBannerContext, j as createKubb, jt as DiagnosticKind, k as GenerateResult, kt as Diagnostic, l as KubbFileProcessingUpdate, lt as ResolvePathOptions, m as KubbGenerationStartContext, mt as ResolverFilePathParams, n as CLIOptions, nt as definePlugin, o as KubbBuildStartContext, ot as ResolveFileOptions, p as KubbGenerationEndContext, pt as ResolverFileParams, q as NormalizedPlugin, r as Config, rt as BannerMeta, s as KubbDiagnosticContext, st as ResolveImportsOptions, t as BuildOutput, tt as ResolvePluginOptions, u as KubbFilesProcessingEndContext, ut as Resolver, v as KubbHooks, vt as RendererFactory, w as PossibleConfig, wt as ReporterContext, x as KubbPluginsEndContext, xt as createStorage, y as KubbInfoContext, yt as createRenderer, z as defineParser, zt as UpdateDiagnostic } from "./types-C_JCC5Dl.js";
2
+ import { $ as PluginFactoryOptions, A as Kubb, At as DiagnosticDoc, B as Exclude, Bt as Hookable, C as KubbWarnContext, Ct as Reporter, D as CreateKubbOptions, Dt as createReporter, E as UserConfig, Et as UserReporter, F as NodeCache, Ft as PerformanceDiagnostic, G as KubbPluginSetupContext, H as Group, Ht as AdapterFactoryOptions, I as KubbDriver, It as ProblemCode, J as Output, K as KubbPluginStartContext, L as FileManagerHooks, Lt as ProblemDiagnostic, M as Generator, Mt as DiagnosticLocation, N as GeneratorContext, Nt as DiagnosticSeverity, O as GenerateOptions, Ot as logLevel, P as defineGenerator, Pt as Diagnostics, Q as Plugin, R as Parser, Rt as SerializedDiagnostic, S as KubbSuccessContext, St as GenerationResult, T as PostGenerateCommand, Tt as ReporterName, U as Include, Ut as AdapterSource, V as Filter, Vt as Adapter, W as KubbPluginEndContext, Wt as createAdapter, X as OutputOptions, Y as OutputMode, Z as Override, _ as KubbHookStartContext, _t as Renderer, a as KubbBuildEndContext, at as ResolveBannerFile, b as KubbLifecycleStartContext, bt as Storage, c as KubbErrorContext, ct as ResolveOptionsContext, d as KubbFilesProcessingStartContext, dt as ResolverDefault, et as PluginName, f as KubbFilesProcessingUpdateContext, ft as ResolverFile, g as KubbHookLineContext, gt as ResolverPathParams, h as KubbHookEndContext, ht as ResolverPatch, i as Input, it as ResolveBannerContext, j as createKubb, jt as DiagnosticKind, k as GenerateResult, kt as Diagnostic, l as KubbFileProcessingUpdate, lt as ResolvePathOptions, m as KubbGenerationStartContext, mt as ResolverFilePathParams, n as CLIOptions, nt as definePlugin, o as KubbBuildStartContext, ot as ResolveFileOptions, p as KubbGenerationEndContext, pt as ResolverFileParams, q as NormalizedPlugin, r as Config, rt as BannerMeta, s as KubbDiagnosticContext, st as ResolveImportsOptions, t as BuildOutput, tt as ResolvePluginOptions, u as KubbFilesProcessingEndContext, ut as Resolver, v as KubbHooks, vt as RendererFactory, w as PossibleConfig, wt as ReporterContext, x as KubbPluginsEndContext, xt as createStorage, y as KubbInfoContext, yt as createRenderer, z as defineParser, zt as UpdateDiagnostic } from "./types-TqpVzVtY.js";
3
3
  //#region src/applyConfigDefaults.d.ts
4
4
  type ApplyConfigDefaultsOptions<TOutput> = {
5
5
  defaultAdapter: Adapter;
@@ -141,7 +141,8 @@ declare function getInputKind(input: NonNullable<Input>): InputKind;
141
141
  *
142
142
  * Writes are deduplicated and directory-safe:
143
143
  * - leading and trailing whitespace is trimmed before writing
144
- * - the write is skipped when the file content is already identical
144
+ * - the write is skipped when the file already holds that content, ignoring any trailing newline
145
+ * a formatter left behind
145
146
  * - missing parent directories are created automatically
146
147
  * - Bun's native file API is used when running under Bun
147
148
  * - concurrent `writeItem` calls are capped at {@link WRITE_CONCURRENCY} in flight, so a caller
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
- import "./rolldown-runtime-C0LytTxp.js";
2
- import { a as clean, c as toPosixPath, d as getErrorMessage, f as toError, i as Hookable, l as write, n as createNodeCache, o as isPathInside, p as camelCase, r as FileManager, s as toFilePath, t as _usingCtx, u as BuildError } from "./usingCtx-D9_Ee6kn.js";
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
+ import { a as OPERATION_FILTER_TYPES, c as clean, d as toPosixPath, f as write, g as camelCase, h as toError, i as Hookable, l as isPathInside, m as getErrorMessage, n as createNodeCache, o as diagnosticCode, p as BuildError, r as FileManager, s as inParallel, t as _usingCtx, u as toFilePath } from "./usingCtx-njZUKKsY.js";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  import { stripVTControlCharacters, styleText } from "node:util";
5
- import { hash } from "node:crypto";
5
+ import { createHash, hash } from "node:crypto";
6
6
  import { access, glob, readFile, rm } from "node:fs/promises";
7
7
  import path, { join, relative, resolve } from "node:path";
8
8
  import { ast, collectImportedRefNames, collectUsedSchemaNames, composeMacros, operationDef, schemaDef, transform } from "@kubb/ast";
9
+ import { existsSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
9
11
  import process$1 from "node:process";
10
12
  //#region src/createAdapter.ts
11
13
  /**
@@ -149,140 +151,11 @@ function randomCliColor(text) {
149
151
  return styleText(color, text);
150
152
  }
151
153
  //#endregion
152
- //#region package.json
153
- var version = "5.0.0-beta.108";
154
- //#endregion
155
- //#region src/constants.ts
156
- /**
157
- * Plugin `include` filter types that select operations directly. When one of these is set
158
- * without a `schemaName` include, the generate phase pre-scans operations to compute the set
159
- * of schemas they reach, so unreachable schemas can be pruned for that plugin.
160
- */
161
- const OPERATION_FILTER_TYPES = /* @__PURE__ */ new Set([
162
- "tag",
163
- "operationId",
164
- "path",
165
- "method",
166
- "contentType"
167
- ]);
168
- /**
169
- * Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
170
- * and stays stable so it can be referenced in tooling and (later) docs. Reference
171
- * these instead of inlining the string at a throw site.
172
- */
173
- const diagnosticCode = {
174
- /**
175
- * Fallback for an unstructured error with no specific code.
176
- */
177
- unknown: "KUBB_UNKNOWN",
178
- /**
179
- * The file or URL set as `input` could not be read.
180
- */
181
- inputNotFound: "KUBB_INPUT_NOT_FOUND",
182
- /**
183
- * A URL set as `input` (or referenced by a `$ref`) answered with a 4xx or 5xx status
184
- * instead of the document.
185
- */
186
- inputRequestFailed: "KUBB_INPUT_REQUEST_FAILED",
187
- /**
188
- * A URL set as `input` (or referenced by a `$ref`) never answered, so the request failed
189
- * before a status was returned.
190
- */
191
- inputUnreachable: "KUBB_INPUT_UNREACHABLE",
192
- /**
193
- * An adapter was configured without an `input`.
194
- */
195
- inputRequired: "KUBB_INPUT_REQUIRED",
196
- /**
197
- * `input` uses the v4 `{ path }` / `{ data }` wrapper, which v5 reads as a parsed
198
- * document instead of a pointer to one.
199
- */
200
- legacyInput: "KUBB_LEGACY_INPUT",
201
- /**
202
- * The parsed `input` carries no `openapi` or `swagger` version, so it is not a
203
- * document the adapter can read.
204
- */
205
- invalidDocument: "KUBB_INVALID_DOCUMENT",
206
- /**
207
- * A `$ref` (or equivalent reference) could not be resolved in the source document.
208
- */
209
- refNotFound: "KUBB_REF_NOT_FOUND",
210
- /**
211
- * A server variable value is not allowed by its `enum`.
212
- */
213
- invalidServerVariable: "KUBB_INVALID_SERVER_VARIABLE",
214
- /**
215
- * A required plugin is missing from the config.
216
- */
217
- pluginNotFound: "KUBB_PLUGIN_NOT_FOUND",
218
- /**
219
- * A plugin threw while generating.
220
- */
221
- pluginFailed: "KUBB_PLUGIN_FAILED",
222
- /**
223
- * A plugin reported a non-fatal warning through `ctx.warn`.
224
- */
225
- pluginWarning: "KUBB_PLUGIN_WARNING",
226
- /**
227
- * A plugin reported an informational message through `ctx.info`.
228
- */
229
- pluginInfo: "KUBB_PLUGIN_INFO",
230
- /**
231
- * A schema uses a `format` Kubb does not map to a specific type. Reserved for
232
- * adapters to emit as a `warning`.
233
- */
234
- unsupportedFormat: "KUBB_UNSUPPORTED_FORMAT",
235
- /**
236
- * A referenced schema or operation is marked `deprecated`. Reserved for adapters
237
- * to emit as an `info`.
238
- */
239
- deprecated: "KUBB_DEPRECATED",
240
- /**
241
- * An adapter is required but the config has none. The build cannot read the input
242
- * without one.
243
- */
244
- adapterRequired: "KUBB_ADAPTER_REQUIRED",
245
- /**
246
- * A resolved output path escapes the output directory, which can stem from a path
247
- * traversal in the spec or a misconfigured `group.name`.
248
- */
249
- pathTraversal: "KUBB_PATH_TRAVERSAL",
250
- /**
251
- * `output.clean` is enabled but `output.path` resolves to the project root or a parent of it,
252
- * so cleaning would delete kubb.config and every source file.
253
- */
254
- cleanRoot: "KUBB_CLEAN_ROOT",
255
- /**
256
- * A plugin's options are invalid, for example `output.mode: 'file'` paired with a `group` option.
257
- */
258
- invalidPluginOptions: "KUBB_INVALID_PLUGIN_OPTIONS",
259
- /**
260
- * A post-generate command (`output.postGenerate`) exited with a failure.
261
- */
262
- postGenerateFailed: "KUBB_POST_GENERATE_FAILED",
263
- /**
264
- * The formatter pass over the generated files failed.
265
- */
266
- formatFailed: "KUBB_FORMAT_FAILED",
267
- /**
268
- * The linter pass over the generated files failed.
269
- */
270
- lintFailed: "KUBB_LINT_FAILED",
271
- /**
272
- * Not a failure. Carries a plugin's elapsed time, summed into the run total.
273
- */
274
- performance: "KUBB_PERFORMANCE",
275
- /**
276
- * Not a failure. A newer Kubb version is available on npm.
277
- */
278
- updateAvailable: "KUBB_UPDATE_AVAILABLE"
279
- };
280
- //#endregion
281
154
  //#region src/Diagnostics.ts
282
155
  /**
283
156
  * Docs major version, derived from the package version so the link tracks the published major.
284
157
  */
285
- const docsMajor = version.split(".")[0] ?? "5";
158
+ const docsMajor = "5.0.0-beta.109".split(".")[0] ?? "5";
286
159
  /**
287
160
  * Builds a type guard that narrows a {@link Diagnostic} to the variant for `kind`. A diagnostic
288
161
  * with no `kind` is treated as a `problem`.
@@ -1531,22 +1404,25 @@ var KubbDriver = class {
1531
1404
  async run() {
1532
1405
  const { hooks, config, fileManager } = this;
1533
1406
  const diagnostics = [];
1534
- const updateBuffer = [];
1535
1407
  const parsersMap = /* @__PURE__ */ new Map();
1536
1408
  for (const parser of config.parsers) if (parser.extNames) for (const ext of parser.extNames) parsersMap.set(ext, parser);
1409
+ const updateBuffer = [];
1537
1410
  const unhookWrites = fileManager.hooks.addHooks({
1538
1411
  start: async (files) => {
1539
1412
  await hooks.callHook("kubb:files:processing:start", { files });
1540
1413
  },
1541
- update: (item) => {
1542
- updateBuffer.push(item);
1414
+ update: ({ file, processed, total, percentage }) => {
1415
+ updateBuffer.push({
1416
+ file,
1417
+ processed,
1418
+ total,
1419
+ percentage,
1420
+ config
1421
+ });
1543
1422
  },
1544
1423
  end: async (files) => {
1545
1424
  updateBuffer.sort((a, b) => a.processed - b.processed);
1546
- await hooks.callHook("kubb:files:processing:update", { files: updateBuffer.map((item) => ({
1547
- ...item,
1548
- config
1549
- })) });
1425
+ await hooks.callHook("kubb:files:processing:update", { files: updateBuffer });
1550
1426
  updateBuffer.length = 0;
1551
1427
  await hooks.callHook("kubb:files:processing:end", { files });
1552
1428
  }
@@ -1613,7 +1489,8 @@ var KubbDriver = class {
1613
1489
  await hooks.callHook("kubb:plugins:end", this.#withFiles({ config }));
1614
1490
  await fileManager.write(fileManager.files, {
1615
1491
  storage: config.storage,
1616
- parsers: parsersMap
1492
+ parsers: parsersMap,
1493
+ manifest: this.options.manifest
1617
1494
  });
1618
1495
  await hooks.callHook("kubb:build:end", {
1619
1496
  files: this.fileManager.files,
@@ -1974,6 +1851,73 @@ var KubbDriver = class {
1974
1851
  }
1975
1852
  };
1976
1853
  //#endregion
1854
+ //#region src/outputManifest.ts
1855
+ /**
1856
+ * Bumped when the stored shape changes, so an older cache is discarded instead of misread.
1857
+ */
1858
+ const VERSION = 1;
1859
+ function hash$1(value) {
1860
+ return createHash("sha256").update(value).digest("hex");
1861
+ }
1862
+ __name(hash$1, "hash");
1863
+ const MANIFEST_KEY = "output-manifest.json";
1864
+ async function loadEntries({ cache }) {
1865
+ try {
1866
+ const stored = await cache.readItem(MANIFEST_KEY);
1867
+ if (stored === null) return {};
1868
+ const data = JSON.parse(stored);
1869
+ if (data.version !== VERSION) return {};
1870
+ if (typeof data.entries !== "object" || data.entries === null || Array.isArray(data.entries)) return {};
1871
+ return data.entries;
1872
+ } catch {
1873
+ return {};
1874
+ }
1875
+ }
1876
+ /**
1877
+ * Loads the stored manifest, starting empty when it is missing, unreadable, or from an older
1878
+ * version. `storage` holds the generated files, `cache` holds the manifest itself.
1879
+ *
1880
+ * @example
1881
+ * ```ts
1882
+ * const manifest = await createOutputManifest({ storage: config.storage, cache: cacheStorage({ root: config.root }) })
1883
+ * ```
1884
+ */
1885
+ async function createOutputManifest({ storage, cache }) {
1886
+ const entries = await loadEntries({ cache });
1887
+ const tracked = /* @__PURE__ */ new Map();
1888
+ return {
1889
+ isUpToDate({ key, source, disk }) {
1890
+ const entry = entries[key];
1891
+ if (!entry) return false;
1892
+ return entry.source === hash$1(source) && entry.output === hash$1(disk);
1893
+ },
1894
+ track({ key, source }) {
1895
+ tracked.set(key, hash$1(source));
1896
+ },
1897
+ async commit() {
1898
+ try {
1899
+ const next = { ...entries };
1900
+ await inParallel({
1901
+ items: [...tracked],
1902
+ limit: 50,
1903
+ run: async ([key, source]) => {
1904
+ const stored = await storage.readItem(key);
1905
+ if (stored === null) return;
1906
+ next[key] = {
1907
+ source,
1908
+ output: hash$1(stored)
1909
+ };
1910
+ }
1911
+ });
1912
+ await cache.writeItem(MANIFEST_KEY, JSON.stringify({
1913
+ version: VERSION,
1914
+ entries: next
1915
+ }));
1916
+ } catch {}
1917
+ }
1918
+ };
1919
+ }
1920
+ //#endregion
1977
1921
  //#region src/createStorage.ts
1978
1922
  /**
1979
1923
  * Defines a custom storage backend. The builder receives user options and
@@ -2050,7 +1994,8 @@ function createLimiter(concurrency) {
2050
1994
  *
2051
1995
  * Writes are deduplicated and directory-safe:
2052
1996
  * - leading and trailing whitespace is trimmed before writing
2053
- * - the write is skipped when the file content is already identical
1997
+ * - the write is skipped when the file already holds that content, ignoring any trailing newline
1998
+ * a formatter left behind
2054
1999
  * - missing parent directories are created automatically
2055
2000
  * - Bun's native file API is used when running under Bun
2056
2001
  * - concurrent `writeItem` calls are capped at {@link WRITE_CONCURRENCY} in flight, so a caller
@@ -2111,6 +2056,60 @@ const fsStorage = createStorage(() => {
2111
2056
  };
2112
2057
  });
2113
2058
  //#endregion
2059
+ //#region src/storages/cacheStorage.ts
2060
+ /**
2061
+ * Directory Kubb keeps build caches in. A project with a `node_modules` gets
2062
+ * `node_modules/.cache/kubb`, the convention babel and eslint already use, so the cache stays out
2063
+ * of version control. Without one it falls back to the OS temp directory, keyed by root so two
2064
+ * projects sharing that directory keep their own cache.
2065
+ *
2066
+ * @example Inside a project
2067
+ * `resolveCacheDir('/project') // '/project/node_modules/.cache/kubb'`
2068
+ */
2069
+ function resolveCacheDir(root) {
2070
+ const nodeModules = join(root, "node_modules");
2071
+ if (existsSync(nodeModules)) return join(nodeModules, ".cache", "kubb");
2072
+ return join(tmpdir(), "kubb", createHash("sha256").update(root).digest("hex").slice(0, 16));
2073
+ }
2074
+ /**
2075
+ * Filesystem storage for build caches rather than generated code. Keys are plain names resolved
2076
+ * inside {@link resolveCacheDir}, so a caller stores `'x.json'` without knowing where the cache
2077
+ * lives. Kept apart from the configured output storage, which may not be a local disk at all.
2078
+ *
2079
+ * @example
2080
+ * ```ts
2081
+ * const cache = cacheStorage({ root: config.root })
2082
+ * await cache.writeItem('output-manifest.json', JSON.stringify(entries))
2083
+ * ```
2084
+ */
2085
+ const cacheStorage = createStorage(({ root = process.cwd() }) => {
2086
+ const dir = resolveCacheDir(root);
2087
+ const storage = fsStorage();
2088
+ const toPath = (key) => join(dir, key);
2089
+ return {
2090
+ name: "cache",
2091
+ async existsItem(key) {
2092
+ return storage.existsItem(toPath(key));
2093
+ },
2094
+ async readItem(key) {
2095
+ return storage.readItem(toPath(key));
2096
+ },
2097
+ async writeItem(key, value) {
2098
+ return storage.writeItem(toPath(key), value);
2099
+ },
2100
+ async removeItem(key) {
2101
+ return storage.removeItem(toPath(key));
2102
+ },
2103
+ async readKeys(base) {
2104
+ return storage.readKeys(base ? toPath(base) : dir);
2105
+ },
2106
+ async empty(base) {
2107
+ if (!base) return;
2108
+ return storage.empty(toPath(base));
2109
+ }
2110
+ };
2111
+ });
2112
+ //#endregion
2114
2113
  //#region src/createKubb.ts
2115
2114
  function resolveConfig(userConfig) {
2116
2115
  return {
@@ -2129,6 +2128,13 @@ function resolveConfig(userConfig) {
2129
2128
  };
2130
2129
  }
2131
2130
  /**
2131
+ * Whether anything runs over the output directory after the files are written. Only then can the
2132
+ * bytes on disk stop matching what Kubb wrote, which is what the manifest exists to track.
2133
+ */
2134
+ function hasOutputPasses(output) {
2135
+ return Boolean(output.format || output.lint || output.postGenerate?.length);
2136
+ }
2137
+ /**
2132
2138
  * Kubb code-generation instance bound to a single config entry. Resolves the user
2133
2139
  * config in the constructor, so `config` is available right away, and shares `hooks`,
2134
2140
  * `storage`, and `driver` across the `setup → build` lifecycle.
@@ -2150,6 +2156,7 @@ var Kubb = class {
2150
2156
  config;
2151
2157
  #driver = null;
2152
2158
  #storage = null;
2159
+ #manifest = null;
2153
2160
  constructor(userConfig, options = {}) {
2154
2161
  this.config = resolveConfig(userConfig);
2155
2162
  this.hooks = options.hooks ?? new Hookable();
@@ -2167,7 +2174,14 @@ var Kubb = class {
2167
2174
  */
2168
2175
  async setup() {
2169
2176
  const config = this.config;
2170
- const driver = new KubbDriver(config, { hooks: this.hooks });
2177
+ const manifest = hasOutputPasses(config.output) ? await createOutputManifest({
2178
+ storage: config.storage,
2179
+ cache: cacheStorage({ root: config.root })
2180
+ }) : void 0;
2181
+ const driver = new KubbDriver(config, {
2182
+ hooks: this.hooks,
2183
+ manifest
2184
+ });
2171
2185
  this.hooks.setMaxListeners(Math.max(10, config.plugins.length * 4));
2172
2186
  if (config.output.clean) {
2173
2187
  const cleanPath = resolve(config.root, config.output.path);
@@ -2183,6 +2197,7 @@ var Kubb = class {
2183
2197
  await driver.setup();
2184
2198
  this.#driver = driver;
2185
2199
  this.#storage = config.storage;
2200
+ this.#manifest = manifest ?? null;
2186
2201
  }
2187
2202
  /**
2188
2203
  * Runs the full pipeline and throws on any plugin error.
@@ -2270,6 +2285,7 @@ var Kubb = class {
2270
2285
  }) : [];
2271
2286
  const finalDiagnostics = [...diagnostics, ...outputDiagnostics];
2272
2287
  const failed = Diagnostics.hasError(outputDiagnostics);
2288
+ if (!failed) await this.#manifest?.commit();
2273
2289
  await hooks.callHook("kubb:generation:end", {
2274
2290
  config,
2275
2291
  storage,