@kubb/core 5.0.0-beta.41 → 5.0.0-beta.43

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.
@@ -25,7 +25,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  }) : target, mod));
26
26
  //#endregion
27
27
  let node_events = require("node:events");
28
+ let node_crypto = require("node:crypto");
28
29
  let node_util = require("node:util");
30
+ let node_fs_promises = require("node:fs/promises");
29
31
  let node_path = require("node:path");
30
32
  node_path = __toESM(node_path, 1);
31
33
  let _kubb_ast = require("@kubb/ast");
@@ -661,7 +663,7 @@ var URLPath = class {
661
663
  };
662
664
  //#endregion
663
665
  //#region package.json
664
- var version = "5.0.0-beta.41";
666
+ var version = "5.0.0-beta.43";
665
667
  //#endregion
666
668
  //#region src/constants.ts
667
669
  /**
@@ -1230,6 +1232,89 @@ function createStorage(build) {
1230
1232
  return (options) => build(options ?? {});
1231
1233
  }
1232
1234
  //#endregion
1235
+ //#region src/Fingerprint.ts
1236
+ /**
1237
+ * Computes the cache key for an incremental build. All methods are static, so call them as
1238
+ * `Fingerprint.compute(...)` and `Fingerprint.stringify(...)`. The key holds no absolute
1239
+ * paths or modification times, so it never depends on where the project lives on disk.
1240
+ */
1241
+ var Fingerprint = class Fingerprint {
1242
+ /**
1243
+ * Bumped when the snapshot format or fingerprint inputs change in an incompatible way, so stale
1244
+ * cache entries from older Kubb builds are never reused.
1245
+ */
1246
+ static version = 1;
1247
+ /**
1248
+ * Deterministically serializes a value to JSON: object keys are sorted recursively and
1249
+ * `undefined` values and functions are dropped. Two structurally equal configs produce the same
1250
+ * string regardless of key order, which keeps the fingerprint stable across machines.
1251
+ */
1252
+ static stringify(value) {
1253
+ return JSON.stringify(Fingerprint.#normalize(value));
1254
+ }
1255
+ /**
1256
+ * Computes a cache key from everything that affects the generated output: the spec content, the
1257
+ * output-shaping config, each plugin's name and options, the middleware names, the running
1258
+ * `@kubb/core` version, and the cache format version. Returns `null` when the input can't be
1259
+ * fingerprinted (remote URL or no adapter source), which disables caching for that build.
1260
+ */
1261
+ static async compute({ config, adapterSource, version }) {
1262
+ if (!adapterSource) return null;
1263
+ const spec = await Fingerprint.#readSpec(adapterSource, config.root);
1264
+ if (spec === null) return null;
1265
+ const input = {
1266
+ cacheVersion: Fingerprint.version,
1267
+ version,
1268
+ spec,
1269
+ name: config.name,
1270
+ output: config.output,
1271
+ adapter: config.adapter?.name,
1272
+ parsers: config.parsers.map((parser) => parser.name),
1273
+ plugins: config.plugins.map((plugin) => ({
1274
+ name: plugin.name,
1275
+ options: plugin.options
1276
+ })),
1277
+ middleware: (config.middleware ?? []).map((middleware) => middleware.name)
1278
+ };
1279
+ return (0, node_crypto.createHash)("sha256").update(Fingerprint.stringify(input)).digest("hex");
1280
+ }
1281
+ static #normalize(value) {
1282
+ if (value === null || typeof value !== "object") return typeof value === "function" ? void 0 : value;
1283
+ if (Array.isArray(value)) return value.map((item) => Fingerprint.#normalize(item));
1284
+ const source = value;
1285
+ const result = {};
1286
+ for (const key of Object.keys(source).sort()) {
1287
+ const normalized = Fingerprint.#normalize(source[key]);
1288
+ if (normalized !== void 0) result[key] = normalized;
1289
+ }
1290
+ return result;
1291
+ }
1292
+ /**
1293
+ * Reads the spec content that feeds the fingerprint. Returns `null` for a remote URL source
1294
+ * (hashing remote content would mean fetching it on every run) or when a file can't be read, so a
1295
+ * missing or virtual spec disables caching instead of failing the build.
1296
+ */
1297
+ static async #readSpec(source, root) {
1298
+ if (source.type === "data") return {
1299
+ kind: "data",
1300
+ data: typeof source.data === "string" ? source.data : Fingerprint.stringify(source.data)
1301
+ };
1302
+ const paths = source.type === "paths" ? source.paths : [source.path];
1303
+ if (paths.some((path) => new URLPath(path).isURL)) return null;
1304
+ try {
1305
+ return {
1306
+ kind: "path",
1307
+ contents: await Promise.all(paths.map(async (path) => ({
1308
+ path: (0, node_path.relative)(root, path),
1309
+ content: await (0, node_fs_promises.readFile)(path, "utf8")
1310
+ })))
1311
+ };
1312
+ } catch {
1313
+ return null;
1314
+ }
1315
+ }
1316
+ };
1317
+ //#endregion
1233
1318
  //#region src/definePlugin.ts
