ddd1020a3a
Решение «Vite не трогать» пересмотрено по данным, а не по желанию обновиться.
`pnpm audit --prod` (то, на что смотрит gate сборки) был чист, но полный audit
показывал в build-цепочке два critical и rollup GHSA DOM clobbering — а он
затрагивает ГЕНЕРИРУЕМЫЙ bundle, то есть уезжает в production. Vite 4.3.1 тянул
rollup 3 без исправления.
Взята 7.3.6 — последняя линия до Rolldown. Vite 8 по-прежнему не берётся: это
смена бандлера, отдельная работа с собственной приёмкой.
Вместе с Vite обновлена цепочка плагинов (@vitejs/plugin-vue 4 -> 6,
unplugin-* и unocss с версий 2022-2023 годов) и линтеры (@typescript-eslint
5 -> 8, prettier 2 -> 3, stylelint 15 -> 17). Новый @typescript-eslint нашёл 7
настоящих замечаний — исправлены по существу, а не подавлением правил; среди
них подавление несуществующего правила ban-types и `{}` вместо `object`.
Отдельно — три мёртвых редактора. Outbounds уже был разобран, здесь то же
самое для ImputMultiple (ACME-домены, inline ACL) и MapAdd (параметры ACME DNS,
заголовки masquerade): оба редактировали конфиг Hysteria в форме с
:disabled="true", получали значения без v-model и эмитили события, которых
никто не слушает, на странице без единого маршрута записи.
Цена ImputMultiple была измеримой. vuedraggable поставляется UMD-сборкой,
поэтому её require("vue") разрешался в vue/dist/vue.cjs.prod.js — полную сборку
Vue с рантайм-компилятором шаблонов. В бандл уезжало ~500 КБ исходников
(vuedraggable + sortablejs + compiler-core + compiler-dom) ради перетаскивания
тегов в недоступной для редактирования форме.
Разбор bundle через sourcemap нашёл и вторую потерю: @vueuse/core собирался
дважды — наш 13.9.0 и 14.4.0 из element-plus. Версии сведены.
bundle: 2 813 525 против 2 404 202 на исходной базовой линии (+17% за Vue 3.5,
Element Plus 2.14, echarts 6 и rollup 4). Промежуточное состояние без этих двух
исправлений было 3 046 172.
pnpm audit --prod: чисто. typecheck/eslint/stylelint: 0. go build с dist: OK.
171 lines
6.4 KiB
Go
171 lines
6.4 KiB
Go
import vue from "@vitejs/plugin-vue";
|
||
|
||
import { defineConfig, UserConfig } from "vite";
|
||
|
||
import AutoImport from "unplugin-auto-import/vite";
|
||
import Components from "unplugin-vue-components/vite";
|
||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||
|
||
import Icons from "unplugin-icons/vite";
|
||
import IconsResolver from "unplugin-icons/resolver";
|
||
|
||
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
|
||
|
||
import UnoCSS from "unocss/vite";
|
||
|
||
import path from "path";
|
||
|
||
const pathSrc = path.resolve(__dirname, "src");
|
||
|
||
// eslint-disable-next-line no-control-regex
|
||
const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;
|
||
const DRIVE_LETTER_REGEX = /^[a-z]:/i;
|
||
const DEV_SERVER_PORT = 8080;
|
||
// Синхронизировано с src/utils/request.ts и constant.AdminAPIBase.
|
||
const API_BASE = "/api";
|
||
|
||
export default defineConfig((): UserConfig => {
|
||
return {
|
||
base: "/",
|
||
resolve: {
|
||
alias: {
|
||
"@": pathSrc,
|
||
},
|
||
},
|
||
css: {
|
||
// CSS preprocessor
|
||
preprocessorOptions: {
|
||
//define global scss variable
|
||
scss: {
|
||
javascriptEnabled: true,
|
||
additionalData: `
|
||
@use "@/styles/variables.scss" as *;
|
||
`,
|
||
},
|
||
},
|
||
},
|
||
server: {
|
||
host: "0.0.0.0",
|
||
port: DEV_SERVER_PORT,
|
||
open: true, // Automatically open browser on start
|
||
proxy: {
|
||
// Reverse proxy for CORS in development
|
||
[API_BASE]: {
|
||
target: "http://127.0.0.1:8081",
|
||
changeOrigin: true,
|
||
},
|
||
},
|
||
},
|
||
plugins: [
|
||
vue(),
|
||
UnoCSS({/* options */}),
|
||
AutoImport({
|
||
// Auto import Vue helpers: ref, reactive, toRef, etc.
|
||
//
|
||
// Из @vueuse/core берутся ПОИМЁННО только те функции, которые
|
||
// действительно используются без явного импорта, а не весь пакет.
|
||
//
|
||
// Причина не стилистическая. С `"@vueuse/core"` целиком в список
|
||
// авто-импорта попадает вся его публичная поверхность — более пятисот
|
||
// имён, — и на VueUse 13 сборка падает:
|
||
//
|
||
// [auto-import] identifier toRef already defined with vue
|
||
//
|
||
// VueUse реэкспортирует `toRef` и `toValue`, и они конфликтуют с
|
||
// одноимёнными функциями Vue. Разрешать конфликт порядком записей в
|
||
// массиве значило бы решать вопрос «какой из двух toRef здесь имеется в
|
||
// виду» невидимо для читателя файла.
|
||
//
|
||
// Побочный эффект — src/types/auto-imports.d.ts сокращается с 526 строк
|
||
// до размера, который можно прочитать глазами.
|
||
imports: [
|
||
"vue",
|
||
{
|
||
"@vueuse/core": ["useVModel", "useFullscreen"],
|
||
},
|
||
],
|
||
eslintrc: {
|
||
// Включено намеренно. Файл перечисляет глобальные имена для eslint и
|
||
// подключается через `extends` в .eslintrc.cjs, но генерация была
|
||
// выключена — то есть список жил своей жизнью и расходился со
|
||
// списком авто-импортов при каждом его изменении.
|
||
//
|
||
// Устаревшие записи здесь не безобидны: eslint перестаёт сообщать об
|
||
// обращении к имени, которого больше нет в авто-импорте, и ошибка
|
||
// доезжает до typecheck или до рантайма вместо линтера.
|
||
enabled: true,
|
||
filepath: "./.eslintrc-auto-import.json", // Default `./.eslintrc-auto-import.json`
|
||
globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
|
||
},
|
||
resolvers: [
|
||
// Auto import Element Plus helpers with styles: ElMessage, ElMessageBox, etc.
|
||
ElementPlusResolver(),
|
||
// Auto import icon components
|
||
IconsResolver({}),
|
||
],
|
||
vueTemplate: true, // Auto import in Vue templates
|
||
dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // Type declarations for auto imports; false disables generation
|
||
}),
|
||
|
||
Components({
|
||
resolvers: [
|
||
// Auto register icon components
|
||
IconsResolver({
|
||
enabledCollections: ["ep"], // @iconify-json/ep is the Element Plus icon collection
|
||
}),
|
||
// Auto import Element Plus components
|
||
ElementPlusResolver(),
|
||
],
|
||
dts: path.resolve(pathSrc, "types", "components.d.ts"), // Type declarations for auto components; false disables generation
|
||
}),
|
||
|
||
Icons({
|
||
// Auto install icon collections
|
||
autoInstall: true,
|
||
}),
|
||
|
||
createSvgIconsPlugin({
|
||
// Icon folders to cache
|
||
iconDirs: [path.resolve(pathSrc, "assets/icons")],
|
||
// symbolId format
|
||
symbolId: "icon-[dir]-[name]",
|
||
}),
|
||
],
|
||
build: {
|
||
rollupOptions: {
|
||
output: {
|
||
manualChunks(id: string) {
|
||
const normalizedId = id.replaceAll("\\", "/");
|
||
|
||
if (!normalizedId.includes("node_modules")) {
|
||
return;
|
||
}
|
||
|
||
if (
|
||
normalizedId.includes("echarts") ||
|
||
normalizedId.includes("zrender") ||
|
||
normalizedId.includes("vue-echarts")
|
||
) {
|
||
return "charts";
|
||
}
|
||
|
||
return "vendor";
|
||
},
|
||
sanitizeFileName(name: string): string {
|
||
// https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
|
||
const match = DRIVE_LETTER_REGEX.exec(name);
|
||
const driveLetter = match ? match[0] : "";
|
||
|
||
// A `:` is only allowed as part of a windows drive letter (ex: C:\foo)
|
||
// Otherwise, avoid them because they can refer to NTFS alternate data streams.
|
||
return (
|
||
driveLetter +
|
||
name.slice(driveLetter.length).replace(INVALID_CHAR_REGEX, "")
|
||
);
|
||
},
|
||
},
|
||
},
|
||
},
|
||
};
|
||
});
|