diff --git a/build/plugins/addElementVnodeExportPlugin.ts b/build/plugins/addElementVnodeExportPlugin.ts deleted file mode 100644 index 1266d13e2..000000000 --- a/build/plugins/addElementVnodeExportPlugin.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Plugin } from 'vite' - -/** - * Vite plugin that adds an alias export for Vue's createBaseVNode as createElementVNode. - * - * This plugin addresses compatibility issues where some components or libraries - * might be using the older createElementVNode function name instead of createBaseVNode. - * It modifies the Vue vendor chunk during build to add the alias export. - * - * @returns {Plugin} A Vite plugin that modifies the Vue vendor chunk exports - */ -export function addElementVnodeExportPlugin(): Plugin { - return { - name: 'add-element-vnode-export-plugin', - - renderChunk(code, chunk, _options) { - if (chunk.name.startsWith('vendor-vue')) { - const exportRegex = /(export\s*\{)([^}]*)(\}\s*;?\s*)$/ - const match = code.match(exportRegex) - - if (match) { - const existingExports = match[2].trim() - const exportsArray = existingExports - .split(',') - .map((e) => e.trim()) - .filter(Boolean) - - const hasCreateBaseVNode = exportsArray.some((e) => - e.startsWith('createBaseVNode') - ) - const hasCreateElementVNode = exportsArray.some((e) => - e.includes('createElementVNode') - ) - - if (hasCreateBaseVNode && !hasCreateElementVNode) { - const newExportStatement = `${match[1]} ${existingExports ? existingExports + ',' : ''} createBaseVNode as createElementVNode ${match[3]}` - const newCode = code.replace(exportRegex, newExportStatement) - - console.log( - `[add-element-vnode-export-plugin] Added 'createBaseVNode as createElementVNode' export to vendor-vue chunk.` - ) - - return { code: newCode, map: null } - } else if (!hasCreateBaseVNode) { - console.warn( - `[add-element-vnode-export-plugin] Warning: 'createBaseVNode' not found in exports of vendor-vue chunk. Cannot add alias.` - ) - } - } else { - console.warn( - `[add-element-vnode-export-plugin] Warning: Could not find expected export block format in vendor-vue chunk.` - ) - } - } - - return null - } - } -} diff --git a/build/plugins/generateImportMapPlugin.ts b/build/plugins/generateImportMapPlugin.ts index c6661a811..80ccb6c9f 100644 --- a/build/plugins/generateImportMapPlugin.ts +++ b/build/plugins/generateImportMapPlugin.ts @@ -1,9 +1,24 @@ -import type { OutputOptions } from 'rollup' -import { HtmlTagDescriptor, Plugin } from 'vite' +import glob from 'fast-glob' +import fs from 'fs-extra' +import { dirname, join } from 'node:path' +import { HtmlTagDescriptor, Plugin, normalizePath } from 'vite' -interface VendorLibrary { +interface ImportMapSource { name: string - pattern: RegExp + pattern: string | RegExp + entry: string + recursiveDependence?: boolean + override?: Record> +} + +const parseDeps = (root: string, pkg: string) => { + const pkgPath = join(root, 'node_modules', pkg, 'package.json') + if (fs.existsSync(pkgPath)) { + const content = fs.readFileSync(pkgPath, 'utf-8') + const pkg = JSON.parse(content) + return Object.keys(pkg.dependencies || {}) + } + return [] } /** @@ -23,53 +38,89 @@ interface VendorLibrary { * @returns {Plugin} A Vite plugin that generates and injects an import map */ export function generateImportMapPlugin( - vendorLibraries: VendorLibrary[] + importMapSources: ImportMapSource[] ): Plugin { const importMapEntries: Record = {} + const resolvedImportMapSources: Map = new Map() + const assetDir = 'assets/lib' + let root: string return { name: 'generate-import-map-plugin', // Configure manual chunks during the build process configResolved(config) { + root = config.root + if (config.build) { // Ensure rollupOptions exists if (!config.build.rollupOptions) { config.build.rollupOptions = {} } - const outputOptions: OutputOptions = { - manualChunks: (id: string) => { - for (const lib of vendorLibraries) { - if (lib.pattern.test(id)) { - return `vendor-${lib.name}` - } + for (const source of importMapSources) { + resolvedImportMapSources.set(source.name, source) + if (source.recursiveDependence) { + const deps = parseDeps(root, source.name) + + while (deps.length) { + const dep = deps.shift()! + const depSource = Object.assign({}, source, { + name: dep, + pattern: dep, + ...source.override?.[dep] + }) + resolvedImportMapSources.set(depSource.name, depSource) + + const _deps = parseDeps(root, depSource.name) + deps.unshift(..._deps) } - return null - }, - // Disable minification of internal exports to preserve function names - minifyInternalExports: false + } } - config.build.rollupOptions.output = outputOptions + + const external: (string | RegExp)[] = [] + for (const [, source] of resolvedImportMapSources) { + external.push(source.pattern) + } + config.build.rollupOptions.external = external } }, - generateBundle(_options, bundle) { - for (const fileName in bundle) { - const chunk = bundle[fileName] - if (chunk.type === 'chunk' && !chunk.isEntry) { - // Find matching vendor library by chunk name - const vendorLib = vendorLibraries.find( - (lib) => chunk.name === `vendor-${lib.name}` - ) + generateBundle(_options) { + for (const [, source] of resolvedImportMapSources) { + if (source.entry) { + const moduleFile = join(source.name, source.entry) + const sourceFile = join(root, 'node_modules', moduleFile) + const targetFile = join(root, 'dist', assetDir, moduleFile) - if (vendorLib) { - const relativePath = `./${chunk.fileName.replace(/\\/g, '/')}` - importMapEntries[vendorLib.name] = relativePath + importMapEntries[source.name] = + './' + normalizePath(join(assetDir, moduleFile)) - console.log( - `[ImportMap Plugin] Found chunk: ${chunk.name} -> Mapped '${vendorLib.name}' to '${relativePath}'` - ) + const targetDir = dirname(targetFile) + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }) + } + fs.copyFileSync(sourceFile, targetFile) + } + + if (source.recursiveDependence) { + const files = glob.sync(['**/*.{js,mjs}'], { + cwd: join(root, 'node_modules', source.name) + }) + + for (const file of files) { + const moduleFile = join(source.name, file) + const sourceFile = join(root, 'node_modules', moduleFile) + const targetFile = join(root, 'dist', assetDir, moduleFile) + + importMapEntries[normalizePath(join(source.name, dirname(file)))] = + './' + normalizePath(join(assetDir, moduleFile)) + + const targetDir = dirname(targetFile) + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }) + } + fs.copyFileSync(sourceFile, targetFile) } } } diff --git a/build/plugins/index.ts b/build/plugins/index.ts index c67473f7a..f8c2d695c 100644 --- a/build/plugins/index.ts +++ b/build/plugins/index.ts @@ -1,3 +1,2 @@ -export { addElementVnodeExportPlugin } from './addElementVnodeExportPlugin' export { comfyAPIPlugin } from './comfyAPIPlugin' export { generateImportMapPlugin } from './generateImportMapPlugin' diff --git a/vite.config.mts b/vite.config.mts index 2fb2864b3..810dd8baa 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -8,11 +8,7 @@ import { createHtmlPlugin } from 'vite-plugin-html' import vueDevTools from 'vite-plugin-vue-devtools' import type { UserConfigExport } from 'vitest/config' -import { - addElementVnodeExportPlugin, - comfyAPIPlugin, - generateImportMapPlugin -} from './build/plugins' +import { comfyAPIPlugin, generateImportMapPlugin } from './build/plugins' dotenv.config() @@ -77,11 +73,40 @@ export default defineConfig({ : [vue()]), comfyAPIPlugin(IS_DEV), generateImportMapPlugin([ - { name: 'vue', pattern: /[\\/]node_modules[\\/]vue[\\/]/ }, - { name: 'primevue', pattern: /[\\/]node_modules[\\/]primevue[\\/]/ }, - { name: 'vue-i18n', pattern: /[\\/]node_modules[\\/]vue-i18n[\\/]/ } + { + name: 'vue', + pattern: 'vue', + entry: './dist/vue.esm-browser.prod.js' + }, + { + name: 'vue-i18n', + pattern: 'vue-i18n', + entry: './dist/vue-i18n.esm-browser.prod.js' + }, + { + name: 'primevue', + pattern: /^primevue\/?.*/, + entry: './index.mjs', + recursiveDependence: true + }, + { + name: '@primevue/themes', + pattern: /^@primevue\/themes\/?.*/, + entry: './index.mjs', + recursiveDependence: true + }, + { + name: '@primevue/forms', + pattern: /^@primevue\/forms\/?.*/, + entry: './index.mjs', + recursiveDependence: true, + override: { + '@primeuix/forms': { + entry: '' + } + } + } ]), - addElementVnodeExportPlugin(), Icons({ compiler: 'vue3'