aslan-h5/vite.config.js
2026-07-24 11:32:26 +08:00

399 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath, URL } from 'node:url'
import { VantResolver } from 'unplugin-vue-components/resolvers'
const PRODUCTION_LIKE_MODES = new Set(['production', 'production-atu'])
const STRIPPABLE_CONSOLE_METHODS = ['log', 'debug', 'info']
// Android 9 首发设备可能仍使用 Chrome 69 级别的 System WebView。
// 显式锁定最低目标,避免 Vite 大版本升级后默认 target 再次抬高。
const ANDROID_9_WEBVIEW_TARGETS = ['Chrome >= 69', 'ChromeAndroid >= 69']
const LOCAL_OSS_MIME_TYPES = {
'.gif': 'image/gif',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.svg': 'image/svg+xml',
'.webp': 'image/webp',
}
function isEscaped(code, index) {
let backslashCount = 0
let cursor = index - 1
while (cursor >= 0 && code[cursor] === '\\') {
backslashCount += 1
cursor -= 1
}
return backslashCount % 2 === 1
}
function findCallEnd(code, openParenIndex) {
let depth = 0
let quote = null
let templateExpressionDepth = 0
let inLineComment = false
let inBlockComment = false
for (let index = openParenIndex; index < code.length; index += 1) {
const current = code[index]
const next = code[index + 1]
if (inLineComment) {
if (current === '\n') {
inLineComment = false
}
continue
}
if (inBlockComment) {
if (current === '*' && next === '/') {
inBlockComment = false
index += 1
}
continue
}
if (quote) {
if (quote === '`') {
if (current === '$' && next === '{' && !isEscaped(code, index)) {
templateExpressionDepth += 1
index += 1
continue
}
if (current === '}' && templateExpressionDepth > 0 && !isEscaped(code, index)) {
templateExpressionDepth -= 1
continue
}
if (current === '`' && templateExpressionDepth === 0 && !isEscaped(code, index)) {
quote = null
}
continue
}
if (current === quote && !isEscaped(code, index)) {
quote = null
}
continue
}
if (current === '/' && next === '/') {
inLineComment = true
index += 1
continue
}
if (current === '/' && next === '*') {
inBlockComment = true
index += 1
continue
}
if (current === '"' || current === "'" || current === '`') {
quote = current
continue
}
if (current === '(') {
depth += 1
continue
}
if (current === ')') {
depth -= 1
if (depth === 0) {
let endIndex = index + 1
while (endIndex < code.length && /\s/.test(code[endIndex])) {
endIndex += 1
}
if (code[endIndex] === ';') {
endIndex += 1
}
return endIndex
}
}
}
return -1
}
function findTokenOutsideLiterals(code, token, fromIndex = 0) {
let quote = null
let templateExpressionDepth = 0
let inLineComment = false
let inBlockComment = false
for (let index = fromIndex; index < code.length; index += 1) {
const current = code[index]
const next = code[index + 1]
if (inLineComment) {
if (current === '\n') {
inLineComment = false
}
continue
}
if (inBlockComment) {
if (current === '*' && next === '/') {
inBlockComment = false
index += 1
}
continue
}
if (quote) {
if (quote === '`') {
if (current === '$' && next === '{' && !isEscaped(code, index)) {
templateExpressionDepth += 1
index += 1
continue
}
if (current === '}' && templateExpressionDepth > 0 && !isEscaped(code, index)) {
templateExpressionDepth -= 1
continue
}
if (current === '`' && templateExpressionDepth === 0 && !isEscaped(code, index)) {
quote = null
}
continue
}
if (current === quote && !isEscaped(code, index)) {
quote = null
}
continue
}
if (current === '/' && next === '/') {
inLineComment = true
index += 1
continue
}
if (current === '/' && next === '*') {
inBlockComment = true
index += 1
continue
}
if (current === '"' || current === "'" || current === '`') {
quote = current
continue
}
if (code.startsWith(token, index)) {
return index
}
}
return -1
}
function stripSelectedConsoleCalls(code) {
let output = code
STRIPPABLE_CONSOLE_METHODS.forEach((method) => {
let searchIndex = 0
const token = `console.${method}`
while (searchIndex < output.length) {
const startIndex = findTokenOutsideLiterals(output, token, searchIndex)
if (startIndex === -1) {
break
}
let cursor = startIndex + token.length
while (cursor < output.length && /\s/.test(output[cursor])) {
cursor += 1
}
if (output[cursor] !== '(') {
searchIndex = startIndex + token.length
continue
}
const endIndex = findCallEnd(output, cursor)
if (endIndex === -1) {
searchIndex = cursor + 1
continue
}
output = `${output.slice(0, startIndex)}void 0;${output.slice(endIndex)}`
searchIndex = startIndex + 'void 0;'.length
}
})
return output
}
function createBuildLogControlPlugin(stripDebugLogs) {
return {
name: 'likei-build-log-control',
apply: 'build',
enforce: 'post',
transform(code, id) {
if (!stripDebugLogs || id.includes('node_modules')) {
return null
}
if (!/\.(vue|js|ts|jsx|tsx|mjs|cjs)(\?|$)/.test(id)) {
return null
}
const transformedCode = stripSelectedConsoleCalls(code)
if (transformedCode === code) {
return null
}
return {
code: transformedCode,
map: null,
}
},
}
}
function createLocalOssAssetPlugin() {
const ossRoot = fileURLToPath(new URL('./public/oss', import.meta.url))
return {
name: 'likei-local-oss-assets',
apply: 'serve',
configureServer(server) {
server.middlewares.use('/oss', (req, res, next) => {
const rawPath = req.url?.split('?')[0] || '/'
let decodedPath = ''
try {
decodedPath = decodeURIComponent(rawPath)
} catch {
next()
return
}
const normalizedPath = path.normalize(decodedPath).replace(/^(\.\.[/\\])+/, '')
const filePath = path.join(ossRoot, normalizedPath)
const safeRoot = `${ossRoot}${path.sep}`
if (!filePath.startsWith(safeRoot)) {
next()
return
}
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
next()
return
}
const contentType = LOCAL_OSS_MIME_TYPES[path.extname(filePath).toLowerCase()]
if (contentType) {
res.setHeader('Content-Type', contentType)
}
fs.createReadStream(filePath).pipe(res)
})
},
}
}
export default defineConfig(({ mode }) => {
const keepBuildLogs = process.env.LIKEI_KEEP_BUILD_LOGS === 'true'
const stripDebugLogs = PRODUCTION_LIKE_MODES.has(mode) && !keepBuildLogs
return {
plugins: [
createLocalOssAssetPlugin(),
vue(),
Components({
resolvers: [VantResolver()],
}),
legacy({
// Chrome 69 已支持 ESM只需要降级现代模块并补齐实际使用的 ES API。
modernTargets: ANDROID_9_WEBVIEW_TARGETS,
modernPolyfills: true,
renderLegacyChunks: false,
}),
createBuildLogControlPlugin(stripDebugLogs),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
build: {
minify: 'esbuild',
sourcemap: false,
cssMinify: 'esbuild',
reportCompressedSize: false,
esbuild: {
drop: stripDebugLogs ? ['debugger'] : [],
charset: 'ascii',
legalComments: 'none',
minifyIdentifiers: true,
minifySyntax: true,
minifyWhitespace: true,
treeShaking: true,
},
rollupOptions: {
output: {
entryFileNames: 'js/[hash].js',
chunkFileNames: 'js/[hash].js',
assetFileNames: (assetInfo) => {
if (/\.(css)$/.test(assetInfo.name)) {
return 'css/[hash].[ext]'
}
return 'assets/[hash].[ext]'
},
},
},
},
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
open: false,
cors: true,
proxy: {
'/oss': {
target: 'https://asset.global-interaction.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/oss/, ''),
configure: (proxy) => {
proxy.on('proxyRes', (proxyRes) => {
delete proxyRes.headers.etag
delete proxyRes.headers['last-modified']
delete proxyRes.headers['cache-control']
delete proxyRes.headers.expires
})
},
},
},
},
preview: {
host: '0.0.0.0',
port: 4173,
strictPort: true,
open: false,
},
}
})