@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 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
+ }