@dlzn-ui/vite-config 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/index.ts +307 -0
- package/modules/aliasElementPlus.ts +20 -0
- package/modules/autoImportStores.ts +19 -0
- package/modules/devSaveFolderAlias.ts +144 -0
- package/modules/tDesignResolve.ts +105 -0
- package/modules/vitePlugins.ts +82 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dlzn-ui
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/index.ts
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import type * as http from 'node:http'
|
|
2
|
+
import type { ConfigEnv, ProxyOptions, UserConfig } from 'vite'
|
|
3
|
+
import { posix, resolve } from 'node:path'
|
|
4
|
+
import baseConfigFn from '@dlzn-ui/vite-config-base'
|
|
5
|
+
import AutoImport from 'unplugin-auto-import/vite'
|
|
6
|
+
import ElementPlus from 'unplugin-element-plus/vite'
|
|
7
|
+
import Components from 'unplugin-vue-components/vite'
|
|
8
|
+
import { loadEnv, mergeConfig } from 'vite'
|
|
9
|
+
import { parseLoadedEnv } from 'vite-plugin-env-parse'
|
|
10
|
+
import { createHtmlPlugin } from 'vite-plugin-html'
|
|
11
|
+
import VueRouter from 'vue-router/vite'
|
|
12
|
+
import aliasElementPlus from './modules/aliasElementPlus.ts'
|
|
13
|
+
import autoImportStoreList from './modules/autoImportStores.ts'
|
|
14
|
+
import tDesignAutoImportResolve from './modules/tDesignResolve.ts'
|
|
15
|
+
import vitePlugins from './modules/vitePlugins.ts'
|
|
16
|
+
|
|
17
|
+
type Arrayable<T> = Array<T> | T
|
|
18
|
+
interface Options {
|
|
19
|
+
autoImportResolvers?: Array<
|
|
20
|
+
UnwrapArrayable<UnwrapArrayable<NonNullable<Parameters<typeof AutoImport>[0]['resolvers']>>>
|
|
21
|
+
>
|
|
22
|
+
autoImports?: Array<NonNullable<UnwrapArrayable<Parameters<typeof AutoImport>[0]['imports']>>>
|
|
23
|
+
root: string // vite.config.ts 的地址
|
|
24
|
+
}
|
|
25
|
+
type UnwrapArrayable<T> = T extends Arrayable<infer U> ? U : never
|
|
26
|
+
|
|
27
|
+
export function needPort(target: string): boolean {
|
|
28
|
+
return target.startsWith('http://192.')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function bypass(
|
|
32
|
+
req: http.IncomingMessage,
|
|
33
|
+
res: http.ServerResponse | undefined,
|
|
34
|
+
options: ProxyOptions,
|
|
35
|
+
): void {
|
|
36
|
+
if (
|
|
37
|
+
req.url !== undefined &&
|
|
38
|
+
options.rewrite !== undefined &&
|
|
39
|
+
typeof options.target === 'string' &&
|
|
40
|
+
res !== undefined
|
|
41
|
+
) {
|
|
42
|
+
const reqUrl = req.url
|
|
43
|
+
const proxyUrl = new URL(options.rewrite(reqUrl), options.target).href
|
|
44
|
+
|
|
45
|
+
res.setHeader('X-Res-ProxyUrl', proxyUrl) // 查看真实的请求地址
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default (_env: ConfigEnv, options: Options): UserConfig => {
|
|
50
|
+
const { command, mode } = _env
|
|
51
|
+
const resolvePath = (p: string) => resolve(options.root, p)
|
|
52
|
+
const envDir = 'env'
|
|
53
|
+
const env = parseLoadedEnv(loadEnv(mode, envDir)) as {
|
|
54
|
+
VITE_API_PROXY_PORT_ARRAY: Array<[string, number]>
|
|
55
|
+
VITE_APP_ID: string
|
|
56
|
+
VITE_APP_NAME?: string
|
|
57
|
+
VITE_BASE_URL?: string
|
|
58
|
+
VITE_FORCE_PWD_CHANGE?: boolean
|
|
59
|
+
VITE_PROXY_TARGET: string | string[]
|
|
60
|
+
VITE_USE_LOGIN_CAPTCHA?: boolean
|
|
61
|
+
}
|
|
62
|
+
const {
|
|
63
|
+
VITE_API_PROXY_PORT_ARRAY,
|
|
64
|
+
VITE_APP_ID,
|
|
65
|
+
VITE_APP_NAME = '',
|
|
66
|
+
VITE_BASE_URL = '/',
|
|
67
|
+
VITE_FORCE_PWD_CHANGE = true,
|
|
68
|
+
VITE_PROXY_TARGET: viteProxyTarget,
|
|
69
|
+
VITE_USE_LOGIN_CAPTCHA = true,
|
|
70
|
+
} = env
|
|
71
|
+
const VITE_PROXY_TARGET = Array.isArray(viteProxyTarget) ? viteProxyTarget : [viteProxyTarget]
|
|
72
|
+
|
|
73
|
+
console.log({
|
|
74
|
+
command,
|
|
75
|
+
mode,
|
|
76
|
+
VITE_API_PROXY_PORT_ARRAY,
|
|
77
|
+
VITE_APP_ID,
|
|
78
|
+
VITE_APP_NAME,
|
|
79
|
+
VITE_BASE_URL,
|
|
80
|
+
VITE_FORCE_PWD_CHANGE,
|
|
81
|
+
VITE_PROXY_TARGET,
|
|
82
|
+
VITE_USE_LOGIN_CAPTCHA,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const isProduction = mode === 'production'
|
|
86
|
+
const getProxyTarget = (index: number, port: number) => {
|
|
87
|
+
const target = VITE_PROXY_TARGET.length > 1 ? VITE_PROXY_TARGET[index] : VITE_PROXY_TARGET[0]
|
|
88
|
+
|
|
89
|
+
return needPort(target) ? `${target}:${port}` : target
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return mergeConfig(
|
|
93
|
+
baseConfigFn({
|
|
94
|
+
tailwindAutoReferenceEntry: 'xxxxx', // todo
|
|
95
|
+
}),
|
|
96
|
+
{
|
|
97
|
+
build: {
|
|
98
|
+
rolldownOptions: {
|
|
99
|
+
output: {
|
|
100
|
+
minify: {
|
|
101
|
+
codegen: true, //
|
|
102
|
+
compress: {
|
|
103
|
+
dropConsole: true, // 默认 false
|
|
104
|
+
// dropDebugger: true, // 默认 true
|
|
105
|
+
},
|
|
106
|
+
mangle: true, // 变量名混淆
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
define: {
|
|
112
|
+
VITE_API_PREFIX_ARRAY: JSON.stringify(VITE_API_PROXY_PORT_ARRAY.map(([prefix]) => prefix)),
|
|
113
|
+
VITE_APP_ID: JSON.stringify(VITE_APP_ID),
|
|
114
|
+
VITE_APP_NAME: JSON.stringify(VITE_APP_NAME),
|
|
115
|
+
VITE_BASE_URL: JSON.stringify(VITE_BASE_URL),
|
|
116
|
+
VITE_FORCE_PWD_CHANGE: JSON.stringify(VITE_FORCE_PWD_CHANGE),
|
|
117
|
+
VITE_USE_LOGIN_CAPTCHA: JSON.stringify(VITE_USE_LOGIN_CAPTCHA),
|
|
118
|
+
},
|
|
119
|
+
// envDir, // 将 env 文件里面的变量注入到 import.meta.env 里面
|
|
120
|
+
optimizeDeps: {
|
|
121
|
+
include: [
|
|
122
|
+
'tdesign-vue-next/dist/tdesign.min.js',
|
|
123
|
+
'path-browserify',
|
|
124
|
+
'lodash-es',
|
|
125
|
+
'pinia-plugin-persistedstate',
|
|
126
|
+
'alova',
|
|
127
|
+
'alova/fetch',
|
|
128
|
+
'alova/vue',
|
|
129
|
+
'@iconify/vue',
|
|
130
|
+
'@formkit/auto-animate',
|
|
131
|
+
'@formkit/auto-animate/vue',
|
|
132
|
+
'dayjs',
|
|
133
|
+
'dayjs/plugin/customParseFormat.js',
|
|
134
|
+
'dayjs/plugin/isSameOrBefore.js',
|
|
135
|
+
'dayjs/plugin/timezone.js',
|
|
136
|
+
'dayjs/plugin/utc.js',
|
|
137
|
+
'wordcloud',
|
|
138
|
+
'echarts/charts',
|
|
139
|
+
'echarts/components',
|
|
140
|
+
'echarts/core',
|
|
141
|
+
'echarts/renderers',
|
|
142
|
+
'@tdesign-vue-next/chat',
|
|
143
|
+
'vue-echarts',
|
|
144
|
+
'dompurify',
|
|
145
|
+
'cron-parser',
|
|
146
|
+
'cronstrue',
|
|
147
|
+
'cronstrue/locales/zh_CN',
|
|
148
|
+
'quartzcron',
|
|
149
|
+
'@bprogress/core',
|
|
150
|
+
'element-plus',
|
|
151
|
+
'element-plus/es/components/config-provider/style/css',
|
|
152
|
+
'element-plus/es/locale/lang/zh-cn',
|
|
153
|
+
'element-plus/es/components/date-picker/style/css',
|
|
154
|
+
'element-plus/es/components/scrollbar/style/css',
|
|
155
|
+
'color',
|
|
156
|
+
'dlzn-ui',
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
plugins: [
|
|
160
|
+
// envParse({
|
|
161
|
+
// dtsPath: 'types/env.d.ts',
|
|
162
|
+
// }),
|
|
163
|
+
createHtmlPlugin({
|
|
164
|
+
entry: `/src/main.${isProduction ? 'prod' : 'dev'}.ts`, // 必须绝对路径,否则类似system/log/operlog 二级页面打不开
|
|
165
|
+
inject: {
|
|
166
|
+
data: {
|
|
167
|
+
appId: VITE_APP_ID,
|
|
168
|
+
mode: isProduction ? 'prod' : 'dev',
|
|
169
|
+
title: VITE_APP_NAME,
|
|
170
|
+
},
|
|
171
|
+
ejsOptions: {
|
|
172
|
+
views: ['node_modules/dlzn-ui/ejs'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
minify: true,
|
|
176
|
+
}),
|
|
177
|
+
VueRouter({
|
|
178
|
+
dts: 'types/typed-router.d.ts',
|
|
179
|
+
}),
|
|
180
|
+
ElementPlus({}),
|
|
181
|
+
AutoImport({
|
|
182
|
+
// 用于自动导入 函数/工具库 的 API
|
|
183
|
+
// function modifyDefaultExportsAlias(imports, options) {
|
|
184
|
+
// if (options.defaultExportByFilename) imports.forEach((i) => {
|
|
185
|
+
// if (i.name === "default") i.as = i.from.split("/").pop()?.split(".")?.shift() ?? i.as;
|
|
186
|
+
// });
|
|
187
|
+
// return imports;
|
|
188
|
+
// }
|
|
189
|
+
// defaultExportByFilename: false, // 默认为 false
|
|
190
|
+
dirs: ['src/plugins/autoImport', 'src/bus/autoImport', 'src/hooks'],
|
|
191
|
+
dirsScanOptions: {
|
|
192
|
+
types: false, // Enable auto import the types under the directories
|
|
193
|
+
},
|
|
194
|
+
dts: 'types/auto-imports.d.ts',
|
|
195
|
+
// dtsMode: 'overwrite', // overwrite the whole existing .d.ts file with the new type definitions.
|
|
196
|
+
imports: [
|
|
197
|
+
'vue',
|
|
198
|
+
'vue-router',
|
|
199
|
+
'pinia',
|
|
200
|
+
'@vueuse/core',
|
|
201
|
+
{
|
|
202
|
+
from: 'alova/client',
|
|
203
|
+
imports: ['useRequest', 'useWatcher', 'usePagination'],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
from: '@/plugins/others/directive.ts',
|
|
207
|
+
imports: ['checkPermissions'],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
from: 'dlzn-ui/utils',
|
|
211
|
+
imports: ['formatNumber', 'isFalsy', 'stringDisplay'],
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
'@/plugins/alova/index.ts': [['default', 'alovaInst']],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
from: '@/components/tDesignReset/TForm/index.ts',
|
|
218
|
+
imports: ['FormItem', 'FormInstance', 'FormExposed'],
|
|
219
|
+
type: true,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
from: '@/components/tDesignReset/TTable/index.ts',
|
|
223
|
+
imports: ['TableCol', 'CellRenderContext'],
|
|
224
|
+
type: true,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
from: '@/components/autoImport/TPageList/index.ts',
|
|
228
|
+
imports: ['PageListProps'],
|
|
229
|
+
type: true,
|
|
230
|
+
},
|
|
231
|
+
autoImportStoreList(options.root),
|
|
232
|
+
...(options.autoImports ?? []),
|
|
233
|
+
],
|
|
234
|
+
resolvers: [
|
|
235
|
+
...(options.autoImportResolvers ?? []),
|
|
236
|
+
...tDesignAutoImportResolve({
|
|
237
|
+
isProduction,
|
|
238
|
+
root: options.root,
|
|
239
|
+
}),
|
|
240
|
+
],
|
|
241
|
+
vueTemplate: true, // 允许插件扫描 Vue 文件的 <template> 部分,并自动导入在模板表达式中使用的 API
|
|
242
|
+
}),
|
|
243
|
+
Components({
|
|
244
|
+
deep: false,
|
|
245
|
+
// 用于自动导入 Vue 组件
|
|
246
|
+
dts: 'types/components.d.ts',
|
|
247
|
+
resolvers: [
|
|
248
|
+
...(options.autoImportResolvers ?? []),
|
|
249
|
+
...tDesignAutoImportResolve({
|
|
250
|
+
isProduction,
|
|
251
|
+
root: options.root,
|
|
252
|
+
}),
|
|
253
|
+
],
|
|
254
|
+
// syncMode: 'overwrite',
|
|
255
|
+
}),
|
|
256
|
+
...vitePlugins({ isProduction, root: options.root }),
|
|
257
|
+
],
|
|
258
|
+
resolve: {
|
|
259
|
+
alias: [
|
|
260
|
+
{
|
|
261
|
+
find: '@',
|
|
262
|
+
replacement: resolvePath('src'), // 使用绝对路径(官方推荐)
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
find: 'img', // 路径以 img 开头
|
|
266
|
+
replacement: resolvePath('src/assets/images'),
|
|
267
|
+
},
|
|
268
|
+
...(isProduction
|
|
269
|
+
? []
|
|
270
|
+
: [
|
|
271
|
+
{
|
|
272
|
+
// find 为正则时只替换匹配段,必须用 ^...$ 匹配完整 id
|
|
273
|
+
// 同时覆盖裸导入与已 resolve 的绝对路径
|
|
274
|
+
find: /^.*tdesign-vue-next[/\\]es[/\\].*\.css$/,
|
|
275
|
+
replacement: resolvePath('src/plugins/tdesign-vue-next-for-dev/empty.css'),
|
|
276
|
+
},
|
|
277
|
+
]),
|
|
278
|
+
...aliasElementPlus(options.root),
|
|
279
|
+
],
|
|
280
|
+
// https://cn.vitejs.dev/guide/performance.html#reduce-resolve-operations
|
|
281
|
+
// 不建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
|
|
282
|
+
extensions: ['.ts', '.mjs', '.js'], // js 和 mjs 打包chat包时需要
|
|
283
|
+
},
|
|
284
|
+
root: options.root,
|
|
285
|
+
server: {
|
|
286
|
+
// vite preview 也会走该代理
|
|
287
|
+
host: '0.0.0.0', // 可以用ip访问
|
|
288
|
+
open: false,
|
|
289
|
+
port: VITE_API_PROXY_PORT_ARRAY[0][1] + 1000,
|
|
290
|
+
proxy: Object.fromEntries(
|
|
291
|
+
VITE_API_PROXY_PORT_ARRAY.map(([prefix, port], index) => [
|
|
292
|
+
`${posix.join(VITE_BASE_URL, prefix)}/`,
|
|
293
|
+
{
|
|
294
|
+
bypass,
|
|
295
|
+
changeOrigin: true,
|
|
296
|
+
rewrite: (p) => {
|
|
297
|
+
return VITE_BASE_URL === '/' ? p : p.replace(new RegExp(VITE_BASE_URL), '')
|
|
298
|
+
},
|
|
299
|
+
target: getProxyTarget(index, port),
|
|
300
|
+
},
|
|
301
|
+
]),
|
|
302
|
+
),
|
|
303
|
+
strictPort: true,
|
|
304
|
+
},
|
|
305
|
+
} satisfies UserConfig,
|
|
306
|
+
)
|
|
307
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { basename, resolve } from 'node:path'
|
|
2
|
+
import { globSync } from 'tinyglobby'
|
|
3
|
+
|
|
4
|
+
export default (root: string) => {
|
|
5
|
+
const elementPlusCss = globSync('src/plugins/element-plus/theme-chalk/*.css', { cwd: root })
|
|
6
|
+
const arr: Array<{
|
|
7
|
+
find: string
|
|
8
|
+
replacement: string
|
|
9
|
+
}> = []
|
|
10
|
+
|
|
11
|
+
elementPlusCss.forEach((file) => {
|
|
12
|
+
const _basename = basename(file)
|
|
13
|
+
|
|
14
|
+
arr.push({
|
|
15
|
+
find: `element-plus/theme-chalk/${_basename}`,
|
|
16
|
+
replacement: resolve(root, `src/plugins/element-plus/theme-chalk/${_basename}`),
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
return arr
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { basename } from 'node:path'
|
|
2
|
+
import { upperFirst } from '@dlzn-ui/build-utils'
|
|
3
|
+
import { globSync } from 'tinyglobby'
|
|
4
|
+
|
|
5
|
+
export default (root: string) => {
|
|
6
|
+
const piniaStoreKeys: string[] = []
|
|
7
|
+
const files = globSync('src/store/modules/*.ts', { cwd: root })
|
|
8
|
+
|
|
9
|
+
files.forEach((p) => {
|
|
10
|
+
piniaStoreKeys.push(basename(p, '.ts'))
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const customerImport: Record<string, [string, string][]> = {}
|
|
14
|
+
|
|
15
|
+
piniaStoreKeys.forEach((key) => {
|
|
16
|
+
customerImport[`@/store/modules/${key}`] = [['default', `use${upperFirst(key)}Store`]]
|
|
17
|
+
})
|
|
18
|
+
return customerImport
|
|
19
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
2
|
+
import type { Connect, Plugin, ViteDevServer } from 'vite'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
|
|
6
|
+
const ENDPOINT = '/__dev__/save-folder-alias'
|
|
7
|
+
const OUTPUT_FILE_WATCH_IGNORE = '**/folder-alias.json'
|
|
8
|
+
const SKIP_COMPONENTS = new Set(['Layout', 'ParentView'])
|
|
9
|
+
|
|
10
|
+
type FolderAliasMap = Record<string, { description: string }>
|
|
11
|
+
interface ResRouterItem {
|
|
12
|
+
children?: ResRouterItem[]
|
|
13
|
+
component: string
|
|
14
|
+
meta: {
|
|
15
|
+
title: string
|
|
16
|
+
}
|
|
17
|
+
path: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 开发环境将 getRouters 转为 folder-alias.json 并写入项目根目录 */
|
|
21
|
+
export default function devSaveFolderAlias(root: string): Plugin {
|
|
22
|
+
const OUTPUT_FILE = resolve(root, 'folder-alias.json')
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
apply: 'serve',
|
|
26
|
+
// Vite 对插件 config() 的返回值会做深度合并(mergeConfig)
|
|
27
|
+
// 交给 chokidar ignored,避免写入触发 HMR / 整页重载
|
|
28
|
+
config() {
|
|
29
|
+
return {
|
|
30
|
+
server: {
|
|
31
|
+
watch: {
|
|
32
|
+
ignored: [OUTPUT_FILE_WATCH_IGNORE],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
configureServer(server) {
|
|
38
|
+
server.middlewares.use(ENDPOINT, (req, res, next) => {
|
|
39
|
+
void handleSaveFolderAlias(req, res, next, server, OUTPUT_FILE)
|
|
40
|
+
})
|
|
41
|
+
},
|
|
42
|
+
name: 'dev-save-folder-alias',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function handleSaveFolderAlias(
|
|
47
|
+
req: IncomingMessage,
|
|
48
|
+
res: ServerResponse,
|
|
49
|
+
next: Connect.NextFunction,
|
|
50
|
+
server: ViteDevServer,
|
|
51
|
+
outPutFilePath: string,
|
|
52
|
+
) {
|
|
53
|
+
try {
|
|
54
|
+
const body = await readBody(req)
|
|
55
|
+
const data = JSON.parse(body) as ResRouterItem[]
|
|
56
|
+
const content = `${JSON.stringify(toFolderAlias(data), null, 2)}\n`
|
|
57
|
+
const resSuccess = () => {
|
|
58
|
+
res.statusCode = 200
|
|
59
|
+
res.setHeader('Content-Type', 'application/json')
|
|
60
|
+
res.end(JSON.stringify({ file: outPutFilePath, ok: true }))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const prev = await fs.readFile(outPutFilePath, 'utf8')
|
|
65
|
+
|
|
66
|
+
if (prev === content) {
|
|
67
|
+
resSuccess()
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// 文件不存在时继续写入
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await fs.writeFile(outPutFilePath, content, 'utf8')
|
|
75
|
+
server.config.logger.info(`[dev-save-folder-alias] written → ${outPutFilePath}`)
|
|
76
|
+
resSuccess()
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
79
|
+
|
|
80
|
+
res.statusCode = 500
|
|
81
|
+
res.setHeader('Content-Type', 'application/json')
|
|
82
|
+
res.end(JSON.stringify({ message, ok: false }))
|
|
83
|
+
server.config.logger.error(`[dev-save-folder-alias] ${message}`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readBody(req: IncomingMessage): Promise<string> {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const chunks: string[] = []
|
|
90
|
+
|
|
91
|
+
req.setEncoding('utf8')
|
|
92
|
+
req.on('data', (chunk: string) => {
|
|
93
|
+
chunks.push(chunk)
|
|
94
|
+
})
|
|
95
|
+
req.on('end', () => {
|
|
96
|
+
resolve(chunks.join(''))
|
|
97
|
+
})
|
|
98
|
+
req.on('error', reject)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function toFolderAlias(routers: ResRouterItem[]): FolderAliasMap {
|
|
103
|
+
const result: FolderAliasMap = {}
|
|
104
|
+
const walk = (items: ResRouterItem[], level = 0) => {
|
|
105
|
+
for (const item of items) {
|
|
106
|
+
const title = item.meta.title
|
|
107
|
+
|
|
108
|
+
if (title !== '') {
|
|
109
|
+
const component = item.component
|
|
110
|
+
|
|
111
|
+
if (!SKIP_COMPONENTS.has(component)) {
|
|
112
|
+
result[`src/views/${component.split('___')[0]}.vue`] = { description: title }
|
|
113
|
+
|
|
114
|
+
const arr = component.split('/')
|
|
115
|
+
|
|
116
|
+
arr.forEach((_, index) => {
|
|
117
|
+
if (index === arr.length - 1) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const key = `src/views/${arr.slice(0, index + 1).join('/')}`
|
|
122
|
+
const description = [
|
|
123
|
+
...new Set([result[key]?.description, title].filter(Boolean)),
|
|
124
|
+
].join('、')
|
|
125
|
+
|
|
126
|
+
result[key] = {
|
|
127
|
+
description:
|
|
128
|
+
index === 0
|
|
129
|
+
? (routers.find((r) => r.path === arr[0])?.meta.title ?? description)
|
|
130
|
+
: description,
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (item.children !== undefined && item.children.length > 0) {
|
|
137
|
+
walk(item.children, level + 1)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
walk(routers)
|
|
143
|
+
return result
|
|
144
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type AutoImport from 'unplugin-auto-import/vite'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { TDesignResolver } from '@tdesign-vue-next/auto-import-resolver'
|
|
4
|
+
import { globSync } from 'tinyglobby'
|
|
5
|
+
|
|
6
|
+
export default (options: { isProduction: boolean; root: string }) => {
|
|
7
|
+
const { isProduction, root } = options
|
|
8
|
+
const tDesignResetComponentsSubFolderFiles = globSync('src/components/tDesignReset/*/Index.vue', {
|
|
9
|
+
cwd: root,
|
|
10
|
+
})
|
|
11
|
+
const tDesignResetComponentsSubFolderName = tDesignResetComponentsSubFolderFiles.map((file) => {
|
|
12
|
+
return path.basename(path.dirname(file))
|
|
13
|
+
})
|
|
14
|
+
const autoImportComponentsSubFolderFiles = globSync('src/components/autoImport/*/Index.vue', {
|
|
15
|
+
cwd: root,
|
|
16
|
+
})
|
|
17
|
+
const autoImportComponentsSubFolderName = autoImportComponentsSubFolderFiles.map((file) => {
|
|
18
|
+
return path.basename(path.dirname(file))
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
resolve: (name: string) => {
|
|
24
|
+
if (['TCard'].includes(name)) {
|
|
25
|
+
return {
|
|
26
|
+
from: 'dlzn-ui',
|
|
27
|
+
name,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
['Icon', 'TBaseTable', 'TDateRangePicker', 'TEnhancedTable', 'TPrimaryTable'].includes(
|
|
33
|
+
name,
|
|
34
|
+
)
|
|
35
|
+
) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (tDesignResetComponentsSubFolderName.includes(name)) {
|
|
40
|
+
return {
|
|
41
|
+
from: `@/components/tDesignReset/${name}/Index.vue`,
|
|
42
|
+
name: 'default',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (autoImportComponentsSubFolderName.includes(name)) {
|
|
47
|
+
return {
|
|
48
|
+
from: `@/components/autoImport/${name}/Index.vue`,
|
|
49
|
+
name: 'default',
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// const xxx = _${compo} 自动导入
|
|
54
|
+
if (
|
|
55
|
+
tDesignResetComponentsSubFolderName
|
|
56
|
+
.map((n) => {
|
|
57
|
+
const sliceName = n.slice(1)
|
|
58
|
+
|
|
59
|
+
return `_${sliceName === 'Table' ? 'EnhancedTable' : sliceName}`
|
|
60
|
+
})
|
|
61
|
+
.includes(name)
|
|
62
|
+
) {
|
|
63
|
+
return {
|
|
64
|
+
as: name,
|
|
65
|
+
from: isProduction ? 'tdesign-vue-next' : '@/plugins/tdesign-vue-next-for-dev',
|
|
66
|
+
name: name.slice(1),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = (
|
|
71
|
+
TDesignResolver({
|
|
72
|
+
library: 'vue-next',
|
|
73
|
+
resolveIcons: false, // 禁用 https://tdesign.tencent.com/icons TDesign 图标独立站点 的图标
|
|
74
|
+
}) as {
|
|
75
|
+
resolve: (name: string) =>
|
|
76
|
+
| undefined
|
|
77
|
+
| {
|
|
78
|
+
from: string
|
|
79
|
+
name: string
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
).resolve(name)
|
|
83
|
+
|
|
84
|
+
if (result !== undefined) {
|
|
85
|
+
return {
|
|
86
|
+
from: isProduction ? result.from : '@/plugins/tdesign-vue-next-for-dev',
|
|
87
|
+
name: result.name,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
type: 'component',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
resolve: (name: string) => {
|
|
95
|
+
if (name === 'Loading') {
|
|
96
|
+
return {
|
|
97
|
+
from: isProduction ? 'tdesign-vue-next' : '@/plugins/tdesign-vue-next-for-dev',
|
|
98
|
+
name: 'LoadingDirective',
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
type: 'directive',
|
|
103
|
+
},
|
|
104
|
+
] satisfies Parameters<typeof AutoImport>[0]['resolvers']
|
|
105
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Plugin } from 'vite'
|
|
2
|
+
import { webUpdateNotice } from '@plugin-web-update-notification/vite'
|
|
3
|
+
import { visualizer } from 'rollup-plugin-visualizer'
|
|
4
|
+
import { compression } from 'vite-plugin-compression2'
|
|
5
|
+
import viteImagemin from 'vite-plugin-imagemin'
|
|
6
|
+
// import vueDevTools from 'vite-plugin-vue-devtools'
|
|
7
|
+
import devSaveFolderAlias from './devSaveFolderAlias.ts'
|
|
8
|
+
|
|
9
|
+
export default (options: { isProduction: boolean; root: string }): Plugin[] => {
|
|
10
|
+
const { isProduction, root } = options
|
|
11
|
+
|
|
12
|
+
if (isProduction) {
|
|
13
|
+
return [
|
|
14
|
+
(viteImagemin as unknown as typeof viteImagemin.default)({
|
|
15
|
+
gifsicle: {
|
|
16
|
+
// https://github.com/imagemin/imagemin-gifsicle
|
|
17
|
+
colors: 256, // 指定 GIF 图像调色板(颜色表)的最大颜色数量, 可丢弃一些颜色达到更大的优化效果
|
|
18
|
+
interlaced: false, // 渐进式加载效果
|
|
19
|
+
optimizationLevel: 2, // 中的优化级别
|
|
20
|
+
},
|
|
21
|
+
mozjpeg: {
|
|
22
|
+
quality: 50, // 图片质量(0-100)
|
|
23
|
+
},
|
|
24
|
+
optipng: {
|
|
25
|
+
optimizationLevel: 3, // 压缩过程中尝试的优化策略和强度(0-7)
|
|
26
|
+
},
|
|
27
|
+
pngquant: {
|
|
28
|
+
quality: [0.65, 0.8], // 压缩后的图片质量范围[最小, 最大]
|
|
29
|
+
speed: 4,
|
|
30
|
+
},
|
|
31
|
+
svgo: {
|
|
32
|
+
datauri: 'base64', // 'base64'|'enc'|'unenc' 指定如何将 SVG 转换为 Data URI 格式
|
|
33
|
+
js2svg: {
|
|
34
|
+
// 控制 SVG 输出的格式
|
|
35
|
+
indent: 4, // 缩进空格数
|
|
36
|
+
pretty: false, // 是否美化输出格式
|
|
37
|
+
},
|
|
38
|
+
multipass: false, // 是否启用多遍优化: false 表示只进行单遍优化,true 会重复优化直到无法进一步优化, 设置为 true 的话可能优化的更大
|
|
39
|
+
plugins: [
|
|
40
|
+
{
|
|
41
|
+
name: 'preset-default',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
webp: {
|
|
46
|
+
quality: 80, // 图片质量(0-100)
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
compression(),
|
|
50
|
+
visualizer({
|
|
51
|
+
filename: 'node_modules/.cache/visualizer/report.html',
|
|
52
|
+
open: true,
|
|
53
|
+
}),
|
|
54
|
+
webUpdateNotice({
|
|
55
|
+
hiddenDefaultNotification: true,
|
|
56
|
+
logVersion: false,
|
|
57
|
+
}),
|
|
58
|
+
] as Plugin[]
|
|
59
|
+
} else {
|
|
60
|
+
return [
|
|
61
|
+
// vueDevTools()
|
|
62
|
+
{
|
|
63
|
+
configureServer(server) {
|
|
64
|
+
const { printUrls } = server
|
|
65
|
+
|
|
66
|
+
server.printUrls = () => {
|
|
67
|
+
if (server.resolvedUrls !== null) {
|
|
68
|
+
server.resolvedUrls.local = []
|
|
69
|
+
server.resolvedUrls.network = server.resolvedUrls.network.filter((url) =>
|
|
70
|
+
url.includes('192.168.'),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
printUrls()
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
name: 'filter-vite-server-urls',
|
|
78
|
+
},
|
|
79
|
+
devSaveFolderAlias(root),
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dlzn-ui/vite-config",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./index.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"vite": "^8.2.0"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@plugin-web-update-notification/vite": "^2.1.3",
|
|
19
|
+
"@tdesign-vue-next/auto-import-resolver": "^0.1.7",
|
|
20
|
+
"rollup-plugin-visualizer": "^7.1.1",
|
|
21
|
+
"tinyglobby": "^0.2.17",
|
|
22
|
+
"unplugin-auto-import": "^21.1.0",
|
|
23
|
+
"unplugin-element-plus": "^0.11.2",
|
|
24
|
+
"unplugin-vue-components": "^32.1.0",
|
|
25
|
+
"vite-plugin-compression2": "^2.5.3",
|
|
26
|
+
"vite-plugin-env-parse": "^1.0.15",
|
|
27
|
+
"vite-plugin-html": "^3.2.2",
|
|
28
|
+
"vite-plugin-imagemin": "^0.6.1",
|
|
29
|
+
"vue-router": "^5.2.0",
|
|
30
|
+
"@dlzn-ui/build-utils": "0.0.1",
|
|
31
|
+
"@dlzn-ui/vite-config-base": "0.0.1"
|
|
32
|
+
}
|
|
33
|
+
}
|