fix(electron): normalize main cjs bundle

This commit is contained in:
wiiiii123
2026-05-06 15:30:35 +07:00
parent b3862bde79
commit 8fc47d3e31
4 changed files with 193 additions and 21 deletions
+147
View File
@@ -0,0 +1,147 @@
import fs from "node:fs/promises";
import { pathToFileURL } from "node:url";
const mainBundleUrl = new URL("../dist-electron/main.cjs", import.meta.url);
function convertNamedImports(namedSpec) {
return namedSpec.replace(/\s+as\s+/g, ": ");
}
function convertImportLine(line) {
const importFromMatch = line.match(
/^([ \t]*)import\s+([^;\n]+?)\s+from\s+(["'][^"']+["'])\s*;?[ \t]*$/,
);
if (importFromMatch) {
const [, indent, rawSpec, moduleLiteral] = importFromMatch;
const spec = rawSpec.trim();
if (spec.startsWith("* as ")) {
return `${indent}const ${spec.slice(5).trim()} = require(${moduleLiteral});`;
}
if (spec.startsWith("{")) {
return `${indent}const ${convertNamedImports(spec)} = require(${moduleLiteral});`;
}
const commaIndex = spec.indexOf(",");
if (commaIndex >= 0) {
const defaultName = spec.slice(0, commaIndex).trim();
const namedSpec = spec.slice(commaIndex + 1).trim();
return [
`${indent}const ${defaultName} = require(${moduleLiteral});`,
`${indent}const ${convertNamedImports(namedSpec)} = ${defaultName};`,
].join("\n");
}
return `${indent}const ${spec} = require(${moduleLiteral});`;
}
const sideEffectImportMatch = line.match(
/^([ \t]*)import\s+(["'][^"']+["'])\s*;?[ \t]*$/,
);
if (sideEffectImportMatch) {
const [, indent, moduleLiteral] = sideEffectImportMatch;
return `${indent}require(${moduleLiteral});`;
}
if (/^[ \t]*export\s*\{\s*\}\s*;?[ \t]*$/.test(line)) {
return "";
}
return null;
}
export function normalizeElectronMainCjsSource(source) {
let changed = false;
const lineBreak = source.includes("\r\n") ? "\r\n" : "\n";
const lines = source.split(/\r?\n/);
const normalizedLines = [];
let firstUnprocessedLine = 0;
for (const line of lines) {
if (/^[ \t]*$/.test(line)) {
normalizedLines.push(line);
firstUnprocessedLine += 1;
continue;
}
const converted = convertImportLine(line);
if (converted === null) {
break;
}
normalizedLines.push(converted);
firstUnprocessedLine += 1;
changed = true;
}
if (!changed) {
return { source, changed };
}
const normalized = [
...normalizedLines,
...lines.slice(firstUnprocessedLine),
].join(lineBreak);
return { source: normalized, changed };
}
export function findElectronMainCjsEsmSyntax(source) {
const lines = source.split(/\r?\n/);
const matches = [];
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
if (/^[ \t]*$/.test(line)) {
continue;
}
const converted = convertImportLine(line);
if (converted === null) {
break;
}
matches.push({
line: index + 1,
text: line.trim(),
});
}
return matches;
}
export async function normalizeElectronMainCjs(bundleUrl = mainBundleUrl) {
let source;
try {
source = await fs.readFile(bundleUrl, "utf8");
} catch (error) {
throw new Error(`Unable to read dist-electron/main.cjs: ${error}`);
}
const normalized = normalizeElectronMainCjsSource(source);
if (normalized.changed) {
await fs.writeFile(bundleUrl, normalized.source, "utf8");
}
return normalized;
}
const isDirectRun =
process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
if (isDirectRun) {
const result = await normalizeElectronMainCjs();
const remainingImports = findElectronMainCjsEsmSyntax(result.source);
if (remainingImports.length > 0) {
const details = remainingImports
.map((match) => `line ${match.line}: ${match.text}`)
.join("\n");
throw new Error(`dist-electron/main.cjs still contains ESM import syntax:\n${details}`);
}
console.log(
result.changed
? "Electron main CJS normalized: dist-electron/main.cjs"
: "Electron main CJS already normalized: dist-electron/main.cjs",
);
}
+18 -16
View File
@@ -1,28 +1,30 @@
import fs from "node:fs/promises";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import {
findElectronMainCjsEsmSyntax,
normalizeElectronMainCjs,
} from "./normalize-electron-main-cjs.mjs";
const mainBundleUrl = new URL("../dist-electron/main.cjs", import.meta.url);
const mainBundlePath = mainBundleUrl.pathname;
const mainBundlePath = fileURLToPath(mainBundleUrl);
let source;
try {
source = await fs.readFile(mainBundleUrl, "utf8");
} catch (error) {
throw new Error(`Unable to read dist-electron/main.cjs: ${error}`);
}
const esmImportPattern = /^[ \t]*import\s+(?:(?:[\w*{][^\n;]*?)\s+from\s+)?["'][^"']+["'];?/gm;
const matches = [...source.matchAll(esmImportPattern)].map((match) => ({
line: source.slice(0, match.index).split(/\r?\n/).length,
text: match[0].trim(),
}));
const { source } = await normalizeElectronMainCjs(mainBundleUrl);
const matches = findElectronMainCjsEsmSyntax(source);
if (matches.length > 0) {
const details = matches.map((match) => `line ${match.line}: ${match.text}`).join("\n");
throw new Error(`dist-electron/main.cjs contains ESM import syntax:\n${details}`);
}
if (/\bimport\.meta\b/.test(source)) {
throw new Error("dist-electron/main.cjs contains import.meta syntax");
const checkResult = spawnSync(process.execPath, ["--check", mainBundlePath], {
encoding: "utf8",
});
if (checkResult.status !== 0) {
const details = [checkResult.stdout, checkResult.stderr].filter(Boolean).join("\n");
throw new Error(
`dist-electron/main.cjs does not parse as CommonJS:${details ? `\n${details}` : ""}`,
);
}
console.log(`Electron main CJS smoke passed: ${mainBundlePath}`);