aslan-h5/vite.config.js

391 lines
8.9 KiB
JavaScript

import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
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']
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()],
}),
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) => {
const info = assetInfo.name.split('.')
const ext = info[info.length - 1]
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,
},
}
})