@kubb/core 5.0.0-beta.1 → 5.0.0-beta.11
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/README.md +9 -39
- package/dist/{PluginDriver-BXibeQk-.cjs → PluginDriver-C1OsqGBJ.cjs} +106 -56
- package/dist/PluginDriver-C1OsqGBJ.cjs.map +1 -0
- package/dist/{PluginDriver-DV3p2Hky.js → PluginDriver-CGypdXHg.js} +101 -57
- package/dist/PluginDriver-CGypdXHg.js.map +1 -0
- package/dist/{types-CuNocrbJ.d.ts → createKubb-BSfMDBwR.d.ts} +1533 -1505
- package/dist/index.cjs +249 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -185
- package/dist/index.js +249 -209
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/dist/mocks.js.map +1 -1
- package/package.json +5 -12
- package/src/FileManager.ts +8 -0
- package/src/FileProcessor.ts +12 -7
- package/src/PluginDriver.ts +49 -7
- package/src/constants.ts +6 -2
- package/src/createAdapter.ts +77 -1
- package/src/createKubb.ts +973 -141
- package/src/defineGenerator.ts +92 -4
- package/src/defineLogger.ts +42 -3
- package/src/defineMiddleware.ts +1 -1
- package/src/definePlugin.ts +304 -8
- package/src/defineResolver.ts +185 -52
- package/src/devtools.ts +8 -1
- package/src/index.ts +1 -1
- package/src/mocks.ts +1 -2
- package/src/storages/fsStorage.ts +6 -31
- package/src/types.ts +38 -1292
- package/dist/PluginDriver-BXibeQk-.cjs.map +0 -1
- package/dist/PluginDriver-DV3p2Hky.js.map +0 -1
- package/src/Kubb.ts +0 -300
- package/src/renderNode.ts +0 -35
- package/src/utils/diagnostics.ts +0 -18
- package/src/utils/isInputPath.ts +0 -10
- package/src/utils/packageJSON.ts +0 -99
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { t as __name } from "./chunk--u3MIqq1.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as definePlugin, c as DEFAULT_STUDIO_URL, i as defineResolver, l as logLevel, n as applyHookResult, o as DEFAULT_BANNER, r as FileManager, s as DEFAULT_EXTENSION, t as PluginDriver, u as camelCase } from "./PluginDriver-CGypdXHg.js";
|
|
3
3
|
import { EventEmitter } from "node:events";
|
|
4
4
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import * as ast from "@kubb/ast";
|
|
7
|
-
import { extractStringsFromNodes, transform, walk } from "@kubb/ast";
|
|
7
|
+
import { collectUsedSchemaNames, extractStringsFromNodes, transform, walk } from "@kubb/ast";
|
|
8
8
|
import { version } from "node:process";
|
|
9
9
|
//#region ../../internals/utils/src/errors.ts
|
|
10
10
|
/**
|
|
@@ -524,6 +524,46 @@ function createAdapter(build) {
|
|
|
524
524
|
return (options) => build(options ?? {});
|
|
525
525
|
}
|
|
526
526
|
//#endregion
|
|
527
|
+
//#region package.json
|
|
528
|
+
var version$1 = "5.0.0-beta.11";
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/createStorage.ts
|
|
531
|
+
/**
|
|
532
|
+
* Factory for implementing custom storage backends that control where generated files are written.
|
|
533
|
+
*
|
|
534
|
+
* Takes a builder function `(options: TOptions) => Storage` and returns a factory `(options?: TOptions) => Storage`.
|
|
535
|
+
* Kubb provides filesystem and in-memory implementations out of the box.
|
|
536
|
+
*
|
|
537
|
+
* @note Call the returned factory with optional options to instantiate the storage adapter.
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```ts
|
|
541
|
+
* import { createStorage } from '@kubb/core'
|
|
542
|
+
*
|
|
543
|
+
* export const memoryStorage = createStorage(() => {
|
|
544
|
+
* const store = new Map<string, string>()
|
|
545
|
+
* return {
|
|
546
|
+
* name: 'memory',
|
|
547
|
+
* async hasItem(key) { return store.has(key) },
|
|
548
|
+
* async getItem(key) { return store.get(key) ?? null },
|
|
549
|
+
* async setItem(key, value) { store.set(key, value) },
|
|
550
|
+
* async removeItem(key) { store.delete(key) },
|
|
551
|
+
* async getKeys(base) {
|
|
552
|
+
* const keys = [...store.keys()]
|
|
553
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
554
|
+
* },
|
|
555
|
+
* async clear(base) { if (!base) store.clear() },
|
|
556
|
+
* }
|
|
557
|
+
* })
|
|
558
|
+
*
|
|
559
|
+
* // Instantiate:
|
|
560
|
+
* const storage = memoryStorage()
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
function createStorage(build) {
|
|
564
|
+
return (options) => build(options ?? {});
|
|
565
|
+
}
|
|
566
|
+
//#endregion
|
|
527
567
|
//#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
528
568
|
var Node$1 = class {
|
|
529
569
|
static {
|
|
@@ -665,7 +705,8 @@ function joinSources(file) {
|
|
|
665
705
|
* @internal
|
|
666
706
|
*/
|
|
667
707
|
var FileProcessor = class {
|
|
668
|
-
|
|
708
|
+
events = new AsyncEventEmitter();
|
|
709
|
+
#limit = pLimit(16);
|
|
669
710
|
async parse(file, { parsers, extension } = {}) {
|
|
670
711
|
const parseExtName = extension?.[file.extname] || void 0;
|
|
671
712
|
if (!parsers || !file.extname) return joinSources(file);
|
|
@@ -673,8 +714,8 @@ var FileProcessor = class {
|
|
|
673
714
|
if (!parser) return joinSources(file);
|
|
674
715
|
return parser.parse(file, { extname: parseExtName });
|
|
675
716
|
}
|
|
676
|
-
async run(files, { parsers, mode = "sequential", extension
|
|
677
|
-
await
|
|
717
|
+
async run(files, { parsers, mode = "sequential", extension } = {}) {
|
|
718
|
+
await this.events.emit("start", files);
|
|
678
719
|
const total = files.length;
|
|
679
720
|
let processed = 0;
|
|
680
721
|
const processOne = async (file) => {
|
|
@@ -684,7 +725,7 @@ var FileProcessor = class {
|
|
|
684
725
|
});
|
|
685
726
|
const currentProcessed = ++processed;
|
|
686
727
|
const percentage = currentProcessed / total * 100;
|
|
687
|
-
await
|
|
728
|
+
await this.events.emit("update", {
|
|
688
729
|
file,
|
|
689
730
|
source,
|
|
690
731
|
processed: currentProcessed,
|
|
@@ -694,56 +735,13 @@ var FileProcessor = class {
|
|
|
694
735
|
};
|
|
695
736
|
if (mode === "sequential") for (const file of files) await processOne(file);
|
|
696
737
|
else await Promise.all(files.map((file) => this.#limit(() => processOne(file))));
|
|
697
|
-
await
|
|
738
|
+
await this.events.emit("end", files);
|
|
698
739
|
return files;
|
|
699
740
|
}
|
|
700
741
|
};
|
|
701
742
|
//#endregion
|
|
702
|
-
//#region src/createStorage.ts
|
|
703
|
-
/**
|
|
704
|
-
* Factory for implementing custom storage backends that control where generated files are written.
|
|
705
|
-
*
|
|
706
|
-
* Takes a builder function `(options: TOptions) => Storage` and returns a factory `(options?: TOptions) => Storage`.
|
|
707
|
-
* Kubb provides filesystem and in-memory implementations out of the box.
|
|
708
|
-
*
|
|
709
|
-
* @note Call the returned factory with optional options to instantiate the storage adapter.
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
712
|
-
* ```ts
|
|
713
|
-
* import { createStorage } from '@kubb/core'
|
|
714
|
-
*
|
|
715
|
-
* export const memoryStorage = createStorage(() => {
|
|
716
|
-
* const store = new Map<string, string>()
|
|
717
|
-
* return {
|
|
718
|
-
* name: 'memory',
|
|
719
|
-
* async hasItem(key) { return store.has(key) },
|
|
720
|
-
* async getItem(key) { return store.get(key) ?? null },
|
|
721
|
-
* async setItem(key, value) { store.set(key, value) },
|
|
722
|
-
* async removeItem(key) { store.delete(key) },
|
|
723
|
-
* async getKeys(base) {
|
|
724
|
-
* const keys = [...store.keys()]
|
|
725
|
-
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
726
|
-
* },
|
|
727
|
-
* async clear(base) { if (!base) store.clear() },
|
|
728
|
-
* }
|
|
729
|
-
* })
|
|
730
|
-
*
|
|
731
|
-
* // Instantiate:
|
|
732
|
-
* const storage = memoryStorage()
|
|
733
|
-
* ```
|
|
734
|
-
*/
|
|
735
|
-
function createStorage(build) {
|
|
736
|
-
return (options) => build(options ?? {});
|
|
737
|
-
}
|
|
738
|
-
//#endregion
|
|
739
743
|
//#region src/storages/fsStorage.ts
|
|
740
744
|
/**
|
|
741
|
-
* Detects the filesystem error used to indicate that a path does not exist.
|
|
742
|
-
*/
|
|
743
|
-
function isMissingPathError(error) {
|
|
744
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
745
|
* Built-in filesystem storage driver.
|
|
748
746
|
*
|
|
749
747
|
* This is the default storage when no `storage` option is configured in the root config.
|
|
@@ -774,17 +772,15 @@ const fsStorage = createStorage(() => ({
|
|
|
774
772
|
try {
|
|
775
773
|
await access(resolve(key));
|
|
776
774
|
return true;
|
|
777
|
-
} catch (
|
|
778
|
-
|
|
779
|
-
throw new Error(`Failed to access storage item "${key}"`, { cause: error });
|
|
775
|
+
} catch (_error) {
|
|
776
|
+
return false;
|
|
780
777
|
}
|
|
781
778
|
},
|
|
782
779
|
async getItem(key) {
|
|
783
780
|
try {
|
|
784
781
|
return await readFile(resolve(key), "utf8");
|
|
785
|
-
} catch (
|
|
786
|
-
|
|
787
|
-
throw new Error(`Failed to read storage item "${key}"`, { cause: error });
|
|
782
|
+
} catch (_error) {
|
|
783
|
+
return null;
|
|
788
784
|
}
|
|
789
785
|
},
|
|
790
786
|
async setItem(key, value) {
|
|
@@ -800,9 +796,8 @@ const fsStorage = createStorage(() => ({
|
|
|
800
796
|
let entries;
|
|
801
797
|
try {
|
|
802
798
|
entries = await readdir(dir, { withFileTypes: true });
|
|
803
|
-
} catch (
|
|
804
|
-
|
|
805
|
-
throw new Error(`Failed to list storage keys under "${resolvedBase}"`, { cause: error });
|
|
799
|
+
} catch (_error) {
|
|
800
|
+
return;
|
|
806
801
|
}
|
|
807
802
|
for (const entry of entries) {
|
|
808
803
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
@@ -819,37 +814,69 @@ const fsStorage = createStorage(() => ({
|
|
|
819
814
|
}
|
|
820
815
|
}));
|
|
821
816
|
//#endregion
|
|
822
|
-
//#region
|
|
823
|
-
var version$1 = "5.0.0-beta.1";
|
|
824
|
-
//#endregion
|
|
825
|
-
//#region src/utils/diagnostics.ts
|
|
817
|
+
//#region src/createKubb.ts
|
|
826
818
|
/**
|
|
827
|
-
*
|
|
819
|
+
* Builds a `Storage` view scoped to the file paths produced by the current build.
|
|
828
820
|
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
821
|
+
* Reads delegate to the underlying `storage` (typically `fsStorage()`) so source bytes
|
|
822
|
+
* stay where they were written instead of being held in an extra in-memory map.
|
|
823
|
+
* Writing via `setItem` stores the content in the underlying storage and registers the
|
|
824
|
+
* key so subsequent reads and `getKeys` are scoped to this build's output.
|
|
831
825
|
*/
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
826
|
+
function createSourcesView(storage) {
|
|
827
|
+
const paths = /* @__PURE__ */ new Set();
|
|
828
|
+
return createStorage(() => ({
|
|
829
|
+
name: `${storage.name}:sources`,
|
|
830
|
+
async hasItem(key) {
|
|
831
|
+
return paths.has(key) && await storage.hasItem(key);
|
|
832
|
+
},
|
|
833
|
+
async getItem(key) {
|
|
834
|
+
return paths.has(key) ? storage.getItem(key) : null;
|
|
835
|
+
},
|
|
836
|
+
async setItem(key, value) {
|
|
837
|
+
paths.add(key);
|
|
838
|
+
await storage.setItem(key, value);
|
|
839
|
+
},
|
|
840
|
+
async removeItem(key) {
|
|
841
|
+
paths.delete(key);
|
|
842
|
+
await storage.removeItem(key);
|
|
843
|
+
},
|
|
844
|
+
async getKeys(base) {
|
|
845
|
+
if (!base) return [...paths];
|
|
846
|
+
const result = [];
|
|
847
|
+
for (const key of paths) if (key.startsWith(base)) result.push(key);
|
|
848
|
+
return result;
|
|
849
|
+
},
|
|
850
|
+
async clear() {
|
|
851
|
+
paths.clear();
|
|
852
|
+
await storage.clear();
|
|
853
|
+
}
|
|
854
|
+
}))();
|
|
845
855
|
}
|
|
846
|
-
//#endregion
|
|
847
|
-
//#region src/createKubb.ts
|
|
848
856
|
async function setup(userConfig, options = {}) {
|
|
849
857
|
const hooks = options.hooks ?? new AsyncEventEmitter();
|
|
850
|
-
const
|
|
858
|
+
const config = {
|
|
859
|
+
...userConfig,
|
|
860
|
+
root: userConfig.root || process.cwd(),
|
|
861
|
+
parsers: userConfig.parsers ?? [],
|
|
862
|
+
adapter: userConfig.adapter,
|
|
863
|
+
output: {
|
|
864
|
+
format: false,
|
|
865
|
+
lint: false,
|
|
866
|
+
extension: DEFAULT_EXTENSION,
|
|
867
|
+
defaultBanner: DEFAULT_BANNER,
|
|
868
|
+
...userConfig.output
|
|
869
|
+
},
|
|
870
|
+
storage: userConfig.storage ?? fsStorage(),
|
|
871
|
+
devtools: userConfig.devtools ? {
|
|
872
|
+
studioUrl: DEFAULT_STUDIO_URL,
|
|
873
|
+
...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
|
|
874
|
+
} : void 0,
|
|
875
|
+
plugins: userConfig.plugins ?? []
|
|
876
|
+
};
|
|
877
|
+
const driver = new PluginDriver(config, { hooks });
|
|
878
|
+
const storage = createSourcesView(config.storage);
|
|
851
879
|
const diagnosticInfo = getDiagnosticInfo();
|
|
852
|
-
if (Array.isArray(userConfig.input)) await hooks.emit("kubb:warn", { message: "This feature is still under development — use with caution" });
|
|
853
880
|
await hooks.emit("kubb:debug", {
|
|
854
881
|
date: /* @__PURE__ */ new Date(),
|
|
855
882
|
logs: [
|
|
@@ -859,7 +886,7 @@ async function setup(userConfig, options = {}) {
|
|
|
859
886
|
` • Output: ${userConfig.output?.path || "not specified"}`,
|
|
860
887
|
` • Plugins: ${userConfig.plugins?.length || 0}`,
|
|
861
888
|
"Output Settings:",
|
|
862
|
-
` • Storage: ${
|
|
889
|
+
` • Storage: ${config.storage.name}`,
|
|
863
890
|
` • Formatter: ${userConfig.output?.format || "none"}`,
|
|
864
891
|
` • Linter: ${userConfig.output?.lint || "none"}`,
|
|
865
892
|
"Environment:",
|
|
@@ -880,73 +907,56 @@ async function setup(userConfig, options = {}) {
|
|
|
880
907
|
throw new Error(`Cannot read file/URL defined in \`input.path\` or set with \`kubb generate PATH\` in the CLI of your Kubb config ${userConfig.input.path}`, { cause: error });
|
|
881
908
|
}
|
|
882
909
|
}
|
|
883
|
-
if (!userConfig.adapter) throw new Error("Adapter should be defined");
|
|
884
|
-
const config = {
|
|
885
|
-
...userConfig,
|
|
886
|
-
root: userConfig.root || process.cwd(),
|
|
887
|
-
parsers: userConfig.parsers ?? [],
|
|
888
|
-
adapter: userConfig.adapter,
|
|
889
|
-
output: {
|
|
890
|
-
format: false,
|
|
891
|
-
lint: false,
|
|
892
|
-
write: true,
|
|
893
|
-
extension: DEFAULT_EXTENSION,
|
|
894
|
-
defaultBanner: DEFAULT_BANNER,
|
|
895
|
-
...userConfig.output
|
|
896
|
-
},
|
|
897
|
-
devtools: userConfig.devtools ? {
|
|
898
|
-
studioUrl: DEFAULT_STUDIO_URL,
|
|
899
|
-
...typeof userConfig.devtools === "boolean" ? {} : userConfig.devtools
|
|
900
|
-
} : void 0,
|
|
901
|
-
plugins: userConfig.plugins
|
|
902
|
-
};
|
|
903
|
-
const storage = config.output.write === false ? null : config.storage ?? fsStorage();
|
|
904
910
|
if (config.output.clean) {
|
|
905
911
|
await hooks.emit("kubb:debug", {
|
|
906
912
|
date: /* @__PURE__ */ new Date(),
|
|
907
913
|
logs: ["Cleaning output directories", ` • Output: ${config.output.path}`]
|
|
908
914
|
});
|
|
909
|
-
await storage
|
|
915
|
+
await config.storage.clear(resolve(config.root, config.output.path));
|
|
910
916
|
}
|
|
911
|
-
const driver = new PluginDriver(config, { hooks });
|
|
912
917
|
function registerMiddlewareHook(event, middlewareHooks) {
|
|
913
918
|
const handler = middlewareHooks[event];
|
|
914
919
|
if (handler) hooks.on(event, handler);
|
|
915
920
|
}
|
|
916
921
|
for (const middleware of config.middleware ?? []) for (const event of Object.keys(middleware.hooks)) registerMiddlewareHook(event, middleware.hooks);
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
}
|
|
922
|
+
if (config.adapter) {
|
|
923
|
+
const source = inputToAdapterSource(config);
|
|
924
|
+
await hooks.emit("kubb:debug", {
|
|
925
|
+
date: /* @__PURE__ */ new Date(),
|
|
926
|
+
logs: [`Running adapter: ${config.adapter.name}`]
|
|
927
|
+
});
|
|
928
|
+
driver.adapter = config.adapter;
|
|
929
|
+
driver.inputNode = await config.adapter.parse(source);
|
|
930
|
+
await hooks.emit("kubb:debug", {
|
|
931
|
+
date: /* @__PURE__ */ new Date(),
|
|
932
|
+
logs: [
|
|
933
|
+
`✓ Adapter '${config.adapter.name}' resolved InputNode`,
|
|
934
|
+
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
935
|
+
` • Operations: ${driver.inputNode.operations.length}`
|
|
936
|
+
]
|
|
937
|
+
});
|
|
938
|
+
}
|
|
934
939
|
return {
|
|
935
940
|
config,
|
|
936
941
|
hooks,
|
|
937
942
|
driver,
|
|
938
|
-
sources,
|
|
939
943
|
storage
|
|
940
944
|
};
|
|
941
945
|
}
|
|
942
946
|
/**
|
|
943
947
|
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
944
948
|
* (`schema`, `operation`, `operations`).
|
|
949
|
+
*
|
|
950
|
+
* When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
|
|
951
|
+
* `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
|
|
952
|
+
* of top-level schema names transitively reachable from the included operations and skips
|
|
953
|
+
* schemas that fall outside that set. This ensures that component schemas referenced
|
|
954
|
+
* exclusively by excluded operations are not generated.
|
|
945
955
|
*/
|
|
946
956
|
async function runPluginAstHooks(plugin, context) {
|
|
947
957
|
const { adapter, inputNode, resolver, driver } = context;
|
|
948
958
|
const { exclude, include, override } = plugin.options;
|
|
949
|
-
if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g.
|
|
959
|
+
if (!adapter || !inputNode) throw new Error(`[${plugin.name}] No adapter found. Add an OAS adapter (e.g. adapterOas()) before this plugin in your Kubb config.`);
|
|
950
960
|
function resolveRenderer(gen) {
|
|
951
961
|
return gen.renderer === null ? void 0 : gen.renderer ?? plugin.renderer ?? context.config.renderer;
|
|
952
962
|
}
|
|
@@ -956,10 +966,27 @@ async function runPluginAstHooks(plugin, context) {
|
|
|
956
966
|
...context,
|
|
957
967
|
resolver: driver.getResolver(plugin.name)
|
|
958
968
|
};
|
|
969
|
+
const operationFilterTypes = new Set([
|
|
970
|
+
"tag",
|
|
971
|
+
"operationId",
|
|
972
|
+
"path",
|
|
973
|
+
"method",
|
|
974
|
+
"contentType"
|
|
975
|
+
]);
|
|
976
|
+
const hasOperationBasedIncludes = include?.some(({ type }) => operationFilterTypes.has(type)) ?? false;
|
|
977
|
+
const hasSchemaNameIncludes = include?.some(({ type }) => type === "schemaName") ?? false;
|
|
978
|
+
let allowedSchemaNames;
|
|
979
|
+
if (hasOperationBasedIncludes && !hasSchemaNameIncludes) allowedSchemaNames = collectUsedSchemaNames(inputNode.operations.filter((op) => resolver.resolveOptions(op, {
|
|
980
|
+
options: plugin.options,
|
|
981
|
+
exclude,
|
|
982
|
+
include,
|
|
983
|
+
override
|
|
984
|
+
}) !== null), inputNode.schemas);
|
|
959
985
|
await walk(inputNode, {
|
|
960
986
|
depth: "shallow",
|
|
961
987
|
async schema(node) {
|
|
962
988
|
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node;
|
|
989
|
+
if (allowedSchemaNames !== void 0 && transformedNode.name && !allowedSchemaNames.has(transformedNode.name)) return;
|
|
963
990
|
const options = resolver.resolveOptions(transformedNode, {
|
|
964
991
|
options: plugin.options,
|
|
965
992
|
exclude,
|
|
@@ -1012,10 +1039,49 @@ async function runPluginAstHooks(plugin, context) {
|
|
|
1012
1039
|
}
|
|
1013
1040
|
}
|
|
1014
1041
|
async function safeBuild(setupResult) {
|
|
1015
|
-
const { driver, hooks,
|
|
1042
|
+
const { driver, hooks, storage } = setupResult;
|
|
1016
1043
|
const failedPlugins = /* @__PURE__ */ new Set();
|
|
1017
1044
|
const pluginTimings = /* @__PURE__ */ new Map();
|
|
1018
1045
|
const config = driver.config;
|
|
1046
|
+
const writtenPaths = /* @__PURE__ */ new Set();
|
|
1047
|
+
const parsersMap = /* @__PURE__ */ new Map();
|
|
1048
|
+
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
1049
|
+
const fileProcessor = new FileProcessor();
|
|
1050
|
+
fileProcessor.events.on("start", async (processingFiles) => {
|
|
1051
|
+
await hooks.emit("kubb:files:processing:start", { files: processingFiles });
|
|
1052
|
+
});
|
|
1053
|
+
fileProcessor.events.on("update", async ({ file, source, processed, total, percentage }) => {
|
|
1054
|
+
await hooks.emit("kubb:file:processing:update", {
|
|
1055
|
+
file,
|
|
1056
|
+
source,
|
|
1057
|
+
processed,
|
|
1058
|
+
total,
|
|
1059
|
+
percentage,
|
|
1060
|
+
config
|
|
1061
|
+
});
|
|
1062
|
+
if (source) await storage.setItem(file.path, source);
|
|
1063
|
+
});
|
|
1064
|
+
fileProcessor.events.on("end", async (processed) => {
|
|
1065
|
+
await hooks.emit("kubb:files:processing:end", { files: processed });
|
|
1066
|
+
await hooks.emit("kubb:debug", {
|
|
1067
|
+
date: /* @__PURE__ */ new Date(),
|
|
1068
|
+
logs: [`✓ File write process completed for ${processed.length} files`]
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
async function flushPendingFiles() {
|
|
1072
|
+
const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path));
|
|
1073
|
+
if (files.length === 0) return;
|
|
1074
|
+
await hooks.emit("kubb:debug", {
|
|
1075
|
+
date: /* @__PURE__ */ new Date(),
|
|
1076
|
+
logs: [`Writing ${files.length} files...`]
|
|
1077
|
+
});
|
|
1078
|
+
await fileProcessor.run(files, {
|
|
1079
|
+
parsers: parsersMap,
|
|
1080
|
+
mode: "parallel",
|
|
1081
|
+
extension: config.output.extension
|
|
1082
|
+
});
|
|
1083
|
+
for (const file of files) writtenPaths.add(file.path);
|
|
1084
|
+
}
|
|
1019
1085
|
try {
|
|
1020
1086
|
await driver.emitSetupHooks();
|
|
1021
1087
|
if (driver.adapter && driver.inputNode) await hooks.emit("kubb:build:start", {
|
|
@@ -1051,6 +1117,7 @@ async function safeBuild(setupResult) {
|
|
|
1051
1117
|
},
|
|
1052
1118
|
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1053
1119
|
});
|
|
1120
|
+
await flushPendingFiles();
|
|
1054
1121
|
await hooks.emit("kubb:debug", {
|
|
1055
1122
|
date: /* @__PURE__ */ new Date(),
|
|
1056
1123
|
logs: [`✓ Plugin started successfully (${formatMs(duration)})`]
|
|
@@ -1070,6 +1137,7 @@ async function safeBuild(setupResult) {
|
|
|
1070
1137
|
},
|
|
1071
1138
|
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1072
1139
|
});
|
|
1140
|
+
await flushPendingFiles();
|
|
1073
1141
|
await hooks.emit("kubb:debug", {
|
|
1074
1142
|
date: errorTimestamp,
|
|
1075
1143
|
logs: [
|
|
@@ -1093,42 +1161,8 @@ async function safeBuild(setupResult) {
|
|
|
1093
1161
|
},
|
|
1094
1162
|
upsertFile: (...files) => driver.fileManager.upsert(...files)
|
|
1095
1163
|
});
|
|
1164
|
+
await flushPendingFiles();
|
|
1096
1165
|
const files = driver.fileManager.files;
|
|
1097
|
-
const parsersMap = /* @__PURE__ */ new Map();
|
|
1098
|
-
for (const parser of config.parsers) if (parser.extNames) for (const extname of parser.extNames) parsersMap.set(extname, parser);
|
|
1099
|
-
const fileProcessor = new FileProcessor();
|
|
1100
|
-
await hooks.emit("kubb:debug", {
|
|
1101
|
-
date: /* @__PURE__ */ new Date(),
|
|
1102
|
-
logs: [`Writing ${files.length} files...`]
|
|
1103
|
-
});
|
|
1104
|
-
await fileProcessor.run(files, {
|
|
1105
|
-
parsers: parsersMap,
|
|
1106
|
-
extension: config.output.extension,
|
|
1107
|
-
onStart: async (processingFiles) => {
|
|
1108
|
-
await hooks.emit("kubb:files:processing:start", { files: processingFiles });
|
|
1109
|
-
},
|
|
1110
|
-
onUpdate: async ({ file, source, processed, total, percentage }) => {
|
|
1111
|
-
await hooks.emit("kubb:file:processing:update", {
|
|
1112
|
-
file,
|
|
1113
|
-
source,
|
|
1114
|
-
processed,
|
|
1115
|
-
total,
|
|
1116
|
-
percentage,
|
|
1117
|
-
config
|
|
1118
|
-
});
|
|
1119
|
-
if (source) {
|
|
1120
|
-
await storage?.setItem(file.path, source);
|
|
1121
|
-
sources.set(file.path, source);
|
|
1122
|
-
}
|
|
1123
|
-
},
|
|
1124
|
-
onEnd: async (processedFiles) => {
|
|
1125
|
-
await hooks.emit("kubb:files:processing:end", { files: processedFiles });
|
|
1126
|
-
await hooks.emit("kubb:debug", {
|
|
1127
|
-
date: /* @__PURE__ */ new Date(),
|
|
1128
|
-
logs: [`✓ File write process completed for ${processedFiles.length} files`]
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
});
|
|
1132
1166
|
await hooks.emit("kubb:build:end", {
|
|
1133
1167
|
files,
|
|
1134
1168
|
config,
|
|
@@ -1139,7 +1173,7 @@ async function safeBuild(setupResult) {
|
|
|
1139
1173
|
files,
|
|
1140
1174
|
driver,
|
|
1141
1175
|
pluginTimings,
|
|
1142
|
-
|
|
1176
|
+
storage
|
|
1143
1177
|
};
|
|
1144
1178
|
} catch (error) {
|
|
1145
1179
|
return {
|
|
@@ -1148,14 +1182,14 @@ async function safeBuild(setupResult) {
|
|
|
1148
1182
|
driver,
|
|
1149
1183
|
pluginTimings,
|
|
1150
1184
|
error,
|
|
1151
|
-
|
|
1185
|
+
storage
|
|
1152
1186
|
};
|
|
1153
1187
|
} finally {
|
|
1154
1188
|
driver.dispose();
|
|
1155
1189
|
}
|
|
1156
1190
|
}
|
|
1157
1191
|
async function build(setupResult) {
|
|
1158
|
-
const { files, driver, failedPlugins, pluginTimings, error,
|
|
1192
|
+
const { files, driver, failedPlugins, pluginTimings, error, storage } = await safeBuild(setupResult);
|
|
1159
1193
|
if (error) throw error;
|
|
1160
1194
|
if (failedPlugins.size > 0) {
|
|
1161
1195
|
const errors = [...failedPlugins].map(({ error }) => error);
|
|
@@ -1167,32 +1201,48 @@ async function build(setupResult) {
|
|
|
1167
1201
|
driver,
|
|
1168
1202
|
pluginTimings,
|
|
1169
1203
|
error: void 0,
|
|
1170
|
-
|
|
1204
|
+
storage
|
|
1171
1205
|
};
|
|
1172
1206
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1207
|
+
/**
|
|
1208
|
+
* Returns a snapshot of the current runtime environment.
|
|
1209
|
+
*
|
|
1210
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
1211
|
+
* issues can be reproduced without manual information gathering.
|
|
1212
|
+
*/
|
|
1213
|
+
function getDiagnosticInfo() {
|
|
1214
|
+
return {
|
|
1215
|
+
nodeVersion: version,
|
|
1216
|
+
KubbVersion: version$1,
|
|
1217
|
+
platform: process.platform,
|
|
1218
|
+
arch: process.arch,
|
|
1219
|
+
cwd: process.cwd()
|
|
1177
1220
|
};
|
|
1178
|
-
|
|
1221
|
+
}
|
|
1222
|
+
function isInputPath(config) {
|
|
1223
|
+
return typeof config?.input === "object" && config.input !== null && "path" in config.input;
|
|
1224
|
+
}
|
|
1225
|
+
function inputToAdapterSource(config) {
|
|
1226
|
+
const input = config.input;
|
|
1227
|
+
if (!input) throw new Error("[kubb] input is required when using an adapter. Provide input.path or input.data in your config.");
|
|
1228
|
+
if ("data" in input) return {
|
|
1179
1229
|
type: "data",
|
|
1180
|
-
data:
|
|
1230
|
+
data: input.data
|
|
1181
1231
|
};
|
|
1182
|
-
if (new URLPath(
|
|
1232
|
+
if (new URLPath(input.path).isURL) return {
|
|
1183
1233
|
type: "path",
|
|
1184
|
-
path:
|
|
1234
|
+
path: input.path
|
|
1185
1235
|
};
|
|
1186
1236
|
return {
|
|
1187
1237
|
type: "path",
|
|
1188
|
-
path: resolve(config.root,
|
|
1238
|
+
path: resolve(config.root, input.path)
|
|
1189
1239
|
};
|
|
1190
1240
|
}
|
|
1191
1241
|
/**
|
|
1192
1242
|
* Creates a Kubb instance bound to a single config entry.
|
|
1193
1243
|
*
|
|
1194
1244
|
* Accepts a user-facing config shape and resolves it to a full {@link Config} during
|
|
1195
|
-
* `setup()`. The instance then holds shared state (`hooks`, `
|
|
1245
|
+
* `setup()`. The instance then holds shared state (`hooks`, `storage`, `driver`, `config`)
|
|
1196
1246
|
* across the `setup → build` lifecycle. Attach event listeners to `kubb.hooks` before
|
|
1197
1247
|
* calling `setup()` or `build()`.
|
|
1198
1248
|
*
|
|
@@ -1214,14 +1264,17 @@ function createKubb(userConfig, options = {}) {
|
|
|
1214
1264
|
get hooks() {
|
|
1215
1265
|
return hooks;
|
|
1216
1266
|
},
|
|
1217
|
-
get
|
|
1218
|
-
|
|
1267
|
+
get storage() {
|
|
1268
|
+
if (!setupResult) throw new Error("[kubb] setup() must be called before accessing storage");
|
|
1269
|
+
return setupResult.storage;
|
|
1219
1270
|
},
|
|
1220
1271
|
get driver() {
|
|
1221
|
-
|
|
1272
|
+
if (!setupResult) throw new Error("[kubb] setup() must be called before accessing driver");
|
|
1273
|
+
return setupResult.driver;
|
|
1222
1274
|
},
|
|
1223
1275
|
get config() {
|
|
1224
|
-
|
|
1276
|
+
if (!setupResult) throw new Error("[kubb] setup() must be called before accessing config");
|
|
1277
|
+
return setupResult.config;
|
|
1225
1278
|
},
|
|
1226
1279
|
async setup() {
|
|
1227
1280
|
setupResult = await setup(userConfig, { hooks });
|
|
@@ -1285,7 +1338,11 @@ function defineGenerator(generator) {
|
|
|
1285
1338
|
/**
|
|
1286
1339
|
* Wraps a logger definition into a typed {@link Logger}.
|
|
1287
1340
|
*
|
|
1288
|
-
*
|
|
1341
|
+
* The optional second type parameter `TInstallReturn` allows loggers to return
|
|
1342
|
+
* a value from `install` — for example, a sink factory that the caller can
|
|
1343
|
+
* forward to hook execution.
|
|
1344
|
+
*
|
|
1345
|
+
* @example Basic logger
|
|
1289
1346
|
* ```ts
|
|
1290
1347
|
* export const myLogger = defineLogger({
|
|
1291
1348
|
* name: 'my-logger',
|
|
@@ -1295,6 +1352,17 @@ function defineGenerator(generator) {
|
|
|
1295
1352
|
* },
|
|
1296
1353
|
* })
|
|
1297
1354
|
* ```
|
|
1355
|
+
*
|
|
1356
|
+
* @example Logger that returns a hook sink factory
|
|
1357
|
+
* ```ts
|
|
1358
|
+
* export const myLogger = defineLogger<LoggerOptions, HookSinkFactory>({
|
|
1359
|
+
* name: 'my-logger',
|
|
1360
|
+
* install(context, options) {
|
|
1361
|
+
* // … register event handlers …
|
|
1362
|
+
* return (commandWithArgs) => ({ onStdout: console.log })
|
|
1363
|
+
* },
|
|
1364
|
+
* })
|
|
1365
|
+
* ```
|
|
1298
1366
|
*/
|
|
1299
1367
|
function defineLogger(logger) {
|
|
1300
1368
|
return logger;
|
|
@@ -1365,34 +1433,6 @@ function defineParser(parser) {
|
|
|
1365
1433
|
return parser;
|
|
1366
1434
|
}
|
|
1367
1435
|
//#endregion
|
|
1368
|
-
//#region src/definePlugin.ts
|
|
1369
|
-
/**
|
|
1370
|
-
* Wraps a factory function and returns a typed `Plugin` with lifecycle handlers grouped under `hooks`.
|
|
1371
|
-
*
|
|
1372
|
-
* Handlers live in a single `hooks` object (inspired by Astro integrations).
|
|
1373
|
-
* All lifecycle events from `KubbHooks` are available for subscription.
|
|
1374
|
-
*
|
|
1375
|
-
* @note For real plugins, use a `PluginFactoryOptions` type parameter to get type-safe context in `kubb:plugin:setup`.
|
|
1376
|
-
* Plugin names should follow the convention `plugin-<feature>` (e.g., `plugin-react-query`, `plugin-zod`).
|
|
1377
|
-
*
|
|
1378
|
-
* @example
|
|
1379
|
-
* ```ts
|
|
1380
|
-
* import { definePlugin } from '@kubb/core'
|
|
1381
|
-
*
|
|
1382
|
-
* export const pluginTs = definePlugin((options: { prefix?: string } = {}) => ({
|
|
1383
|
-
* name: 'plugin-ts',
|
|
1384
|
-
* hooks: {
|
|
1385
|
-
* 'kubb:plugin:setup'(ctx) {
|
|
1386
|
-
* ctx.setResolver(resolverTs)
|
|
1387
|
-
* },
|
|
1388
|
-
* },
|
|
1389
|
-
* }))
|
|
1390
|
-
* ```
|
|
1391
|
-
*/
|
|
1392
|
-
function definePlugin(factory) {
|
|
1393
|
-
return (options) => factory(options ?? {});
|
|
1394
|
-
}
|
|
1395
|
-
//#endregion
|
|
1396
1436
|
//#region src/storages/memoryStorage.ts
|
|
1397
1437
|
/**
|
|
1398
1438
|
* In-memory storage driver. Useful for testing and dry-run scenarios where
|