@kubb/core 5.0.0-beta.103 → 5.0.0-beta.104

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/mocks.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_usingCtx = require("./usingCtx-CO6pGcST.cjs");
2
+ const require_usingCtx = require("./usingCtx-BYOZEkiy.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_usingCtx.__toESM(node_path, 1);
5
5
  let _kubb_ast = require("@kubb/ast");
@@ -83,6 +83,7 @@ function createMockedPluginContext(opts) {
83
83
  circularNames: [],
84
84
  enumNames: []
85
85
  },
86
+ cache: require_usingCtx.createNodeCache(),
86
87
  addFile: async (...files) => opts.driver.fileManager.add(...files),
87
88
  upsertFile: async (...files) => opts.driver.fileManager.upsert(...files),
88
89
  hooks: opts.driver.hooks ?? new require_usingCtx.Hookable(),
@@ -104,10 +105,12 @@ async function renderGeneratorSchema(generator, node, opts) {
104
105
  if (!generator.schema) return;
105
106
  const context = createMockedPluginContext(opts);
106
107
  const transformedNode = opts.plugin.macros?.length ? (0, _kubb_ast.applyMacros)(node, opts.plugin.macros) : node;
107
- const result = await generator.schema(transformedNode, {
108
+ const ctx = {
108
109
  ...context,
109
110
  options: opts.options
110
- });
111
+ };
112
+ if (!(generator.match ? await generator.match(transformedNode, ctx) : true)) return;
113
+ const result = await generator.schema(transformedNode, ctx);
111
114
  await opts.driver.dispatch({
112
115
  result,
113
116
  renderer: generator.renderer
@@ -126,10 +129,12 @@ async function renderGeneratorOperation(generator, node, opts) {
126
129
  if (!generator.operation) return;
127
130
  const context = createMockedPluginContext(opts);
128
131
  const transformedNode = opts.plugin.macros?.length ? (0, _kubb_ast.applyMacros)(node, opts.plugin.macros) : node;
129
- const result = await generator.operation(transformedNode, {
132
+ const ctx = {
130
133
  ...context,
131
134
  options: opts.options
132
- });
135
+ };
136
+ if (!(generator.match ? await generator.match(transformedNode, ctx) : true)) return;
137
+ const result = await generator.operation(transformedNode, ctx);
133
138
  await opts.driver.dispatch({
134
139
  result,
135
140
  renderer: generator.renderer
@@ -1 +1 @@
1
- {"version":3,"file":"mocks.cjs","names":["FileManager","ast","Hookable","path","camelCase"],"sources":["../src/mocks.ts"],"sourcesContent":["import path, { resolve } from 'node:path'\nimport { camelCase } from '@internals/utils'\nimport type { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from '@kubb/ast'\nimport { applyMacros, ast } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { Hookable } from './Hookable.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport type {\n Adapter,\n AdapterFactoryOptions,\n Config,\n Generator,\n GeneratorContext,\n KubbHooks,\n NormalizedPlugin,\n PluginFactoryOptions,\n RendererFactory,\n} from './types.ts'\n\n/**\n * Creates a minimal `KubbDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): KubbDriver {\n const fileManager = new FileManager()\n\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager,\n async dispatch({ result, renderer }: { result: unknown; renderer?: RendererFactory | null }): Promise<void> {\n if (!result) return\n\n if (Array.isArray(result)) {\n fileManager.upsert(...(result as Array<FileNode>))\n return\n }\n\n if (!renderer) return\n\n using instance = renderer()\n await instance.render(result)\n fileManager.upsert(...instance.files)\n },\n } as unknown as KubbDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default. Override via `options.parse`.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n } = {},\n): Adapter<TOptions> {\n const adapter: Adapter<TOptions> = {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n document: null,\n parse: options.parse ?? (async () => ast.factory.createInput()),\n validate: async () => {},\n }\n return adapter\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n macros?: Array<Macro>\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n macros: params.macros,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n meta?: InputMeta\n driver: KubbDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n meta: opts.meta ?? { circularNames: [], enumNames: [] },\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? new Hookable<KubbHooks>(),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const result = await generator.schema(transformedNode, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const result = await generator.operation(transformedNode, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => applyMacros(n, opts.plugin.macros!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\ntype MatchFilesOptions = {\n /**\n * Parsers indexed by file extension, used to render each `FileNode` to source.\n * Without a matching parser the file's raw content is used.\n */\n parsers?: Map<FileNode['extname'], Parser>\n /**\n * Formatter applied to non-JSON output before snapshotting, e.g. prettier. When\n * omitted the parsed source is snapshotted as-is.\n */\n format?: (source?: string) => string | Promise<string>\n /**\n * Subfolder under `__snapshots__`, camelCased. Useful to keep variant snapshots apart.\n */\n pre?: string\n}\n\n/**\n * Renders the driver's collected `FileNode`s to source and asserts each against a file snapshot.\n * Pair it with the `renderGenerator*` helpers to snapshot a generator's output.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files, { parsers, format })\n * ```\n */\nexport async function matchFiles(files: Array<FileNode> | undefined, options: MatchFilesOptions = {}): Promise<Map<string, string> | undefined> {\n if (!files?.length) return\n\n const { parsers = new Map(), format, pre } = options\n const fileManager = new FileManager()\n const processed = new Map<string, string>()\n\n for (const file of files) {\n if (!file?.path || processed.has(file.path)) {\n continue\n }\n\n const parsed = await fileManager.parse(file, { parsers })\n const code = file.baseName.endsWith('.json') || !format ? parsed : await format(parsed)\n\n processed.set(file.path, code)\n\n const snapshotPath = path.join('__snapshots__', ...(pre ? [camelCase(pre)] : []), file.baseName)\n await expect(code).toMatchFileSnapshot(snapshotPath)\n }\n\n return processed\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,yBAAyB,UAAyE,CAAC,GAAe;CAChI,MAAM,cAAc,IAAIA,iBAAAA,YAAY;CAEpC,OAAO;EACL,QAAQ,SAAS,UAAU;GACzB,MAAM;GACN,QAAQ,EACN,MAAM,SACR;EACF;EACA,UAAU,aAAmD;GAC3D,OAAO,SAAS;EAClB;EACA,cAAc,gBAAwB,SAAS,QAAQ;EACvD;EACA,MAAM,SAAS,EAAE,QAAQ,YAAmF;;;IAC1G,IAAI,CAAC,QAAQ;IAEb,IAAI,MAAM,QAAQ,MAAM,GAAG;KACzB,YAAY,OAAO,GAAI,MAA0B;KACjD;IACF;IAEA,IAAI,CAAC,UAAU;IAEf,MAAM,WAAA,YAAA,EAAW,SAAS,CAAA;IAC1B,MAAM,SAAS,OAAO,MAAM;IAC5B,YAAY,OAAO,GAAG,SAAS,KAAK;;;;;;EACtC;CACF;AACF;;;;;AAMA,SAAgB,oBACd,UAII,CAAC,GACc;CAQnB,OAAO;EANL,MAAO,QAAQ,QAAQ;EACvB,SAAU,QAAQ,mBAAmB,CAAC;EACtC,UAAU;EACV,OAAO,QAAQ,UAAU,YAAYC,UAAAA,IAAI,QAAQ,YAAY;EAC7D,UAAU,YAAY,CAAC;CAEZ;AACf;;;;;;;AAQA,SAAgB,mBAAiF,QAMlE;CAC7B,OAAO;EACL,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,QAAQ,OAAO;EACf,cAAc,OAAO;EACrB,OAAO,CAAC;CACV;AACF;AAYA,SAAS,0BAAiE,MAAqF;CAC7J,MAAM,QAAA,GAAA,UAAA,QAAA,CAAe,KAAK,OAAO,MAAM,KAAK,OAAO,OAAO,IAAI;CAE9D,OAAO;EACL,QAAQ,KAAK;EACb;EACA,SAAS,KAAK;EACd,UAAU,KAAK;EACf,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,cAAc,SAAiB,KAAK,OAAO,YAAY,IAAI;EAC3D,MAAM,KAAK,QAAQ;GAAE,eAAe,CAAC;GAAG,WAAW,CAAC;EAAE;EACtD,SAAS,OAAO,GAAG,UAA2B,KAAK,OAAO,YAAY,IAAI,GAAG,KAAK;EAClF,YAAY,OAAO,GAAG,UAA2B,KAAK,OAAO,YAAY,OAAO,GAAG,KAAK;EACxF,OAAO,KAAK,OAAO,SAAS,IAAIC,iBAAAA,SAAoB;EACpD,OAAO,QAAgB,QAAQ,KAAK,GAAG;EACvC,QAAQ,QAAgB,QAAQ,MAAM,GAAG;EACzC,OAAO,QAAgB,QAAQ,KAAK,GAAG;CACzC;AACF;;;;;;;;;;AAWA,eAAsB,sBACpB,WACA,MACA,MACe;CACf,IAAI,CAAC,UAAU,QAAQ;CACvB,MAAM,UAAU,0BAA0B,IAAI;CAC9C,MAAM,kBAAkB,KAAK,OAAO,QAAQ,UAAA,GAAA,UAAA,YAAA,CAAqB,MAAM,KAAK,OAAO,MAAM,IAAI;CAC7F,MAAM,SAAS,MAAM,UAAU,OAAO,iBAAiB;EACrD,GAAG;EACH,SAAS,KAAK;CAChB,CAAC;CACD,MAAM,KAAK,OAAO,SAAS;EAAE;EAAQ,UAAU,UAAU;CAAS,CAAC;AACrE;;;;;;;;;;AAWA,eAAsB,yBACpB,WACA,MACA,MACe;CACf,IAAI,CAAC,UAAU,WAAW;CAC1B,MAAM,UAAU,0BAA0B,IAAI;CAC9C,MAAM,kBAAkB,KAAK,OAAO,QAAQ,UAAA,GAAA,UAAA,YAAA,CAAqB,MAAM,KAAK,OAAO,MAAM,IAAI;CAC7F,MAAM,SAAS,MAAM,UAAU,UAAU,iBAAiB;EACxD,GAAG;EACH,SAAS,KAAK;CAChB,CAAC;CACD,MAAM,KAAK,OAAO,SAAS;EAAE;EAAQ,UAAU,UAAU;CAAS,CAAC;AACrE;;;;;;;;;;AAWA,eAAsB,0BACpB,WACA,OACA,MACe;CACf,IAAI,CAAC,UAAU,YAAY;CAC3B,MAAM,UAAU,0BAA0B,IAAI;CAC9C,MAAM,mBAAmB,KAAK,OAAO,QAAQ,SAAS,MAAM,KAAK,OAAA,GAAA,UAAA,YAAA,CAAkB,GAAG,KAAK,OAAO,MAAO,CAAC,IAAI;CAC9G,MAAM,SAAS,MAAM,UAAU,WAAW,kBAAkB;EAC1D,GAAG;EACH,SAAS,KAAK;CAChB,CAAC;CACD,MAAM,KAAK,OAAO,SAAS;EAAE;EAAQ,UAAU,UAAU;CAAS,CAAC;AACrE;;;;;;;;;;;AA6BA,eAAsB,WAAW,OAAoC,UAA6B,CAAC,GAA6C;CAC9I,IAAI,CAAC,OAAO,QAAQ;CAEpB,MAAM,EAAE,0BAAU,IAAI,IAAI,GAAG,QAAQ,QAAQ;CAC7C,MAAM,cAAc,IAAIF,iBAAAA,YAAY;CACpC,MAAM,4BAAY,IAAI,IAAoB;CAE1C,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,MAAM,QAAQ,UAAU,IAAI,KAAK,IAAI,GACxC;EAGF,MAAM,SAAS,MAAM,YAAY,MAAM,MAAM,EAAE,QAAQ,CAAC;EACxD,MAAM,OAAO,KAAK,SAAS,SAAS,OAAO,KAAK,CAAC,SAAS,SAAS,MAAM,OAAO,MAAM;EAEtF,UAAU,IAAI,KAAK,MAAM,IAAI;EAE7B,MAAM,eAAeG,UAAAA,QAAK,KAAK,iBAAiB,GAAI,MAAM,CAACC,iBAAAA,UAAU,GAAG,CAAC,IAAI,CAAC,GAAI,KAAK,QAAQ;EAC/F,OAAA,GAAA,OAAA,OAAA,CAAa,IAAI,CAAC,CAAC,oBAAoB,YAAY;CACrD;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"mocks.cjs","names":["FileManager","ast","createNodeCache","Hookable","path","camelCase"],"sources":["../src/mocks.ts"],"sourcesContent":["import path, { resolve } from 'node:path'\nimport { camelCase } from '@internals/utils'\nimport type { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from '@kubb/ast'\nimport { applyMacros, ast } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { Hookable } from './Hookable.ts'\nimport { createNodeCache } from './nodeCache.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport type {\n Adapter,\n AdapterFactoryOptions,\n Config,\n Generator,\n GeneratorContext,\n KubbHooks,\n NormalizedPlugin,\n PluginFactoryOptions,\n RendererFactory,\n} from './types.ts'\n\n/**\n * Creates a minimal `KubbDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): KubbDriver {\n const fileManager = new FileManager()\n\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager,\n async dispatch({ result, renderer }: { result: unknown; renderer?: RendererFactory | null }): Promise<void> {\n if (!result) return\n\n if (Array.isArray(result)) {\n fileManager.upsert(...(result as Array<FileNode>))\n return\n }\n\n if (!renderer) return\n\n using instance = renderer()\n await instance.render(result)\n fileManager.upsert(...instance.files)\n },\n } as unknown as KubbDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default. Override via `options.parse`.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n } = {},\n): Adapter<TOptions> {\n const adapter: Adapter<TOptions> = {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n document: null,\n parse: options.parse ?? (async () => ast.factory.createInput()),\n validate: async () => {},\n }\n return adapter\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n macros?: Array<Macro>\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n macros: params.macros,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n meta?: InputMeta\n driver: KubbDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n meta: opts.meta ?? { circularNames: [], enumNames: [] },\n cache: createNodeCache(),\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? new Hookable<KubbHooks>(),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const ctx = { ...context, options: opts.options }\n const matches = generator.match ? await generator.match(transformedNode, ctx) : true\n if (!matches) return\n const result = await generator.schema(transformedNode, ctx)\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const ctx = { ...context, options: opts.options }\n const matches = generator.match ? await generator.match(transformedNode, ctx) : true\n if (!matches) return\n const result = await generator.operation(transformedNode, ctx)\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => applyMacros(n, opts.plugin.macros!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\ntype MatchFilesOptions = {\n /**\n * Parsers indexed by file extension, used to render each `FileNode` to source.\n * Without a matching parser the file's raw content is used.\n */\n parsers?: Map<FileNode['extname'], Parser>\n /**\n * Formatter applied to non-JSON output before snapshotting, e.g. prettier. When\n * omitted the parsed source is snapshotted as-is.\n */\n format?: (source?: string) => string | Promise<string>\n /**\n * Subfolder under `__snapshots__`, camelCased. Useful to keep variant snapshots apart.\n */\n pre?: string\n}\n\n/**\n * Renders the driver's collected `FileNode`s to source and asserts each against a file snapshot.\n * Pair it with the `renderGenerator*` helpers to snapshot a generator's output.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files, { parsers, format })\n * ```\n */\nexport async function matchFiles(files: Array<FileNode> | undefined, options: MatchFilesOptions = {}): Promise<Map<string, string> | undefined> {\n if (!files?.length) return\n\n const { parsers = new Map(), format, pre } = options\n const fileManager = new FileManager()\n const processed = new Map<string, string>()\n\n for (const file of files) {\n if (!file?.path || processed.has(file.path)) {\n continue\n }\n\n const parsed = await fileManager.parse(file, { parsers })\n const code = file.baseName.endsWith('.json') || !format ? parsed : await format(parsed)\n\n processed.set(file.path, code)\n\n const snapshotPath = path.join('__snapshots__', ...(pre ? [camelCase(pre)] : []), file.baseName)\n await expect(code).toMatchFileSnapshot(snapshotPath)\n }\n\n return processed\n}\n"],"mappings":"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"}
package/dist/mocks.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { Bt as Adapter, F as KubbDriver, K as NormalizedPlugin, L as Parser, M as Generator, Q as PluginFactoryOptions, Vt as AdapterFactoryOptions, r as Config } from "./types-CYp43kqg.js";
2
+ import { $ as PluginFactoryOptions, Ht as AdapterFactoryOptions, I as KubbDriver, M as Generator, R as Parser, Vt as Adapter, q as NormalizedPlugin, r as Config } from "./types-D116CBzm.js";
3
3
  import { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from "@kubb/ast";
4
4
  //#region src/mocks.d.ts
5
5
  /**
package/dist/mocks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./rolldown-runtime-C0LytTxp.js";
2
- import { f as camelCase, n as FileManager, r as Hookable, t as _usingCtx } from "./usingCtx-C8EG-wqH.js";
2
+ import { i as Hookable, n as createNodeCache, p as camelCase, r as FileManager, t as _usingCtx } from "./usingCtx-C24YU3KM.js";
3
3
  import path, { resolve } from "node:path";
4
4
  import { applyMacros, ast } from "@kubb/ast";
5
5
  import { expect } from "vitest";
@@ -82,6 +82,7 @@ function createMockedPluginContext(opts) {
82
82
  circularNames: [],
83
83
  enumNames: []
84
84
  },
85
+ cache: createNodeCache(),
85
86
  addFile: async (...files) => opts.driver.fileManager.add(...files),
86
87
  upsertFile: async (...files) => opts.driver.fileManager.upsert(...files),
87
88
  hooks: opts.driver.hooks ?? new Hookable(),
@@ -103,10 +104,12 @@ async function renderGeneratorSchema(generator, node, opts) {
103
104
  if (!generator.schema) return;
104
105
  const context = createMockedPluginContext(opts);
105
106
  const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node;
106
- const result = await generator.schema(transformedNode, {
107
+ const ctx = {
107
108
  ...context,
108
109
  options: opts.options
109
- });
110
+ };
111
+ if (!(generator.match ? await generator.match(transformedNode, ctx) : true)) return;
112
+ const result = await generator.schema(transformedNode, ctx);
110
113
  await opts.driver.dispatch({
111
114
  result,
112
115
  renderer: generator.renderer
@@ -125,10 +128,12 @@ async function renderGeneratorOperation(generator, node, opts) {
125
128
  if (!generator.operation) return;
126
129
  const context = createMockedPluginContext(opts);
127
130
  const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node;
128
- const result = await generator.operation(transformedNode, {
131
+ const ctx = {
129
132
  ...context,
130
133
  options: opts.options
131
- });
134
+ };
135
+ if (!(generator.match ? await generator.match(transformedNode, ctx) : true)) return;
136
+ const result = await generator.operation(transformedNode, ctx);
132
137
  await opts.driver.dispatch({
133
138
  result,
134
139
  renderer: generator.renderer
package/dist/mocks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mocks.js","names":[],"sources":["../src/mocks.ts"],"sourcesContent":["import path, { resolve } from 'node:path'\nimport { camelCase } from '@internals/utils'\nimport type { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from '@kubb/ast'\nimport { applyMacros, ast } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { Hookable } from './Hookable.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport type {\n Adapter,\n AdapterFactoryOptions,\n Config,\n Generator,\n GeneratorContext,\n KubbHooks,\n NormalizedPlugin,\n PluginFactoryOptions,\n RendererFactory,\n} from './types.ts'\n\n/**\n * Creates a minimal `KubbDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): KubbDriver {\n const fileManager = new FileManager()\n\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager,\n async dispatch({ result, renderer }: { result: unknown; renderer?: RendererFactory | null }): Promise<void> {\n if (!result) return\n\n if (Array.isArray(result)) {\n fileManager.upsert(...(result as Array<FileNode>))\n return\n }\n\n if (!renderer) return\n\n using instance = renderer()\n await instance.render(result)\n fileManager.upsert(...instance.files)\n },\n } as unknown as KubbDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default. Override via `options.parse`.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n } = {},\n): Adapter<TOptions> {\n const adapter: Adapter<TOptions> = {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n document: null,\n parse: options.parse ?? (async () => ast.factory.createInput()),\n validate: async () => {},\n }\n return adapter\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n macros?: Array<Macro>\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n macros: params.macros,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n meta?: InputMeta\n driver: KubbDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n meta: opts.meta ?? { circularNames: [], enumNames: [] },\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? new Hookable<KubbHooks>(),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const result = await generator.schema(transformedNode, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const result = await generator.operation(transformedNode, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => applyMacros(n, opts.plugin.macros!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\ntype MatchFilesOptions = {\n /**\n * Parsers indexed by file extension, used to render each `FileNode` to source.\n * Without a matching parser the file's raw content is used.\n */\n parsers?: Map<FileNode['extname'], Parser>\n /**\n * Formatter applied to non-JSON output before snapshotting, e.g. prettier. When\n * omitted the parsed source is snapshotted as-is.\n */\n format?: (source?: string) => string | Promise<string>\n /**\n * Subfolder under `__snapshots__`, camelCased. Useful to keep variant snapshots apart.\n */\n pre?: string\n}\n\n/**\n * Renders the driver's collected `FileNode`s to source and asserts each against a file snapshot.\n * Pair it with the `renderGenerator*` helpers to snapshot a generator's output.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files, { parsers, format })\n * ```\n */\nexport async function matchFiles(files: Array<FileNode> | undefined, options: MatchFilesOptions = {}): Promise<Map<string, string> | undefined> {\n if (!files?.length) return\n\n const { parsers = new Map(), format, pre } = options\n const fileManager = new FileManager()\n const processed = new Map<string, string>()\n\n for (const file of files) {\n if (!file?.path || processed.has(file.path)) {\n continue\n }\n\n const parsed = await fileManager.parse(file, { parsers })\n const code = file.baseName.endsWith('.json') || !format ? parsed : await format(parsed)\n\n processed.set(file.path, code)\n\n const snapshotPath = path.join('__snapshots__', ...(pre ? [camelCase(pre)] : []), file.baseName)\n await expect(code).toMatchFileSnapshot(snapshotPath)\n }\n\n return processed\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mocks.js","names":[],"sources":["../src/mocks.ts"],"sourcesContent":["import path, { resolve } from 'node:path'\nimport { camelCase } from '@internals/utils'\nimport type { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from '@kubb/ast'\nimport { applyMacros, ast } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { Hookable } from './Hookable.ts'\nimport { createNodeCache } from './nodeCache.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport type {\n Adapter,\n AdapterFactoryOptions,\n Config,\n Generator,\n GeneratorContext,\n KubbHooks,\n NormalizedPlugin,\n PluginFactoryOptions,\n RendererFactory,\n} from './types.ts'\n\n/**\n * Creates a minimal `KubbDriver` mock for unit tests.\n */\nexport function createMockedPluginDriver(options: { name?: string; plugin?: NormalizedPlugin; config?: Config } = {}): KubbDriver {\n const fileManager = new FileManager()\n\n return {\n config: options?.config ?? {\n root: '.',\n output: {\n path: './path',\n },\n },\n getPlugin(_pluginName: string): NormalizedPlugin | undefined {\n return options?.plugin\n },\n getResolver: (_pluginName: string) => options?.plugin?.resolver,\n fileManager,\n async dispatch({ result, renderer }: { result: unknown; renderer?: RendererFactory | null }): Promise<void> {\n if (!result) return\n\n if (Array.isArray(result)) {\n fileManager.upsert(...(result as Array<FileNode>))\n return\n }\n\n if (!renderer) return\n\n using instance = renderer()\n await instance.render(result)\n fileManager.upsert(...instance.files)\n },\n } as unknown as KubbDriver\n}\n\n/**\n * Creates a minimal `Adapter` mock for unit tests.\n * `parse` returns an empty `InputNode` by default. Override via `options.parse`.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n } = {},\n): Adapter<TOptions> {\n const adapter: Adapter<TOptions> = {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n document: null,\n parse: options.parse ?? (async () => ast.factory.createInput()),\n validate: async () => {},\n }\n return adapter\n}\n\n/**\n * Creates a minimal plugin mock for unit tests.\n *\n * @example\n * `const plugin = createMockedPlugin<PluginTs>({ name: '@kubb/plugin-ts', options })`\n */\nexport function createMockedPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions>(params: {\n name: TOptions['name']\n options: TOptions['resolvedOptions']\n resolver?: TOptions['resolver']\n macros?: Array<Macro>\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n macros: params.macros,\n dependencies: params.dependencies,\n hooks: {},\n } as unknown as NormalizedPlugin<TOptions>\n}\n\ntype RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {\n config: Config\n adapter: Adapter\n meta?: InputMeta\n driver: KubbDriver\n plugin: NormalizedPlugin<TOptions>\n options: TOptions['resolvedOptions']\n resolver: TOptions['resolver']\n}\n\nfunction createMockedPluginContext<TOptions extends PluginFactoryOptions>(opts: RenderGeneratorOptions<TOptions>): Omit<GeneratorContext<TOptions>, 'options'> {\n const root = resolve(opts.config.root, opts.config.output.path)\n\n return {\n config: opts.config,\n root,\n adapter: opts.adapter,\n resolver: opts.resolver,\n plugin: opts.plugin,\n driver: opts.driver,\n getResolver: (name: string) => opts.driver.getResolver(name),\n meta: opts.meta ?? { circularNames: [], enumNames: [] },\n cache: createNodeCache(),\n addFile: async (...files: Array<FileNode>) => opts.driver.fileManager.add(...files),\n upsertFile: async (...files: Array<FileNode>) => opts.driver.fileManager.upsert(...files),\n hooks: opts.driver.hooks ?? new Hookable<KubbHooks>(),\n warn: (msg: string) => console.warn(msg),\n error: (msg: string) => console.error(msg),\n info: (msg: string) => console.info(msg),\n } as unknown as Omit<GeneratorContext<TOptions>, 'options'>\n}\n\n/**\n * Renders a generator's `schema` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorSchema<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: SchemaNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.schema) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const ctx = { ...context, options: opts.options }\n const matches = generator.match ? await generator.match(transformedNode, ctx) : true\n if (!matches) return\n const result = await generator.schema(transformedNode, ctx)\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operation` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperation(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperation<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n node: OperationNode,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operation) return\n const context = createMockedPluginContext(opts)\n const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node\n const ctx = { ...context, options: opts.options }\n const matches = generator.match ? await generator.match(transformedNode, ctx) : true\n if (!matches) return\n const result = await generator.operation(transformedNode, ctx)\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\n/**\n * Renders a generator's `operations` method in a test context.\n *\n * @example\n * ```ts\n * await renderGeneratorOperations(classClientGenerator, nodes, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files)\n * ```\n */\nexport async function renderGeneratorOperations<TOptions extends PluginFactoryOptions>(\n generator: Generator<TOptions>,\n nodes: Array<OperationNode>,\n opts: RenderGeneratorOptions<TOptions>,\n): Promise<void> {\n if (!generator.operations) return\n const context = createMockedPluginContext(opts)\n const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => applyMacros(n, opts.plugin.macros!)) : nodes\n const result = await generator.operations(transformedNodes, {\n ...context,\n options: opts.options,\n })\n await opts.driver.dispatch({ result, renderer: generator.renderer })\n}\n\ntype MatchFilesOptions = {\n /**\n * Parsers indexed by file extension, used to render each `FileNode` to source.\n * Without a matching parser the file's raw content is used.\n */\n parsers?: Map<FileNode['extname'], Parser>\n /**\n * Formatter applied to non-JSON output before snapshotting, e.g. prettier. When\n * omitted the parsed source is snapshotted as-is.\n */\n format?: (source?: string) => string | Promise<string>\n /**\n * Subfolder under `__snapshots__`, camelCased. Useful to keep variant snapshots apart.\n */\n pre?: string\n}\n\n/**\n * Renders the driver's collected `FileNode`s to source and asserts each against a file snapshot.\n * Pair it with the `renderGenerator*` helpers to snapshot a generator's output.\n *\n * @example\n * ```ts\n * await renderGeneratorSchema(typeGenerator, node, { config, adapter, driver, plugin, options, resolver })\n * await matchFiles(driver.fileManager.files, { parsers, format })\n * ```\n */\nexport async function matchFiles(files: Array<FileNode> | undefined, options: MatchFilesOptions = {}): Promise<Map<string, string> | undefined> {\n if (!files?.length) return\n\n const { parsers = new Map(), format, pre } = options\n const fileManager = new FileManager()\n const processed = new Map<string, string>()\n\n for (const file of files) {\n if (!file?.path || processed.has(file.path)) {\n continue\n }\n\n const parsed = await fileManager.parse(file, { parsers })\n const code = file.baseName.endsWith('.json') || !format ? parsed : await format(parsed)\n\n processed.set(file.path, code)\n\n const snapshotPath = path.join('__snapshots__', ...(pre ? [camelCase(pre)] : []), file.baseName)\n await expect(code).toMatchFileSnapshot(snapshotPath)\n }\n\n return processed\n}\n"],"mappings":"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"}
@@ -1207,6 +1207,10 @@ declare class Resolver {
1207
1207
  * schemas (`targetName`) import the emitted name. Names and paths go through the top-level
1208
1208
  * `name` and `file`, so import entries follow the plugin's conventions, and a per-call
1209
1209
  * `name` override wins over both.
1210
+ *
1211
+ * The subtree scan runs through `collectImportedRefNames`, which memoizes by node identity, so a
1212
+ * schema shared across the ts, zod, and faker plugins is walked once and every plugin's resolver
1213
+ * reads the same ref set instead of re-scanning it per plugin.
1210
1214
  */
1211
1215
  imports(options: ResolveImportsOptions): Array<ImportNode>;
1212
1216
  /**
@@ -1854,7 +1858,7 @@ declare class KubbDriver {
1854
1858
  * name that is not a registered plugin.
1855
1859
  */
1856
1860
  getResolver<TName extends PluginName>(pluginName: TName): ResolvePluginOptions<TName>['resolver'];
1857
- getContext<TOptions extends PluginFactoryOptions>(plugin: NormalizedPlugin<TOptions>): Omit<GeneratorContext<TOptions>, 'options'>;
1861
+ getContext<TOptions extends PluginFactoryOptions>(plugin: NormalizedPlugin<TOptions>): Omit<GeneratorContext<TOptions>, 'options' | 'cache'>;
1858
1862
  getPlugin<TName extends PluginName>(pluginName: TName): Plugin<ResolvePluginOptions<TName>> | undefined;
1859
1863
  /**
1860
1864
  * Like `getPlugin` but throws a descriptive error when the plugin is not found.
@@ -1862,6 +1866,38 @@ declare class KubbDriver {
1862
1866
  requirePlugin<TName extends PluginName>(pluginName: TName, context?: RequirePluginContext): Plugin<ResolvePluginOptions<TName>>;
1863
1867
  }
1864
1868
  //#endregion
1869
+ //#region src/nodeCache.d.ts
1870
+ /**
1871
+ * Per-node memo shared by every plugin that generates from the same schema or operation node in
1872
+ * one generate pass. The driver creates one `NodeCache` per node during the walk and hands the
1873
+ * same instance to each plugin's generator context, so work derived purely from the node (its
1874
+ * resolved name, imports, parameters) is computed by the first plugin that needs it and reused by
1875
+ * the rest instead of being recomputed per plugin.
1876
+ *
1877
+ * Keys are namespaced by convention (`'plugin-ts:imports'`) so two plugins caching different
1878
+ * derivations of the same node never collide.
1879
+ *
1880
+ * @example Fill on first read, reuse afterwards
1881
+ * ```ts
1882
+ * const imports = ctx.cache.getOrSet('plugin-ts:imports', () => ctx.resolver.imports({ node, root, output }))
1883
+ * ```
1884
+ */
1885
+ type NodeCache = {
1886
+ /**
1887
+ * Returns the value stored under `key`, or `undefined` when nothing is stored yet.
1888
+ */
1889
+ getItem<TValue>(key: string): TValue | undefined;
1890
+ /**
1891
+ * Stores `value` under `key`, overwriting any previous value, and returns it.
1892
+ */
1893
+ setItem<TValue>(key: string, value: TValue): TValue;
1894
+ /**
1895
+ * Returns the value stored under `key`, computing and storing it with `factory` on the first
1896
+ * call. Later calls with the same key return the stored value without running `factory` again.
1897
+ */
1898
+ getOrSet<TValue>(key: string, factory: () => TValue): TValue;
1899
+ };
1900
+ //#endregion
1865
1901
  //#region src/defineGenerator.d.ts
1866
1902
  /**
1867
1903
  * Context passed to a generator's `schema`, `operation`, and `operations` methods.
@@ -1956,6 +1992,13 @@ type GeneratorContext<TOptions extends PluginFactoryOptions = PluginFactoryOptio
1956
1992
  * Resolved options after exclude/include/override filtering.
1957
1993
  */
1958
1994
  options: TOptions['resolvedOptions'];
1995
+ /**
1996
+ * Cache scoped to the node being generated, shared by every plugin that generates from that
1997
+ * same node in the current pass. Use it to compute node-derived work (resolved names, imports,
1998
+ * parameters) once and let the other plugins reuse it. For the `operations` batch call, where
1999
+ * there is no single node, the cache is a fresh scratch scope for that call.
2000
+ */
2001
+ cache: NodeCache;
1959
2002
  };
1960
2003
  /**
1961
2004
  * Declares a named generator unit that walks the AST and emits files.
@@ -2007,6 +2050,24 @@ type Generator<TOptions extends PluginFactoryOptions = PluginFactoryOptions, TEl
2007
2050
  * ```
2008
2051
  */
2009
2052
  renderer?: RendererFactory<TElement> | null;
2053
+ /**
2054
+ * Predicate checked before `schema` or `operation` runs for a node, mirroring `Macro['match']`
2055
+ * in `@kubb/ast`. Returning `false` skips the call for that node entirely, with no context work
2056
+ * beyond what the driver already builds per node and no render call, instead of the generator
2057
+ * itself being invoked and returning early. Omit it to run for every node, the default when
2058
+ * unset.
2059
+ *
2060
+ * Does not gate `operations`, which already runs once per plugin on the full batch rather than
2061
+ * per node.
2062
+ *
2063
+ * @example Only match GET operations
2064
+ * ```ts
2065
+ * match(node, ctx) {
2066
+ * return ast.isHttpOperationNode(node) && node.method.toLowerCase() === 'get'
2067
+ * }
2068
+ * ```
2069
+ */
2070
+ match?: (node: SchemaNode | OperationNode, ctx: GeneratorContext<TOptions>) => PossiblePromise<boolean>;
2010
2071
  /**
2011
2072
  * Called for each schema node in the AST walk.
2012
2073
  * `ctx` carries the plugin context with `adapter` and `meta` (document metadata),
@@ -2879,5 +2940,5 @@ type BuildOutput = {
2879
2940
  storage: Storage;
2880
2941
  };
2881
2942
  //#endregion
2882
- export { PluginName as $, Kubb$1 as A, DiagnosticKind as At, Filter as B, Adapter as Bt, KubbWarnContext as C, ReporterContext as Ct, CreateKubbOptions as D, logLevel as Dt, UserConfig as E, createReporter as Et, KubbDriver as F, ProblemCode as Ft, KubbPluginStartContext as G, Include as H, AdapterSource as Ht, FileManagerHooks as I, ProblemDiagnostic as It, OutputMode as J, NormalizedPlugin as K, Parser as L, SerializedDiagnostic as Lt, Generator as M, DiagnosticSeverity as Mt, GeneratorContext as N, Diagnostics as Nt, GenerateOptions as O, Diagnostic as Ot, defineGenerator as P, PerformanceDiagnostic as Pt, PluginFactoryOptions as Q, defineParser as R, UpdateDiagnostic as Rt, KubbSuccessContext as S, Reporter as St, PostGenerateCommand as T, UserReporter as Tt, KubbPluginEndContext as U, createAdapter as Ut, Group as V, AdapterFactoryOptions as Vt, KubbPluginSetupContext as W, Override as X, OutputOptions as Y, Plugin as Z, KubbHookStartContext as _, RendererFactory as _t, KubbBuildEndContext as a, ResolveFileOptions as at, KubbLifecycleStartContext as b, createStorage as bt, KubbErrorContext as c, ResolvePathOptions as ct, KubbFilesProcessingStartContext as d, ResolverFile as dt, ResolvePluginOptions as et, KubbFilesProcessingUpdateContext as f, ResolverFileParams as ft, KubbHookLineContext as g, Renderer as gt, KubbHookEndContext as h, ResolverPathParams as ht, Input as i, ResolveBannerFile as it, createKubb as j, DiagnosticLocation as jt, GenerateResult as k, DiagnosticDoc as kt, KubbFileProcessingUpdate as l, Resolver as lt, KubbGenerationStartContext as m, ResolverPatch as mt, CLIOptions as n, BannerMeta as nt, KubbBuildStartContext as o, ResolveImportsOptions as ot, KubbGenerationEndContext as p, ResolverFilePathParams as pt, Output as q, Config as r, ResolveBannerContext as rt, KubbDiagnosticContext as s, ResolveOptionsContext as st, BuildOutput as t, definePlugin as tt, KubbFilesProcessingEndContext as u, ResolverDefault as ut, KubbHooks as v, createRenderer as vt, PossibleConfig as w, ReporterName as wt, KubbPluginsEndContext as x, GenerationResult as xt, KubbInfoContext as y, Storage as yt, Exclude$1 as z, Hookable as zt };
2883
- //# sourceMappingURL=types-CYp43kqg.d.ts.map
2943
+ export { PluginFactoryOptions as $, Kubb$1 as A, DiagnosticDoc as At, Exclude$1 as B, Hookable as Bt, KubbWarnContext as C, Reporter as Ct, CreateKubbOptions as D, createReporter as Dt, UserConfig as E, UserReporter as Et, NodeCache as F, PerformanceDiagnostic as Ft, KubbPluginSetupContext as G, Group as H, AdapterFactoryOptions as Ht, KubbDriver as I, ProblemCode as It, Output as J, KubbPluginStartContext as K, FileManagerHooks as L, ProblemDiagnostic as Lt, Generator as M, DiagnosticLocation as Mt, GeneratorContext as N, DiagnosticSeverity as Nt, GenerateOptions as O, logLevel as Ot, defineGenerator as P, Diagnostics as Pt, Plugin as Q, Parser as R, SerializedDiagnostic as Rt, KubbSuccessContext as S, GenerationResult as St, PostGenerateCommand as T, ReporterName as Tt, Include as U, AdapterSource as Ut, Filter as V, Adapter as Vt, KubbPluginEndContext as W, createAdapter as Wt, OutputOptions as X, OutputMode as Y, Override as Z, KubbHookStartContext as _, Renderer as _t, KubbBuildEndContext as a, ResolveBannerFile as at, KubbLifecycleStartContext as b, Storage as bt, KubbErrorContext as c, ResolveOptionsContext as ct, KubbFilesProcessingStartContext as d, ResolverDefault as dt, PluginName as et, KubbFilesProcessingUpdateContext as f, ResolverFile as ft, KubbHookLineContext as g, ResolverPathParams as gt, KubbHookEndContext as h, ResolverPatch as ht, Input as i, ResolveBannerContext as it, createKubb as j, DiagnosticKind as jt, GenerateResult as k, Diagnostic as kt, KubbFileProcessingUpdate as l, ResolvePathOptions as lt, KubbGenerationStartContext as m, ResolverFilePathParams as mt, CLIOptions as n, definePlugin as nt, KubbBuildStartContext as o, ResolveFileOptions as ot, KubbGenerationEndContext as p, ResolverFileParams as pt, NormalizedPlugin as q, Config as r, BannerMeta as rt, KubbDiagnosticContext as s, ResolveImportsOptions as st, BuildOutput as t, ResolvePluginOptions as tt, KubbFilesProcessingEndContext as u, Resolver as ut, KubbHooks as v, RendererFactory as vt, PossibleConfig as w, ReporterContext as wt, KubbPluginsEndContext as x, createStorage as xt, KubbInfoContext as y, createRenderer as yt, defineParser as z, UpdateDiagnostic as zt };
2944
+ //# sourceMappingURL=types-D116CBzm.d.ts.map
@@ -563,6 +563,30 @@ var FileManager = class {
563
563
  }
564
564
  };
565
565
  //#endregion
566
+ //#region src/nodeCache.ts
567
+ /**
568
+ * Builds an empty {@link NodeCache} backed by a plain `Map`. Called once per node in the generate
569
+ * walk.
570
+ */
571
+ function createNodeCache() {
572
+ const store = /* @__PURE__ */ new Map();
573
+ return {
574
+ getItem(key) {
575
+ return store.get(key);
576
+ },
577
+ setItem(key, value) {
578
+ store.set(key, value);
579
+ return value;
580
+ },
581
+ getOrSet(key, factory) {
582
+ if (store.has(key)) return store.get(key);
583
+ const value = factory();
584
+ store.set(key, value);
585
+ return value;
586
+ }
587
+ };
588
+ }
589
+ //#endregion
566
590
  //#region \0@oxc-project+runtime@0.139.0/helpers/esm/usingCtx.js
567
591
  function _usingCtx() {
568
592
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
@@ -671,6 +695,12 @@ Object.defineProperty(exports, "clean", {
671
695
  return clean;
672
696
  }
673
697
  });
698
+ Object.defineProperty(exports, "createNodeCache", {
699
+ enumerable: true,
700
+ get: function() {
701
+ return createNodeCache;
702
+ }
703
+ });
674
704
  Object.defineProperty(exports, "getErrorMessage", {
675
705
  enumerable: true,
676
706
  get: function() {
@@ -708,4 +738,4 @@ Object.defineProperty(exports, "write", {
708
738
  }
709
739
  });
710
740
 
711
- //# sourceMappingURL=usingCtx-CO6pGcST.cjs.map
741
+ //# sourceMappingURL=usingCtx-BYOZEkiy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usingCtx-BYOZEkiy.cjs","names":["#emitter","NodeEventEmitter","#emitAll","#cache","#store","#dedupe","ast","#sorted"],"sources":["../../../internals/utils/src/casing.ts","../../../internals/utils/src/errors.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../src/Hookable.ts","../src/FileManager.ts","../src/nodeCache.ts"],"sourcesContent":["type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Thrown when one or more errors occur during a Kubb build.\n * Carries the full list of underlying errors on `errors`.\n *\n * @example\n * ```ts\n * throw new BuildError('Build failed', { errors: [err1, err2] })\n * ```\n */\nexport class BuildError extends Error {\n errors: Array<Error>\n\n constructor(message: string, options: { cause?: Error; errors: Array<Error> }) {\n super(message, { cause: options.cause })\n this.name = 'BuildError'\n this.errors = options.errors\n }\n}\n\n/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new BuildError('Build failed', { cause: toError(err), errors: [] })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n\n/**\n * Extracts the `.cause` of an `Error` as an `Error`, or `undefined` when absent or not an `Error`.\n *\n * @example\n * ```ts\n * const cause = toCause(buildError) // Error | undefined\n * ```\n */\nexport function toCause(error: Error): Error | undefined {\n return error.cause instanceof Error ? error.cause : undefined\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { camelCase } from './casing.ts'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Writes `data` to `path`, trimming leading/trailing whitespace before saving.\n * Skips the write when the trimmed content is empty or identical to what is already on disk.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns trimmed content\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const resolved = resolve(path)\n\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n const oldContent = (await file.exists()) ? await file.text() : null\n if (oldContent === trimmed) return null\n await Bun.write(resolved, trimmed)\n return trimmed\n }\n\n try {\n const oldContent = await readFile(resolved, { encoding: 'utf-8' })\n if (oldContent === trimmed) return null\n } catch {\n /* file doesn't exist yet */\n }\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, trimmed, { encoding: 'utf-8' })\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== trimmed) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return trimmed\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards destructive operations: before wiping an output directory, check that it does not contain\n * the project root, otherwise a `clean` would delete `kubb.config` and every source file.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) return true\n\n const rel = relative(resolvedParent, resolvedPath)\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Builds a nested file path from a dotted name. Splits on dots that precede a letter\n * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases\n * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.\n *\n * Empty segments are dropped before joining. They arise when the name starts with a dot\n * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to\n * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an\n * absolute path, letting generated files escape the configured output directory.\n *\n * @example Nested path from a dotted name\n * `toFilePath('pet.petId') // 'pet/petId'`\n *\n * @example PascalCase the final segment\n * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`\n *\n * @example Suffix applied to the final segment only\n * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`\n */\nexport function toFilePath(name: string, caseLast: (part: string) => string = camelCase): string {\n const parts = name.split(/\\.(?=[a-zA-Z])/)\n return parts\n .map((part, i) => (i === parts.length - 1 ? caseLast(part) : camelCase(part)))\n .filter(Boolean)\n .join('/')\n}\n","import { EventEmitter as NodeEventEmitter } from 'node:events'\nimport { toError } from '@internals/utils'\n\n/**\n * A function that can be registered as a hook listener, synchronous or async. Any return value is\n * allowed and ignored, so handlers that return a result for their own callers still register.\n */\ntype AsyncListener<TArgs extends Array<unknown>> = (...args: TArgs) => unknown\n\n/**\n * Typed hook emitter that awaits all async listeners before resolving.\n * Wraps Node's `EventEmitter` with full TypeScript hook-map inference.\n *\n * @example\n * ```ts\n * const hooks = new Hookable<{ build: [name: string] }>()\n * hooks.hook('build', async (name) => { console.log(name) })\n * await hooks.callHook('build', 'petstore') // all listeners awaited\n * ```\n */\nexport class Hookable<THooks extends { [K in keyof THooks]: Array<unknown> }> {\n /**\n * Maximum number of listeners per hook before Node emits a memory-leak warning.\n * @default 10\n */\n constructor(maxListener = 10) {\n this.#emitter.setMaxListeners(maxListener)\n }\n\n #emitter = new NodeEventEmitter()\n\n /**\n * Calls `hookName` and awaits all registered listeners sequentially.\n * Throws if any listener rejects, wrapping the cause with the hook name and serialized arguments.\n *\n * @example\n * ```ts\n * await hooks.callHook('build', 'petstore')\n * ```\n */\n callHook<THookName extends keyof THooks & string>(hookName: THookName, ...hookArgs: THooks[THookName]): Promise<void> | void {\n const listeners = this.#emitter.listeners(hookName) as Array<AsyncListener<THooks[THookName]>>\n\n if (listeners.length === 0) {\n return\n }\n\n return this.#emitAll(hookName, listeners, hookArgs)\n }\n\n async #emitAll<THookName extends keyof THooks & string>(\n hookName: THookName,\n listeners: Array<AsyncListener<THooks[THookName]>>,\n hookArgs: THooks[THookName],\n ): Promise<void> {\n for (const listener of listeners) {\n try {\n await listener(...hookArgs)\n } catch (err) {\n let serializedArgs: string\n try {\n serializedArgs = JSON.stringify(hookArgs)\n } catch {\n serializedArgs = String(hookArgs)\n }\n throw new Error(`Error in async listener for \"${hookName}\" with hookArgs ${serializedArgs}`, { cause: toError(err) })\n }\n }\n }\n\n /**\n * Registers a persistent listener for `hookName` and returns a function that removes it.\n *\n * @example\n * ```ts\n * const unhook = hooks.hook('build', async (name) => { console.log(name) })\n * unhook() // removes it\n * ```\n */\n hook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): () => void {\n this.#emitter.on(hookName, handler as AsyncListener<Array<unknown>>)\n return () => this.removeHook(hookName, handler)\n }\n\n /**\n * Registers every handler in `configHooks` at once and returns a function that removes them\n * all. Undefined entries are skipped, so a partial hook object registers only its present keys.\n *\n * @example\n * ```ts\n * const unhook = hooks.addHooks({ build: onBuild, done: onDone })\n * unhook() // removes both\n * ```\n */\n addHooks(configHooks: Partial<{ [K in keyof THooks & string]: AsyncListener<THooks[K]> }>): () => void {\n const unhooks = (Object.keys(configHooks) as Array<keyof THooks & string>)\n .filter((name) => configHooks[name])\n .map((name) => this.hook(name, configHooks[name]!))\n\n return () => {\n for (const unhook of unhooks) unhook()\n }\n }\n\n /**\n * Removes a previously registered listener.\n *\n * @example\n * ```ts\n * hooks.removeHook('build', handler)\n * ```\n */\n removeHook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): void {\n this.#emitter.off(hookName, handler as AsyncListener<Array<unknown>>)\n }\n\n /**\n * Returns the number of listeners registered for `hookName`.\n *\n * @example\n * ```ts\n * hooks.hook('build', handler)\n * hooks.listenerCount('build') // 1\n * ```\n */\n listenerCount<THookName extends keyof THooks & string>(hookName: THookName): number {\n return this.#emitter.listenerCount(hookName)\n }\n\n /**\n * Raises or lowers the per-hook listener ceiling before Node warns about a memory leak.\n * Set this above the expected listener count when many listeners attach by design.\n *\n * @example\n * ```ts\n * hooks.setMaxListeners(40)\n * ```\n */\n setMaxListeners(max: number): void {\n this.#emitter.setMaxListeners(max)\n }\n\n /**\n * Removes all listeners from every hook channel.\n *\n * @example\n * ```ts\n * hooks.removeAllHooks()\n * ```\n */\n removeAllHooks(): void {\n this.#emitter.removeAllListeners()\n }\n}\n","import { read } from '@internals/utils'\nimport { ast, extractStringsFromNodes, type CodeNode, type FileNode } from '@kubb/ast'\nimport type { Storage } from './createStorage.ts'\nimport type { Parser } from './defineParser.ts'\nimport { Hookable } from './Hookable.ts'\n\n/**\n * Hooks fired around a `FileManager#write` batch: `start` before it, `update` per file, `end` after.\n */\nexport type FileManagerHooks = {\n start: [files: Array<FileNode>]\n update: [params: { file: FileNode; source?: string; processed: number; total: number; percentage: number }]\n end: [files: Array<FileNode>]\n}\n\ntype ParseOptions = {\n parsers?: Map<FileNode['extname'], Parser>\n}\n\ntype WriteOptions = ParseOptions & {\n storage: Storage\n}\n\n// Cap how many files are parsed and written at once. A small spec runs all its files in parallel;\n// a spec with thousands of files keeps at most this many parsed sources in memory instead of every\n// source at once, while each file's write still overlaps the next file's parse. Disk writes are\n// throttled separately inside `fsStorage`.\nconst WRITE_CONCURRENCY = 50\n\nfunction joinSources(file: FileNode): string {\n return file.sources\n .map((source) => extractStringsFromNodes(source.nodes as Array<CodeNode>))\n .filter(Boolean)\n .join('\\n\\n')\n}\n\nasync function parseCopy(file: FileNode): Promise<string> {\n let content: string\n try {\n content = await read(file.copy as string)\n } catch (err) {\n throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err })\n }\n\n return [file.banner, content, file.footer]\n .filter((segment): segment is string => Boolean(segment))\n .map((segment) => segment.trimEnd())\n .join('\\n')\n}\n\nfunction mergeFile<TMeta extends object = object>(a: FileNode<TMeta>, b: FileNode<TMeta>): FileNode<TMeta> {\n return {\n ...a,\n // Incoming file (b) takes precedence for banner/footer so a barrel file (whose\n // banner/footer the barrel plugin resolves last) wins over a plugin-generated\n // file at the same path.\n banner: b.banner,\n footer: b.footer,\n // A verbatim-copy file cannot be merged with rendered content; the incoming `copy` wins.\n copy: b.copy ?? a.copy,\n sources: a.sources.length ? (b.sources.length ? [...a.sources, ...b.sources] : a.sources) : b.sources,\n imports: a.imports.length ? (b.imports.length ? [...a.imports, ...b.imports] : a.imports) : b.imports,\n exports: a.exports.length ? (b.exports.length ? [...a.exports, ...b.exports] : a.exports) : b.exports,\n }\n}\n\nfunction isIndexPath(path: string): boolean {\n return path.endsWith('/index.ts') || path === 'index.ts'\n}\n\n// Sort order: shortest path first. Within a length bucket, index.ts barrels last.\nfunction compareFiles(a: FileNode, b: FileNode): number {\n const lenDiff = a.path.length - b.path.length\n if (lenDiff !== 0) return lenDiff\n const aIsIndex = isIndexPath(a.path)\n const bIsIndex = isIndexPath(b.path)\n if (aIsIndex && !bIsIndex) return 1\n if (!aIsIndex && bIsIndex) return -1\n\n return 0\n}\n\n/**\n * In-memory file store for generated files, and the writer that turns them into source\n * strings on `storage`. Files sharing a `path` are merged (sources/imports/exports\n * concatenated). The `files` getter is sorted by path length (barrel `index.ts` last\n * within a bucket).\n *\n * @example\n * ```ts\n * const manager = new FileManager()\n * manager.upsert(myFile)\n * manager.files // sorted view\n * await manager.write(manager.files, { storage: fsStorage() })\n * ```\n */\nexport class FileManager {\n readonly hooks = new Hookable<FileManagerHooks>()\n readonly #cache = new Map<string, FileNode>()\n // Cached sorted view. Null means stale and rebuilt lazily on next `files` read.\n // Nulled (not mutated) on every write so callers holding a prior reference keep\n // their snapshot. `dispose()` must not silently empty an array the consumer\n // already holds.\n #sorted: Array<FileNode> | null = null\n\n add(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, false)\n }\n\n upsert(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, true)\n }\n\n #store(files: ReadonlyArray<FileNode>, mergeExisting: boolean): Array<FileNode> {\n const batch = files.length > 1 ? this.#dedupe(files) : files\n const resolved: Array<FileNode> = []\n\n for (const file of batch) {\n const existing = this.#cache.get(file.path)\n const merged = existing && mergeExisting ? ast.factory.createFile(mergeFile(existing, file)) : ast.factory.createFile(file)\n this.#cache.set(merged.path, merged)\n resolved.push(merged)\n }\n\n if (resolved.length > 0) this.#sorted = null\n return resolved\n }\n\n // Merges same-path entries within a batch so the cache update loop stays\n // uniform. Only called for multi-file batches.\n #dedupe(files: ReadonlyArray<FileNode>): Array<FileNode> {\n const seen = new Map<string, FileNode>()\n for (const file of files) {\n const prev = seen.get(file.path)\n seen.set(file.path, prev ? mergeFile(prev, file) : file)\n }\n return [...seen.values()]\n }\n\n clear(): void {\n this.#cache.clear()\n this.#sorted = null\n }\n\n /**\n * Releases all stored files and clears every `hooks` listener. Called by the core after\n * `kubb:build:end`.\n */\n dispose(): void {\n this.clear()\n this.hooks.removeAllHooks()\n }\n\n /**\n * All stored files in stable sort order (shortest path first, barrel files\n * last within a length bucket). Returns a cached view, do not mutate.\n */\n get files(): Array<FileNode> {\n return (this.#sorted ??= [...this.#cache.values()].sort(compareFiles))\n }\n\n /**\n * Converts a file's AST sources (or its `copy` source) into the final on-disk string.\n */\n async parse(file: FileNode, { parsers }: ParseOptions = {}): Promise<string> {\n if (file.copy) {\n return parseCopy(file)\n }\n\n if (!parsers || !file.extname) {\n return joinSources(file)\n }\n\n const parser = parsers.get(file.extname)\n\n if (!parser) {\n return joinSources(file)\n }\n\n return parser.parse(file)\n }\n\n /**\n * Parses and writes every file through a bounded pool of workers. A small spec runs all its files\n * at once; a spec with thousands of files keeps at most `WRITE_CONCURRENCY` parsed sources in\n * memory rather than holding every source, while still overlapping each file's write with the\n * next file's parse. Each `update` carries the file's input position, so a consumer can present\n * the files in generation order even though they finish in whatever order they parse.\n */\n async write(files: Array<FileNode>, { storage, parsers }: WriteOptions): Promise<void> {\n if (files.length === 0) return\n\n await this.hooks.callHook('start', files)\n\n const total = files.length\n // Workers share one iterator, so each `next()` hands the next file to whichever worker is free.\n const entries = files.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, file] of entries) {\n const source = await this.parse(file, { parsers })\n await this.hooks.callHook('update', { file, source, processed: index + 1, total, percentage: ((index + 1) / total) * 100 })\n if (source) await storage.setItem(file.path, source)\n }\n }\n\n await Promise.all(Array.from({ length: Math.min(WRITE_CONCURRENCY, total) }, () => worker()))\n\n await this.hooks.callHook('end', files)\n }\n}\n","/**\n * Per-node memo shared by every plugin that generates from the same schema or operation node in\n * one generate pass. The driver creates one `NodeCache` per node during the walk and hands the\n * same instance to each plugin's generator context, so work derived purely from the node (its\n * resolved name, imports, parameters) is computed by the first plugin that needs it and reused by\n * the rest instead of being recomputed per plugin.\n *\n * Keys are namespaced by convention (`'plugin-ts:imports'`) so two plugins caching different\n * derivations of the same node never collide.\n *\n * @example Fill on first read, reuse afterwards\n * ```ts\n * const imports = ctx.cache.getOrSet('plugin-ts:imports', () => ctx.resolver.imports({ node, root, output }))\n * ```\n */\nexport type NodeCache = {\n /**\n * Returns the value stored under `key`, or `undefined` when nothing is stored yet.\n */\n getItem<TValue>(key: string): TValue | undefined\n /**\n * Stores `value` under `key`, overwriting any previous value, and returns it.\n */\n setItem<TValue>(key: string, value: TValue): TValue\n /**\n * Returns the value stored under `key`, computing and storing it with `factory` on the first\n * call. Later calls with the same key return the stored value without running `factory` again.\n */\n getOrSet<TValue>(key: string, factory: () => TValue): TValue\n}\n\n/**\n * Builds an empty {@link NodeCache} backed by a plain `Map`. Called once per node in the generate\n * walk.\n */\nexport function createNodeCache(): NodeCache {\n const store = new Map<string, unknown>()\n\n return {\n getItem<TValue>(key: string): TValue | undefined {\n return store.get(key) as TValue | undefined\n },\n setItem<TValue>(key: string, value: TValue): TValue {\n store.set(key, value)\n return value\n },\n getOrSet<TValue>(key: string, factory: () => TValue): TValue {\n if (store.has(key)) return store.get(key) as TValue\n\n const value = factory()\n store.set(key, value)\n return value\n },\n }\n}\n"],"mappings":"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"}
@@ -538,6 +538,30 @@ var FileManager = class {
538
538
  }
539
539
  };
540
540
  //#endregion
541
+ //#region src/nodeCache.ts
542
+ /**
543
+ * Builds an empty {@link NodeCache} backed by a plain `Map`. Called once per node in the generate
544
+ * walk.
545
+ */
546
+ function createNodeCache() {
547
+ const store = /* @__PURE__ */ new Map();
548
+ return {
549
+ getItem(key) {
550
+ return store.get(key);
551
+ },
552
+ setItem(key, value) {
553
+ store.set(key, value);
554
+ return value;
555
+ },
556
+ getOrSet(key, factory) {
557
+ if (store.has(key)) return store.get(key);
558
+ const value = factory();
559
+ store.set(key, value);
560
+ return value;
561
+ }
562
+ };
563
+ }
564
+ //#endregion
541
565
  //#region \0@oxc-project+runtime@0.139.0/helpers/esm/usingCtx.js
542
566
  function _usingCtx() {
543
567
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
@@ -598,6 +622,6 @@ function _usingCtx() {
598
622
  };
599
623
  }
600
624
  //#endregion
601
- export { isPathInside as a, write as c, toError as d, camelCase as f, clean as i, BuildError as l, FileManager as n, toFilePath as o, Hookable as r, toPosixPath as s, _usingCtx as t, getErrorMessage as u };
625
+ export { clean as a, toPosixPath as c, getErrorMessage as d, toError as f, Hookable as i, write as l, createNodeCache as n, isPathInside as o, camelCase as p, FileManager as r, toFilePath as s, _usingCtx as t, BuildError as u };
602
626
 
603
- //# sourceMappingURL=usingCtx-C8EG-wqH.js.map
627
+ //# sourceMappingURL=usingCtx-C24YU3KM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usingCtx-C24YU3KM.js","names":["#emitter","NodeEventEmitter","#emitAll","#cache","#store","#dedupe","#sorted"],"sources":["../../../internals/utils/src/casing.ts","../../../internals/utils/src/errors.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../src/Hookable.ts","../src/FileManager.ts","../src/nodeCache.ts"],"sourcesContent":["type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Thrown when one or more errors occur during a Kubb build.\n * Carries the full list of underlying errors on `errors`.\n *\n * @example\n * ```ts\n * throw new BuildError('Build failed', { errors: [err1, err2] })\n * ```\n */\nexport class BuildError extends Error {\n errors: Array<Error>\n\n constructor(message: string, options: { cause?: Error; errors: Array<Error> }) {\n super(message, { cause: options.cause })\n this.name = 'BuildError'\n this.errors = options.errors\n }\n}\n\n/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new BuildError('Build failed', { cause: toError(err), errors: [] })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n\n/**\n * Extracts the `.cause` of an `Error` as an `Error`, or `undefined` when absent or not an `Error`.\n *\n * @example\n * ```ts\n * const cause = toCause(buildError) // Error | undefined\n * ```\n */\nexport function toCause(error: Error): Error | undefined {\n return error.cause instanceof Error ? error.cause : undefined\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { camelCase } from './casing.ts'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Writes `data` to `path`, trimming leading/trailing whitespace before saving.\n * Skips the write when the trimmed content is empty or identical to what is already on disk.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns trimmed content\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const resolved = resolve(path)\n\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n const oldContent = (await file.exists()) ? await file.text() : null\n if (oldContent === trimmed) return null\n await Bun.write(resolved, trimmed)\n return trimmed\n }\n\n try {\n const oldContent = await readFile(resolved, { encoding: 'utf-8' })\n if (oldContent === trimmed) return null\n } catch {\n /* file doesn't exist yet */\n }\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, trimmed, { encoding: 'utf-8' })\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== trimmed) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return trimmed\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards destructive operations: before wiping an output directory, check that it does not contain\n * the project root, otherwise a `clean` would delete `kubb.config` and every source file.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) return true\n\n const rel = relative(resolvedParent, resolvedPath)\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Builds a nested file path from a dotted name. Splits on dots that precede a letter\n * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases\n * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.\n *\n * Empty segments are dropped before joining. They arise when the name starts with a dot\n * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to\n * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an\n * absolute path, letting generated files escape the configured output directory.\n *\n * @example Nested path from a dotted name\n * `toFilePath('pet.petId') // 'pet/petId'`\n *\n * @example PascalCase the final segment\n * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`\n *\n * @example Suffix applied to the final segment only\n * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`\n */\nexport function toFilePath(name: string, caseLast: (part: string) => string = camelCase): string {\n const parts = name.split(/\\.(?=[a-zA-Z])/)\n return parts\n .map((part, i) => (i === parts.length - 1 ? caseLast(part) : camelCase(part)))\n .filter(Boolean)\n .join('/')\n}\n","import { EventEmitter as NodeEventEmitter } from 'node:events'\nimport { toError } from '@internals/utils'\n\n/**\n * A function that can be registered as a hook listener, synchronous or async. Any return value is\n * allowed and ignored, so handlers that return a result for their own callers still register.\n */\ntype AsyncListener<TArgs extends Array<unknown>> = (...args: TArgs) => unknown\n\n/**\n * Typed hook emitter that awaits all async listeners before resolving.\n * Wraps Node's `EventEmitter` with full TypeScript hook-map inference.\n *\n * @example\n * ```ts\n * const hooks = new Hookable<{ build: [name: string] }>()\n * hooks.hook('build', async (name) => { console.log(name) })\n * await hooks.callHook('build', 'petstore') // all listeners awaited\n * ```\n */\nexport class Hookable<THooks extends { [K in keyof THooks]: Array<unknown> }> {\n /**\n * Maximum number of listeners per hook before Node emits a memory-leak warning.\n * @default 10\n */\n constructor(maxListener = 10) {\n this.#emitter.setMaxListeners(maxListener)\n }\n\n #emitter = new NodeEventEmitter()\n\n /**\n * Calls `hookName` and awaits all registered listeners sequentially.\n * Throws if any listener rejects, wrapping the cause with the hook name and serialized arguments.\n *\n * @example\n * ```ts\n * await hooks.callHook('build', 'petstore')\n * ```\n */\n callHook<THookName extends keyof THooks & string>(hookName: THookName, ...hookArgs: THooks[THookName]): Promise<void> | void {\n const listeners = this.#emitter.listeners(hookName) as Array<AsyncListener<THooks[THookName]>>\n\n if (listeners.length === 0) {\n return\n }\n\n return this.#emitAll(hookName, listeners, hookArgs)\n }\n\n async #emitAll<THookName extends keyof THooks & string>(\n hookName: THookName,\n listeners: Array<AsyncListener<THooks[THookName]>>,\n hookArgs: THooks[THookName],\n ): Promise<void> {\n for (const listener of listeners) {\n try {\n await listener(...hookArgs)\n } catch (err) {\n let serializedArgs: string\n try {\n serializedArgs = JSON.stringify(hookArgs)\n } catch {\n serializedArgs = String(hookArgs)\n }\n throw new Error(`Error in async listener for \"${hookName}\" with hookArgs ${serializedArgs}`, { cause: toError(err) })\n }\n }\n }\n\n /**\n * Registers a persistent listener for `hookName` and returns a function that removes it.\n *\n * @example\n * ```ts\n * const unhook = hooks.hook('build', async (name) => { console.log(name) })\n * unhook() // removes it\n * ```\n */\n hook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): () => void {\n this.#emitter.on(hookName, handler as AsyncListener<Array<unknown>>)\n return () => this.removeHook(hookName, handler)\n }\n\n /**\n * Registers every handler in `configHooks` at once and returns a function that removes them\n * all. Undefined entries are skipped, so a partial hook object registers only its present keys.\n *\n * @example\n * ```ts\n * const unhook = hooks.addHooks({ build: onBuild, done: onDone })\n * unhook() // removes both\n * ```\n */\n addHooks(configHooks: Partial<{ [K in keyof THooks & string]: AsyncListener<THooks[K]> }>): () => void {\n const unhooks = (Object.keys(configHooks) as Array<keyof THooks & string>)\n .filter((name) => configHooks[name])\n .map((name) => this.hook(name, configHooks[name]!))\n\n return () => {\n for (const unhook of unhooks) unhook()\n }\n }\n\n /**\n * Removes a previously registered listener.\n *\n * @example\n * ```ts\n * hooks.removeHook('build', handler)\n * ```\n */\n removeHook<THookName extends keyof THooks & string>(hookName: THookName, handler: AsyncListener<THooks[THookName]>): void {\n this.#emitter.off(hookName, handler as AsyncListener<Array<unknown>>)\n }\n\n /**\n * Returns the number of listeners registered for `hookName`.\n *\n * @example\n * ```ts\n * hooks.hook('build', handler)\n * hooks.listenerCount('build') // 1\n * ```\n */\n listenerCount<THookName extends keyof THooks & string>(hookName: THookName): number {\n return this.#emitter.listenerCount(hookName)\n }\n\n /**\n * Raises or lowers the per-hook listener ceiling before Node warns about a memory leak.\n * Set this above the expected listener count when many listeners attach by design.\n *\n * @example\n * ```ts\n * hooks.setMaxListeners(40)\n * ```\n */\n setMaxListeners(max: number): void {\n this.#emitter.setMaxListeners(max)\n }\n\n /**\n * Removes all listeners from every hook channel.\n *\n * @example\n * ```ts\n * hooks.removeAllHooks()\n * ```\n */\n removeAllHooks(): void {\n this.#emitter.removeAllListeners()\n }\n}\n","import { read } from '@internals/utils'\nimport { ast, extractStringsFromNodes, type CodeNode, type FileNode } from '@kubb/ast'\nimport type { Storage } from './createStorage.ts'\nimport type { Parser } from './defineParser.ts'\nimport { Hookable } from './Hookable.ts'\n\n/**\n * Hooks fired around a `FileManager#write` batch: `start` before it, `update` per file, `end` after.\n */\nexport type FileManagerHooks = {\n start: [files: Array<FileNode>]\n update: [params: { file: FileNode; source?: string; processed: number; total: number; percentage: number }]\n end: [files: Array<FileNode>]\n}\n\ntype ParseOptions = {\n parsers?: Map<FileNode['extname'], Parser>\n}\n\ntype WriteOptions = ParseOptions & {\n storage: Storage\n}\n\n// Cap how many files are parsed and written at once. A small spec runs all its files in parallel;\n// a spec with thousands of files keeps at most this many parsed sources in memory instead of every\n// source at once, while each file's write still overlaps the next file's parse. Disk writes are\n// throttled separately inside `fsStorage`.\nconst WRITE_CONCURRENCY = 50\n\nfunction joinSources(file: FileNode): string {\n return file.sources\n .map((source) => extractStringsFromNodes(source.nodes as Array<CodeNode>))\n .filter(Boolean)\n .join('\\n\\n')\n}\n\nasync function parseCopy(file: FileNode): Promise<string> {\n let content: string\n try {\n content = await read(file.copy as string)\n } catch (err) {\n throw new Error(`[kubb] Could not copy file into output: ${file.copy}`, { cause: err })\n }\n\n return [file.banner, content, file.footer]\n .filter((segment): segment is string => Boolean(segment))\n .map((segment) => segment.trimEnd())\n .join('\\n')\n}\n\nfunction mergeFile<TMeta extends object = object>(a: FileNode<TMeta>, b: FileNode<TMeta>): FileNode<TMeta> {\n return {\n ...a,\n // Incoming file (b) takes precedence for banner/footer so a barrel file (whose\n // banner/footer the barrel plugin resolves last) wins over a plugin-generated\n // file at the same path.\n banner: b.banner,\n footer: b.footer,\n // A verbatim-copy file cannot be merged with rendered content; the incoming `copy` wins.\n copy: b.copy ?? a.copy,\n sources: a.sources.length ? (b.sources.length ? [...a.sources, ...b.sources] : a.sources) : b.sources,\n imports: a.imports.length ? (b.imports.length ? [...a.imports, ...b.imports] : a.imports) : b.imports,\n exports: a.exports.length ? (b.exports.length ? [...a.exports, ...b.exports] : a.exports) : b.exports,\n }\n}\n\nfunction isIndexPath(path: string): boolean {\n return path.endsWith('/index.ts') || path === 'index.ts'\n}\n\n// Sort order: shortest path first. Within a length bucket, index.ts barrels last.\nfunction compareFiles(a: FileNode, b: FileNode): number {\n const lenDiff = a.path.length - b.path.length\n if (lenDiff !== 0) return lenDiff\n const aIsIndex = isIndexPath(a.path)\n const bIsIndex = isIndexPath(b.path)\n if (aIsIndex && !bIsIndex) return 1\n if (!aIsIndex && bIsIndex) return -1\n\n return 0\n}\n\n/**\n * In-memory file store for generated files, and the writer that turns them into source\n * strings on `storage`. Files sharing a `path` are merged (sources/imports/exports\n * concatenated). The `files` getter is sorted by path length (barrel `index.ts` last\n * within a bucket).\n *\n * @example\n * ```ts\n * const manager = new FileManager()\n * manager.upsert(myFile)\n * manager.files // sorted view\n * await manager.write(manager.files, { storage: fsStorage() })\n * ```\n */\nexport class FileManager {\n readonly hooks = new Hookable<FileManagerHooks>()\n readonly #cache = new Map<string, FileNode>()\n // Cached sorted view. Null means stale and rebuilt lazily on next `files` read.\n // Nulled (not mutated) on every write so callers holding a prior reference keep\n // their snapshot. `dispose()` must not silently empty an array the consumer\n // already holds.\n #sorted: Array<FileNode> | null = null\n\n add(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, false)\n }\n\n upsert(...files: Array<FileNode>): Array<FileNode> {\n return this.#store(files, true)\n }\n\n #store(files: ReadonlyArray<FileNode>, mergeExisting: boolean): Array<FileNode> {\n const batch = files.length > 1 ? this.#dedupe(files) : files\n const resolved: Array<FileNode> = []\n\n for (const file of batch) {\n const existing = this.#cache.get(file.path)\n const merged = existing && mergeExisting ? ast.factory.createFile(mergeFile(existing, file)) : ast.factory.createFile(file)\n this.#cache.set(merged.path, merged)\n resolved.push(merged)\n }\n\n if (resolved.length > 0) this.#sorted = null\n return resolved\n }\n\n // Merges same-path entries within a batch so the cache update loop stays\n // uniform. Only called for multi-file batches.\n #dedupe(files: ReadonlyArray<FileNode>): Array<FileNode> {\n const seen = new Map<string, FileNode>()\n for (const file of files) {\n const prev = seen.get(file.path)\n seen.set(file.path, prev ? mergeFile(prev, file) : file)\n }\n return [...seen.values()]\n }\n\n clear(): void {\n this.#cache.clear()\n this.#sorted = null\n }\n\n /**\n * Releases all stored files and clears every `hooks` listener. Called by the core after\n * `kubb:build:end`.\n */\n dispose(): void {\n this.clear()\n this.hooks.removeAllHooks()\n }\n\n /**\n * All stored files in stable sort order (shortest path first, barrel files\n * last within a length bucket). Returns a cached view, do not mutate.\n */\n get files(): Array<FileNode> {\n return (this.#sorted ??= [...this.#cache.values()].sort(compareFiles))\n }\n\n /**\n * Converts a file's AST sources (or its `copy` source) into the final on-disk string.\n */\n async parse(file: FileNode, { parsers }: ParseOptions = {}): Promise<string> {\n if (file.copy) {\n return parseCopy(file)\n }\n\n if (!parsers || !file.extname) {\n return joinSources(file)\n }\n\n const parser = parsers.get(file.extname)\n\n if (!parser) {\n return joinSources(file)\n }\n\n return parser.parse(file)\n }\n\n /**\n * Parses and writes every file through a bounded pool of workers. A small spec runs all its files\n * at once; a spec with thousands of files keeps at most `WRITE_CONCURRENCY` parsed sources in\n * memory rather than holding every source, while still overlapping each file's write with the\n * next file's parse. Each `update` carries the file's input position, so a consumer can present\n * the files in generation order even though they finish in whatever order they parse.\n */\n async write(files: Array<FileNode>, { storage, parsers }: WriteOptions): Promise<void> {\n if (files.length === 0) return\n\n await this.hooks.callHook('start', files)\n\n const total = files.length\n // Workers share one iterator, so each `next()` hands the next file to whichever worker is free.\n const entries = files.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, file] of entries) {\n const source = await this.parse(file, { parsers })\n await this.hooks.callHook('update', { file, source, processed: index + 1, total, percentage: ((index + 1) / total) * 100 })\n if (source) await storage.setItem(file.path, source)\n }\n }\n\n await Promise.all(Array.from({ length: Math.min(WRITE_CONCURRENCY, total) }, () => worker()))\n\n await this.hooks.callHook('end', files)\n }\n}\n","/**\n * Per-node memo shared by every plugin that generates from the same schema or operation node in\n * one generate pass. The driver creates one `NodeCache` per node during the walk and hands the\n * same instance to each plugin's generator context, so work derived purely from the node (its\n * resolved name, imports, parameters) is computed by the first plugin that needs it and reused by\n * the rest instead of being recomputed per plugin.\n *\n * Keys are namespaced by convention (`'plugin-ts:imports'`) so two plugins caching different\n * derivations of the same node never collide.\n *\n * @example Fill on first read, reuse afterwards\n * ```ts\n * const imports = ctx.cache.getOrSet('plugin-ts:imports', () => ctx.resolver.imports({ node, root, output }))\n * ```\n */\nexport type NodeCache = {\n /**\n * Returns the value stored under `key`, or `undefined` when nothing is stored yet.\n */\n getItem<TValue>(key: string): TValue | undefined\n /**\n * Stores `value` under `key`, overwriting any previous value, and returns it.\n */\n setItem<TValue>(key: string, value: TValue): TValue\n /**\n * Returns the value stored under `key`, computing and storing it with `factory` on the first\n * call. Later calls with the same key return the stored value without running `factory` again.\n */\n getOrSet<TValue>(key: string, factory: () => TValue): TValue\n}\n\n/**\n * Builds an empty {@link NodeCache} backed by a plain `Map`. Called once per node in the generate\n * walk.\n */\nexport function createNodeCache(): NodeCache {\n const store = new Map<string, unknown>()\n\n return {\n getItem<TValue>(key: string): TValue | undefined {\n return store.get(key) as TValue | undefined\n },\n setItem<TValue>(key: string, value: TValue): TValue {\n store.set(key, value)\n return value\n },\n getOrSet<TValue>(key: string, factory: () => TValue): TValue {\n if (store.has(key)) return store.get(key) as TValue\n\n const value = factory()\n store.set(key, value)\n return value\n },\n }\n}\n"],"mappings":"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"}