Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,74 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer";
import svgr from "vite-plugin-svgr";
import tsconfigPaths from "vite-tsconfig-paths";

// from our electron build
const CHROME = "chrome140";
const NODE = "node22";

/**
* Rewrites all ESM import forms from "electron" so that every access goes
* through the CJS default export (module.exports) at runtime. This works
* around Node.js v24's stricter CJS-to-ESM static analysis which cannot
* detect lazy-getter exports (BaseWindow, BrowserWindow, etc.) from
* Electron's CJS module.
*
* Three patterns are handled:
*
* import * as electron from "electron"
* → import electron from "electron"
* (namespace → default; electron.X now hits module.exports.X directly)
*
* import electron__default, { app, net as net$1 } from "electron"
* → import electron__default from "electron"
* const { app, net: net$1 } = electron__default
*
* import { app, BaseWindow } from "electron"
* → import __electron__ from "electron"
* const { app, BaseWindow } = __electron__
*/
function electronEsmInteropPlugin() {
const importAsToDestructure = (namedImports: string): string =>
namedImports
.split(",")
.map((s) => s.trim())
.filter(Boolean)
.map((s) => s.replace(/^([\w$]+)\s+as\s+([\w$]+)$/, "$1: $2"))
.join(", ");

return {
name: "electron-esm-interop",
renderChunk(code: string) {
let result = code;
let transformed = false;

result = result.replace(
/import\s+([\w$]+)\s*,\s*\{([^}]+)\}\s*from\s*["']electron["']\s*;?/g,
(_match: string, defaultName: string, named: string) => {
transformed = true;
return `import ${defaultName} from "electron";\nconst { ${importAsToDestructure(named)} } = ${defaultName};`;
}
);

result = result.replace(
/import\s*\{([^}]+)\}\s*from\s*["']electron["']\s*;?/g,
(_match: string, named: string) => {
transformed = true;
return `import __electron__ from "electron";\nconst { ${importAsToDestructure(named)} } = __electron__;`;
}
Comment thread
keshav-k3 marked this conversation as resolved.
);

result = result.replace(
/import\s*\*\s*as\s+([\w$]+)\s+from\s*["']electron["']\s*;?/g,
(_match: string, nsName: string) => {
transformed = true;
return `import ${nsName} from "electron";`;
}
);

return transformed ? { code: result, map: null } : null;
},
};
}

// for debugging
// target is like -- path.resolve(__dirname, "frontend/app/workspace/workspace-layout-model.ts");
function whoImportsTarget(target: string) {
Expand Down Expand Up @@ -85,7 +149,7 @@ export default defineConfig({
outDir: "dist/main",
externalizeDeps: false,
},
plugins: [tsconfigPaths()],
plugins: [tsconfigPaths(), electronEsmInteropPlugin()],
resolve: {
alias: {
"@": "frontend",
Expand Down