1234
1319
  /**
1235
1320
  * Wraps a plugin factory and returns a function that accepts user options and
@@ -2001,7 +2086,7 @@ var Transform = class {
2001
2086
  }
2002
2087
  };
2003
2088
  //#endregion
2004
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/usingCtx.js
2089
+ //#region \0@oxc-project+runtime@0.134.0/helpers/esm/usingCtx.js
2005
2090
  function _usingCtx() {
2006
2091
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
2007
2092
  var n = Error();
@@ -2124,9 +2209,10 @@ var KubbDriver = class KubbDriver {
2124
2209
  }
2125
2210
  async setup() {
2126
2211
  const normalized = this.config.plugins.map((rawPlugin) => this.#normalizePlugin(rawPlugin));
2212
+ const dependenciesByName = new Map(normalized.map((plugin) => [plugin.name, new Set(plugin.dependencies ?? [])]));
2127
2213
  normalized.sort((a, b) => {
2128
- if (b.dependencies?.includes(a.name)) return -1;
2129
- if (a.dependencies?.includes(b.name)) return 1;
2214
+ if (dependenciesByName.get(b.name)?.has(a.name)) return -1;
2215
+ if (dependenciesByName.get(a.name)?.has(b.name)) return 1;
2130
2216
  return enforceOrder(a.enforce) - enforceOrder(b.enforce);
2131
2217
  });
2132
2218
  for (const plugin of normalized) {
@@ -2351,8 +2437,10 @@ var KubbDriver = class KubbDriver {
2351
2437
  await hooks.emit("kubb:files:processing:start", { files });
2352
2438
  });
2353
2439
  const updateBuffer = [];
2440
+ const snapshotSources = /* @__PURE__ */ new Map();
2354
2441
  processor.hooks.on("update", (item) => {
2355
2442
  updateBuffer.push(item);
2443
+ if (item.source !== void 0) snapshotSources.set(item.file.path, item.source);
2356
2444
  });
