Skip to content

Commit 7355209

Browse files
jtydhr88Chenlei Hu
andauthored
build vue and primevue separately and generate importmap (#3473)
Co-authored-by: Chenlei Hu <[email protected]>
1 parent 2aef0a9 commit 7355209

File tree

2 files changed

+164
-3
lines changed

2 files changed

+164
-3
lines changed

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"src/**/*.vue",
3636
"src/types/**/*.d.ts",
3737
"tests-ui/**/*",
38-
"global.d.ts"
38+
"global.d.ts",
39+
"vite.config.mts"
3940
]
4041
}

vite.config.mts

Lines changed: 162 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import vue from '@vitejs/plugin-vue'
22
import dotenv from 'dotenv'
33
import path from 'path'
4+
import type { OutputOptions } from 'rollup'
45
import IconsResolver from 'unplugin-icons/resolver'
56
import Icons from 'unplugin-icons/vite'
67
import Components from 'unplugin-vue-components/vite'
7-
import { Plugin, defineConfig } from 'vite'
8+
import { HtmlTagDescriptor, Plugin, defineConfig } from 'vite'
89
import type { UserConfigExport } from 'vitest/config'
910

1011
dotenv.config()
@@ -26,6 +27,160 @@ function isLegacyFile(id: string): boolean {
2627
)
2728
}
2829

