Skip to content

Commit a0740fa

Browse files
committed
feat: refactor Playground component to support dynamic file generation and update API response handling for improved file management
1 parent 28629ad commit a0740fa

6 files changed

Lines changed: 43 additions & 46 deletions

File tree

0 Bytes
Binary file not shown.

docs/.docs/components/Playground.vue

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<div
3333
class="px-4 py-3 border-b border-[var(--ui-border)] bg-[var(--ui-bg-muted)] flex items-center font-medium text-sm flex-shrink-0">
3434
<Tabs
35+
v-if="tabs.length"
3536
v-model="activeTab"
3637
:tabs="tabs"
3738
/>
@@ -100,8 +101,10 @@ const swaggerJson = ref(`{
100101
101102
const selectedPreset = ref('axios')
102103
const selectedMode = ref('ts')
103-
const activeTab = ref<'main' | 'type'>('main')
104-
const generatedCode = ref<{ main?: string; type?: string }>({})
104+
/** 当前选中的文件名,如 index.ts */
105+
const activeTab = ref('index.ts')
106+
/** 接口返回的文件列表,main 为 index.ts,type 为 index.type.ts 等 */
107+
const generatedFiles = ref<{ filename: string; code: string }[]>([])
105108
const highlighter = ref<Highlighter | null>(null)
106109
107110
const isTanstackPreset = computed(() => selectedPreset.value === 'tanstackQuery')
@@ -136,23 +139,16 @@ const modeOptions = computed(() => {
136139
return options
137140
})
138141
139-
const tabs = computed(() => {
140-
const items = [
141-
{ value: 'main', label: 'index.ts' },
142-
]
143-
if (generatedCode.value.type) {
144-
items.push({ value: 'type', label: 'index.type.ts' })
145-
}
146-
return items
147-
})
142+
const tabs = computed(() =>
143+
generatedFiles.value.map(file => ({
144+
value: file.filename,
145+
label: file.filename,
146+
})),
147+
)
148148
149-
const activeCode = computed(() => {
150-
if (activeTab.value === 'main') {
151-
return generatedCode.value.main || ''
152-
} else {
153-
return generatedCode.value.type || ''
154-
}
155-
})
149+
const activeCode = computed(() =>
150+
generatedFiles.value.find(f => f.filename === activeTab.value)?.code ?? '',
151+
)
156152
157153
const jsonHighlighted = computed(() => {
158154
if (!highlighter.value || !swaggerJson.value.trim()) {
@@ -168,7 +164,8 @@ const codeHighlighted = computed(() => {
168164
if (!highlighter.value || !activeCode.value)
169165
return ''
170166
const isTsLike = ['ts', 'react', 'vue'].includes(selectedMode.value)
171-
const lang = activeTab.value === 'type' ? 'typescript' : (isTsLike ? 'typescript' : 'javascript')
167+
const isTypeFile = activeTab.value.endsWith('.type.ts')
168+
const lang = isTypeFile ? 'typescript' : (isTsLike ? 'typescript' : 'javascript')
172169
return highlighter.value.codeToHtml(activeCode.value, {
173170
lang,
174171
theme: theme.value,
@@ -203,7 +200,7 @@ async function generateCode() {
203200
204201
JSON.parse(swaggerJson.value) // validate JSON
205202
206-
const response = await $fetch<{ main?: string; type?: string; error?: string }>('/api/generate', {
203+
const response = await $fetch<{ files?: { filename: string; code: string }[]; error?: string }>('/api/generate', {
207204
method: 'POST',
208205
body: {
209206
swagger: swaggerJson.value,
@@ -212,25 +209,18 @@ async function generateCode() {
212209
},
213210
})
214211
215-
if (response.error) {
212+
if (response.error || !response.files?.length) {
216213
return
217214
}
218215
219-
generatedCode.value = {
220-
main: response.main || '',
221-
type: response.type || '',
222-
}
223-
224-
// If type file exists, show main file by default
225-
if (generatedCode.value.main) {
226-
activeTab.value = 'main'
227-
}
216+
generatedFiles.value = response.files
217+
activeTab.value = response.files[0].filename
228218
}
229219
230220
function handleJsonChange() {
231221
// Auto-generate code
232222
if (!swaggerJson.value.trim()) {
233-
generatedCode.value = {}
223+
generatedFiles.value = []
234224
return
235225
}
236226
debouncedGenerateCode()

docs/.docs/server/api/generate.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ApiPipeline } from '@genapi/core'
2+
import path from 'node:path'
23
import { defineConfig } from '@genapi/core'
34
import presets from '@genapi/presets'
45
import pPipe from 'p-pipe'
@@ -34,22 +35,28 @@ export default defineEventHandler(async (event) => {
3435
configRead => generate(configRead, { printWidth: 80 }),
3536
)
3637

38+
// TanStack Query 需要三文件:main(hooks)、api(apis)、type(types),且 main 引用 api、api 引用 type
39+
const isTanstackQuery = body.preset === 'tanstackQuery'
40+
const output: { main: string, type?: string | false, api?: string } = isTanstackQuery
41+
? { main: 'index.ts', type: 'index.type.ts', api: 'index.api.ts' }
42+
: {
43+
main: 'index.ts',
44+
type: body.mode === 'ts' ? 'index.type.ts' : false as const,
45+
}
46+
3747
const apiConfig = defineConfig({
3848
preset: presetPipeline,
3949
input: { json: body.swagger },
40-
output: {
41-
main: 'index.ts',
42-
type: body.mode === 'ts' ? 'index.type.ts' : false,
43-
},
50+
output,
4451
})
4552

4653
const configRead = await runPipeline(apiConfig) as ApiPipeline.ConfigRead
4754

48-
const mainOutput = configRead.outputs.find(o => o.type === 'request')
49-
const typeOutput = configRead.outputs.find(o => o.type === 'typings')
55+
// pipeline 的 output.type 为 'main' | 'type' | 其他 key,不是 'request'/'typings'
56+
const files = configRead.outputs.map(output => ({
57+
filename: path.basename(output.path),
58+
code: output.code ?? '',
59+
}))
5060

51-
return {
52-
main: mainOutput?.code || '',
53-
type: typeOutput?.code || '',
54-
}
61+
return { files }
5562
})

packages/presets/src/_shared/query/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function ensureQueryOutput(userConfig: ApiPipeline.Config) {
1313
out.api = out.api ?? (typeof out.main === 'string' ? out.main.replace(/index\.(ts|js)$/, 'index.api.$1') : 'src/api/index.api.ts')
1414
}
1515

16-
/** main import api file (api.xxx reference); api import type file (Types reference) */
16+
/** main import api file (apis.xxx reference); api import type file (Types reference) */
1717
export function addApiImportToMain(configRead: ApiPipeline.ConfigRead) {
1818
const mainOut = configRead.outputs.find(o => o.type === 'main')
1919
const apiOut = configRead.outputs.find(o => o.type === 'api')
@@ -22,7 +22,7 @@ export function addApiImportToMain(configRead: ApiPipeline.ConfigRead) {
2222
if (mainOut && apiOut) {
2323
const rel = path.relative(path.dirname(mainOut.path), apiOut.path).replace(/\.(ts|js)$/, '')
2424
const value = (rel.startsWith('.') ? rel : `./${rel}`).replace(/\\/g, '/')
25-
imports.add('main', { value, namespace: true, name: 'api' })
25+
imports.add('main', { value, namespace: true, name: 'apis' })
2626
}
2727
if (apiOut && typeOut) {
2828
const rel = path.relative(path.dirname(apiOut.path), typeOut.path).replace(/\.(ts|js)$/, '')

packages/presets/src/_shared/query/parser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function createQueryParser() {
4949
url = transformUrlSyntax(url, { baseURL: configRead.config.meta?.baseURL })
5050
const fetchBody = transformFetchBody(url, optList, spaceResponseType)
5151

52-
const fetcherRef = `api.${name}`
52+
const fetcherRef = `apis.${name}`
5353
functions.add('api', {
5454
export: true,
5555
async: true,
@@ -67,7 +67,7 @@ export function createQueryParser() {
6767
const paramNames = fetcherParams.map(p => p.name).join(', ')
6868

6969
if (isRead) {
70-
const keyItems = `'${name}', ${paramNames}`
70+
const keyItems = `${fetcherRef}.name, ${paramNames}`
7171
functions.add('main', {
7272
export: true,
7373
name: hook,

packages/presets/test/tanstack/react/config.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('tanstack/react config', () => {
3636
expect(main?.path).toMatch(/[\\/]src[\\/]api[\\/]index\.ts$/)
3737
expect(api?.path).toMatch(/[\\/]src[\\/]api[\\/]index\.api\.ts$/)
3838
expect(type?.path).toMatch(/[\\/]src[\\/]api[\\/]index\.type\.ts$/)
39-
expect(configRead.graphs.scopes.main.imports.some(i => i.name === 'Api')).toBe(true)
39+
expect(configRead.graphs.scopes.main.imports.some(i => i.name === 'apis')).toBe(true)
4040
expect(configRead.graphs.scopes.api?.imports.some(i => i.name === 'Types')).toBe(true)
4141
})
4242

0 commit comments

Comments
 (0)