2357
2445
  processor.hooks.on("end", async (files) => {
2358
2446
  await hooks.emit("kubb:files:processing:update", { files: updateBuffer.map((item) => ({
@@ -2368,6 +2456,19 @@ var KubbDriver = class KubbDriver {
2368
2456
  this.fileManager.hooks.on("upsert", onFileUpsert);
2369
2457
  return Diagnostics.scope((diagnostic) => diagnostics.push(diagnostic), async () => {
2370
2458
  try {
2459
+ const cache = config.cache;
2460
+ const outputRoot = (0, node_path.resolve)(config.root, config.output.path);
2461
+ const cacheKey = cache ? await Fingerprint.compute({
2462
+ config,
2463
+ adapterSource: this.#adapterSource,
2464
+ version
2465
+ }) : null;
2466
+ if (cache && cacheKey && await this.#restoreSnapshot({
2467
+ cache,
2468
+ cacheKey,
2469
+ outputRoot,
2470
+ storage
2471
+ })) return { diagnostics: Diagnostics.dedupe(diagnostics) };
2371
2472
  await this.#parseInput();
2372
2473
  await this.emitSetupHooks();
2373
2474
  if (this.adapter && this.inputNode) await hooks.emit("kubb:build:start", Object.assign({
@@ -2426,7 +2527,13 @@ var KubbDriver = class KubbDriver {
2426
2527
  await hooks.emit("kubb:build:end", {
2427
2528
  files: this.fileManager.files,
2428
2529
  config,
2429
- outputDir: (0, node_path.resolve)(config.root, config.output.path)
2530
+ outputDir: outputRoot
2531
+ });
2532
+ if (cache && cacheKey && !Diagnostics.hasError(diagnostics)) await this.#persistSnapshot({
2533
+ cache,
2534
+ cacheKey,
2535
+ outputRoot,
2536
+ sources: snapshotSources
2430
2537
  });
2431
2538
  return { diagnostics: Diagnostics.dedupe(diagnostics) };
2432
2539
  } catch (caughtError) {
@@ -2437,6 +2544,40 @@ var KubbDriver = class KubbDriver {
2437
2544
  }
2438
2545
  });
2439
2546
  }
2547
+ /**
2548
+ * Writes a restored snapshot straight to storage and emits `kubb:build:end`. Returns `true` on a
2549
+ * hit (the build is done), `false` on a miss so the caller falls through to a full build.
2550
+ */
2551
+ async #restoreSnapshot({ cache, cacheKey, outputRoot, storage }) {
2552
+ const snapshot = await cache.restore({ key: cacheKey });
2553
+ if (!snapshot) return false;
2554
+ for (const [relativePath, source] of Object.entries(snapshot.files)) {
2555
+ const absolutePath = (0, node_path.join)(outputRoot, relativePath);
2556
+ this.fileManager.upsert((0, _kubb_ast.createFile)({
2557
+ path: absolutePath,
2558
+ baseName: (0, node_path.basename)(relativePath)
2559
+ }));
2560
+ await storage.setItem(absolutePath, source);
2561
+ }
2562
+ await this.hooks.emit("kubb:build:end", {
2563
+ files: this.fileManager.files,
2564
+ config: this.config,
2565
+ outputDir: outputRoot
2566
+ });
2567
+ return true;
2568
+ }
2569
+ /**
2570
+ * Stores this run's rendered output, keyed by the input fingerprint, so the next unchanged build
2571
+ * restores it instead of regenerating. `sources` is keyed by absolute path and relativized here.
2572
+ */
2573
+ async #persistSnapshot({ cache, cacheKey, outputRoot, sources }) {
2574
+ const files = {};
2575
+ for (const [absolutePath, source] of sources) files[(0, node_path.relative)(outputRoot, absolutePath)] = source;
2576
+ await cache.persist({
2577
+ key: cacheKey,
2578
+ snapshot: { files }
2579
+ });
2580
+ }
2440
2581
  #filesPayload() {
2441
2582
  const driver = this;
2442
2583
  return {
@@ -2513,10 +2654,8 @@ var KubbDriver = class KubbDriver {
2513
2654
  const emitsSchemaHook = this.hooks.listenerCount("kubb:generate:schema") > 0;
2514
2655
  const emitsOperationHook = this.hooks.listenerCount("kubb:generate:operation") > 0;
2515
2656
  const emitsOperationsHook = this.hooks.listenerCount("kubb:generate:operations") > 0;
2516
- const schemasBuffer = [];
2517
- for await (const schema of schemas) schemasBuffer.push(schema);
2518
- const operationsBuffer = [];
2519
- for await (const operation of operations) operationsBuffer.push(operation);
2657
+ const schemasBuffer = await Array.fromAsync(schemas);
2658
+ const operationsBuffer = await Array.fromAsync(operations);
2520
2659
  const pruningStates = states.filter(({ plugin }) => {
2521
2660
  const { include } = plugin.options;
2522
2661
  return (include?.some(({ type }) => OPERATION_FILTER_TYPES.has(type)) ?? false) && !(include?.some(({ type }) => type === "schemaName") ?? false);
@@ -2990,4 +3129,4 @@ Object.defineProperty(exports, "memoryStorage", {
2990
3129
  }
2991
3130
  });
2992
3131
 
2993
- //# sourceMappingURL=memoryStorage-DZqlEW7H.cjs.map
3132
+ //# sourceMappingURL=memoryStorage-Biujme_q.cjs.map