UNPKG

@voya-kit/vite-config

Version:

Voya vite config

346 lines (333 loc) 11.3 kB
import { join, resolve } from 'node:path'; import dayjs from 'dayjs'; import { readPackageJSON } from 'pkg-types'; import { defineConfig, loadEnv, mergeConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import vueJsx from '@vitejs/plugin-vue-jsx'; import colors from 'picocolors'; import dotenv from 'dotenv'; import fsExtra from 'fs-extra'; import { createHash } from 'node:crypto'; import compressPlugin from 'vite-plugin-compression'; import { createHtmlPlugin } from 'vite-plugin-html'; import visualizer from 'rollup-plugin-visualizer'; import { existsSync, mkdir, writeFile } from 'node:fs'; import { setupName } from '@voya-kit/vite-plugins'; import dts from 'vite-plugin-dts'; function getConfFiles() { const script = process.env.npm_lifecycle_script; const reg = new RegExp('--mode ([a-z_\\d]+)'); const result = reg.exec(script); if (result) { const mode = result[1]; return ['.env', `.env.${mode}`]; } return ['.env', '.env.production']; } async function getEnvConfig(match = 'VITE_', confFiles = getConfFiles()) { let envConfig = {}; for (const confFile of confFiles) { try { const { readFile } = fsExtra; const envPath = await readFile(join(process.cwd(), confFile), { encoding: 'utf8' }); const env = dotenv.parse(envPath); envConfig = { ...envConfig, ...env }; } catch (e) { console.error(`Error in parsing ${confFile}`, e); } } const reg = new RegExp(`^(${match})`); Object.keys(envConfig).forEach((key) => { if (!reg.test(key)) { Reflect.deleteProperty(envConfig, key); } }); return envConfig; } function createContentHash(content, hashLSize = 12) { const hash = createHash('sha256').update(content); return hash.digest('hex').slice(0, hashLSize); } const GLOBAL_CONFIG_FILE_NAME = '_app.config.js'; const PLUGIN_NAME = 'app-config'; async function createAppConfigPlugin({ root, isBuild, }) { let publicPath; let source; if (!isBuild) { return { name: PLUGIN_NAME, }; } const { version = '' } = await readPackageJSON(root); return { name: PLUGIN_NAME, async configResolved(_config) { let appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? ""; appTitle = appTitle.replace(/\s/g, '_').replace(/-/g, '_'); publicPath = _config.base; source = await getConfigSource(appTitle); }, async transformIndexHtml(html) { publicPath = publicPath.endsWith('/') ? publicPath : `${publicPath}/`; const appConfigSrc = `${publicPath || '/'}${GLOBAL_CONFIG_FILE_NAME}?v=${version}-${createContentHash(source)}`; return { html, tags: [ { tag: 'script', attrs: { src: appConfigSrc, }, }, ], }; }, async generateBundle() { try { this.emitFile({ type: 'asset', fileName: GLOBAL_CONFIG_FILE_NAME, source, }); console.log(colors.cyan(`configuration file is build successfully!`)); } catch (error) { console.log(colors.red('configuration file configuration file failed to package:\n' + error)); } }, }; } const getVariableName = (title) => { function strToHex(str) { const result = []; for (let i = 0; i < str.length; ++i) { const hex = str.charCodeAt(i).toString(16); result.push(('000' + hex).slice(-4)); } return result.join('').toUpperCase(); } return `__PRODUCTION__${strToHex(title) || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, ''); }; async function getConfigSource(appTitle) { const config = await getEnvConfig(); const variableName = getVariableName(appTitle); const windowVariable = `window.${variableName}`; let source = `${windowVariable}=${JSON.stringify(config)};`; source += ` Object.freeze(${windowVariable}); Object.defineProperty(window, "${variableName}", { configurable: false, writable: false, }); `.replace(/\s/g, ''); return source; } function configCompressPlugin({ compress, deleteOriginFile = false, }) { const compressList = compress.split(','); const plugins = []; if (compressList.includes('gzip')) { plugins.push(compressPlugin({ ext: '.gz', deleteOriginFile, })); } if (compressList.includes('brotli')) { plugins.push(compressPlugin({ ext: '.br', algorithm: 'brotliCompress', deleteOriginFile, })); } return plugins; } function configHtmlPlugin({ isBuild }) { const htmlPlugin = createHtmlPlugin({ minify: isBuild, }); return htmlPlugin; } function configVisualizerConfig() { return visualizer({ filename: 'stats.html', open: true, gzipSize: true, brotliSize: true, }); } const writeVersion = (versionFilePath, content) => { writeFile(versionFilePath, content, (err) => { if (err) throw err; }); }; const configVersionPlugin = (options) => { let config = null; return { name: 'version-update', configResolved(resolvedConfig) { config = resolvedConfig; }, buildStart() { if (!config) { throw new Error('Config not resolved before build start.'); } const file = resolve(config.publicDir, 'version.json'); const content = JSON.stringify({ lastBuildTime: options.lastBuildTime }); if (existsSync(config.publicDir)) { writeVersion(file, content); } else { mkdir(config.publicDir, { recursive: true }, (err) => { if (err) throw err; writeVersion(file, content); }); } }, }; }; async function createPlugins({ isBuild, root, compress, enableAnalyze }, lastBuildTime) { const vitePlugins = [vue(), vueJsx()]; const appConfigPlugin = await createAppConfigPlugin({ root, isBuild }); vitePlugins.push(appConfigPlugin); vitePlugins.push(configHtmlPlugin({ isBuild })); vitePlugins.push(configVersionPlugin({ lastBuildTime })); if (isBuild) { vitePlugins.push(configCompressPlugin({ compress, })); } if (enableAnalyze) { vitePlugins.push(configVisualizerConfig()); } return vitePlugins; } const commonConfig = (mode) => ({ server: { port: 60006, }, esbuild: { drop: mode === 'production' ? ['console', 'debugger'] : [], }, build: { reportCompressedSize: false, chunkSizeWarningLimit: 1500, rollupOptions: { maxParallelFileOps: 3, }, }, plugins: [setupName()], }); function defineApplicationConfig(defineOptions = {}) { const { overrides = {} } = defineOptions; return defineConfig(async ({ command, mode }) => { const root = process.cwd(); const isBuild = command === 'build'; const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv(mode, root); const lastBuildTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); const defineData = await createDefineData(root, lastBuildTime); const plugins = await createPlugins({ isBuild, root, enableAnalyze: VITE_ENABLE_ANALYZE === 'true', enableMock: VITE_USE_MOCK === 'true', compress: VITE_BUILD_COMPRESS || '', }, lastBuildTime); const pathResolve = (pathname) => resolve(root, '.', pathname); const applicationConfig = { resolve: { alias: [ { find: 'vue-i18n', replacement: 'vue-i18n/dist/vue-i18n.cjs.js', }, { find: /\/@\//, replacement: pathResolve('src') + '/', }, { find: /\/#\//, replacement: pathResolve('types') + '/', }, { find: /@\//, replacement: pathResolve('src') + '/', }, { find: /#\//, replacement: pathResolve('types') + '/', }, ], }, define: defineData, build: { target: 'es2015', cssTarget: 'chrome80', rollupOptions: { output: { entryFileNames: 'assets/[name].js', manualChunks: { vue: ['vue', 'pinia', 'vue-router'], antd: ['ant-design-vue', '@ant-design/icons-vue'], }, }, }, }, css: { preprocessorOptions: { less: { javascriptEnabled: true, }, }, }, plugins, }; const mergedConfig = mergeConfig(commonConfig(mode), applicationConfig); return mergeConfig(mergedConfig, overrides); }); } async function createDefineData(root, lastBuildTime) { try { const pkgJson = await readPackageJSON(root); const { dependencies, devDependencies, name, version } = pkgJson; const __APP_INFO__ = { pkg: { dependencies, devDependencies, name, version }, lastBuildTime: lastBuildTime, }; return { __APP_INFO__: JSON.stringify(__APP_INFO__), }; } catch (error) { return {}; } } function definePackageConfig(defineOptions = {}) { const { overrides = {} } = defineOptions; const root = process.cwd(); return defineConfig(async ({ mode }) => { const { dependencies = {}, peerDependencies = {} } = await readPackageJSON(root); const packageConfig = { build: { lib: { entry: 'src/index.ts', formats: ['es'], fileName: () => 'index.mjs', }, rollupOptions: { external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)], }, }, plugins: [ dts({ logLevel: 'error', }), ], }; const mergedConfig = mergeConfig(commonConfig(mode), packageConfig); return mergeConfig(mergedConfig, overrides); }); } export { defineApplicationConfig, definePackageConfig }; //# sourceMappingURL=index.mjs.map