mirror of
https://github.com/chansee97/nova-admin.git
synced 2026-01-02 18:47:52 +08:00
chore(build): 集成新版 vite-plugin-auto-proxy
This commit is contained in:
parent
753ded47eb
commit
60031951b2
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
100
build/plugins.ts
100
build/plugins.ts
@ -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
|
|
||||||
}
|
|
||||||
@ -83,6 +83,7 @@
|
|||||||
"unplugin-vue-components": "^28.8.0",
|
"unplugin-vue-components": "^28.8.0",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.0.6",
|
||||||
"vite-bundle-visualizer": "^1.2.1",
|
"vite-bundle-visualizer": "^1.2.1",
|
||||||
|
"vite-plugin-auto-proxy": "^1.0.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-vue-devtools": "8.0.0",
|
"vite-plugin-vue-devtools": "8.0.0",
|
||||||
"vue-tsc": "^3.0.5"
|
"vue-tsc": "^3.0.5"
|
||||||
|
|||||||
@ -1,15 +1,106 @@
|
|||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
import { defineConfig, loadEnv } from 'vite'
|
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/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
// 根据当前工作目录中的 `mode` 加载 .env 文件
|
// 根据当前工作目录中的 `mode` 加载 .env 文件
|
||||||
const env = loadEnv(mode, __dirname, '') as ImportMetaEnv
|
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 {
|
return {
|
||||||
base: env.VITE_BASE_URL,
|
base: env.VITE_BASE_URL,
|
||||||
plugins: createVitePlugins(env),
|
plugins,
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': resolve(__dirname, 'src'),
|
'@': resolve(__dirname, 'src'),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user