chore(build): 集成新版 vite-plugin-auto-proxy

This commit is contained in:
chansee97 2025-12-19 22:45:19 +08:00
parent 753ded47eb
commit 60031951b2
4 changed files with 94 additions and 328 deletions

View File

@ -1,226 +0,0 @@
import type { ProxyOptions, UserConfig } from 'vite'
import { mkdirSync, writeFileSync } from 'node:fs'
import { dirname } from 'node:path'
/** 服务配置接口 */
interface ServiceConfig {
[key: string]: string
}
/** 服务环境类型 */
type ServiceEnvType = string
/** 完整的服务配置类型 */
interface FullServiceConfig {
[key: ServiceEnvType]: ServiceConfig
}
/** 代理项接口 */
interface ProxyItem {
/** 代理路径 */
path: string
/** 原始地址 */
rawPath: string
}
/** 代理地址映射接口 */
interface ProxyMapping {
[serviceName: string]: ProxyItem
}
/** 插件选项接口 */
export interface ServiceProxyPluginOptions {
/** 服务配置对象(必填) */
serviceConfig: FullServiceConfig
/** 代理路径前缀(可选,默认为 'proxy-' */
proxyPrefix?: string
/** 是否启用代理配置 */
enableProxy?: boolean
/** 环境变量名(可选,默认为 '__URL_MAP__' */
envName?: string
/** d.ts 类型文件生成路径(可选,如果传入路径则在该路径生成 d.ts 类型文件) */
dts?: string
}
export default function createServiceProxyPlugin(options: ServiceProxyPluginOptions) {
const {
serviceConfig,
proxyPrefix = 'proxy-',
enableProxy = true,
envName = '__URL_MAP__',
dts,
} = options
return {
name: 'vite-auto-proxy',
config(config: UserConfig, { mode, command }: { mode: string, command: 'build' | 'serve' }) {
// 只在开发环境serve命令时生成代理配置
const isDev = command === 'serve'
// 在非开发环境也注入空的代理映射,避免运行时错误
if (!config.define) {
config.define = {}
}
if (!enableProxy || !isDev) {
const rawMapping: ProxyMapping = {}
const envConfig = serviceConfig[mode]
if (envConfig) {
Object.entries(envConfig).forEach(([serviceName, serviceUrl]) => {
rawMapping[serviceName] = {
path: serviceUrl,
rawPath: serviceUrl,
}
})
console.warn(`[auto-proxy] 已加载 ${Object.keys(envConfig).length} 个服务地址`)
}
else {
console.warn(`[auto-proxy] 未找到环境 "${mode}" 的配置`)
}
config.define[envName] = JSON.stringify(rawMapping)
// 生成 d.ts 类型文件(如果指定了路径)
if (dts) {
generateDtsFile(rawMapping, dts, envName)
}
return
}
console.warn(`[auto-proxy] 已加载${mode}模式 ${Object.keys(serviceConfig[mode]).length} 个服务地址`)
const { proxyConfig, proxyMapping } = generateProxyFromServiceConfig(serviceConfig, mode, proxyPrefix)
Object.entries(proxyMapping).forEach(([serviceName, proxyItem]) => {
console.warn(`[auto-proxy] 服务: ${serviceName} | 代理地址: ${proxyItem.path} | 实际地址: ${proxyItem.rawPath}`)
})
if (proxyConfig && Object.keys(proxyConfig).length > 0) {
// 确保 server 对象存在
if (!config.server) {
config.server = {}
}
// 合并代理配置
config.server.proxy = {
...config.server.proxy,
...proxyConfig,
}
config.define[envName] = JSON.stringify(proxyMapping)
console.warn(`[auto-proxy] 代理映射已注入到 ${envName}`)
// 生成 d.ts 类型文件(如果指定了路径)
if (dts) {
generateDtsFile(proxyMapping, dts, envName)
}
}
else {
console.warn(`[auto-proxy] 未生成任何代理配置`)
config.define[envName] = JSON.stringify({})
// 生成空的 d.ts 类型文件(如果指定了路径)
if (dts) {
generateDtsFile({}, dts, envName)
}
}
},
}
}
function generateProxyFromServiceConfig(
serviceConfig: FullServiceConfig,
mode: ServiceEnvType,
proxyPrefix: string,
): { proxyConfig: Record<string, ProxyOptions>, proxyMapping: ProxyMapping } {
try {
// 获取当前环境的配置
const envConfig = serviceConfig[mode]
if (!envConfig) {
console.warn(`[auto-proxy] 未找到环境 "${mode}" 的配置,使用 development 配置`)
const defaultConfig = serviceConfig.development
if (!defaultConfig) {
console.error(`[auto-proxy] 也未找到 development 配置`)
return { proxyConfig: {}, proxyMapping: {} }
}
return generateProxyFromConfig(defaultConfig, proxyPrefix)
}
return generateProxyFromConfig(envConfig, proxyPrefix)
}
catch (error) {
console.error(`[auto-proxy] 生成代理配置失败:`, (error as Error).message)
return { proxyConfig: {}, proxyMapping: {} }
}
}
function generateProxyFromConfig(
envConfig: ServiceConfig,
proxyPrefix: string,
): { proxyConfig: Record<string, ProxyOptions>, proxyMapping: ProxyMapping } {
const proxyConfig: Record<string, ProxyOptions> = {}
const proxyMapping: ProxyMapping = {}
Object.entries(envConfig).forEach(([serviceName, serviceUrl]) => {
if (typeof serviceUrl === 'string' && serviceUrl.trim()) {
const proxyPath = `/${proxyPrefix}${serviceName}`
const isWs = serviceUrl.startsWith('ws://') || serviceUrl.startsWith('wss://')
// 生成代理配置
proxyConfig[proxyPath] = {
target: serviceUrl,
changeOrigin: true,
ws: isWs,
rewrite: (path: string): string => path.replace(new RegExp(`^/${proxyPrefix}${serviceName}`), ''),
}
// 生成代理映射
proxyMapping[serviceName] = {
path: proxyPath,
rawPath: serviceUrl,
}
}
})
return { proxyConfig, proxyMapping }
}
function generateDtsFile(
mapping: ProxyMapping,
outputPath: string,
envName: string,
) {
try {
const serviceNames = Object.keys(mapping).map(name => `'${name}'`).join(' | ')
const serviceNameType = serviceNames || 'never'
const dtsContent = `/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by auto-proxy
// biome-ignore lint: disable
export {}
type serviceName = ${serviceNameType}
declare global {
const ${envName}: {
[K in serviceName]: {
path: string
rawPath: string
}
}
}
`
const dir = dirname(outputPath)
if (dir) {
mkdirSync(dir, { recursive: true })
}
writeFileSync(outputPath, dtsContent, 'utf-8')
}
catch (error) {
console.error(`[auto-proxy] 生成 d.ts 文件失败:`, (error as Error).message)
}
}

