Merge 533d54ad1e43616e02707e88a9bf40680183c2d3 into 608461c04cf74cbf27b47f9916e10e225cbfe8f1

This commit is contained in:
dawn 2026-08-22 09:37:40 +08:00 committed by GitHub
commit 94ddf0c622
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -6,29 +6,14 @@
import MagicString from 'magic-string'
import type { SourceMapInput } from 'rollup'
import type { Plugin } from 'vite'
import type { ESTree, Plugin } from 'vite'
import { supportImportMetaPaths } from '../electron'
const CJSyntaxRe = /__filename|__dirname|require\(|require\.resolve\(/
const CJSShim_normal = `
// -- CommonJS Shims --
import __cjs_url__ from 'node:url';
import __cjs_path__ from 'node:path';
import __cjs_mod__ from 'node:module';
const __filename = __cjs_url__.fileURLToPath(import.meta.url);
const __dirname = __cjs_path__.dirname(__filename);
const require = __cjs_mod__.createRequire(import.meta.url);
`
const CJSShim_node_20_11 = `
// -- CommonJS Shims --
import __cjs_mod__ from 'node:module';
const __filename = import.meta.filename;
const __dirname = import.meta.dirname;
const require = __cjs_mod__.createRequire(import.meta.url);
`
const CJSShimMarker = '// -- CommonJS Shims --'
const CJSFilenameRe = /__filename/
const CJSDirnameRe = /__dirname/
const CJSRequireRe = /require\(|require\.resolve\(/
const ESMStaticImportRe =
/(?<=\s|^|;)import\s*([\s"']*(?<imports>[\p{L}\p{M}\w\t\n\r $*,/{}@.]+)from\s*)?["']\s*(?<specifier>(?<="\s*)[^"]*[^\s"](?=\s*")|(?<='\s*)[^']*[^\s'](?=\s*'))\s*["'][\s;]*/gmu
@ -45,19 +30,98 @@ function findStaticImports(code: string): StaticImport[] {
return matches
}
export default function esmShimPlugin(): Plugin {
const CJSShim = supportImportMetaPaths() ? CJSShim_node_20_11 : CJSShim_normal
function addBindingIdentifiers(pattern: ESTree.BindingPattern, bindings: Set<string>): void {
if (pattern.type === 'Identifier') {
bindings.add(pattern.name)
} else if (pattern.type === 'ObjectPattern') {
for (const property of pattern.properties) {
addBindingIdentifiers(property.type === 'RestElement' ? property.argument : property.value, bindings)
}
} else if (pattern.type === 'ArrayPattern') {
for (const element of pattern.elements) {
if (element) addBindingIdentifiers(element.type === 'RestElement' ? element.argument : element, bindings)
}
} else {
addBindingIdentifiers(pattern.left, bindings)
}
}
function getUniqueBindingName(base: string, bindings: Set<string>): string {
let name = base
let index = 1
while (bindings.has(name)) name = `${base}${index++}`
bindings.add(name)
return name
}
function createCJSShim(code: string, bindings: Set<string>): string | null {
const needsFilename = !bindings.has('__filename') && CJSFilenameRe.test(code)
const needsDirname = !bindings.has('__dirname') && CJSDirnameRe.test(code)
const needsRequire = !bindings.has('require') && CJSRequireRe.test(code)
if (!needsFilename && !needsDirname && !needsRequire) {
return null
}
const imports: string[] = []
const declarations: string[] = []
if (supportImportMetaPaths()) {
if (needsFilename) declarations.push('const __filename = import.meta.filename;')
if (needsDirname) declarations.push('const __dirname = import.meta.dirname;')
} else if (needsFilename || needsDirname) {
const urlBinding = getUniqueBindingName('__cjs_url__', bindings)
imports.push(`import ${urlBinding} from 'node:url';`)
if (needsFilename) declarations.push(`const __filename = ${urlBinding}.fileURLToPath(import.meta.url);`)
if (needsDirname) {
const pathBinding = getUniqueBindingName('__cjs_path__', bindings)
imports.push(`import ${pathBinding} from 'node:path';`)
declarations.push(`const __dirname = ${pathBinding}.dirname(${urlBinding}.fileURLToPath(import.meta.url));`)
}
}
if (needsRequire) {
const moduleBinding = getUniqueBindingName('__cjs_mod__', bindings)
imports.push(`import ${moduleBinding} from 'node:module';`)
declarations.push(`const require = ${moduleBinding}.createRequire(import.meta.url);`)
}
return `\n${CJSShimMarker}\n${imports.join('\n')}\n${declarations.join('\n')}\n`
}
export default function esmShimPlugin(): Plugin {
return {
name: 'vite:esm-shim',
apply: 'build',
enforce: 'post',
renderChunk(code, _chunk, { format, sourcemap }): { code: string; map?: SourceMapInput } | null {
if (format === 'es') {
if (code.includes(CJSShim) || !CJSyntaxRe.test(code)) {
return null
const bindings = new Set<string>()
for (const node of this.parse(code).body) {
if (node.type === 'ImportDeclaration') {
for (const specifier of node.specifiers) {
bindings.add(specifier.local.name)
}
continue
}
const declaration =
node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration' ? node.declaration : node
if (declaration?.type === 'VariableDeclaration') {
for (const declarator of declaration.declarations) {
addBindingIdentifiers(declarator.id, bindings)
}
} else if (
(declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') &&
declaration.id
) {
bindings.add(declaration.id.name)
}
}
const CJSShim = createCJSShim(code, bindings)
if (!CJSShim) return null
const lastESMImport = findStaticImports(code).pop()
const indexToAppend = lastESMImport ? lastESMImport.end : 0
const s = new MagicString(code)