30+
/**
31+
* Vite plugin that adds an alias export for Vue's createBaseVNode as createElementVNode.
32+
*
33+
* This plugin addresses compatibility issues where some components or libraries
34+
* might be using the older createElementVNode function name instead of createBaseVNode.
35+
* It modifies the Vue vendor chunk during build to add the alias export.
36+
*
37+
* @returns {Plugin} A Vite plugin that modifies the Vue vendor chunk exports
38+
*/
39+
function addElementVnodeExportPlugin(): Plugin {
40+
return {
41+
name: 'add-element-vnode-export-plugin',
42+
43+
renderChunk(code, chunk, _options) {
44+
if (chunk.name.startsWith('vendor-vue')) {
45+
const exportRegex = /(export\s*\{)([^}]*)(\}\s*;?\s*)$/
46+
const match = code.match(exportRegex)
47+
48+
if (match) {
49+
const existingExports = match[2].trim()
50+
const exportsArray = existingExports
51+
.split(',')
52+
.map((e) => e.trim())
53+
.filter(Boolean)
54+
55+
const hasCreateBaseVNode = exportsArray.some((e) =>
56+
e.startsWith('createBaseVNode')
57+
)
58+
const hasCreateElementVNode = exportsArray.some((e) =>
59+
e.includes('createElementVNode')
60+
)
61+
62+
if (hasCreateBaseVNode && !hasCreateElementVNode) {
63+
const newExportStatement = `${match[1]} ${existingExports ? existingExports + ',' : ''} createBaseVNode as createElementVNode ${match[3]}`
64+
const newCode = code.replace(exportRegex, newExportStatement)
65+
66+
console.log(
67+
`[add-element-vnode-export-plugin] Added 'createBaseVNode as createElementVNode' export to vendor-vue chunk.`
68+
)
69+
70+
return { code: newCode, map: null }
71+
} else if (!hasCreateBaseVNode) {
72+
console.warn(
73+
`[add-element-vnode-export-plugin] Warning: 'createBaseVNode' not found in exports of vendor-vue chunk. Cannot add alias.`
74+
)
75+
}
76+
} else {
77+
console.warn(
78+
`[add-element-vnode-export-plugin] Warning: Could not find expected export block format in vendor-vue chunk.`
79+
)
80+
}
81+
}
82+
83+
return null
84+
}
85+
}
86+
}
87+
88+
/**
89+
* Vite plugin that generates an import map for vendor chunks.
90+
*
91+
* This plugin creates a browser-compatible import map that maps module specifiers
92+
* (like 'vue' or 'primevue') to their actual file locations in the build output.
93+
* This improves module loading in modern browsers and enables better caching.
94+
*
95+
* The plugin:
96+
* 1. Tracks vendor chunks during bundle generation
97+
* 2. Creates mappings between module names and their file paths
98+
* 3. Injects an import map script tag into the HTML head
99+
* 4. Configures manual chunk splitting for vendor libraries
100+
*
101+
* @param vendorLibraries - An array of vendor libraries to split into separate chunks
102+
* @returns {Plugin} A Vite plugin that generates and injects an import map
103+
*/
104+
function generateImportMapPlugin(
105+
vendorLibraries: { name: string; pattern: string }[]
106+
): Plugin {
107+
const importMapEntries: Record<string, string> = {}
108+
109+
return {
110+
name: 'generate-import-map-plugin',
111+
112+
// Configure manual chunks during the build process
113+
configResolved(config) {
114+
if (config.build) {
115+
// Ensure rollupOptions exists
116+
if (!config.build.rollupOptions) {
117+
config.build.rollupOptions = {}
118+
}
119+
120+
const outputOptions: OutputOptions = {
121+
manualChunks: (id: string) => {
122+
for (const lib of vendorLibraries) {
123+
if (id.includes(lib.pattern)) {
124+
return `vendor-${lib.name}`
125+
}
126+
}
127+
return null
128+
},
129+
// Disable minification of internal exports to preserve function names
130+
minifyInternalExports: false
131+
}
132+
config.build.rollupOptions.output = outputOptions
133+
}
134+
},
135+
136+
generateBundle(_options, bundle) {
137+
for (const fileName in bundle) {
138+
const chunk = bundle[fileName]
139+
if (chunk.type === 'chunk' && !chunk.isEntry) {
140+
// Find matching vendor library by chunk name
141+
const vendorLib = vendorLibraries.find(
142+
(lib) => chunk.name === `vendor-${lib.name}`
143+
)
144+
145+
if (vendorLib) {
146+
const relativePath = `./${chunk.fileName.replace(/\\/g, '/')}`
147+
importMapEntries[vendorLib.name] = relativePath
148+
149+
console.log(
150+
`[ImportMap Plugin] Found chunk: ${chunk.name} -> Mapped '${vendorLib.name}' to '${relativePath}'`
151+
)
152+
}
153+
}
154+
}
155+
},
156+
157+
transformIndexHtml(html) {
158+
if (Object.keys(importMapEntries).length === 0) {
159+
console.warn(
160+
'[ImportMap Plugin] No vendor chunks found to create import map.'
161+
)
162+
return html
163+
}
164+
165+
const importMap = {
166+
imports: importMapEntries
167+
}
168+
169+
const importMapTag: HtmlTagDescriptor = {
170+
tag: 'script',
171+
attrs: { type: 'importmap' },
172+
children: JSON.stringify(importMap, null, 2),
173+
injectTo: 'head'
174+
}
175+
176+
return {
177+
html,
178+
tags: [importMapTag]
179+
}
180+
}
181+
}
182+
}
183+
29184
function comfyAPIPlugin(): Plugin {
30185
return {
31186
name: 'comfy-api-plugin',
@@ -110,7 +265,7 @@ export default defineConfig({
110265
target: DEV_SERVER_COMFYUI_URL,
111266
// Return empty array for extensions API as these modules
112267
// are not on vite's dev server.
113-
bypass: (req, res, options) => {
268+
bypass: (req, res, _options) => {
114269
if (req.url === '/api/extensions') {
115270
res.end(JSON.stringify([]))
116271
}
@@ -137,6 +292,11 @@ export default defineConfig({
137292
plugins: [
138293
vue(),
139294
comfyAPIPlugin(),
295+
generateImportMapPlugin([
296+
{ name: 'vue', pattern: 'node_modules/vue/' },
297+
{ name: 'primevue', pattern: 'node_modules/primevue/' }
298+
]),
299+
addElementVnodeExportPlugin(),
140300

141301
Icons({
142302
compiler: 'vue3'

0 commit comments

Comments
 (0)