UNPKG

@plugin-light/project-config-vite

Version:
408 lines (401 loc) 13.3 kB
import path from 'node:path'; import { loadEnv } from 'vite'; import { CDN_MAP, getSubProjectRoot, getSubProjectConfig, DEFAULT_CONTEXT_OBJECT, scssLogger } from '@plugin-light/shared'; import { addCodeAtEndVitePlugin } from '@plugin-light/vite-plugin-add-code-at-end'; import { aliasForLibrary } from '@plugin-light/vite-plugin-alias-for-library'; import { crossGameStyleVitePlugin } from '@plugin-light/vite-plugin-cross-game-style'; import { crossPlatformVitePlugin } from '@plugin-light/vite-plugin-cross-platform'; import { exportDefaultInVue } from '@plugin-light/vite-plugin-export-default-in-vue'; import { genVersionWebVitePlugin } from '@plugin-light/vite-plugin-gen-version'; import { ifdefVitePlugin } from '@plugin-light/vite-plugin-ifdef'; import basicSsl from '@vitejs/plugin-basic-ssl'; import legacy from '@vitejs/plugin-legacy'; import vue from '@vitejs/plugin-vue'; import vueJsx from '@vitejs/plugin-vue-jsx'; import { visualizer } from 'rollup-plugin-visualizer'; import AutoImport from 'unplugin-auto-import/vite'; import { VantResolver, ElementPlusResolver, TDesignResolver } from 'unplugin-vue-components/resolvers'; import Components from 'unplugin-vue-components/vite'; import importToCDN from 'vite-plugin-cdn-import'; import commonjs from 'vite-plugin-commonjs'; import viteCompression from 'vite-plugin-compression'; import { createHtmlPlugin } from 'vite-plugin-html'; import mkcert from 'vite-plugin-mkcert'; import mockDevServerPlugin from 'vite-plugin-mock-dev-server'; import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; import vueSetupExtend from 'vite-plugin-vue-setup-extend'; import { cdn } from 'vite-plugin-cdn2'; /** * 移除第一个反斜杠 * * @export * @param {string} [str=''] 输入字符串 * @returns {string} 字符串 * @example * ```ts * removeFirstSlash('/abc/ddd/') * * 'abc/ddd/' * ``` */ /** * 移除最后一个反斜杠 * * @export * @param {string} [str=''] 输入字符串 * @returns {string} 字符串 * * @example * ```ts * removeLastSlash('/abc/') * * '/abc' * ``` */ function removeLastSlash(str) { if (str === void 0) { str = ''; } return str.replace(/\/$/, ''); } var BUILD_NAME_MAP = { build: '_vConsoleBuildInfo', commit: '_vConsoleCommitInfo' }; function enableCDN(isEnabled) { if (isEnabled === 'true') { return cdn({ // url 可以更换为私有或其他源 // url: "https://cdn.jsdelivr.net/npm/", url: 'https://unpkg.com/', modules: ['vue', 'vue-demi', 'pinia', 'axios', 'vant', 'vue-router'], }); } } const getCdnList = ({ useElementPlusCDN, }) => { const modules = [ { name: 'vue', var: 'Vue', path: CDN_MAP.VUE_V3, }, { name: 'vue-demi', var: 'VueDemi', path: CDN_MAP.VUE_DEMI, }, { name: 'vue-router', var: 'VueRouter', path: CDN_MAP.VUE_ROUTER_V4, }, { name: 'aegis-web-sdk', var: 'Aegis', path: CDN_MAP.AEGIS_WEB, }, { name: 'pinia', var: 'Pinia', path: CDN_MAP.PINIA, }, ]; if (useElementPlusCDN) { modules.push({ name: 'element-plus', var: 'ElementPlus', css: CDN_MAP.ELEMENT_PLUS_CSS, path: CDN_MAP.ELEMENT_PLUS, }); } return { modules, }; }; // 以下内容经常被 include // md5 // js-cookie // qs // axios const DEFAULT_OPTIMIZE_DEPS_INCLUDES = []; const DEFAULT_OPTIMIZE_DEPS_EXCLUDES = []; const DEFAULT_ADD_CODE_AT_END_OPTIONS = {}; // 之前版本内容 // { // list: [ // { // id: 'vue.js', // code: 'export default function(){}', // exact: false, // }, // { // id: 'vue.runtime.esm-bundler.js', // code: 'export default function(){}', // exact: false, // number: 1, // }, // ], // }; const DEFAULT_ALIAS_FOR_LIBRARY_OPTIONS = { list: [ 'press-ui', 'press-plus', 'pmd-aegis', 'pmd-app-info', 'pmd-config', 'pmd-location', 'pmd-login', 'pmd-network', 'pmd-report', 'pmd-tools', 'pmd-types', 'pmd-widget', 'pmd-vue', 'pmd-jsapi', ], target: 'src/library', }; const DEFAULT_ALIAS = { PRESS_UI: 'src/library/press-ui', PRESS_PLUS: 'src/library/press-plus', }; const getDefaultPmdAliasMap = (root) => [ 'pmd-aegis', 'pmd-app-info', 'pmd-config', 'pmd-location', 'pmd-login', 'pmd-network', 'pmd-report', 'pmd-tools', 'pmd-types', 'pmd-widget', 'pmd-vue', 'pmd-jsapi', ].reduce((acc, item) => { const list = item.split('/'); const libName = list[list.length - 1]; acc[item] = path.resolve(root, `src/library/${libName}`); return acc; }, {}); const DEFAULT_MKCERT_OPTIONS = { source: 'coding', }; const ENV_PREFIX = ['VITE_', 'VUE_APP']; // 当前工作目录路径 const root = process.cwd(); function getAlias({ subProjectRoot, pressUiAlias, pressPlusAlias, pmdAliasMap, usePMDNetworkV2, }) { const result = { '@': subProjectRoot, src: path.resolve(root, 'src'), ...(pmdAliasMap || {}), }; if (pressUiAlias) { result['press-ui'] = path.resolve(root, pressUiAlias); } if (pressPlusAlias) { result['press-plus'] = path.resolve(root, pressPlusAlias); } if (usePMDNetworkV2) { result['pmd-network'] = path.resolve(root, 'node_modules/pmd-network-v2'); } return result; } function getViteBaseConfig({ mode, serverHttps, serverPort, serverHost = true, optimizeDepsIncludes, optimizeDepsExcludes, addCodeAtEndOptions, pressUiAlias = DEFAULT_ALIAS.PRESS_UI, pressPlusAlias = DEFAULT_ALIAS.PRESS_PLUS, aliasForLibraryOptions = DEFAULT_ALIAS_FOR_LIBRARY_OPTIONS, pmdAliasMap = getDefaultPmdAliasMap(root), customElements = [], useCdn = false, useElementPlusCDN = false, mkcertOptions = DEFAULT_MKCERT_OPTIONS, prePlugins = [], postPlugins = [], autoImportVant = true, autoImportElementPlus = true, autoImportTDesign = { library: 'vue-next', }, compressionOptions = true, legacyOptions = true, usePMDNetworkV2 = false, }) { // 环境变量 const env = loadEnv(mode, root, ENV_PREFIX); const isProduction = mode === 'production'; const appDir = env.VUE_APP_DIR || ''; const vueAppBase = env.VUE_APP_PUBLICPATH; exportDefaultInVue({ root, }); const subProjectRoot = getSubProjectRoot({ root, appDir, }); const subProjectConfig = getSubProjectConfig(subProjectRoot); const autoImportComponents = [ autoImportVant ? VantResolver(typeof autoImportVant === 'boolean' ? undefined : autoImportVant) : null, autoImportElementPlus ? ElementPlusResolver(typeof autoImportElementPlus === 'boolean' ? undefined : autoImportElementPlus) : null, autoImportTDesign ? TDesignResolver(typeof autoImportTDesign === 'boolean' ? undefined : autoImportTDesign) : null, ].filter(Boolean); const plugins = [ ...prePlugins, aliasForLibrary({ root, ...(aliasForLibraryOptions || {}), }), ifdefVitePlugin({ context: { ...DEFAULT_CONTEXT_OBJECT, H5: true, WEB: true, VUE3: true, __NOT_UNI__: true, }, type: ['css', 'js', 'html'], }), crossPlatformVitePlugin({ platform: 'web', }), crossGameStyleVitePlugin({ styleName: subProjectConfig?.styleName || '', }), addCodeAtEndOptions ? addCodeAtEndVitePlugin(typeof addCodeAtEndOptions === 'boolean' ? DEFAULT_ADD_CODE_AT_END_OPTIONS : addCodeAtEndOptions) : null, serverHttps ? basicSsl() : '', commonjs(), vue({ template: { compilerOptions: { // @ts-ignore isCustomElement: (tag) => customElements.includes(tag), }, }, }), vueJsx(), mockDevServerPlugin(), AutoImport({ resolvers: autoImportComponents, }), Components({ dts: 'typings/components.d.ts', resolvers: autoImportComponents, }), // svg icon createSvgIconsPlugin({ // 指定图标文件夹 iconDirs: [path.resolve(subProjectRoot, './icons/svg')], // 指定 symbolId 格式 symbolId: 'icon-[dir]-[name]', }), // 允许 setup 语法糖上添加组件名属性 vueSetupExtend(), // 生产环境 gzip 压缩资源 compressionOptions ? viteCompression(typeof compressionOptions === 'boolean' ? undefined : compressionOptions) : null, // 注入模板数据 createHtmlPlugin({ inject: { data: { ENABLE_ERUDA: env.VITE_ENABLE_ERUDA || 'false', }, }, }), // 生产环境默认不启用 CDN 加速 enableCDN(env.VITE_CDN_DEPS), genVersionWebVitePlugin({ buildName: BUILD_NAME_MAP.build, commitName: BUILD_NAME_MAP.commit, delay: 10, }), legacyOptions ? legacy(typeof legacyOptions === 'boolean' ? { targets: ['> 1%, last 1 version, ie >= 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'], // 面向IE11时需要此插件 } : legacyOptions) : null, isProduction && useCdn ? importToCDN(getCdnList({ useElementPlusCDN, })) : null, isProduction ? visualizer({ open: !!env.VITE_VISUALIZER, filename: path.resolve(subProjectRoot, 'dist', 'stats.html'), gzipSize: true, brotliSize: true, // 收集 brotli 大小并将其显示 }) : null, mkcertOptions ? mkcert(typeof mkcertOptions === 'boolean' ? DEFAULT_MKCERT_OPTIONS : mkcertOptions) : null, ...postPlugins, ].filter(item => !!item); const experimentalConfig = vueAppBase ? { experimental: { renderBuiltUrl(filename, { hostId, hostType, type }) { console.log('[experimental] ', hostType, hostId, type, filename); return `${removeLastSlash(vueAppBase)}/${filename}`; }, }, } : {}; // 生成环境默认开启 sourcemap const buildSourceMap = isProduction; return { root: subProjectRoot, envDir: process.cwd(), base: vueAppBase || './', ...experimentalConfig, optimizeDeps: { // include: ["press-ui/**/*.vue"] include: optimizeDepsIncludes || DEFAULT_OPTIMIZE_DEPS_INCLUDES, exclude: optimizeDepsExcludes || DEFAULT_OPTIMIZE_DEPS_EXCLUDES, }, plugins, resolve: { alias: getAlias({ subProjectRoot, pressUiAlias, pressPlusAlias, pmdAliasMap, usePMDNetworkV2, }), extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], }, server: { port: serverPort ?? (env.VUE_APP_PORT ? +(env.VUE_APP_PORT) : 443), https: serverHttps ?? {}, host: serverHost, // 仅在 proxy 中配置的代理前缀, mock-dev-server 才会拦截并 mock // doc: https://github.com/pengzhanbo/vite-plugin-mock-dev-server proxy: { // '^/dev-api': { // target: '', // }, }, }, build: { target: 'es2015', rollupOptions: { input: { 1: path.resolve(subProjectRoot, './index.html'), }, output: { chunkFileNames: 'static/js/[name]-[hash].js', entryFileNames: 'static/js/[name]-[hash].js', assetFileNames: 'static/[ext]/[name]-[hash].[ext]', }, }, sourcemap: buildSourceMap, }, esbuild: isProduction ? { pure: ['console.log'], drop: ['debugger'], } : {}, css: { preprocessorOptions: { scss: { // api: 'modern', // https://sass-lang.com/documentation/breaking-changes/legacy-js-api/ // https://sass-lang.com/documentation/breaking-changes/import/ // https://sass-lang.com/documentation/breaking-changes/mixed-decls/ silenceDeprecations: ['import', 'legacy-js-api', 'mixed-decls'], logger: scssLogger, }, }, }, }; } export { getViteBaseConfig };