dx: avoid big .vite files slowing down safari
Massimo Melina committed
May 22, 2026 at 18:29 UTC
4f43d6233eab9253d8cd5997592896339e99573d
1 file changed
+75
-12
admin/vite.config.ts
+75
-12
@@ -1,6 +1,12 @@
1
import { defineConfig } from 'vite'
2
-//import vitePluginImport from 'vite-plugin-babel-import';
3
-// package.json/devDependencies: "vite-plugin-babel-import": "github:rejetto/vite-plugin-babel-import"
2
+import { readdirSync, readFileSync, statSync } from 'node:fs'
3
+import { dirname, extname, resolve } from 'node:path'
4
+import { fileURLToPath } from 'node:url'
5
+
6
+const DIR = dirname(fileURLToPath(import.meta.url))
7
+const ICONS_MODULE = '@mui/icons-material'
8
+const TYPE_EXPORTS = new Set(['SvgIconComponent'])
9
+const ICON_IMPORT_RE = /import\s*{\s*([^}]+?)\s*}\s*from\s*['"]@mui\/icons-material['"];?/gs
10
11
// https://vitejs.dev/config/
12
export default defineConfig({
@@ -9,22 +15,20 @@ export default defineConfig({
15
emptyOutDir: true,
16
target: "es2015",
17
rollupOptions: {
12
- /* plugins: [
13
- vitePluginImport([
14
- { // speed up build process (~2s on my M1) by bringing "modules transformed" from 11k+ down to 1.7k+
15
- libraryName: '@mui/icons-material',
16
- libraryDirectory: '',
17
- libraryChangeCase: "camelCase",
18
- ignoreStyles: [],
19
- },
20
- ])
21
- ],*/
18
onwarn(warning, warn) {
19
if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes(`"use client"`)) return
20
warn(warning)
21
},
22
}
23
},
24
+ plugins: [
25
+ muiIconsDeepImportPlugin(),
26
+ ],
27
+ optimizeDeps: {
28
+ exclude: [ICONS_MODULE],
29
+ // optimizeDeps scans before plugins, so workspace barrel imports need explicit deep entries
30
+ include: getMuiIconImports().map(name => `${ICONS_MODULE}/${name}`),
31
+ },
32
server: {
33
port: 3006,
34
host: '127.0.0.1',
@@ -39,3 +43,62 @@ export default defineConfig({
43
}
44
}
45
})
46
+
47
+function muiIconsDeepImportPlugin() {
48
+ return {
49
+ name: 'mui-icons-deep-import',
50
+ enforce: 'pre' as const,
51
+ transform(code: string, id: string) {
52
+ if (!/\.[jt]sx?$/.test(id) || !code.includes(ICONS_MODULE))
53
+ return
54
+ // deep imports keep Vite from pre-bundling the full icons barrel
55
+ const replaced = code.replace(ICON_IMPORT_RE, (full, specifiers) => {
56
+ const imports = specifiers.split(',')
57
+ .map(x => x.trim())
58
+ .filter(Boolean)
59
+ .map(parseSpecifier)
60
+ const typeImports = imports.filter(x => TYPE_EXPORTS.has(x.imported))
61
+ const valueImports = imports.filter(x => !TYPE_EXPORTS.has(x.imported))
62
+ return [
63
+ ...typeImports.length ? [`import type { ${typeImports.map(formatTypeImport).join(', ')} } from '${ICONS_MODULE}'`] : [],
64
+ ...valueImports.map(({ imported, local }) => `import ${local} from '${ICONS_MODULE}/${imported}'`),
65
+ ].join('\n')
66
+ })
67
+ return replaced === code ? undefined : { code: replaced, map: null }
68
+ },
69
+ }
70
+
71
+ function parseSpecifier(specifier: string) {
72
+ const [imported, local = imported] = specifier.split(/\s+as\s+/)
73
+ return { imported: imported.trim(), local: local.trim() }
74
+ }
75
+
76
+ function formatTypeImport({ imported, local }: ReturnType<typeof parseSpecifier>) {
77
+ return local === imported ? imported : `${imported} as ${local}`
78
+ }
79
+}
80
+
81
+function getMuiIconImports() {
82
+ const icons = new Set<string>()
83
+ for (const dir of ['src', '../mui-grid-form'])
84
+ scan(resolve(DIR, dir))
85
+ return [...icons].sort()
86
+
87
+ function scan(path: string) {
88
+ const stat = statSync(path)
89
+ if (stat.isDirectory()) {
90
+ for (const name of readdirSync(path))
91
+ scan(resolve(path, name))
92
+ return
93
+ }
94
+ if (!['.js', '.jsx', '.ts', '.tsx'].includes(extname(path)))
95
+ return
96
+ for (const match of readFileSync(path, 'utf8').matchAll(ICON_IMPORT_RE)) {
97
+ for (const specifier of match[1].split(',')) {
98
+ const imported = specifier.trim().split(/\s+as\s+/)[0]
99
+ if (imported && !TYPE_EXPORTS.has(imported))
100
+ icons.add(imported)
101
+ }
102
+ }
103
+ }
104
+}