diff --git a/.changeset/brave-parrots-smell.md b/.changeset/brave-parrots-smell.md new file mode 100644 index 000000000..679af75ea --- /dev/null +++ b/.changeset/brave-parrots-smell.md @@ -0,0 +1,15 @@ +--- +"@kubb/swagger-tanstack-query": patch +"@kubb/swagger-client": patch +"@kubb/swagger-zodios": patch +"@kubb/swagger-faker": patch +"@kubb/swagger-msw": patch +"@kubb/swagger-swr": patch +"@kubb/swagger-zod": patch +"@kubb/swagger-ts": patch +"@kubb/swagger": patch +"@kubb/react": patch +"@kubb/core": patch +--- + +useOperation and useSchema with a component Oas and Oas.Operation diff --git a/.changeset/orange-dots-sort.md b/.changeset/orange-dots-sort.md new file mode 100644 index 000000000..91dc8bfb6 --- /dev/null +++ b/.changeset/orange-dots-sort.md @@ -0,0 +1,5 @@ +--- +"@kubb/swagger-zod": patch +--- + +use of `useOperationHelpers` to abstract operation logic diff --git a/docs/plugins/development/system.md b/docs/plugins/development/system.md index df4fbc8a0..5de17f404 100644 --- a/docs/plugins/development/system.md +++ b/docs/plugins/development/system.md @@ -47,7 +47,7 @@ export const definePlugin = createPlugin((options) => { name: pluginName, options, pre: [], - resolvePath(baseName, directory, options) { + resolvePath(baseName, mode, options) { const root = path.resolve(this.config.root, this.config.output.path) return path.resolve(root, output.path, baseName) @@ -176,7 +176,7 @@ Add some extra functionality to your plugin, here you can even use functions whi This will be called when pluginManager.resolvePath is called, see [Pluginmanager and resolving a path](/reference/pluginManager/#pluginmanager-resolvepath). -- **Type:** `(this: PluginContext, baseName: string, directory?: string | undefined, options?: object) => KubbFile.OptionalPath`
+- **Type:** `(this: PluginContext, baseName: string, mode?: 'file' | 'directory', options?: object) => KubbFile.OptionalPath`
### resolveName diff --git a/docs/plugins/swagger/hooks/index.md b/docs/plugins/swagger/hooks/index.md index e4e605736..d53fca1cc 100644 --- a/docs/plugins/swagger/hooks/index.md +++ b/docs/plugins/swagger/hooks/index.md @@ -20,7 +20,7 @@ See [Oas](https://github.com/readmeio/oas) to understand how to use the `Oas` in import { useOas } from '@kubb/react' function Component() { - const oas = useOas() + const { oas } = useOas() return null } diff --git a/examples/client/templates/client/index.tsx b/examples/client/templates/client/index.tsx index 4ed2ff7c3..20a4baadd 100644 --- a/examples/client/templates/client/index.tsx +++ b/examples/client/templates/client/index.tsx @@ -7,12 +7,12 @@ export const templates = { const clientParams = [client.path.template, client.withData ? 'data' : undefined, 'options'].filter(Boolean).join(', ') return ( - + <> {`return axios.${client.method}(${clientParams})`} - + ) }, } as const diff --git a/examples/faker/petStore.yaml b/examples/faker/petStore.yaml index e81d04f4c..a44084865 100644 --- a/examples/faker/petStore.yaml +++ b/examples/faker/petStore.yaml @@ -721,7 +721,7 @@ components: example: 1 nationalityCode: type: string - pattern: "^[A-Z]{2}$" + pattern: ^[A-Z]{2}$ xml: name: user Tag: diff --git a/examples/react-query-v5/kubb.config.ts b/examples/react-query-v5/kubb.config.ts index d75e99a14..d43d89f90 100644 --- a/examples/react-query-v5/kubb.config.ts +++ b/examples/react-query-v5/kubb.config.ts @@ -4,6 +4,7 @@ import createSwaggerTanstackQuery from '@kubb/swagger-tanstack-query' import createSwaggerTS from '@kubb/swagger-ts' import * as queryKey from './templates/queryKey/index' +import * as operations from './templates/operations/index' /** @type {import('@kubb/core').UserConfig} */ export const config = { @@ -57,6 +58,9 @@ export const config = { }, }, }], + templates: { + operations: operations.templates, + }, }), ], } diff --git a/examples/react-query-v5/src/gen/hooks/index.ts b/examples/react-query-v5/src/gen/hooks/index.ts index 3cd1ec1db..3ceb31e1b 100644 --- a/examples/react-query-v5/src/gen/hooks/index.ts +++ b/examples/react-query-v5/src/gen/hooks/index.ts @@ -1,3 +1,4 @@ +export * from './operations' export * from './useAddPetHook' export * from './useCreateUserHook' export * from './useCreateUsersWithListInputHook' diff --git a/examples/react-query-v5/src/gen/hooks/operations.ts b/examples/react-query-v5/src/gen/hooks/operations.ts new file mode 100644 index 000000000..46c44214f --- /dev/null +++ b/examples/react-query-v5/src/gen/hooks/operations.ts @@ -0,0 +1,60 @@ +export function updatePet() { + return '/pet' +} +export function addPet() { + return '/pet' +} +export function findPetsByStatus() { + return '/pet/findByStatus' +} +export function findPetsByTags() { + return '/pet/findByTags' +} +export function getPetById() { + return '/pet/{petId}' +} +export function updatePetWithForm() { + return '/pet/{petId}' +} +export function deletePet() { + return '/pet/{petId}' +} +export function uploadFile() { + return '/pet/{petId}/uploadImage' +} +export function getInventory() { + return '/store/inventory' +} +export function placeOrder() { + return '/store/order' +} +export function placeOrderPatch() { + return '/store/order' +} +export function getOrderById() { + return '/store/order/{orderId}' +} +export function deleteOrder() { + return '/store/order/{orderId}' +} +export function createUser() { + return '/user' +} +export function createUsersWithListInput() { + return '/user/createWithList' +} +export function loginUser() { + return '/user/login' +} +export function logoutUser() { + return '/user/logout' +} +export function getUserByName() { + return '/user/{username}' +} +export function updateUser() { + return '/user/{username}' +} +export function deleteUser() { + return '/user/{username}' +} diff --git a/examples/react-query-v5/src/gen/index.ts b/examples/react-query-v5/src/gen/index.ts index 6e6f12d44..9d83c2121 100644 --- a/examples/react-query-v5/src/gen/index.ts +++ b/examples/react-query-v5/src/gen/index.ts @@ -1,2 +1,2 @@ -export * from './models/index' export * from './hooks/index' +export * from './models/index' diff --git a/examples/react-query-v5/templates/operations/index.tsx b/examples/react-query-v5/templates/operations/index.tsx new file mode 100644 index 000000000..fcb10323b --- /dev/null +++ b/examples/react-query-v5/templates/operations/index.tsx @@ -0,0 +1,44 @@ +import { Editor, Function, File, useFile, usePlugin } from '@kubb/react' +import { useOperations } from '@kubb/swagger/hooks' +import { Operations } from '@kubb/swagger-tanstack-query/components' +import React from 'react' +import { FileMeta, PluginOptions } from '@kubb/swagger-tanstack-query' +import { Operation } from '@kubb/swagger/oas' + +export const templates = { + ...Operations.templates, + editor: function({ children }: React.ComponentProps) { + const { key: pluginKey } = usePlugin() + + const file = useFile({ name: 'operations', extName: '.ts', pluginKey }) + + return ( + + + baseName={file.baseName} + path={file.path} + meta={file.meta} + > + + {children} + + + + ) + }, + default: function({}: React.ComponentProps) { + const operations = useOperations() + + return ( + <> + {operations.map((item) => { + return ( + + return {JSON.stringify(item.path)} + + ) + })} + + ) + }, +} as const diff --git a/examples/react-query-v5/templates/queryKey/index.tsx b/examples/react-query-v5/templates/queryKey/index.tsx index 097a39360..3c85fef8b 100644 --- a/examples/react-query-v5/templates/queryKey/index.tsx +++ b/examples/react-query-v5/templates/queryKey/index.tsx @@ -22,7 +22,7 @@ export const templates = { ].filter(Boolean) return ( - + <> {`[${keys}] as const`} @@ -30,7 +30,7 @@ export const templates = { {`ReturnType`} - + ) }, } as const diff --git a/packages/core/mocks/index.ts b/packages/core/mocks/index.ts index 11d0f082a..a7d1ee784 100644 --- a/packages/core/mocks/index.ts +++ b/packages/core/mocks/index.ts @@ -1,6 +1,8 @@ import { pascalCase } from '../src/transformers/casing.ts' import { PluginManager } from '../src/PluginManager.ts' +import { readSync } from '../src/fs/read.ts' + export const mockedPluginManager = { resolveName: ({ name, type }) => { if (type === 'type') { @@ -17,4 +19,23 @@ export const mockedPluginManager = { on(eventName, args) {}, logLevel: 'info', }, + getFile: ({ name, extName, pluginKey }) => { + const baseName = `${name}${extName}` + let source = '' + + try { + source = readSync(baseName) + } catch (_e) { + // + } + + return { + path: baseName, + baseName, + meta: { + pluginKey, + }, + source, + } + }, } as PluginManager diff --git a/packages/core/src/PluginManager.ts b/packages/core/src/PluginManager.ts index f2dfba4ab..c7b485837 100644 --- a/packages/core/src/PluginManager.ts +++ b/packages/core/src/PluginManager.ts @@ -2,6 +2,7 @@ import PQueue from 'p-queue' +import { readSync } from './fs/read.ts' import { transformReservedWord } from './transformers/transformReservedWord.ts' import { EventEmitter } from './utils/EventEmitter.ts' import { setUniqueName } from './utils/uniqueName.ts' @@ -73,6 +74,14 @@ type Events = { error: [error: Error] } +type GetFileProps = { + name: string + mode?: KubbFile.Mode + extName: KubbFile.Extname + pluginKey: Plugin['key'] + options?: TOptions +} + export class PluginManager { readonly plugins: PluginWithLifeCycle[] readonly fileManager: FileManager @@ -118,12 +127,37 @@ export class PluginManager { return this } + getFile({ name, mode, extName, pluginKey, options }: GetFileProps): KubbFile.File<{ pluginKey: Plugin['key'] }> { + let source = '' + const baseName = `${name}${extName}` as const + const path = this.resolvePath({ baseName, mode, pluginKey, options }) + + if (!path) { + throw new Error(`Filepath should be defined for resolvedName "${name}" and pluginKey [${JSON.stringify(pluginKey)}]`) + } + + try { + source = readSync(path) + } catch (_e) { + // + } + + return { + path, + baseName, + meta: { + pluginKey, + }, + source, + } + } + resolvePath = (params: ResolvePathParams): KubbFile.OptionalPath => { if (params.pluginKey) { const paths = this.hookForPluginSync({ pluginKey: params.pluginKey, hookName: 'resolvePath', - parameters: [params.baseName, params.directory, params.options as object], + parameters: [params.baseName, params.mode, params.options as object], }) if (paths && paths?.length > 1 && this.logger.logLevel === LogLevel.debug) { @@ -138,7 +172,7 @@ export class PluginManager { } return this.hookFirstSync({ hookName: 'resolvePath', - parameters: [params.baseName, params.directory, params.options as object], + parameters: [params.baseName, params.mode, params.options as object], }).result } resolveName = (params: ResolveNameParams): string => { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 230f808b2..fa644df53 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -148,11 +148,6 @@ export type PluginFactoryOptions< * When calling `resolvePath` you can specify better types. */ TResolvePathOptions extends object = object, - /** - * When using @kubb/react(based on React) you can specify here which types should be used when calling render. - * Always extend from `AppMeta` of the core. - */ - TAppMeta = unknown, > = { name: TName /** @@ -165,8 +160,8 @@ export type PluginFactoryOptions< resolvePathOptions: TResolvePathOptions appMeta: { pluginManager: PluginManager - plugin: Plugin> - } & TAppMeta + plugin: Plugin> + } } export type GetPluginFactoryOptions = TPlugin extends UserPlugin ? X : never @@ -202,7 +197,7 @@ export type UserPlugin = UserPlugin & PluginLifecycle -type UnknownUserPlugin = UserPlugin> +type UnknownUserPlugin = UserPlugin> export type Plugin = & { @@ -254,7 +249,7 @@ export type PluginLifecycle '/src/gen/Pet.ts' */ - resolvePath?: (this: PluginContext, baseName: string, directory?: string, options?: TOptions['resolvePathOptions']) => KubbFile.OptionalPath + resolvePath?: (this: PluginContext, baseName: string, mode?: KubbFile.Mode, options?: TOptions['resolvePathOptions']) => KubbFile.OptionalPath /** * Resolve to a name based on a string. * Useful when converting to PascalCase or camelCase. @@ -293,7 +288,7 @@ export type PluginCache = Record export type ResolvePathParams = { pluginKey?: Plugin['key'] baseName: string - directory?: string | undefined + mode?: KubbFile.Mode /** * Options to be passed to 'resolvePath' 3th parameter */ diff --git a/packages/react/src/client/createRoot.ts b/packages/react/src/client/createRoot.ts index 8cd8b5d83..d6cc9a9f7 100644 --- a/packages/react/src/client/createRoot.ts +++ b/packages/react/src/client/createRoot.ts @@ -2,7 +2,7 @@ import { createNode } from '../shared/dom.ts' import { ReactTemplate } from '../shared/ReactTemplate.tsx' import type { Logger } from '@kubb/core/logger' -import type { AppContextProps } from '../components/AppContext.ts' +import type { AppContextProps } from '../components/App.tsx' import type { DOMElement } from '../types.ts' import type { RootType } from './types.ts' diff --git a/packages/react/src/components/App.tsx b/packages/react/src/components/App.tsx index 75ae745b5..5a3ba77c1 100644 --- a/packages/react/src/components/App.tsx +++ b/packages/react/src/components/App.tsx @@ -1,6 +1,4 @@ -import { Component } from 'react' - -import { AppContext } from './AppContext.ts' +import { Component, createContext } from 'react' import type { Logger } from '@kubb/core/logger' import type { KubbNode } from '../types.ts' @@ -34,6 +32,14 @@ class ErrorBoundary extends Component<{ onError: Props['onError']; logger?: Logg } } +export type AppContextProps = Record> = { + meta: Meta +} + +const AppContext = createContext({ + meta: {}, +}) + export function App = Record>({ onError, logger, meta, children }: Props): KubbNode { return ( @@ -41,3 +47,5 @@ export function App = Record ) } + +App.Context = AppContext diff --git a/packages/react/src/components/AppContext.ts b/packages/react/src/components/AppContext.ts deleted file mode 100644 index 49d88861e..000000000 --- a/packages/react/src/components/AppContext.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createContext } from 'react' - -export type AppContextProps = Record> = { - meta: Meta -} - -export const AppContext = createContext({ - meta: {}, -}) diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index d4abc848f..8eadcb212 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -1,4 +1,4 @@ -export type { AppContextProps } from './AppContext.ts' +export type { AppContextProps } from './App.tsx' export type { EditorLanguage } from './Editor.tsx' export { Editor, TypeScript } from './Editor.tsx' export { File } from './File.tsx' diff --git a/packages/react/src/hooks/useApp.ts b/packages/react/src/hooks/useApp.ts index 26c727462..b01fe7856 100644 --- a/packages/react/src/hooks/useApp.ts +++ b/packages/react/src/hooks/useApp.ts @@ -1,9 +1,9 @@ import { useContext } from 'react' -import { AppContext } from '../components/AppContext.ts' +import { App } from '../components/App.tsx' -import type { AppContextProps } from '../components/AppContext.ts' +import type { AppContextProps } from '../components/App.tsx' export function useApp = Record>(): AppContextProps { - return useContext(AppContext) as AppContextProps + return useContext(App.Context) as AppContextProps } diff --git a/packages/react/src/hooks/useFile.ts b/packages/react/src/hooks/useFile.ts index f7f071256..50b07dead 100644 --- a/packages/react/src/hooks/useFile.ts +++ b/packages/react/src/hooks/useFile.ts @@ -1,38 +1,17 @@ -import { readSync } from '@kubb/core/fs' import { usePluginManager } from '@kubb/react' import type { KubbFile, Plugin } from '@kubb/core' type Props = { name: string + mode?: KubbFile.Mode extName: KubbFile.Extname pluginKey: Plugin['key'] options?: TOptions } -export function useFile({ name, extName, pluginKey, options }: Props): KubbFile.File<{ pluginKey: Plugin['key'] }> { +export function useFile({ name, mode, extName, pluginKey, options }: Props): KubbFile.File<{ pluginKey: Plugin['key'] }> { const pluginManager = usePluginManager() - let source = '' - const baseName = `${name}${extName}` as const - const path = pluginManager.resolvePath({ baseName, pluginKey, options }) - - if (!path) { - throw new Error(`Filepath should be defined for resolvedName "${name}" and pluginKey [${JSON.stringify(pluginKey)}]`) - } - - try { - source = readSync(path) - } catch (_e) { - // - } - - return { - path, - baseName, - meta: { - pluginKey, - }, - source, - } + return pluginManager.getFile({ name, mode, extName, pluginKey, options }) } diff --git a/packages/react/src/hooks/useMeta.ts b/packages/react/src/hooks/useMeta.ts index 1003f307f..3cf0f64d8 100644 --- a/packages/react/src/hooks/useMeta.ts +++ b/packages/react/src/hooks/useMeta.ts @@ -1,6 +1,6 @@ import { useApp } from './useApp.ts' -import type { AppContextProps } from '../components/AppContext.ts' +import type { AppContextProps } from '../components/App.tsx' export function useMeta = Record>(): AppContextProps['meta'] { const app = useApp() diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 9e95040b8..6f504f2d5 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,5 +1,3 @@ -import React from 'react' - export type { RootType } from './client/index.ts' export { createRoot } from './client/index.ts' export type { AppContextProps } from './components/index.ts' @@ -19,5 +17,4 @@ export { useResolvePath, } from './hooks/index.ts' export type * from './types.ts' - -export default React +export { createContext, useContext } from 'react' diff --git a/packages/react/src/server/createRootServer.ts b/packages/react/src/server/createRootServer.ts index 607fe619b..867c1095d 100644 --- a/packages/react/src/server/createRootServer.ts +++ b/packages/react/src/server/createRootServer.ts @@ -3,7 +3,7 @@ import { ReactTemplate } from '../shared/ReactTemplate.tsx' import { format } from './format.ts' import type { Logger } from '@kubb/core/logger' -import type { AppContextProps } from '../components/AppContext.ts' +import type { AppContextProps } from '../components/App.tsx' import type { DOMElement } from '../types.ts' import type { RootType } from './types.ts' diff --git a/packages/react/src/shared/ReactTemplate.tsx b/packages/react/src/shared/ReactTemplate.tsx index 1f6d4be23..63b69c750 100644 --- a/packages/react/src/shared/ReactTemplate.tsx +++ b/packages/react/src/shared/ReactTemplate.tsx @@ -10,7 +10,7 @@ import { renderer } from './renderer.ts' import type { KubbFile } from '@kubb/core' import type { Logger } from '@kubb/core/logger' import type { ReactNode } from 'react' -import type { AppContextProps } from '../components/AppContext.ts' +import type { AppContextProps } from '../components/App.tsx' import type { FiberRoot } from '../reconciler.ts' import type { DOMElement } from '../types.ts' @@ -106,6 +106,10 @@ export class ReactTemplate { this.#lastFiles = files } onError(error: Error): void { + if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') { + console.error(error) + } + if (!this.logger) { console.error(error) } diff --git a/packages/swagger-client/src/OperationGenerator.test.tsx b/packages/swagger-client/src/OperationGenerator.test.tsx index 4ed492398..60542b09a 100644 --- a/packages/swagger-client/src/OperationGenerator.test.tsx +++ b/packages/swagger-client/src/OperationGenerator.test.tsx @@ -45,7 +45,7 @@ describe('OperationGenerator', async () => { }, ) const operation = oas.operation('/pets/{pet_id}', 'get') - const files = await og.get(operation, og.getSchemas(operation), options) as KubbFile.File[] + const files = await og.get(operation, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() @@ -78,7 +78,7 @@ describe('OperationGenerator', async () => { }, ) const operation = oas.operation('/pets/{pet_id}', 'get') - const files = await og.get(operation, og.getSchemas(operation), options) as KubbFile.File[] + const files = await og.get(operation, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() @@ -93,6 +93,7 @@ describe('OperationGenerator', async () => { operations: Operations.templates, client: { default: CustomClientTemplate, + editor: Client.templates.editor, }, }, client: { @@ -113,7 +114,7 @@ describe('OperationGenerator', async () => { }, ) const operation = oas.operation('/pets/{pet_id}', 'get') - const files = await og.get(operation, og.getSchemas(operation), options) as KubbFile.File[] + const files = await og.get(operation, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() diff --git a/packages/swagger-client/src/OperationGenerator.tsx b/packages/swagger-client/src/OperationGenerator.tsx index 4f1fafc56..9a2f7915d 100644 --- a/packages/swagger-client/src/OperationGenerator.tsx +++ b/packages/swagger-client/src/OperationGenerator.tsx @@ -1,18 +1,19 @@ import { createRoot } from '@kubb/react' import { OperationGenerator as Generator } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { Client, Operations } from './components/index.ts' import type { AppContextProps } from '@kubb/react' -import type { OperationMethodResult, OperationSchemas, Paths } from '@kubb/swagger' +import type { OperationMethodResult, OperationsByMethod } from '@kubb/swagger' import type { Operation } from '@kubb/swagger/oas' import type { FileMeta, PluginOptions } from './types.ts' export class OperationGenerator extends Generator { - async all(paths: Paths): OperationMethodResult { + async all(operations: Operation[], operationsByMethod: OperationsByMethod): OperationMethodResult { const { pluginManager, oas, plugin } = this.context - const root = createRoot({ logger: pluginManager.logger }) + const root = createRoot>({ logger: pluginManager.logger }) const templates = { operations: Operations.templates, @@ -20,16 +21,17 @@ export class OperationGenerator extends Generator, { meta: { oas, pluginManager, plugin } }) + root.render( + this.getSchemas(...props)}> + {templates.operations && } + , + { meta: { pluginManager, plugin } }, + ) return root.files } - async #generate(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { + async #generate(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { const { oas, pluginManager, plugin } = this.context const root = createRoot>({ logger: pluginManager.logger }) @@ -44,26 +46,33 @@ export class OperationGenerator extends Generator, { meta: { oas, pluginManager, plugin: { ...plugin, options }, schemas, operation } }) + root.render( + this.getSchemas(...props)}> + + + + , + { meta: { pluginManager, plugin: { ...plugin, options } } }, + ) return root.files } - async get(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async get(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async post(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async post(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async put(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async put(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async patch(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async patch(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async delete(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async delete(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } } diff --git a/packages/swagger-client/src/components/Client.test.tsx b/packages/swagger-client/src/components/Client.test.tsx index 9aad9a69c..c51810e81 100644 --- a/packages/swagger-client/src/components/Client.test.tsx +++ b/packages/swagger-client/src/components/Client.test.tsx @@ -1,6 +1,7 @@ import { mockedPluginManager } from '@kubb/core/mocks' import { createRootServer } from '@kubb/react/server' import { OasManager } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { OperationGenerator } from '../OperationGenerator.tsx' import { Client } from './Client.tsx' @@ -43,12 +44,18 @@ describe('', async () => { test('showPetById', async () => { const operation = oas.operation('/pets/{pet_id}', 'get') - const schemas = og.getSchemas(operation) - const context: AppContextProps = { meta: { oas, pluginManager: mockedPluginManager, plugin, schemas, operation } } + const context: AppContextProps = { meta: { pluginManager: mockedPluginManager, plugin } } const Component = () => { - return + return ( + og.getSchemas(...props)}> + + + + + ) } + const root = createRootServer({ logger: mockedPluginManager.logger }) const output = await root.renderToString(, context) diff --git a/packages/swagger-client/src/components/Client.tsx b/packages/swagger-client/src/components/Client.tsx index 5015949d7..bf4997a1a 100644 --- a/packages/swagger-client/src/components/Client.tsx +++ b/packages/swagger-client/src/components/Client.tsx @@ -204,4 +204,4 @@ Client.File = function(props: FileProps): KubbNode { ) } -Client.templates = defaultTemplates as Templates +Client.templates = defaultTemplates diff --git a/packages/swagger-client/src/components/Operations.test.tsx b/packages/swagger-client/src/components/Operations.test.tsx index af80424e8..ce4bd6332 100644 --- a/packages/swagger-client/src/components/Operations.test.tsx +++ b/packages/swagger-client/src/components/Operations.test.tsx @@ -1,13 +1,14 @@ import { mockedPluginManager } from '@kubb/core/mocks' import { createRootServer } from '@kubb/react/server' import { OasManager } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { OperationGenerator } from '../OperationGenerator.tsx' import { Operations } from './Operations.tsx' import type { Plugin } from '@kubb/core' import type { AppContextProps } from '@kubb/react' -import type { GetOperationGeneratorOptions, Paths } from '@kubb/swagger' +import type { GetOperationGeneratorOptions, OperationsByMethod } from '@kubb/swagger' import type { PluginOptions } from '../types.ts' describe('', async () => { @@ -44,23 +45,28 @@ describe('', async () => { test('showPetById', async () => { const operation = oas.operation('/pets/{pet_id}', 'get') const schemas = og.getSchemas(operation) - const context: AppContextProps = { meta: { oas, pluginManager: mockedPluginManager, plugin, schemas, operation } } + + const context: AppContextProps = { meta: { pluginManager: mockedPluginManager, plugin } } const Component = () => { return ( - + og.getSchemas(...props)}> + + + + ) } + const root = createRootServer({ logger: mockedPluginManager.logger }) const output = await root.renderToString(, context) diff --git a/packages/swagger-client/src/components/Operations.tsx b/packages/swagger-client/src/components/Operations.tsx index d86a2525e..36b9e307c 100644 --- a/packages/swagger-client/src/components/Operations.tsx +++ b/packages/swagger-client/src/components/Operations.tsx @@ -4,7 +4,7 @@ import { useFile } from '@kubb/react' import { useOas } from '@kubb/swagger/hooks' import type { KubbNode } from '@kubb/react' -import type { Paths } from '@kubb/swagger' +import type { OperationsByMethod } from '@kubb/swagger' import type { HttpMethod, Oas } from '@kubb/swagger/oas' import type { ComponentProps, ComponentType } from 'react' import type { FileMeta, PluginOptions } from '../types.ts' @@ -28,13 +28,38 @@ function Template({ ) } -const defaultTemplates = { default: Template } as const +type EditorTemplateProps = { + children?: React.ReactNode +} + +function EditorTemplate({ children }: EditorTemplateProps) { + const { key: pluginKey } = usePlugin() + const file = useFile({ name: 'operations', extName: '.ts', pluginKey }) + + return ( + + + baseName={file.baseName} + path={file.path} + meta={file.meta} + > + + {children} + + + + ) +} -function getOperations(oas: Oas, paths: Paths): Record { +const defaultTemplates = { default: Template, editor: EditorTemplate } as const + +type Templates = Partial + +function getOperations(oas: Oas, operationsByMethod: OperationsByMethod): Record { const operations: Record = {} - Object.keys(paths).forEach((path) => { - const methods = paths[path] || [] + Object.keys(operationsByMethod).forEach((path) => { + const methods = operationsByMethod[path] || [] Object.keys(methods).forEach((method) => { const operation = oas.operation(path, method as HttpMethod) if (operation) { @@ -50,7 +75,7 @@ function getOperations(oas: Oas, paths: Paths): Record() - const file = useFile({ name, extName: '.ts', pluginKey }) +Operations.File = function(props: FileProps): KubbNode { + const templates = { ...defaultTemplates, ...props.templates } const Template = templates.default + const EditorTemplate = templates.editor return ( - - - baseName={file.baseName} - path={file.path} - meta={file.meta} - > - - - - - + + + ) } diff --git a/packages/swagger-client/src/plugin.ts b/packages/swagger-client/src/plugin.ts index e1707ef60..d54c9f55a 100644 --- a/packages/swagger-client/src/plugin.ts +++ b/packages/swagger-client/src/plugin.ts @@ -47,9 +47,9 @@ export const definePlugin = createPlugin((options) => { }, }, pre: [swaggerPluginName], - resolvePath(baseName, directory, options) { + resolvePath(baseName, pathMode, options) { const root = path.resolve(this.config.root, this.config.output.path) - const mode = FileManager.getMode(path.resolve(root, output.path)) + const mode = pathMode ?? FileManager.getMode(path.resolve(root, output.path)) if (mode === 'file') { /** diff --git a/packages/swagger-client/src/types.ts b/packages/swagger-client/src/types.ts index 0f0ebb57e..43c25f967 100644 --- a/packages/swagger-client/src/types.ts +++ b/packages/swagger-client/src/types.ts @@ -1,5 +1,5 @@ import type { KubbFile, Plugin, PluginFactoryOptions, ResolveNameParams } from '@kubb/core' -import type { AppMeta as SwaggerAppMeta, Exclude, Include, Override, ResolvePathOptions } from '@kubb/swagger' +import type { Exclude, Include, Override, ResolvePathOptions } from '@kubb/swagger' import type { Client, Operations } from './components/index.ts' type Templates = { @@ -116,9 +116,7 @@ export type FileMeta = { tag?: string } -type AppMeta = SwaggerAppMeta - -export type PluginOptions = PluginFactoryOptions<'swagger-client', Options, ResolvedOptions, never, ResolvePathOptions, AppMeta> +export type PluginOptions = PluginFactoryOptions<'swagger-client', Options, ResolvedOptions, never, ResolvePathOptions> declare module '@kubb/core' { export interface _Register { diff --git a/packages/swagger-faker/src/OperationGenerator.test.tsx b/packages/swagger-faker/src/OperationGenerator.test.tsx index 766ed3c53..abcbfcdc7 100644 --- a/packages/swagger-faker/src/OperationGenerator.test.tsx +++ b/packages/swagger-faker/src/OperationGenerator.test.tsx @@ -39,8 +39,8 @@ describe('OperationGenerator', async () => { const operation = oas.operation('/pets', 'get') const operationShowById = oas.operation('/pets/{petId}', 'get') - const files = await og.get(operation, og.getSchemas(operation), options) as KubbFile.File[] - const getShowByIdFiles = await og.get(operationShowById, og.getSchemas(operationShowById), options) as KubbFile.File[] + const files = await og.get(operation, options) as KubbFile.File[] + const getShowByIdFiles = await og.get(operationShowById, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() @@ -75,8 +75,8 @@ describe('OperationGenerator', async () => { const operation = oas.operation('/pets', 'get') const operationShowById = oas.operation('/pets/{petId}', 'get') - const files = await og.get(operation, og.getSchemas(operation), options) as KubbFile.File[] - const getShowByIdFiles = await og.get(operationShowById, og.getSchemas(operationShowById), options) as KubbFile.File[] + const files = await og.get(operation, options) as KubbFile.File[] + const getShowByIdFiles = await og.get(operationShowById, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() @@ -109,7 +109,7 @@ describe('OperationGenerator', async () => { }, ) const operation = oas.operation('/pets', 'post') - const files = await og.post(operation, og.getSchemas(operation), options) as KubbFile.File[] + const files = await og.post(operation, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() @@ -138,7 +138,7 @@ describe('OperationGenerator', async () => { }, ) const operation = oas.operation('/pet/{petId}', 'delete') - const files = await og.delete(operation, og.getSchemas(operation), options) as KubbFile.File[] + const files = await og.delete(operation, options) as KubbFile.File[] files.forEach(file => { expect(FileManager.getSource(file)).toMatchSnapshot() diff --git a/packages/swagger-faker/src/OperationGenerator.tsx b/packages/swagger-faker/src/OperationGenerator.tsx index 8b48cb475..f44219c2a 100644 --- a/packages/swagger-faker/src/OperationGenerator.tsx +++ b/packages/swagger-faker/src/OperationGenerator.tsx @@ -1,5 +1,6 @@ import { createRoot } from '@kubb/react' import { OperationGenerator as Generator } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { Mutation } from './components/Mutation.tsx' import { Query } from './components/Query.tsx' @@ -15,37 +16,45 @@ export class OperationGenerator extends Generator { + async get(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { const { oas, pluginManager, plugin } = this.context const root = createRoot>({ logger: pluginManager.logger }) root.render( - , - { meta: { oas, pluginManager, plugin: { ...plugin, options }, schemas, operation } }, + this.getSchemas(...props)}> + + + + , + { meta: { pluginManager, plugin: { ...plugin, options } } }, ) return root.files } - async post(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { + async post(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { const { oas, pluginManager, plugin } = this.context const root = createRoot>({ logger: pluginManager.logger }) root.render( - , - { meta: { oas, pluginManager, plugin: { ...plugin, options }, schemas, operation } }, + this.getSchemas(...props)}> + + + + , + { meta: { pluginManager, plugin: { ...plugin, options } } }, ) return root.files } - async put(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.post(operation, schemas, options) + async put(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.post(operation, options) } - async patch(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.post(operation, schemas, options) + async patch(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.post(operation, options) } - async delete(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.post(operation, schemas, options) + async delete(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.post(operation, options) } } diff --git a/packages/swagger-faker/src/components/Mutation.test.tsx b/packages/swagger-faker/src/components/Mutation.test.tsx index ca901eb43..c8e0c6bfa 100644 --- a/packages/swagger-faker/src/components/Mutation.test.tsx +++ b/packages/swagger-faker/src/components/Mutation.test.tsx @@ -2,6 +2,7 @@ import { mockedPluginManager } from '@kubb/core/mocks' import { camelCase, pascalCase } from '@kubb/core/transformers' import { createRootServer } from '@kubb/react/server' import { OasManager } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { OperationGenerator } from '../OperationGenerator.tsx' import { Mutation } from './Mutation.tsx' @@ -52,11 +53,16 @@ describe('', async () => { test('pets', async () => { const operation = oas.operation('/pets', 'post') - const schemas = og.getSchemas(operation) - const context: AppContextProps = { meta: { oas, pluginManager: mockedPluginManager, plugin, schemas, operation } } + const context: AppContextProps = { meta: { pluginManager: mockedPluginManager, plugin } } const Component = () => { - return + return ( + og.getSchemas(...props)}> + + + + + ) } const root = createRootServer({ logger: mockedPluginManager.logger }) const output = await root.renderToString(, context) diff --git a/packages/swagger-faker/src/components/Mutation.tsx b/packages/swagger-faker/src/components/Mutation.tsx index 16e68c573..a83580921 100644 --- a/packages/swagger-faker/src/components/Mutation.tsx +++ b/packages/swagger-faker/src/components/Mutation.tsx @@ -30,7 +30,7 @@ Mutation.File = function({}: FileProps): ReactNode { const schemas = useSchemas() const pluginManager = usePluginManager() - const oas = useOas() + const { oas } = useOas() const file = useOperationFile() const builder = new FakerBuilder(options, { oas, pluginManager }) diff --git a/packages/swagger-faker/src/components/Query.test.tsx b/packages/swagger-faker/src/components/Query.test.tsx index ee428b225..88b3848d5 100644 --- a/packages/swagger-faker/src/components/Query.test.tsx +++ b/packages/swagger-faker/src/components/Query.test.tsx @@ -2,6 +2,7 @@ import { mockedPluginManager } from '@kubb/core/mocks' import { camelCase, pascalCase } from '@kubb/core/transformers' import { createRootServer } from '@kubb/react/server' import { OasManager } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' import { OperationGenerator } from '../OperationGenerator.tsx' import { Query } from './Query.tsx' @@ -52,11 +53,16 @@ describe('', async () => { test('showPetById', async () => { const operation = oas.operation('/pets/{petId}', 'get') - const schemas = og.getSchemas(operation) - const context: AppContextProps = { meta: { oas, pluginManager: mockedPluginManager, plugin, schemas, operation } } + const context: AppContextProps = { meta: { pluginManager: mockedPluginManager, plugin } } const Component = () => { - return + return ( + og.getSchemas(...props)}> + + + + + ) } const root = createRootServer({ logger: mockedPluginManager.logger }) const output = await root.renderToString(, context) diff --git a/packages/swagger-faker/src/components/Query.tsx b/packages/swagger-faker/src/components/Query.tsx index d5d56eb54..f55f64cc6 100644 --- a/packages/swagger-faker/src/components/Query.tsx +++ b/packages/swagger-faker/src/components/Query.tsx @@ -30,7 +30,7 @@ Query.File = function({}: FileProps): ReactNode { const schemas = useSchemas() const pluginManager = usePluginManager() - const oas = useOas() + const { oas } = useOas() const file = useOperationFile() const builder = new FakerBuilder(options, { oas, pluginManager }) diff --git a/packages/swagger-faker/src/plugin.ts b/packages/swagger-faker/src/plugin.ts index 24e3b9803..5edd4076e 100644 --- a/packages/swagger-faker/src/plugin.ts +++ b/packages/swagger-faker/src/plugin.ts @@ -43,9 +43,9 @@ export const definePlugin = createPlugin((options) => { unknownType, }, pre: [swaggerPluginName, swaggerTypeScriptPluginName], - resolvePath(baseName, directory, options) { + resolvePath(baseName, pathMode, options) { const root = path.resolve(this.config.root, this.config.output.path) - const mode = FileManager.getMode(path.resolve(root, output.path)) + const mode = pathMode ?? FileManager.getMode(path.resolve(root, output.path)) if (mode === 'file') { /** diff --git a/packages/swagger-faker/src/types.ts b/packages/swagger-faker/src/types.ts index c9d7d48da..373dfdfe1 100644 --- a/packages/swagger-faker/src/types.ts +++ b/packages/swagger-faker/src/types.ts @@ -1,5 +1,5 @@ import type { KubbFile, Plugin, PluginFactoryOptions, ResolveNameParams } from '@kubb/core' -import type { AppMeta as SwaggerAppMeta, Exclude, Include, Override, ResolvePathOptions } from '@kubb/swagger' +import type { Exclude, Include, Override, ResolvePathOptions } from '@kubb/swagger' import type { OasTypes } from '@kubb/swagger/oas' import type { FakerMeta } from './fakerParser.ts' @@ -107,9 +107,7 @@ export type FileMeta = { tag?: string } -type AppMeta = SwaggerAppMeta - -export type PluginOptions = PluginFactoryOptions<'swagger-faker', Options, ResolvedOptions, never, ResolvePathOptions, AppMeta> +export type PluginOptions = PluginFactoryOptions<'swagger-faker', Options, ResolvedOptions, never, ResolvePathOptions> declare module '@kubb/core' { export interface _Register { diff --git a/packages/swagger-msw/src/OperationGenerator.tsx b/packages/swagger-msw/src/OperationGenerator.tsx index 81a762c4d..ce807645f 100644 --- a/packages/swagger-msw/src/OperationGenerator.tsx +++ b/packages/swagger-msw/src/OperationGenerator.tsx @@ -1,42 +1,44 @@ import { createRoot } from '@kubb/react' import { OperationGenerator as Generator } from '@kubb/swagger' +import { Oas } from '@kubb/swagger/components' -import { Handlers } from './components/Handlers.tsx' import { Mock } from './components/Mock.tsx' +import { Operations } from './components/Operations.tsx' import type { AppContextProps } from '@kubb/react' -import type { OperationMethodResult, OperationSchemas, Paths } from '@kubb/swagger' +import type { OperationMethodResult, OperationsByMethod } from '@kubb/swagger' import type { Operation } from '@kubb/swagger/oas' import type { FileMeta, PluginOptions } from './types.ts' export class OperationGenerator extends Generator { - async all(paths: Paths): OperationMethodResult { + async all(operations: Operation[], operationsByMethod: OperationsByMethod): OperationMethodResult { const { oas, pluginManager, plugin } = this.context - const root = createRoot({ logger: pluginManager.logger }) + const root = createRoot>({ logger: pluginManager.logger }) const templates = { - handlers: Handlers.templates, + operations: Operations.templates, mock: Mock.templates, ...this.options.templates, } - if (!templates?.handlers) { - return [] - } - - root.render(, { meta: { oas, pluginManager, plugin } }) + root.render( + this.getSchemas(...props)}> + {templates?.operations && } + , + { meta: { pluginManager, plugin } }, + ) return root.files } - async #generate(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { + async #generate(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { const { oas, pluginManager, plugin } = this.context const root = createRoot>({ logger: pluginManager.logger }) const templates = { - handlers: Handlers.templates, + handlers: Operations.templates, mock: Mock.templates, ...options.templates, } @@ -45,26 +47,33 @@ export class OperationGenerator extends Generator, { meta: { oas, pluginManager, plugin: { ...plugin, options }, schemas, operation } }) + root.render( + this.getSchemas(...props)}> + + + + , + { meta: { pluginManager, plugin: { ...plugin, options } } }, + ) return root.files } - async get(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async get(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async post(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async post(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async put(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async put(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async patch(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async patch(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } - async delete(operation: Operation, schemas: OperationSchemas, options: PluginOptions['resolvedOptions']): OperationMethodResult { - return this.#generate(operation, schemas, options) + async delete(operation: Operation, options: PluginOptions['resolvedOptions']): OperationMethodResult { + return this.#generate(operation, options) } } diff --git a/packages/swagger-msw/src/components/Handlers.tsx b/packages/swagger-msw/src/components/Handlers.tsx deleted file mode 100644 index c1e2abec0..000000000 --- a/packages/swagger-msw/src/components/Handlers.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { Editor, File, usePlugin, usePluginManager } from '@kubb/react' -import { useFile } from '@kubb/react' - -import type { KubbFile, ResolveNameParams, ResolvePathParams } from '@kubb/core' -import type { Paths } from '@kubb/swagger' -import type { Operation } from '@kubb/swagger/oas' -import type { ReactNode } from 'react' -import type { FileMeta, PluginOptions } from '../types.ts' - -type TemplateProps = { - /** - * Name of the function - */ - name: string - handlers: string[] -} - -function Template({ - name, - handlers, -}: TemplateProps): ReactNode { - return ( - <> - {`export const ${name} = ${JSON.stringify(handlers).replaceAll(`"`, '')} as const`} - - ) -} - -const defaultTemplates = { default: Template } as const - -function getHandlers( - paths: Paths, - { resolveName, pluginKey }: { resolveName: (params: ResolveNameParams) => string; pluginKey: ResolveNameParams['pluginKey'] }, -): Array<{ name: string; operation: Operation }> { - const handlers: Array<{ name: string; operation: Operation }> = [] - - Object.keys(paths).forEach((path) => { - const operations = paths[path] - const filteredOperations = [operations?.get, operations?.post, operations?.patch, operations?.put, operations?.delete].filter(Boolean) - - filteredOperations.forEach(({ operation }) => { - const operationId = operation.getOperationId() - const name = resolveName({ name: operationId, pluginKey, type: 'function' }) - - handlers.push({ name, operation }) - }) - }) - - return handlers -} - -function getHandlersImports( - paths: Paths, - { resolveName, resolvePath, pluginKey }: { - resolveName: (params: ResolveNameParams) => string - resolvePath: (params: ResolvePathParams) => KubbFile.OptionalPath - pluginKey: ResolveNameParams['pluginKey'] - }, -): Array<{ name: string; path: KubbFile.OptionalPath }> { - const handlers = getHandlers(paths, { resolveName, pluginKey }) - - return handlers.map(({ name, operation }) => { - const path = resolvePath({ - pluginKey, - baseName: `${name}.ts`, - options: { - tag: operation?.getTags()[0]?.name, - }, - }) - return { name, path } - }) -} - -type Props = { - paths: Paths - /** - * This will make it possible to override the default behaviour. - */ - Template?: React.ComponentType> -} - -export function Handlers({ - paths, - Template = defaultTemplates.default, -}: Props): ReactNode { - const { key: pluginKey } = usePlugin() - const pluginManager = usePluginManager() - - const handlers = getHandlers(paths, { resolveName: pluginManager.resolveName, pluginKey }) - - return ( -