mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
59
build/plugins/addElementVnodeExportPlugin.ts
Normal file
59
build/plugins/addElementVnodeExportPlugin.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,9 @@
|
|||||||
import glob from 'fast-glob'
|
import type { OutputOptions } from 'rollup'
|
||||||
import fs from 'fs-extra'
|
import { HtmlTagDescriptor, Plugin } from 'vite'
|
||||||
import { dirname, join } from 'node:path'
|
|
||||||
import { HtmlTagDescriptor, Plugin, normalizePath } from 'vite'
|
|
||||||
|
|
||||||
interface ImportMapSource {
|
interface VendorLibrary {
|
||||||
name: string
|
name: string
|
||||||
pattern: string | RegExp
|
pattern: RegExp
|
||||||
entry: string
|
|
||||||
recursiveDependence?: boolean
|
|
||||||
override?: Record<string, Partial<ImportMapSource>>
|
|
||||||
}
|
|
||||||
|
|
||||||
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 []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,89 +23,53 @@ const parseDeps = (root: string, pkg: string) => {
|
|||||||
* @returns {Plugin} A Vite plugin that generates and injects an import map
|
* @returns {Plugin} A Vite plugin that generates and injects an import map
|
||||||
*/
|
*/
|
||||||
export function generateImportMapPlugin(
|
export function generateImportMapPlugin(
|
||||||
importMapSources: ImportMapSource[]
|
vendorLibraries: VendorLibrary[]
|
||||||
): Plugin {
|
): Plugin {
|
||||||
const importMapEntries: Record<string, string> = {}
|
const importMapEntries: Record<string, string> = {}
|
||||||
const resolvedImportMapSources: Map<string, ImportMapSource> = new Map()
|
|
||||||
const assetDir = 'assets/lib'
|
|
||||||
let root: string
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'generate-import-map-plugin',
|
name: 'generate-import-map-plugin',
|
||||||
|
|
||||||
// Configure manual chunks during the build process
|
// Configure manual chunks during the build process
|
||||||
configResolved(config) {
|
configResolved(config) {
|
||||||
root = config.root
|
|
||||||
|
|
||||||
if (config.build) {
|
if (config.build) {
|
||||||
// Ensure rollupOptions exists
|
// Ensure rollupOptions exists
|
||||||
if (!config.build.rollupOptions) {
|
if (!config.build.rollupOptions) {
|
||||||
config.build.rollupOptions = {}
|
config.build.rollupOptions = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const source of importMapSources) {
|
const outputOptions: OutputOptions = {
|
||||||
resolvedImportMapSources.set(source.name, source)
|
manualChunks: (id: string) => {
|
||||||
if (source.recursiveDependence) {
|
for (const lib of vendorLibraries) {
|
||||||
const deps = parseDeps(root, source.name)
|
if (lib.pattern.test(id)) {
|
||||||
|
return `vendor-${lib.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) {
|
generateBundle(_options, bundle) {
|
||||||
for (const [, source] of resolvedImportMapSources) {
|
for (const fileName in bundle) {
|
||||||
if (source.entry) {
|
const chunk = bundle[fileName]
|
||||||
const moduleFile = join(source.name, source.entry)
|
if (chunk.type === 'chunk' && !chunk.isEntry) {
|
||||||
const sourceFile = join(root, 'node_modules', moduleFile)
|
// Find matching vendor library by chunk name
|
||||||
const targetFile = join(root, 'dist', assetDir, moduleFile)
|
const vendorLib = vendorLibraries.find(
|
||||||
|
(lib) => chunk.name === `vendor-${lib.name}`
|
||||||
|
)
|
||||||
|
|
||||||
importMapEntries[source.name] =
|
if (vendorLib) {
|
||||||
'./' + normalizePath(join(assetDir, moduleFile))
|
const relativePath = `./${chunk.fileName.replace(/\\/g, '/')}`
|
||||||
|
importMapEntries[vendorLib.name] = relativePath
|
||||||
|
|
||||||
const targetDir = dirname(targetFile)
|
console.log(
|
||||||
if (!fs.existsSync(targetDir)) {
|
`[ImportMap Plugin] Found chunk: ${chunk.name} -> Mapped '${vendorLib.name}' to '${relativePath}'`
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
|
export { addElementVnodeExportPlugin } from './addElementVnodeExportPlugin'
|
||||||
export { comfyAPIPlugin } from './comfyAPIPlugin'
|
export { comfyAPIPlugin } from './comfyAPIPlugin'
|
||||||
export { generateImportMapPlugin } from './generateImportMapPlugin'
|
export { generateImportMapPlugin } from './generateImportMapPlugin'
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import { createHtmlPlugin } from 'vite-plugin-html'
|
|||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
import type { UserConfigExport } from 'vitest/config'
|
import type { UserConfigExport } from 'vitest/config'
|
||||||
|
|
||||||
import { comfyAPIPlugin, generateImportMapPlugin } from './build/plugins'
|
import {
|
||||||
|
addElementVnodeExportPlugin,
|
||||||
|
comfyAPIPlugin,
|
||||||
|
generateImportMapPlugin
|
||||||
|
} from './build/plugins'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
@@ -85,40 +89,11 @@ export default defineConfig({
|
|||||||
: [vue()]),
|
: [vue()]),
|
||||||
comfyAPIPlugin(IS_DEV),
|
comfyAPIPlugin(IS_DEV),
|
||||||
generateImportMapPlugin([
|
generateImportMapPlugin([
|
||||||
{
|
{ name: 'vue', pattern: /[\\/]node_modules[\\/]vue[\\/]/ },
|
||||||
name: 'vue',
|
{ name: 'primevue', pattern: /[\\/]node_modules[\\/]primevue[\\/]/ },
|
||||||
pattern: 'vue',
|
{ name: 'vue-i18n', pattern: /[\\/]node_modules[\\/]vue-i18n[\\/]/ }
|
||||||
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({
|
Icons({
|
||||||
compiler: 'vue3'
|
compiler: 'vue3'
|
||||||
|
|||||||
Reference in New Issue
Block a user