Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/blue-maps-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"alova-vscode-extension": patch
"@alova/wormhole": patch
---

fix apifox plugin issue
2 changes: 1 addition & 1 deletion packages/wormhole/src/helper/config/ConfigHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class ConfigHelper {
const allAlovaJSon = this.configManager
.getConfig()
.generator
.map(async item => TemplateHelper.readData(this.projectPath, item.output))
.map(async item => TemplateHelper.readData(this.projectPath, item.output!))
return Promise.all(allAlovaJSon)
}
}
Expand Down
14 changes: 11 additions & 3 deletions packages/wormhole/src/helper/config/ConfigManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Config, GeneratorConfig } from './type'
import { isArray, isObject, mergeWith, omit } from 'lodash'
import { fromError } from 'zod-validation-error'
import prepareConfig from '@/functions/prepareConfig'
import { logger } from '@/helper'
import { generatorHelper } from '@/helper/config/GeneratorHelper'
import { zConfig } from './zType'
Expand Down Expand Up @@ -32,10 +33,10 @@ export class ConfigManager {
* 加载并验证配置
*/
public async load(config: Partial<Config>): Promise<void> {
// 合并配置
const mergedConfig = this.mergeConfig(this.defaultConfig, config)
// 处理配置
const userConfig = await this.handleConfig(config)
// 验证配置
const validatedConfig = this.validateConfig(mergedConfig)
const validatedConfig = this.validateConfig(userConfig)
// 更新配置
this.config = validatedConfig
this.readConfig = Object.freeze(this.config)
Expand All @@ -56,6 +57,13 @@ export class ConfigManager {
await this.load({ ...this.config, ...partialConfig })
}

private async handleConfig(config: Partial<Config>) {
// 合并配置
const userConfig = this.mergeConfig(this.defaultConfig, config)
// 处理插件的config配置
userConfig.generator = await Promise.all(userConfig.generator.map(item => prepareConfig(item)))
return this.mergeConfig(this.defaultConfig, userConfig)
}
/**
* 验证配置
*/
Expand Down
22 changes: 7 additions & 15 deletions packages/wormhole/src/helper/config/GeneratorHelper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { OutputFileOptions } from '@/helper'
import type { AlovaVersion, GeneratorConfig, TemplateType } from '@/type'
import path from 'node:path'
import { isEqual, pick } from 'lodash'
import { isEqual } from 'lodash'
import { fromError } from 'zod-validation-error'
import { openApiParser, TemplateParser } from '@/core/parser'
import getAlovaVersion from '@/functions/getAlovaVersion'
import getAutoTemplateType from '@/functions/getAutoTemplateType'
import prepareConfig from '@/functions/prepareConfig'
import { logger, PluginDriver, TemplateHelper } from '@/helper'
import { existsPromise, generateFile, toCase as transformFileName } from '@/utils'
import { zGeneratorConfig } from './zType'
Expand Down Expand Up @@ -118,7 +117,7 @@ export class GeneratorHelper {
}

static openApiData(config: GeneratorConfig, projectPath: string) {
return openApiParser.parse(config.input, {
return openApiParser.parse(config.input!, {
projectPath,
platformType: config.platform,
fetchOptions: config.fetchOptions,
Expand All @@ -132,20 +131,13 @@ export class GeneratorHelper {
force?: boolean
},
) {
// plugin: handle extends
config = await prepareConfig(config)

const pluginDriver = new PluginDriver(config.plugins)

// plugin: handle before parse openapi
const configBeforeParse = await pluginDriver.hookSeq(
await pluginDriver.hookParallel(
'beforeOpenapiParse',
[pick(config, ['input', 'plugins', 'platform'])],
(result, args) => {
return result ? [result] : args
},
[Object.freeze(config)],
)
config = { ...config, ...configBeforeParse }

let document = await this.openApiData(config, options.projectPath)
if (!document) {
Expand All @@ -157,7 +149,7 @@ export class GeneratorHelper {
return result ? [result] : args
}) ?? document

const output = path.resolve(options.projectPath, config.output)
const output = path.resolve(options.projectPath, config.output!)
const version = GeneratorHelper.getAlovaVersion(config, options.projectPath)
const templateHelper = TemplateHelper.load({
type: this.getTemplateType(config, options.projectPath),
Expand All @@ -167,7 +159,7 @@ export class GeneratorHelper {
projectPath: options.projectPath,
generatorConfig: config,
})
const oldTemplateData = TemplateHelper.getData(options.projectPath, config.output)
const oldTemplateData = TemplateHelper.getData(options.projectPath, config.output!)
// Transform output filename by config.fileNameCase without changing template filename
const toCase = (name: string) => transformFileName(name, config.fileNameCase)
// Inject computed filenames into template render data for templates to reference
Expand Down Expand Up @@ -197,7 +189,7 @@ export class GeneratorHelper {
], { output })
}

await TemplateHelper.setData(templateData, options.projectPath, config.output)
await TemplateHelper.setData(templateData, options.projectPath, config.output!)

const generateFiles: OutputFileOptions[] = [
{
Expand Down
11 changes: 5 additions & 6 deletions packages/wormhole/src/helper/config/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ export interface ApiPlugin {
*/
config?: (config: GeneratorConfig) => MaybePromise<GeneratorConfig | undefined | null | void>
/**
* Manipulate the input config before parsing the openapi file.
* Returning null does NOT replacing anything.
* Called before parsing the OpenAPI file.
*/
beforeOpenapiParse?: (
inputConfig: Pick<GeneratorConfig, 'input' | 'platform' | 'plugins' | 'fetchOptions'>
) => MaybePromise<Pick<GeneratorConfig, 'input' | 'platform' | 'plugins' | 'fetchOptions'> | undefined | null | void>
config: GeneratorConfig
) => void
/**
* Manipulate the openapi document after parsing.
* Returning null does NOT replacing anything.
Expand Down Expand Up @@ -66,7 +65,7 @@ export interface GeneratorConfig {
* input: 'http://192.168.5.123:8080' -> When it does not point to the openapi file, it must be used with the `platform` parameter
*/

input: string
input?: string
// Fetch options used by remote OpenAPI retrieval (headers, timeout, insecure). See FetchOptions in '@/utils/base'.
fetchOptions?: FetchOptions
/**
Expand All @@ -92,7 +91,7 @@ export interface GeneratorConfig {
* The output path of the interface file and type file, multiple generators cannot have repeated addresses, otherwise the generated codes will cover each other, which is meaningless.
* @requires true
*/
output: string
output?: string

/**
* Specify the media type of the generated response data. After specifying, use this data type to generate the response ts format of the 2xx status code.
Expand Down
27 changes: 12 additions & 15 deletions packages/wormhole/src/helper/config/zType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,23 @@ function zPluginReturn<T extends z.ZodTypeAny>(schema: T) {
return zMaybePromise(z.union([schema, z.undefined(), z.null(), z.void()]))
}

// 定义 beforeOpenapiParse 的输入配置类型
const zInputConfig = z.lazy(() => z.object({
input: z.string(),
platform: zPlatformType.optional(),
plugins: z.array(zApiPlugin).optional(),
fetchOptions: zFetchOptions.optional(),
})) as z.ZodSchema<Pick<GeneratorConfig, 'input' | 'platform' | 'plugins' | 'fetchOptions'>>

// 定义 OpenAPIDocument 类型(简化版本,因为完整的 OpenAPI 规范非常复杂)
const zOpenAPIDocument = z.any() as z.ZodSchema<OpenAPIDocument>

export const zApiPlugin = z.object({
name: z.string().optional(),
config: z.lazy(
() => z.function().args(_zGeneratorConfig).returns(zPluginReturn(_zGeneratorConfig)).optional(),
() => z.function()
.args(_zGeneratorConfig)
.returns(zPluginReturn(_zGeneratorConfig))
.optional(),
),
beforeOpenapiParse: z.lazy(
() => z.function()
.args(_zGeneratorConfig)
.returns(z.void())
.optional(),
),
beforeOpenapiParse: z.function()
.args(zInputConfig)
.returns(zPluginReturn(zInputConfig))
.optional(),
afterOpenapiParse: z.function()
.args(zOpenAPIDocument)
.returns(zPluginReturn(zOpenAPIDocument))
Expand Down Expand Up @@ -234,15 +231,15 @@ export const zConfig = z.object({
const globalKeySet = new Set<string>()
const outputSet = new Set<string>()
data.forEach((item) => {
if (outputSet.has(path.join(item.output))) {
if (outputSet.has(path.join(item.output ?? ''))) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['generator', 'output'],
message: `output \`${item.output}\` is repated`,
})
return
}
outputSet.add(path.join(item.output))
outputSet.add(path.join(item.output ?? ''))
if (!item.global) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
Expand Down
11 changes: 5 additions & 6 deletions packages/wormhole/src/plugins/presets/apifox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,12 @@ export function apifox({
}
return {
name: 'apifox',
async beforeOpenapiParse(inputConfig) {
const next = { ...inputConfig }
config(config) {
const base = 'https://api.apifox.com/v1/projects'
if (projectId && apifoxToken) {
next.input = `${base}/${encodeURIComponent(projectId)}/export-openapi?locale=${encodeURIComponent(locale)}`
next.fetchOptions = {
...next.fetchOptions,
config.input = `${base}/${encodeURIComponent(projectId)}/export-openapi?locale=${encodeURIComponent(locale)}`
config.fetchOptions = {
...config.fetchOptions,
headers: {
'X-Apifox-Api-Version': apifoxVersion,
'Authorization': `Bearer ${apifoxToken}`,
Expand All @@ -80,7 +79,7 @@ export function apifox({
data: body,
}
}
return next
return config
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/wormhole/src/plugins/presets/tagModifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function processApiTags(apiDescriptor: ApiDescriptor, handler: ModifierHa
const modifiedTag = handler(tag)

// If handler returns null/undefined/void, remove this tag
if (modifiedTag == null) {
if (!modifiedTag) {
return null
}

Expand Down
2 changes: 1 addition & 1 deletion packages/wormhole/src/readConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function getApiDocs(config: Config, projectPath = process.cwd()) {
}
await configHelper.load(config, projectPath)
return configHelper.getOutput().map((output) => {
const templateData = TemplateHelper.getData(projectPath, output)
const templateData = TemplateHelper.getData(projectPath, output!)
return templateData?.pathApis ?? []
})
}
Loading