View File

@ -1,100 +0,0 @@
import UnoCSS from '@unocss/vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
// https://github.com/antfu/unplugin-icons
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import { ProNaiveUIResolver } from 'pro-naive-ui-resolver'
import Components from 'unplugin-vue-components/vite'
import viteCompression from 'vite-plugin-compression'
import VueDevTools from 'vite-plugin-vue-devtools'
import AutoProxy from './autoProxy'
import { serviceConfig } from '../service.config'
/**
* @description: vite插件配置
* @param {*} env -
* @return {*}
*/
export function createVitePlugins(env: ImportMetaEnv) {
const plugins = [
// support vue
vue(),
vueJsx(),
VueDevTools(),
// support unocss
UnoCSS(),
// auto import api of lib
AutoImport({
imports: [
'vue',
'vue-router',
'pinia',
'@vueuse/core',
'vue-i18n',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar',
'useModal',
],
},
],
include: [
/\.[tj]sx?$/,
/\.vue$/,
/\.vue\?vue/,
/\.md$/,
],
dts: 'src/typings/auto-imports.d.ts',
}),
// auto import components lib
Components({
dts: 'src/typings/components.d.ts',
resolvers: [
IconsResolver({
prefix: false,
customCollections: [
'svg-icons',
],
}),
NaiveUiResolver(),
ProNaiveUIResolver(),
],
}),
// auto import iconify's icons
Icons({
defaultStyle: 'display:inline-block',
compiler: 'vue3',
customCollections: {
'svg-icons': FileSystemIconLoader(
'src/assets/svg-icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" width="1.2em" height="1.2em"'),
),
},
}),
AutoProxy({
enableProxy: env.VITE_HTTP_PROXY === 'Y',
serviceConfig,
dts: 'src/typings/auto-proxy.d.ts',
}),
]
// use compression
if (env.VITE_BUILD_COMPRESS === 'Y') {
const { VITE_COMPRESS_TYPE = 'gzip' } = env
plugins.push(viteCompression({
algorithm: VITE_COMPRESS_TYPE, // 压缩算法
}))
}
return plugins
}

