From 98ccaf7fab895b228e0605ab1d94215e3cc07ec5 Mon Sep 17 00:00:00 2001 From: Kirk Lin Date: Fri, 13 Oct 2023 12:22:15 +0800 Subject: [PATCH] feat: refactor vite config --- package.json | 2 + pnpm-lock.yaml | 141 +++++++++++++++++ src/pages/login/index.vue | 7 +- tsconfig.json | 2 +- vite-config/configs/application.ts | 97 ++++++++++++ vite-config/constants.ts | 8 + vite-config/index.ts | 23 +++ vite-config/plugins/generateConfig.ts | 18 +++ vite-config/plugins/index.ts | 81 ++++++++++ vite-config/plugins/pwa.ts | 26 ++++ vite-config/plugins/unocss.ts | 90 +++++++++++ vite-config/plugins/unpluginAutoImport.ts | 22 +++ vite-config/plugins/unpluginIcons.ts | 20 +++ vite-config/plugins/unpluginVueComponets.ts | 17 +++ vite-config/plugins/visualizer.ts | 15 ++ vite-config/plugins/vueI18nPlugin.ts | 12 ++ vite-config/utils/index.ts | 96 ++++++++++++ vite.config.ts | 159 +------------------- 18 files changed, 674 insertions(+), 162 deletions(-) create mode 100644 vite-config/configs/application.ts create mode 100644 vite-config/constants.ts create mode 100644 vite-config/index.ts create mode 100644 vite-config/plugins/generateConfig.ts create mode 100644 vite-config/plugins/index.ts create mode 100644 vite-config/plugins/pwa.ts create mode 100644 vite-config/plugins/unocss.ts create mode 100644 vite-config/plugins/unpluginAutoImport.ts create mode 100644 vite-config/plugins/unpluginIcons.ts create mode 100644 vite-config/plugins/unpluginVueComponets.ts create mode 100644 vite-config/plugins/visualizer.ts create mode 100644 vite-config/plugins/vueI18nPlugin.ts create mode 100644 vite-config/utils/index.ts diff --git a/package.json b/package.json index f355f25..09a6753 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@types/node": "^20.8.4", "@types/nprogress": "^0.2.1", "@vitejs/plugin-vue": "^4.4.0", + "@vitejs/plugin-vue-jsx": "^3.0.2", "@vitest/coverage-c8": "^0.33.0", "@vue/compiler-dom": "^3.3.4", "@vue/test-utils": "^2.4.1", @@ -68,6 +69,7 @@ "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "pnpm": "^8.9.0", + "rollup-plugin-visualizer": "^5.9.2", "sass": "^1.69.3", "typescript": "^5.2.2", "unocss": "^0.56.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 929f1e8..46d2afb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,6 +97,9 @@ devDependencies: '@vitejs/plugin-vue': specifier: ^4.4.0 version: 4.4.0(vite@4.4.11)(vue@3.3.4) + '@vitejs/plugin-vue-jsx': + specifier: ^3.0.2 + version: 3.0.2(vite@4.4.11)(vue@3.3.4) '@vitest/coverage-c8': specifier: ^0.33.0 version: 0.33.0(vitest@0.34.6) @@ -124,6 +127,9 @@ devDependencies: pnpm: specifier: ^8.9.0 version: 8.9.0 + rollup-plugin-visualizer: + specifier: ^5.9.2 + version: 5.9.2(rollup@2.79.1) sass: specifier: ^1.69.3 version: 1.69.3 @@ -646,6 +652,16 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.20): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.20): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -720,6 +736,16 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.20): + resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.22.20): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} @@ -1224,6 +1250,19 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true + /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.22.20): + resolution: {integrity: sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.20 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.20) + dev: true + /@babel/plugin-transform-unicode-escapes@7.22.5(@babel/core@7.22.20): resolution: {integrity: sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==} engines: {node: '>=6.9.0'} @@ -2836,6 +2875,22 @@ packages: vue: 3.3.4 dev: false + /@vitejs/plugin-vue-jsx@3.0.2(vite@4.4.11)(vue@3.3.4): + resolution: {integrity: sha512-obF26P2Z4Ogy3cPp07B4VaW6rpiu0ue4OT2Y15UxT5BZZ76haUY9guOsZV3uWh/I6xc+VeiW+ZVabRE82FyzWw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + vue: ^3.0.0 + dependencies: + '@babel/core': 7.22.20 + '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.22.20) + '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.22.20) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.3) + vue: 3.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /@vitejs/plugin-vue@4.4.0(vite@4.4.11)(vue@3.3.4): resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2916,6 +2971,29 @@ packages: '@volar/language-core': 1.10.4 dev: true + /@vue/babel-helper-vue-transform-on@1.1.5: + resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==} + dev: true + + /@vue/babel-plugin-jsx@1.1.5(@babel/core@7.22.20): + resolution: {integrity: sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.20) + '@babel/template': 7.22.15 + '@babel/traverse': 7.22.20 + '@babel/types': 7.22.19 + '@vue/babel-helper-vue-transform-on': 1.1.5 + camelcase: 6.3.0 + html-tags: 3.3.1 + svg-tags: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /@vue/compiler-core@3.3.4: resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} dependencies: @@ -3356,6 +3434,11 @@ packages: engines: {node: '>=6'} dev: true + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + /caniuse-lite@1.0.30001535: resolution: {integrity: sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==} dev: true @@ -3747,6 +3830,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + /define-properties@1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} engines: {node: '>= 0.4'} @@ -4790,6 +4878,11 @@ packages: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true + /html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + dev: true + /htmlparser2@8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} dependencies: @@ -4963,6 +5056,12 @@ packages: resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} dev: true + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5090,6 +5189,13 @@ packages: call-bind: 1.0.2 dev: true + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -5792,6 +5898,15 @@ packages: mimic-fn: 4.0.0 dev: true + /open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + /optionator@0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -6267,6 +6382,23 @@ packages: terser: 5.18.0 dev: true + /rollup-plugin-visualizer@5.9.2(rollup@2.79.1): + resolution: {integrity: sha512-waHktD5mlWrYFrhOLbti4YgQCn1uR24nYsNuXxg7LkPH8KdTXVWR9DNY1WU0QqokyMixVXJS4J04HNrVTMP01A==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x + peerDependenciesMeta: + rollup: + optional: true + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + rollup: 2.79.1 + source-map: 0.7.4 + yargs: 17.6.2 + dev: true + /rollup@2.79.1: resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} engines: {node: '>=10.0.0'} @@ -6420,6 +6552,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -6601,6 +6738,10 @@ packages: engines: {node: '>= 0.4'} dev: true + /svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + dev: true + /symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue index e75b261..2d67fb6 100644 --- a/src/pages/login/index.vue +++ b/src/pages/login/index.vue @@ -2,8 +2,6 @@ import { showSuccessToast, showToast } from "vant"; import { ref, shallowReactive, toRef } from "vue"; import { useRouter } from "vue-router"; -import LoginEyeOffSvg from "virtual:icons/login/eye-off"; -import LoginEyeOnSvg from "virtual:icons/login/eye-on"; import { codeRules, mobileRules, passwordRules } from "~/utils"; import NavBar from "~/layouts/Navbar/index.vue"; @@ -68,8 +66,9 @@ const { sendMobileCode, countdownValue, formRef } = useMobileCode( :type="loginFormData.showPassword ? 'text' : 'password'" > = loadEnv(mode, root); + const defineData = await createDefineData(root); + const viteEnv = updateEnvVariables(env); + const { + VITE_PORT, + VITE_PROXY, + VITE_USE_HTTPS, + VITE_PUBLIC_PATH, + VITE_DROP_CONSOLE, + } = viteEnv; + const plugins = configVitePlugins(root, viteEnv, isProductionBuild); + const pathResolve = (pathname: string) => resolve(root, ".", pathname); + + const applicationConfig: UserConfig = { + root, + base: VITE_PUBLIC_PATH, + + resolve: { + alias: { + "vue-i18n": "vue-i18n/dist/vue-i18n.esm-bundler.js", + "~/": `${pathResolve("src")}/`, + }, + }, + server: { + // Listening on all local IPs + host: VITE_USE_HTTPS, + port: VITE_PORT, + open: true, + https: false, + proxy: !VITE_USE_HTTPS ? configureProxy(VITE_PROXY) : {}, + }, + esbuild: { + pure: VITE_DROP_CONSOLE ? ["console.log", "debugger"] : [], + }, + define: defineData, + build: { + target: "es2015", + minify: "terser", + cssTarget: "chrome80", + rollupOptions: { + output: { + chunkFileNames: "assets/js/[name]-[hash].js", + entryFileNames: "assets/js/[name]-[hash].js", + assetFileNames: "assets/[ext]/[name]-[hash].[ext]", + manualChunks: { + vue: ["vue", "pinia", "vue-router"], + echarts: ["echarts", "vue-echarts"], + celerisComponents: ["@celeris/components", "@celeris/ca-components"], + }, + }, + }, + }, + css: { + preprocessorOptions: { + scss: { + additionalData: ` + @import "~/styles/variables.scss"; + `, + javascriptEnabled: true, + }, + }, + }, + // https://github.com/vitest-dev/vitest + test: { + environment: "jsdom", + }, + plugins, + }; + return applicationConfig; +} + +async function createDefineData(root: string) { + try { + const pkgJson = await readPackageJSON(root); + const { dependencies, devDependencies, name, version } = pkgJson; + + const __APP_INFO__ = { + pkg: { dependencies, devDependencies, name, version }, + lastBuildTime: formatToDateTime(new Date()), + }; + return { + __APP_INFO__: JSON.stringify(__APP_INFO__), + }; + } catch (error) { + return {}; + } +} diff --git a/vite-config/constants.ts b/vite-config/constants.ts new file mode 100644 index 0000000..b03467c --- /dev/null +++ b/vite-config/constants.ts @@ -0,0 +1,8 @@ +// This constant defines the name of the configuration file that will be used in the production environment +export const GLOB_CONFIG_FILE_NAME = "_app.config.js"; + +// This constant sets the output directory for the Vite package +export const OUTPUT_DIR = "dist"; + +// This constant sets the name of the application +export const APP_NAME = "boot-vant"; diff --git a/vite-config/index.ts b/vite-config/index.ts new file mode 100644 index 0000000..9008c53 --- /dev/null +++ b/vite-config/index.ts @@ -0,0 +1,23 @@ +import process from "node:process"; +import type { UserConfig } from "vite"; +import { defineConfig, mergeConfig } from "vite"; +import { createApplicationViteConfig } from "./configs/application"; + +interface ApplicationViteConfigOptions { + overrides?: UserConfig; + // options?: {}; +} + +export async function createViteConfig(applicationViteConfigOptions: ApplicationViteConfigOptions = {}) { + const { overrides = {} } = applicationViteConfigOptions; + const root = process.cwd(); + return defineConfig(async ({ command, mode }) => { + return mergeConfigs([overrides, await createApplicationViteConfig(command, mode, root)]); + }); +} + +export function mergeConfigs(configs: UserConfig[]): Record { + return configs.reduce((mergedConfig, config) => { + return mergeConfig(mergedConfig, config); + }, {}); +} diff --git a/vite-config/plugins/generateConfig.ts b/vite-config/plugins/generateConfig.ts new file mode 100644 index 0000000..a7bd002 --- /dev/null +++ b/vite-config/plugins/generateConfig.ts @@ -0,0 +1,18 @@ +import GenerateConfig from "unplugin-config/vite"; +import type { PluginOption } from "vite"; +import { APP_NAME, GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from "../constants"; + +export function createConfigPluginConfig(shouldGenerateConfig: boolean): PluginOption { + // https://github.com/kirklin/unplugin-config + return GenerateConfig({ + appName: APP_NAME, + envVariables: { + prefix: "VITE_GLOB_", + }, + configFile: { + generate: shouldGenerateConfig, + fileName: GLOB_CONFIG_FILE_NAME, + outputDir: OUTPUT_DIR, + }, + }); +} diff --git a/vite-config/plugins/index.ts b/vite-config/plugins/index.ts new file mode 100644 index 0000000..e705d6c --- /dev/null +++ b/vite-config/plugins/index.ts @@ -0,0 +1,81 @@ +import type { PluginOption } from "vite"; +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; + +import { createConfigPluginConfig } from "./generateConfig"; +import { createPWAPluginConfig } from "./pwa"; +import { createUnoCSSPluginConfig } from "./unocss"; +import { createAutoImportPluginConfig } from "./unpluginAutoImport"; +import { createVueComponentsPluginConfig } from "./unpluginVueComponets"; +import { createVisualizerPluginConfig } from "./visualizer"; +import { createVueI18nPluginConfig } from "./vueI18nPlugin"; +import { createIconsPluginConfig } from "./unpluginIcons"; +import type { ViteEnvVariables } from "#/config"; + +/** + * Configure the Vite plugins. + * + * @param rootDir The root directory of the project. + * @param viteEnv The Vite environment variables. + * @param isProductionBuild Whether the current command is for a production build. + */ +export function configVitePlugins( + rootDir: string, + viteEnv: Partial, + isProductionBuild: boolean, +): Array { + const vitePlugins: Array = []; + + // Add the Vue plugin. + // 添加 Vue 插件 + vitePlugins.push(vue({ + script: { + defineModel: true, + }, + })); + + // Add the Vue JSX plugin. + // 添加 Vue JSX 插件 + vitePlugins.push(vueJsx()); + + // Add the unplugin-auto-import plugin. + // 添加 unplugin-auto-import 插件 + // https://github.com/antfu/unplugin-auto-import + vitePlugins.push(createAutoImportPluginConfig()); + + // Add the unplugin-vue-components plugin. + // 添加 unplugin-vue-components 插件 + // https://github.com/antfu/unplugin-vue-components + vitePlugins.push(createVueComponentsPluginConfig()); + + // Add the UnoCSS plugin. + // 添加 UnoCSS 插件 + vitePlugins.push(createUnoCSSPluginConfig()); + + // Add the GenerateConfig plugin. + // 添加 生成配置 插件 + vitePlugins.push(createConfigPluginConfig(viteEnv.VITE_GLOB_BUILD_GENERATE_CONFIG ?? true)); + + // Add the Vue I18n plugin. + // 添加 Vue国际化 插件 + vitePlugins.push(createVueI18nPluginConfig()); + + // Add the Icons plugin. + // 添加 图标 插件 + vitePlugins.push(createIconsPluginConfig()); + + // Add the rollup-plugin-visualizer + // 添加 打包分析 插件 + // https://github.com/btd/rollup-plugin-visualizer + viteEnv.VITE_USE_BUILD_ANALYZER && vitePlugins.push(createVisualizerPluginConfig()); + + // The following plugins only work in the production environment + // 生产环境才会添加的插件 + if (isProductionBuild) { + // Add the vite-plugin-pwa + // 添加 PWA 插件 + vitePlugins.push(createPWAPluginConfig(viteEnv)); + } + + return vitePlugins; +} diff --git a/vite-config/plugins/pwa.ts b/vite-config/plugins/pwa.ts new file mode 100644 index 0000000..20dac71 --- /dev/null +++ b/vite-config/plugins/pwa.ts @@ -0,0 +1,26 @@ +/** + * Zero-config PWA for Vite + * https://github.com/antfu/vite-plugin-pwa + */ +import { VitePWA } from "vite-plugin-pwa"; +import type { ViteEnvVariables } from "#/config"; + +/** + * Create PWA plugin configuration + * 创建PWA插件配置 + * @param env Vite environment variables Vite环境变量 + * @returns Vite plugin configuration array Vite插件配置数组 + */ +export function createPWAPluginConfig(env: Partial) { + const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env; + + if (VITE_USE_PWA) { + return VitePWA({ + manifest: { + name: VITE_GLOB_APP_TITLE, + short_name: VITE_GLOB_APP_SHORT_NAME, + }, + }); + } + return []; +} diff --git a/vite-config/plugins/unocss.ts b/vite-config/plugins/unocss.ts new file mode 100644 index 0000000..3ef9403 --- /dev/null +++ b/vite-config/plugins/unocss.ts @@ -0,0 +1,90 @@ +import { presetAttributify, presetIcons, presetUno, transformerDirectives, transformerVariantGroup } from "unocss"; +import UnoCSS from "unocss/vite"; +import presetChinese from "unocss-preset-chinese"; +import presetEase from "unocss-preset-ease"; +import type { PluginOption } from "vite"; + +export function createUnoCSSPluginConfig(): PluginOption { + return UnoCSS({ + content: { + pipeline: { + exclude: ["node_modules", ".git", "dist"], + }, + }, + presets: [ + presetUno({ dark: "class" }), + presetAttributify(), + presetChinese(), + presetEase(), + presetIcons({ + scale: 1.2, + warn: true, + }), + ], + shortcuts: { + // position + "pr": "relative", + "pa": "absolute", + "pf": "fixed", + "ps": "sticky", + + // position layout + "position-x-center": "absolute left-1/2 -translate-x-1/2", + "pxc": "position-x-center", + "position-y-center": "absolute top-1/2 -translate-y-1/2", + "pyc": "position-y-center", + "position-center": "position-x-center position-y-center", + "pc": "position-center", + + // size + "size-0": "w-0 h-0", + "size-full": "w-full h-full", + "size-screen": "w-screen h-screen", + "size-1/2": "w-1/2 h-1/2", + + // flex layout + "flex-center": "flex justify-center items-center", + "flex-col-center": "flex-center flex-col", + "flex-x-center": "flex justify-center", + "flex-y-center": "flex items-center", + }, + theme: { + colors: { + primary: "rgb(var(--primary-color))", + primary_hover: "rgb(var(--primary-color-hover))", + primary_suppl: "rgb(var(--primary-color-suppl))", + primary_pressed: "rgb(var(--primary-color-pressed))", + primary_1: "rgb(var(--primary-color1))", + primary_2: "rgb(var(--primary-color2))", + primary_3: "rgb(var(--primary-color3))", + primary_4: "rgb(var(--primary-color4))", + primary_5: "rgb(var(--primary-color5))", + primary_6: "rgb(var(--primary-color6))", + primary_7: "rgb(var(--primary-color7))", + primary_8: "rgb(var(--primary-color8))", + primary_9: "rgb(var(--primary-color9))", + primary_10: "rgb(var(--primary-color10))", + info: "rgb(var(--info-color))", + info_hover: "rgb(var(--info-color-hover))", + info_suppl: "rgb(var(--info-color-suppl))", + info_pressed: "rgb(var(--info-color-pressed))", + success: "rgb(var(--success-color))", + success_hover: "rgb(var(--success-color-hover))", + success_suppl: "rgb(var(--success-color-suppl))", + success_pressed: "rgb(var(--success-color-pressed))", + warning: "rgb(var(--warning-color))", + warning_hover: "rgb(var(--warning-color-hover))", + warning_suppl: "rgb(var(--warning-color-suppl))", + warning_pressed: "rgb(var(--warning-color-pressed))", + error: "rgb(var(--error-color))", + error_hover: "rgb(var(--error-color-hover))", + error_suppl: "rgb(var(--error-color-suppl))", + error_pressed: "rgb(var(--error-color-pressed))", + }, + }, + transformers: [ + transformerDirectives(), + transformerVariantGroup(), + ], + }); +} diff --git a/vite-config/plugins/unpluginAutoImport.ts b/vite-config/plugins/unpluginAutoImport.ts new file mode 100644 index 0000000..a2a99aa --- /dev/null +++ b/vite-config/plugins/unpluginAutoImport.ts @@ -0,0 +1,22 @@ +import AutoImport from "unplugin-auto-import/vite"; +import type { PluginOption } from "vite"; + +export function createAutoImportPluginConfig(): PluginOption { + // https://github.com/antfu/unplugin-auto-import + return AutoImport({ + imports: [ + "vue", + "vue-router", + "vue-i18n", + "vue/macros", + "@vueuse/head", + "@vueuse/core", + ], + dts: "types/auto-imports.d.ts", + dirs: [ + "src/composables", + "src/store", + ], + vueTemplate: true, + }); +} diff --git a/vite-config/plugins/unpluginIcons.ts b/vite-config/plugins/unpluginIcons.ts new file mode 100644 index 0000000..ba2854d --- /dev/null +++ b/vite-config/plugins/unpluginIcons.ts @@ -0,0 +1,20 @@ +import { resolve } from "node:path"; +import type { PluginOption } from "vite"; +import Icons from "unplugin-icons/vite"; +import { FileSystemIconLoader } from "unplugin-icons/loaders"; + +export function createIconsPluginConfig(): PluginOption { + // https://github.com/antfu/unplugin-auto-import + return Icons({ + scale: 1, // Scale of icons against 1em + defaultStyle: "width:16px;height:16px", // Style apply to icons + defaultClass: "stroke-current", // Class names apply to icons + compiler: "vue3", // "vue2", "vue3", "jsx" + jsx: "react", // "react" or "preact" + autoInstall: true, + customCollections: { + // TODO 解决Icons问题 + login: FileSystemIconLoader(resolve("/src", "assets/icons/login")), + }, + }); +} diff --git a/vite-config/plugins/unpluginVueComponets.ts b/vite-config/plugins/unpluginVueComponets.ts new file mode 100644 index 0000000..5762f2b --- /dev/null +++ b/vite-config/plugins/unpluginVueComponets.ts @@ -0,0 +1,17 @@ +import Components from "unplugin-vue-components/vite"; +import type { PluginOption } from "vite"; +import IconsResolver from "unplugin-icons/resolver"; +import { VantResolver } from "unplugin-vue-components/resolvers"; + +export function createVueComponentsPluginConfig(): PluginOption { + return Components({ + extensions: ["vue"], + include: [/\.vue$/, /\.vue\?vue/], + dts: "types/components.d.ts", + exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], + resolvers: [ + IconsResolver(), + VantResolver(), + ], + }); +} diff --git a/vite-config/plugins/visualizer.ts b/vite-config/plugins/visualizer.ts new file mode 100644 index 0000000..8c53440 --- /dev/null +++ b/vite-config/plugins/visualizer.ts @@ -0,0 +1,15 @@ +/** + * Visualize and analyze your Rollup bundle to see which modules are taking up space. + * https://github.com/btd/rollup-plugin-visualizer + */ +import visualizer from "rollup-plugin-visualizer"; +import type { PluginOption } from "vite"; + +export function createVisualizerPluginConfig(): PluginOption { + return visualizer({ + filename: "./node_modules/.cache/visualizer/stats.html", + open: true, + gzipSize: true, + brotliSize: true, + }) as PluginOption; +} diff --git a/vite-config/plugins/vueI18nPlugin.ts b/vite-config/plugins/vueI18nPlugin.ts new file mode 100644 index 0000000..b201e78 --- /dev/null +++ b/vite-config/plugins/vueI18nPlugin.ts @@ -0,0 +1,12 @@ +import { resolve } from "node:path"; +import type { PluginOption } from "vite"; +import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; + +export function createVueI18nPluginConfig(): PluginOption { + return VueI18nPlugin({ + runtimeOnly: true, + compositionOnly: true, + fullInstall: true, + include: [resolve("/src", "/locales/**")], + }); +} diff --git a/vite-config/utils/index.ts b/vite-config/utils/index.ts new file mode 100644 index 0000000..b9067a6 --- /dev/null +++ b/vite-config/utils/index.ts @@ -0,0 +1,96 @@ +import process from "node:process"; +import type { ProxyOptions } from "vite"; +import dayjs from "dayjs"; +import type { ViteEnvVariables } from "#/config"; + +/** + * 读取所有环境变量配置文件,并将它们添加到process.env中 + * Read all environment variable configuration files and add them to process.env + * + * @param envConf 包含环境变量配置的对象 Object containing environment variable configuration + * @returns 包含更新后的环境变量的对象 Object with the updated environment variables + */ +export function updateEnvVariables(envConf: Recordable): Partial { + // 创建一个空的对象,用于存储Vite环境变量 + // Create an empty object to store Vite environment variables + const viteEnv: Partial = {}; + + // 遍历envConf中的所有键值对 + // Iterate over all key-value pairs in envConf + for (const [key, value] of Object.entries(envConf)) { + // 将值转换为字符串,并替换换行符为\n + // Convert the value to a string and replace line breaks with \n + let realValue: string | number | boolean | Recordable = String(value).replace(/\\n/g, "\n"); + + // 根据键名和值类型进行特殊处理 + // Perform special handling according to key name and value type + switch (key) { + case "VITE_PORT": + realValue = Number(realValue) || 8888; + break; + case "VITE_PROXY": + if (realValue) { + try { + // 将VITE_PROXY转换为JSON对象,并替换单引号为双引号,如果失败则设置为空对象 + // Convert VITE_PROXY to a JSON object and replace single quotes with double quotes, if it fails then set it to an empty object + realValue = JSON.parse(realValue.replace(/'/g, "\"")); + } catch (error) { + realValue = {}; + } + } + break; + default: + // 将字符串"true"和"false"转换为布尔值,其他情况不变 + // Convert string "true" and "false" to boolean values, otherwise keep unchanged + realValue = realValue === "true" ? true : realValue === "false" ? false : realValue; + } + + // 设置环境变量的值,如果是字符串类型则直接赋值,否则转换为JSON字符串赋值,并将其添加到viteEnv对象中 + // Set the value of the environment variable, if it is a string type then assign directly, otherwise convert to JSON string and assign, and add it to the viteEnv object + if (realValue) { + viteEnv[key] = realValue; + process.env[key] = typeof realValue === "string" ? realValue : JSON.stringify(realValue); + } + } + + return viteEnv; +} + +/** + * 根据代理列表配置代理选项,并返回一个代理对象 + * configure proxy options according to the proxy list and return a proxy object + * @param proxyList + */ +export function configureProxy(proxyList: [string, string][] = []) { + // 创建一个空的代理对象 + // Create an empty proxy object + const proxyConfig: Recordable = {}; + // 遍历代理列表中的每一对前缀和目标地址 + // Iterate over each pair of prefix and target address in the proxy list + for (const [prefix, target] of proxyList) { + // 判断目标地址是否是https协议 + // Determine if the target address is https protocol + const isHttps = /^https:\/\//.test(target); + // 根据前缀和目标地址设置代理选项,参考https://github.com/http-party/node-http-proxy#options + // Set proxy options according to prefix and target address, refer to https://github.com/http-party/node-http-proxy#options + proxyConfig[prefix] = { + target, + changeOrigin: true, + ws: true, + // 使用正则表达式替换路径中的前缀为空字符串,实现重写路径的功能 + // Use regular expression to replace the prefix in the path with an empty string, to achieve the function of rewriting the path + rewrite: (path: string) => path.replace(new RegExp(`^${prefix}`), ""), + // 如果目标地址是https协议,需要设置secure为false,否则不需要设置该选项 + // If the target address is https protocol, need to set secure to false, otherwise do not need to set this option + ...(isHttps ? { secure: false } : {}), + }; + } + // 返回代理对象 + // Return the proxy object + return proxyConfig; +} + +const DATE_TIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; +export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string { + return dayjs(date).format(format); +} diff --git a/vite.config.ts b/vite.config.ts index e9f687b..850dfb0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,158 +1,3 @@ -import { resolve } from "node:path"; -import * as path from "node:path"; -import { defineConfig } from "vite"; -import Vue from "@vitejs/plugin-vue"; -import Components from "unplugin-vue-components/vite"; -import AutoImport from "unplugin-auto-import/vite"; -import { VantResolver } from "unplugin-vue-components/resolvers"; -import Icons from "unplugin-icons/vite"; -import { FileSystemIconLoader } from "unplugin-icons/loaders"; -import IconsResolver from "unplugin-icons/resolver"; -import { VitePWA } from "vite-plugin-pwa"; -import GenerateConfig from "unplugin-config/vite"; +import { createViteConfig } from "./vite-config"; -// vite.config.ts -import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; -import UnoCss from "unocss/vite"; - -// This constant defines the name of the configuration file that will be used in the production environment -const GLOB_CONFIG_FILE_NAME = "_app.config.js"; - -// This constant sets the output directory for the Vite package -const OUTPUT_DIR = "dist"; - -// This constant sets the name of the application -const APP_NAME = "boot-vant"; - -// https://vitejs.dev/config/ -export default defineConfig({ - server: { - host: "localhost", - port: 8888, - open: true, - https: false, - proxy: {}, - }, - plugins: [ - Vue( - { - script: { - propsDestructure: true, - defineModel: true, - }, - }, - ), - Icons({ - scale: 1, // Scale of icons against 1em - defaultStyle: "width:16px;height:16px", // Style apply to icons - defaultClass: "stroke-current", // Class names apply to icons - compiler: "vue3", // "vue2", "vue3", "jsx" - jsx: "react", // "react" or "preact" - autoInstall: true, - customCollections: { - login: FileSystemIconLoader(path.resolve("src", "assets/icons/login")), - }, - }), - // https://github.com/antfu/unplugin-auto-import - AutoImport({ - imports: [ - "vue", - "vue-router", - "vue-i18n", - "vue/macros", - "@vueuse/head", - "@vueuse/core", - ], - dts: "types/auto-imports.d.ts", - dirs: [ - "src/composables", - "src/store", - ], - vueTemplate: true, - }), - - // https://github.com/antfu/unplugin-vue-components - Components({ - extensions: ["vue"], - include: [/\.vue$/, /\.vue\?vue/], - dts: "types/components.d.ts", - exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], - resolvers: [ - IconsResolver(), - VantResolver(), - ], - }), - - // https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n - VueI18nPlugin({ - runtimeOnly: true, - compositionOnly: true, - fullInstall: true, - include: [resolve(__dirname, "src/locales/**")], - }), - - // https://github.com/antfu/vite-plugin-pwa - VitePWA({ - registerType: "autoUpdate", - includeAssets: ["favicon.ico"], - manifest: { - name: "BootVant", - short_name: "BootVant", - theme_color: "#ffffff", - icons: [ - { - src: "/pwa-192x192.png", - sizes: "192x192", - type: "image/png", - }, - { - src: "/pwa-512x512.png", - sizes: "512x512", - type: "image/png", - }, - { - src: "/pwa-512x512.png", - sizes: "512x512", - type: "image/png", - purpose: "any maskable", - }, - ], - }, - }), - - // https://github.com/unocss/unocss - // see unocss.config.ts for config - UnoCss(), - - GenerateConfig({ - appName: APP_NAME, - envVariables: { - prefix: "VITE_GLOB_", - }, - configFile: { - generate: true, - fileName: GLOB_CONFIG_FILE_NAME, - outputDir: OUTPUT_DIR, - }, - }), - ], - resolve: { - alias: { - "~/": `${resolve(__dirname, "src")}/`, - }, - }, - css: { - preprocessorOptions: { - scss: { - additionalData: ` - @import "~/styles/variables.scss"; - `, - javascriptEnabled: true, - }, - }, - }, - // https://github.com/vitest-dev/vitest - test: { - environment: "jsdom", - }, -}); +export default createViteConfig();