@kubb/core 5.0.0-beta.60 → 5.0.0-beta.62
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/{diagnostics-B-UZnFqP.d.ts → diagnostics-D0G07LHG.d.ts} +12 -14
- package/dist/index.cjs +71 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +72 -64
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +4 -4
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +3 -3
- package/dist/mocks.js +5 -5
- package/dist/mocks.js.map +1 -1
- package/package.json +4 -4
- package/src/KubbDriver.ts +23 -38
- package/src/Transform.ts +57 -27
- package/src/defineGenerator.ts +1 -8
- package/src/definePlugin.ts +10 -5
- package/src/mocks.ts +7 -7
package/dist/mocks.cjs
CHANGED
|
@@ -72,7 +72,7 @@ function createMockedPlugin(params) {
|
|
|
72
72
|
name: params.name,
|
|
73
73
|
options: params.options,
|
|
74
74
|
resolver: params.resolver,
|
|
75
|
-
|
|
75
|
+
macros: params.macros,
|
|
76
76
|
dependencies: params.dependencies,
|
|
77
77
|
hooks: {}
|
|
78
78
|
};
|
|
@@ -111,7 +111,7 @@ function createMockedPluginContext(opts) {
|
|
|
111
111
|
async function renderGeneratorSchema(generator, node, opts) {
|
|
112
112
|
if (!generator.schema) return;
|
|
113
113
|
const context = createMockedPluginContext(opts);
|
|
114
|
-
const transformedNode = opts.plugin.
|
|
114
|
+
const transformedNode = opts.plugin.macros?.length ? (0, _kubb_ast.applyMacros)(node, opts.plugin.macros) : node;
|
|
115
115
|
const result = await generator.schema(transformedNode, {
|
|
116
116
|
...context,
|
|
117
117
|
options: opts.options
|
|
@@ -133,7 +133,7 @@ async function renderGeneratorSchema(generator, node, opts) {
|
|
|
133
133
|
async function renderGeneratorOperation(generator, node, opts) {
|
|
134
134
|
if (!generator.operation) return;
|
|
135
135
|
const context = createMockedPluginContext(opts);
|
|
136
|
-
const transformedNode = opts.plugin.
|
|
136
|
+
const transformedNode = opts.plugin.macros?.length ? (0, _kubb_ast.applyMacros)(node, opts.plugin.macros) : node;
|
|
137
137
|
const result = await generator.operation(transformedNode, {
|
|
138
138
|
...context,
|
|
139
139
|
options: opts.options
|
|
@@ -155,7 +155,7 @@ async function renderGeneratorOperation(generator, node, opts) {
|
|
|
155
155
|
async function renderGeneratorOperations(generator, nodes, opts) {
|
|
156
156
|
if (!generator.operations) return;
|
|
157
157
|
const context = createMockedPluginContext(opts);
|
|
158
|
-
const transformedNodes = opts.plugin.
|
|
158
|
+
const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => (0, _kubb_ast.applyMacros)(n, opts.plugin.macros)) : nodes;
|
|
159
159
|
const result = await generator.operations(transformedNodes, {
|
|
160
160
|
...context,
|
|
161
161
|
options: opts.options
|
package/dist/mocks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mocks.cjs","names":["FileManager","FileProcessor","memoryStorage","path","camelCase"],"sources":["../src/mocks.ts"],"sourcesContent":["import path, { resolve } from 'node:path'\nimport { camelCase } from '@internals/utils'\nimport type { FileNode, InputMeta, OperationNode, SchemaNode, Visitor } from '@kubb/ast'\nimport { transform } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { FileProcessor } from './FileProcessor.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport { memoryStorage } from './storages/memoryStorage.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions, RendererFactory } from './types.ts'\n\n/**\n * Creates a minimal `PluginDriver` 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 if (instance.stream) {\n for (const file of instance.stream(result)) fileManager.upsert(file)\n return\n }\n\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 * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\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 transformer?: Visitor\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n transformer: params.transformer,\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 ?? ({} as never),\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.transformer ? transform(node, opts.plugin.transformer) : 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.transformer ? transform(node, opts.plugin.transformer) : 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.transformer ? nodes.map((n) => transform(n, opts.plugin.transformer!)) : 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 fileProcessor = new FileProcessor({ storage: memoryStorage(), parsers })\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 = fileProcessor.parse(file)\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.cjs","names":["FileManager","FileProcessor","memoryStorage","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 } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { FileProcessor } from './FileProcessor.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport { memoryStorage } from './storages/memoryStorage.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions, RendererFactory } from './types.ts'\n\n/**\n * Creates a minimal `PluginDriver` 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 if (instance.stream) {\n for (const file of instance.stream(result)) fileManager.upsert(file)\n return\n }\n\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 * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\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 ?? ({} as never),\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 fileProcessor = new FileProcessor({ storage: memoryStorage(), parsers })\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 = fileProcessor.parse(file)\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,6 +1,6 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-C0LytTxp.js";
|
|
2
|
-
import { At as Adapter, G as Generator, J as KubbDriver, U as Parser, h as Config, jt as AdapterFactoryOptions, st as PluginFactoryOptions, tt as NormalizedPlugin } from "./diagnostics-
|
|
3
|
-
import { FileNode, InputMeta, OperationNode, SchemaNode
|
|
2
|
+
import { At as Adapter, G as Generator, J as KubbDriver, U as Parser, h as Config, jt as AdapterFactoryOptions, st as PluginFactoryOptions, tt as NormalizedPlugin } from "./diagnostics-D0G07LHG.js";
|
|
3
|
+
import { FileNode, InputMeta, Macro, OperationNode, SchemaNode } from "@kubb/ast";
|
|
4
4
|
|
|
5
5
|
//#region src/mocks.d.ts
|
|
6
6
|
/**
|
|
@@ -32,7 +32,7 @@ declare function createMockedPlugin<TOptions extends PluginFactoryOptions = Plug
|
|
|
32
32
|
name: TOptions['name'];
|
|
33
33
|
options: TOptions['resolvedOptions'];
|
|
34
34
|
resolver?: TOptions['resolver'];
|
|
35
|
-
|
|
35
|
+
macros?: Array<Macro>;
|
|
36
36
|
dependencies?: Array<string>;
|
|
37
37
|
}): NormalizedPlugin<TOptions>;
|
|
38
38
|
type RenderGeneratorOptions<TOptions extends PluginFactoryOptions> = {
|
package/dist/mocks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./chunk-C0LytTxp.js";
|
|
2
2
|
import { c as camelCase, i as FileManager, n as _usingCtx, r as FileProcessor, t as memoryStorage } from "./memoryStorage-CWFzAz4o.js";
|
|
3
3
|
import path, { resolve } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { applyMacros } from "@kubb/ast";
|
|
5
5
|
import { expect } from "vitest";
|
|
6
6
|
//#region src/mocks.ts
|
|
7
7
|
/**
|
|
@@ -71,7 +71,7 @@ function createMockedPlugin(params) {
|
|
|
71
71
|
name: params.name,
|
|
72
72
|
options: params.options,
|
|
73
73
|
resolver: params.resolver,
|
|
74
|
-
|
|
74
|
+
macros: params.macros,
|
|
75
75
|
dependencies: params.dependencies,
|
|
76
76
|
hooks: {}
|
|
77
77
|
};
|
|
@@ -110,7 +110,7 @@ function createMockedPluginContext(opts) {
|
|
|
110
110
|
async function renderGeneratorSchema(generator, node, opts) {
|
|
111
111
|
if (!generator.schema) return;
|
|
112
112
|
const context = createMockedPluginContext(opts);
|
|
113
|
-
const transformedNode = opts.plugin.
|
|
113
|
+
const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node;
|
|
114
114
|
const result = await generator.schema(transformedNode, {
|
|
115
115
|
...context,
|
|
116
116
|
options: opts.options
|
|
@@ -132,7 +132,7 @@ async function renderGeneratorSchema(generator, node, opts) {
|
|
|
132
132
|
async function renderGeneratorOperation(generator, node, opts) {
|
|
133
133
|
if (!generator.operation) return;
|
|
134
134
|
const context = createMockedPluginContext(opts);
|
|
135
|
-
const transformedNode = opts.plugin.
|
|
135
|
+
const transformedNode = opts.plugin.macros?.length ? applyMacros(node, opts.plugin.macros) : node;
|
|
136
136
|
const result = await generator.operation(transformedNode, {
|
|
137
137
|
...context,
|
|
138
138
|
options: opts.options
|
|
@@ -154,7 +154,7 @@ async function renderGeneratorOperation(generator, node, opts) {
|
|
|
154
154
|
async function renderGeneratorOperations(generator, nodes, opts) {
|
|
155
155
|
if (!generator.operations) return;
|
|
156
156
|
const context = createMockedPluginContext(opts);
|
|
157
|
-
const transformedNodes = opts.plugin.
|
|
157
|
+
const transformedNodes = opts.plugin.macros?.length ? nodes.map((n) => applyMacros(n, opts.plugin.macros)) : nodes;
|
|
158
158
|
const result = await generator.operations(transformedNodes, {
|
|
159
159
|
...context,
|
|
160
160
|
options: opts.options
|
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, OperationNode, SchemaNode, Visitor } from '@kubb/ast'\nimport { transform } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { FileProcessor } from './FileProcessor.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport { memoryStorage } from './storages/memoryStorage.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions, RendererFactory } from './types.ts'\n\n/**\n * Creates a minimal `PluginDriver` 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 if (instance.stream) {\n for (const file of instance.stream(result)) fileManager.upsert(file)\n return\n }\n\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 * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\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 transformer?: Visitor\n dependencies?: Array<string>\n}): NormalizedPlugin<TOptions> {\n return {\n name: params.name,\n options: params.options,\n resolver: params.resolver,\n transformer: params.transformer,\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 ?? ({} as never),\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.transformer ? transform(node, opts.plugin.transformer) : 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.transformer ? transform(node, opts.plugin.transformer) : 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.transformer ? nodes.map((n) => transform(n, opts.plugin.transformer!)) : 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 fileProcessor = new FileProcessor({ storage: memoryStorage(), parsers })\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 = fileProcessor.parse(file)\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":";;;;;;;;;AAeA,SAAgB,yBAAyB,UAAyE,CAAC,GAAe;CAChI,MAAM,cAAc,IAAI,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,IAAI,SAAS,QAAQ;KACnB,KAAK,MAAM,QAAQ,SAAS,OAAO,MAAM,GAAG,YAAY,OAAO,IAAI;KACnE;IACF;IAEA,MAAM,SAAS,OAAO,MAAM;IAC5B,YAAY,OAAO,GAAG,SAAS,KAAK;;;;;;EACtC;CACF;AACF;;;;;;AAOA,SAAgB,oBACd,UAKI,CAAC,GACc;CACnB,OAAO;EACL,MAAO,QAAQ,QAAQ;EACvB,SAAU,QAAQ,mBAAmB,CAAC;EACtC,OAAO,QAAQ,UAAU,aAAa;GAAE,MAAM;GAAkB,SAAS,CAAC;GAAG,YAAY,CAAC;EAAE;EAC5F,YAAY,QAAQ,gBAAgB,OAAmB,aAAqE,CAAC;CAC/H;AACF;;;;;;;AAQA,SAAgB,mBAAiF,QAMlE;CAC7B,OAAO;EACL,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,aAAa,OAAO;EACpB,cAAc,OAAO;EACrB,OAAO,CAAC;CACV;AACF;AAYA,SAAS,0BAAiE,MAAqF;CAC7J,MAAM,OAAO,QAAQ,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,SAAU,CAAC;EAC9B,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,cAAc,UAAU,MAAM,KAAK,OAAO,WAAW,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,cAAc,UAAU,MAAM,KAAK,OAAO,WAAW,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,cAAc,MAAM,KAAK,MAAM,UAAU,GAAG,KAAK,OAAO,WAAY,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,gBAAgB,IAAI,cAAc;EAAE,SAAS,cAAc;EAAG;CAAQ,CAAC;CAC7E,MAAM,4BAAY,IAAI,IAAoB;CAE1C,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,MAAM,QAAQ,UAAU,IAAI,KAAK,IAAI,GACxC;EAGF,MAAM,SAAS,cAAc,MAAM,IAAI;EACvC,MAAM,OAAO,KAAK,SAAS,SAAS,OAAO,KAAK,CAAC,SAAS,SAAS,MAAM,OAAO,MAAM;EAEtF,UAAU,IAAI,KAAK,MAAM,IAAI;EAE7B,MAAM,eAAe,KAAK,KAAK,iBAAiB,GAAI,MAAM,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,GAAI,KAAK,QAAQ;EAC/F,MAAM,OAAO,IAAI,CAAC,CAAC,oBAAoB,YAAY;CACrD;CAEA,OAAO;AACT"}
|
|
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 } from '@kubb/ast'\nimport { expect } from 'vitest'\nimport type { Parser } from './defineParser.ts'\nimport { FileManager } from './FileManager.ts'\nimport { FileProcessor } from './FileProcessor.ts'\nimport type { KubbDriver } from './KubbDriver.ts'\nimport { memoryStorage } from './storages/memoryStorage.ts'\nimport type { Adapter, AdapterFactoryOptions, Config, Generator, GeneratorContext, NormalizedPlugin, PluginFactoryOptions, RendererFactory } from './types.ts'\n\n/**\n * Creates a minimal `PluginDriver` 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 if (instance.stream) {\n for (const file of instance.stream(result)) fileManager.upsert(file)\n return\n }\n\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 * `getImports` returns `[]` by default.\n */\nexport function createMockedAdapter<TOptions extends AdapterFactoryOptions = AdapterFactoryOptions>(\n options: {\n name?: TOptions['name']\n resolvedOptions?: TOptions['resolvedOptions']\n parse?: Adapter<TOptions>['parse']\n getImports?: Adapter<TOptions>['getImports']\n } = {},\n): Adapter<TOptions> {\n return {\n name: (options.name ?? 'oas') as TOptions['name'],\n options: (options.resolvedOptions ?? {}) as TOptions['resolvedOptions'],\n parse: options.parse ?? (async () => ({ kind: 'Input' as const, schemas: [], operations: [] })),\n getImports: options.getImports ?? ((_node: SchemaNode, _resolve: (schemaName: string) => { name: string; path: string }) => []),\n } as Adapter<TOptions>\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 ?? ({} as never),\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 fileProcessor = new FileProcessor({ storage: memoryStorage(), parsers })\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 = fileProcessor.parse(file)\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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/core",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.62",
|
|
4
4
|
"description": "Core engine for Kubb's plugin-based code generation system. Provides the plugin driver, file manager, defineConfig, and build orchestration used by every Kubb plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"code-generator",
|
|
@@ -56,14 +56,14 @@
|
|
|
56
56
|
"registry": "https://registry.npmjs.org/"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@kubb/ast": "5.0.0-beta.
|
|
59
|
+
"@kubb/ast": "5.0.0-beta.62"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@internals/utils": "0.0.0",
|
|
63
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
63
|
+
"@kubb/renderer-jsx": "5.0.0-beta.62"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
66
|
+
"@kubb/renderer-jsx": "5.0.0-beta.62"
|
|
67
67
|
},
|
|
68
68
|
"engines": {
|
|
69
69
|
"node": ">=22"
|
package/src/KubbDriver.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { resolve } from 'node:path'
|
|
|
2
2
|
import { arrayToAsyncIterable, type AsyncEventEmitter, forBatches, getElapsedMs, isPromise, memoize, Url } from '@internals/utils'
|
|
3
3
|
import * as factory from '@kubb/ast/factory'
|
|
4
4
|
import { collectUsedSchemaNames } from '@kubb/ast/utils'
|
|
5
|
-
import type { FileNode, InputMeta, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
5
|
+
import type { Enforce, FileNode, InputMeta, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
6
6
|
import { OPERATION_FILTER_TYPES, SCHEMA_PARALLEL } from './constants.ts'
|
|
7
7
|
import { type Diagnostic, Diagnostics, type ProblemDiagnostic } from './diagnostics.ts'
|
|
8
8
|
import type { RendererFactory } from './createRenderer.ts'
|
|
@@ -45,7 +45,7 @@ type RequirePluginContext = {
|
|
|
45
45
|
requiredBy?: string
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
function enforceOrder(enforce:
|
|
48
|
+
function enforceOrder(enforce: Enforce | undefined): number {
|
|
49
49
|
return enforce === 'pre' ? -1 : enforce === 'post' ? 1 : 0
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -88,7 +88,7 @@ export class KubbDriver {
|
|
|
88
88
|
readonly #listeners: Array<ListenerEntry> = []
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
* Transform registry. Plugins populate it during `kubb:plugin:setup` via `
|
|
91
|
+
* Transform registry. Plugins populate it during `kubb:plugin:setup` via `addMacro`/`setMacros`,
|
|
92
92
|
* and `#runGenerators` reads it once per `(plugin, node)` pair through `applyTo`.
|
|
93
93
|
*/
|
|
94
94
|
readonly #transforms = new Transform()
|
|
@@ -188,7 +188,7 @@ export class KubbDriver {
|
|
|
188
188
|
* Registers a hook-style plugin's lifecycle handlers on the shared `AsyncEventEmitter`.
|
|
189
189
|
*
|
|
190
190
|
* The `kubb:plugin:setup` listener wraps the global context in a plugin-specific one so
|
|
191
|
-
* `addGenerator`, `setResolver`, and `
|
|
191
|
+
* `addGenerator`, `setResolver`, and `setMacros` target the right `normalizedPlugin`.
|
|
192
192
|
* Every other `KubbHooks` event registers as a pass-through listener that external tooling
|
|
193
193
|
* can observe via `hooks.on(...)`.
|
|
194
194
|
*
|
|
@@ -213,8 +213,11 @@ export class KubbDriver {
|
|
|
213
213
|
setResolver: (resolver) => {
|
|
214
214
|
this.setPluginResolver(plugin.name, resolver)
|
|
215
215
|
},
|
|
216
|
-
|
|
217
|
-
this.#transforms.
|
|
216
|
+
addMacro: (macro) => {
|
|
217
|
+
this.#transforms.add(plugin.name, macro)
|
|
218
|
+
},
|
|
219
|
+
setMacros: (macros) => {
|
|
220
|
+
this.#transforms.set(plugin.name, macros)
|
|
218
221
|
},
|
|
219
222
|
setOptions: (opts) => {
|
|
220
223
|
plugin.options = { ...plugin.options, ...opts }
|
|
@@ -245,7 +248,7 @@ export class KubbDriver {
|
|
|
245
248
|
|
|
246
249
|
/**
|
|
247
250
|
* Emits the `kubb:plugin:setup` event so that all registered hook-style plugin listeners
|
|
248
|
-
* can configure generators, resolvers,
|
|
251
|
+
* can configure generators, resolvers, macros and renderers before `buildStart` runs.
|
|
249
252
|
*
|
|
250
253
|
* Call this once from `safeBuild` before the plugin execution loop begins.
|
|
251
254
|
*/
|
|
@@ -257,7 +260,8 @@ export class KubbDriver {
|
|
|
257
260
|
options: {},
|
|
258
261
|
addGenerator: noop,
|
|
259
262
|
setResolver: noop,
|
|
260
|
-
|
|
263
|
+
addMacro: noop,
|
|
264
|
+
setMacros: noop,
|
|
261
265
|
setOptions: noop,
|
|
262
266
|
injectFile: noop,
|
|
263
267
|
updateConfig: noop,
|
|
@@ -278,37 +282,21 @@ export class KubbDriver {
|
|
|
278
282
|
* Call this method inside `addGenerator()` (in `kubb:plugin:setup`) to wire up a generator.
|
|
279
283
|
*/
|
|
280
284
|
registerGenerator(pluginName: string, generator: Generator): void {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (ctx.plugin.name !== pluginName) return
|
|
284
|
-
const result = await generator.schema!(node, ctx)
|
|
285
|
-
|
|
286
|
-
await this.dispatch({ result, renderer: generator.renderer })
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
this.#trackListener('kubb:generate:schema', schemaHandler)
|
|
290
|
-
}
|
|
285
|
+
const register = <TNode>(event: keyof KubbHooks & string, method: ((node: TNode, ctx: GeneratorContext) => unknown) | undefined): void => {
|
|
286
|
+
if (!method) return
|
|
291
287
|
|
|
292
|
-
|
|
293
|
-
const operationHandler = async (node: OperationNode, ctx: GeneratorContext) => {
|
|
288
|
+
const handler = async (node: TNode, ctx: GeneratorContext) => {
|
|
294
289
|
if (ctx.plugin.name !== pluginName) return
|
|
295
|
-
|
|
296
|
-
const result = await generator.operation!(node, ctx)
|
|
290
|
+
const result = await method(node, ctx)
|
|
297
291
|
await this.dispatch({ result, renderer: generator.renderer })
|
|
298
292
|
}
|
|
299
293
|
|
|
300
|
-
this.#trackListener(
|
|
294
|
+
this.#trackListener(event, handler as HookListener<KubbHooks[typeof event], unknown>)
|
|
301
295
|
}
|
|
302
296
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const result = await generator.operations!(nodes, ctx)
|
|
307
|
-
await this.dispatch({ result, renderer: generator.renderer })
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
this.#trackListener('kubb:generate:operations', operationsHandler)
|
|
311
|
-
}
|
|
297
|
+
register('kubb:generate:schema', generator.schema)
|
|
298
|
+
register('kubb:generate:operation', generator.operation)
|
|
299
|
+
register('kubb:generate:operations', generator.operations)
|
|
312
300
|
|
|
313
301
|
this.#eventGeneratorPlugins.add(pluginName)
|
|
314
302
|
}
|
|
@@ -373,7 +361,7 @@ export class KubbDriver {
|
|
|
373
361
|
|
|
374
362
|
// Parse the adapter source into the streaming `InputNode`.
|
|
375
363
|
await this.#parseInput()
|
|
376
|
-
// Emit `kubb:plugin:setup` so plugins can register
|
|
364
|
+
// Emit `kubb:plugin:setup` so plugins can register macros via `addMacro`/`setMacros`.
|
|
377
365
|
// Each call writes into `this.#transforms`, which `#runGenerators` later reads through
|
|
378
366
|
// `transforms.applyTo`.
|
|
379
367
|
await this.emitSetupHooks()
|
|
@@ -465,7 +453,7 @@ export class KubbDriver {
|
|
|
465
453
|
|
|
466
454
|
/**
|
|
467
455
|
* Streams schemas and operations through every plugin's generators. Each node is run
|
|
468
|
-
* through the plugin's
|
|
456
|
+
* through the plugin's macros (from `this.#transforms`) before the generator sees it,
|
|
469
457
|
* so plugins stay isolated and the hot path stays per-node. Schemas run before operations
|
|
470
458
|
* because the two passes share `flushPending` and the FileProcessor's event emitter.
|
|
471
459
|
* A failing plugin contributes an error diagnostic so the rest of the build continues.
|
|
@@ -561,7 +549,7 @@ export class KubbDriver {
|
|
|
561
549
|
}
|
|
562
550
|
}
|
|
563
551
|
|
|
564
|
-
// Apply the plugin's
|
|
552
|
+
// Apply the plugin's macros, then resolve options (skipping the resolver when
|
|
565
553
|
// optionsAreStatic). Returns null when include/exclude/override rules out the node.
|
|
566
554
|
// The per-node dispatch and the collected-operations tail both go through this so
|
|
567
555
|
// they agree on what a plugin sees.
|
|
@@ -835,9 +823,6 @@ export class KubbDriver {
|
|
|
835
823
|
get resolver() {
|
|
836
824
|
return driver.getResolver(plugin.name)
|
|
837
825
|
},
|
|
838
|
-
get transformer() {
|
|
839
|
-
return driver.#transforms.get(plugin.name)
|
|
840
|
-
},
|
|
841
826
|
warn(message: string) {
|
|
842
827
|
report({ code: Diagnostics.code.pluginWarning, severity: 'warning', message })
|
|
843
828
|
},
|
package/src/Transform.ts
CHANGED
|
@@ -1,53 +1,65 @@
|
|
|
1
|
-
import type { OperationNode, SchemaNode, Visitor } from '@kubb/ast'
|
|
2
|
-
import { transform } from '@kubb/ast'
|
|
1
|
+
import type { Macro, OperationNode, SchemaNode, Visitor } from '@kubb/ast'
|
|
2
|
+
import { composeMacros, transform } from '@kubb/ast'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Holds
|
|
6
|
-
* isolation on the original adapter node
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Holds an ordered list of macros per plugin, keyed by plugin name. Each plugin's macros run in
|
|
6
|
+
* isolation on the original adapter node and are composed into a single `Visitor` that the
|
|
7
|
+
* `@kubb/ast` `transform` primitive applies. `applyTo` is a per-plugin lookup, not a cross-plugin
|
|
8
|
+
* chain, so plugin A's macros never see plugin B's output. When a plugin has no macros, `applyTo`
|
|
9
|
+
* returns the original node reference, and `transform` does the same when the composed visitor
|
|
10
|
+
* leaves the tree untouched, so callers can detect a no-op by identity.
|
|
10
11
|
*
|
|
11
|
-
* Registration order matches the order setup hooks fire, which the driver has already sorted
|
|
12
|
-
*
|
|
12
|
+
* Registration order matches the order setup hooks fire, which the driver has already sorted by
|
|
13
|
+
* `enforce` and dependency edges. The registry preserves that order; macro `enforce` only reorders
|
|
14
|
+
* within a single plugin's list.
|
|
13
15
|
*/
|
|
14
16
|
export class Transform {
|
|
15
|
-
readonly #
|
|
17
|
+
readonly #macros = new Map<string, Array<Macro>>()
|
|
18
|
+
// Composed visitor per plugin, rebuilt lazily after the macro list changes.
|
|
19
|
+
readonly #composed = new Map<string, Visitor>()
|
|
16
20
|
// Memoized results per plugin. Repeated `applyTo` calls return the same node identity, so
|
|
17
21
|
// downstream WeakMap caches keyed by node (the resolver's resolveOptions memo) hit when the
|
|
18
|
-
// driver resolves a node a second time, and a stateful
|
|
22
|
+
// driver resolves a node a second time, and a stateful macro runs once per node.
|
|
19
23
|
readonly #memo = new Map<string, WeakMap<SchemaNode | OperationNode, SchemaNode | OperationNode>>()
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
|
-
* Number of plugins with
|
|
26
|
+
* Number of plugins with at least one registered macro.
|
|
23
27
|
*/
|
|
24
28
|
get size(): number {
|
|
25
|
-
return this.#
|
|
29
|
+
return this.#macros.size
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
|
-
*
|
|
30
|
-
* replaces the first.
|
|
33
|
+
* Appends `macro` to the plugin's list, after any macros already registered.
|
|
31
34
|
*/
|
|
32
|
-
|
|
33
|
-
this.#
|
|
34
|
-
|
|
35
|
+
add(pluginName: string, macro: Macro): void {
|
|
36
|
+
const list = this.#macros.get(pluginName)
|
|
37
|
+
if (list) list.push(macro)
|
|
38
|
+
else this.#macros.set(pluginName, [macro])
|
|
39
|
+
this.#invalidate(pluginName)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Replaces the plugin's macro list with `macros`.
|
|
44
|
+
*/
|
|
45
|
+
set(pluginName: string, macros: ReadonlyArray<Macro>): void {
|
|
46
|
+
this.#macros.set(pluginName, [...macros])
|
|
47
|
+
this.#invalidate(pluginName)
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
/**
|
|
38
|
-
* Looks up the
|
|
39
|
-
* read their own visitor through `ctx.transformer`.
|
|
51
|
+
* Looks up the composed visitor for `pluginName`, or `undefined` when the plugin has no macros.
|
|
40
52
|
*/
|
|
41
53
|
get(pluginName: string): Visitor | undefined {
|
|
42
|
-
return this.#
|
|
54
|
+
return this.#visitorFor(pluginName)
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
* Runs the plugin's
|
|
47
|
-
*
|
|
58
|
+
* Runs the plugin's macros on `node`. Returns the original node reference when the plugin has no
|
|
59
|
+
* macros, so callers can compare by identity to detect a no-op.
|
|
48
60
|
*/
|
|
49
61
|
applyTo<TNode extends SchemaNode | OperationNode>(pluginName: string, node: TNode): TNode {
|
|
50
|
-
const visitor = this.#
|
|
62
|
+
const visitor = this.#visitorFor(pluginName)
|
|
51
63
|
if (!visitor) return node
|
|
52
64
|
|
|
53
65
|
let memo = this.#memo.get(pluginName)
|
|
@@ -65,11 +77,29 @@ export class Transform {
|
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
/**
|
|
68
|
-
* Clears every registration. Called from the driver's `dispose()` so
|
|
69
|
-
*
|
|
80
|
+
* Clears every registration. Called from the driver's `dispose()` so macros do not leak across
|
|
81
|
+
* builds.
|
|
70
82
|
*/
|
|
71
83
|
dispose(): void {
|
|
72
|
-
this.#
|
|
84
|
+
this.#macros.clear()
|
|
85
|
+
this.#composed.clear()
|
|
73
86
|
this.#memo.clear()
|
|
74
87
|
}
|
|
88
|
+
|
|
89
|
+
#invalidate(pluginName: string): void {
|
|
90
|
+
this.#composed.delete(pluginName)
|
|
91
|
+
this.#memo.delete(pluginName)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#visitorFor(pluginName: string): Visitor | undefined {
|
|
95
|
+
const macros = this.#macros.get(pluginName)
|
|
96
|
+
if (!macros || macros.length === 0) return undefined
|
|
97
|
+
|
|
98
|
+
let composed = this.#composed.get(pluginName)
|
|
99
|
+
if (!composed) {
|
|
100
|
+
composed = composeMacros(macros)
|
|
101
|
+
this.#composed.set(pluginName, composed)
|
|
102
|
+
}
|
|
103
|
+
return composed
|
|
104
|
+
}
|
|
75
105
|
}
|
package/src/defineGenerator.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AsyncEventEmitter, PossiblePromise } from '@internals/utils'
|
|
2
|
-
import type { FileNode, InputMeta, OperationNode, SchemaNode
|
|
2
|
+
import type { FileNode, InputMeta, OperationNode, SchemaNode } from '@kubb/ast'
|
|
3
3
|
import type { Adapter } from './createAdapter.ts'
|
|
4
4
|
import type { RendererFactory } from './createRenderer.ts'
|
|
5
5
|
import type { KubbHooks } from './types.ts'
|
|
@@ -74,13 +74,6 @@ export type GeneratorContext<TOptions extends PluginFactoryOptions = PluginFacto
|
|
|
74
74
|
* `ctx.resolver.resolveFile({ name: 'pet', extname: '.ts' }, { root, output })`
|
|
75
75
|
*/
|
|
76
76
|
resolver: TOptions['resolver']
|
|
77
|
-
/**
|
|
78
|
-
* The AST visitor this plugin registered through `setTransformer` during
|
|
79
|
-
* `kubb:plugin:setup`, or `undefined` when it never registered one. The driver already
|
|
80
|
-
* applies the visitor to every schema and operation node before a generator sees it, so
|
|
81
|
-
* read it here only to inspect or re-run the transformation.
|
|
82
|
-
*/
|
|
83
|
-
transformer: Visitor | undefined
|
|
84
77
|
/**
|
|
85
78
|
* Report a warning. Collected as a `warning` diagnostic attributed to the current
|
|
86
79
|
* plugin. It surfaces in the run summary but does not fail the build. For a structured
|
package/src/definePlugin.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileNode, HttpMethod,
|
|
1
|
+
import type { Enforce, FileNode, HttpMethod, Macro, UserFileNode } from '@kubb/ast'
|
|
2
2
|
import { diagnosticCode } from './constants.ts'
|
|
3
3
|
import type { Generator } from './defineGenerator.ts'
|
|
4
4
|
import type { BannerMeta, Resolver } from './defineResolver.ts'
|
|
@@ -297,9 +297,14 @@ export type KubbPluginSetupContext<TFactory extends PluginFactoryOptions = Plugi
|
|
|
297
297
|
*/
|
|
298
298
|
setResolver(resolver: Partial<TFactory['resolver']>): void
|
|
299
299
|
/**
|
|
300
|
-
*
|
|
300
|
+
* Add a macro that rewrites AST nodes before they reach generators. Macros run in the order they
|
|
301
|
+
* are added, after any macros from earlier `addMacro` calls.
|
|
301
302
|
*/
|
|
302
|
-
|
|
303
|
+
addMacro(macro: Macro): void
|
|
304
|
+
/**
|
|
305
|
+
* Replace this plugin's macros with `macros`.
|
|
306
|
+
*/
|
|
307
|
+
setMacros(macros: ReadonlyArray<Macro>): void
|
|
303
308
|
/**
|
|
304
309
|
* Set resolved options merged into the normalized plugin's `options`.
|
|
305
310
|
* Call this in `kubb:plugin:setup` to provide options generators need.
|
|
@@ -346,7 +351,7 @@ export type Plugin<TFactory extends PluginFactoryOptions = PluginFactoryOptions>
|
|
|
346
351
|
*
|
|
347
352
|
* Dependency constraints always take precedence over `enforce`.
|
|
348
353
|
*/
|
|
349
|
-
enforce?:
|
|
354
|
+
enforce?: Enforce
|
|
350
355
|
/**
|
|
351
356
|
* The options passed by the user when calling the plugin factory.
|
|
352
357
|
*/
|
|
@@ -376,7 +381,7 @@ export type NormalizedPlugin<TOptions extends PluginFactoryOptions = PluginFacto
|
|
|
376
381
|
override: Array<Override<TOptions['resolvedOptions']>>
|
|
377
382
|
}
|
|
378
383
|
resolver: TOptions['resolver']
|
|
379
|
-
|
|
384
|
+
macros?: Array<Macro>
|
|
380
385
|
generators?: Array<Generator>
|
|
381
386
|
apply?: (config: Config) => boolean
|
|
382
387
|
version?: string
|