View File

@ -83,6 +83,7 @@
"unplugin-vue-components": "^28.8.0",
"vite": "^7.0.6",
"vite-bundle-visualizer": "^1.2.1",
"vite-plugin-auto-proxy": "^1.0.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "8.0.0",
"vue-tsc": "^3.0.5"

View File

@ -1,15 +1,106 @@
import { resolve } from 'node:path'
import { defineConfig, loadEnv } from 'vite'
import { createVitePlugins } from './build/plugins'
import UnoCSS from '@unocss/vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import { ProNaiveUIResolver } from 'pro-naive-ui-resolver'
import Components from 'unplugin-vue-components/vite'
import viteCompression from 'vite-plugin-compression'
import VueDevTools from 'vite-plugin-vue-devtools'
import AutoProxy from 'vite-plugin-auto-proxy'
import { serviceConfig } from './service.config'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
// 根据当前工作目录中的 `mode` 加载 .env 文件
const env = loadEnv(mode, __dirname, '') as ImportMetaEnv
const plugins = [
// support vue
vue(),
vueJsx(),
VueDevTools(),
// support unocss
UnoCSS(),
// auto import api of lib
AutoImport({
imports: [
'vue',
'vue-router',
'pinia',
'@vueuse/core',
'vue-i18n',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar',
'useModal',
],
},
],
include: [
/\.[tj]sx?$/,
/\.vue$/,
/\.vue\?vue/,
/\.md$/,
],
dts: 'src/typings/auto-imports.d.ts',
}),
// auto import components lib
Components({
dts: 'src/typings/components.d.ts',
resolvers: [
IconsResolver({
prefix: false,
customCollections: [
'svg-icons',
],
}),
NaiveUiResolver(),
ProNaiveUIResolver(),
],
}),
// auto import iconify's icons
Icons({
defaultStyle: 'display:inline-block',
compiler: 'vue3',
customCollections: {
'svg-icons': FileSystemIconLoader(
'src/assets/svg-icons',
svg => svg.replace(/^<svg /, '<svg fill="currentColor" width="1.2em" height="1.2em"'),
),
},
}),
AutoProxy({
enableProxy: env.VITE_HTTP_PROXY === 'Y',
serviceConfig,
dts: 'src/typings/auto-proxy.d.ts',
}),
]
// use compression
if (env.VITE_BUILD_COMPRESS === 'Y') {
const { VITE_COMPRESS_TYPE = 'gzip' } = env
plugins.push(viteCompression({
algorithm: VITE_COMPRESS_TYPE, // 压缩算法
}))
}
return {
base: env.VITE_BASE_URL,
plugins: createVitePlugins(env),
plugins,
resolve: {
alias: {
'@': resolve(__dirname, 'src'),