Cannot find a module in both the development and production of vite-electron-builder

I got the error related to the path of the main process of vite-electron-builder. I thought I set the wrong path, but the problem was due to both Rollup and mixed modules including ES and CommonJS.

Error: Cannot find module './foo.js' in development

First, in development, the error "Cannot find module './foo.js'" arose. The source code of packages/main/src/index.js was as follows.

import { app, BrowserWindow, Menu, shell, ipcMain } from 'electron';
import * as path from 'path';
import { pathToFileURL } from 'url';
import { autoUpdater } from 'electron-updater';
const foo = require('./foo.js'); 

foo.js was in the same directory as index.js. The relative path wasn't wrong.

root/
|-- packages/
|   |-- main/
|		|--dist/
|		|	`--index.cjs
|		|
|		|--src/
|			|--index.js
|			`--foo.js

After trial and error, I fixed it by setting the path to ../src/foo.js. Because the output file path by Vite was packages/main/dist/index.cjs, index.cjs resolved the path to foo.js.

Error: Cannot find module '../src/foo.js' in production

Second, in production, the same error arose again. In development, the relative path from packages/main/dist/index.cjs to packages/main/src/foo.js was correct, but in production, the directory packages/main/src didn't exist so the same relative path was wrong.

I set the path back to ./foo.js in production. But the error arose yet... Why?

These errors seemed to be not a simple path-related problem.

Cause: Rollup didn't support importing CommonJS modules by default

After reading the official documents and the source code of Vite, the root cause turned out that Rollup didn't support importing CommonJS modules by default. build function of Vite imported rollup and then rollup bundled the modules in the target directory.

// write or generate files with rollup
const { rollup } = await import('rollup')
bundle = await rollup(rollupOptions)

Rollup supports compiling ES modules back down to existing supported formats such as CommonJS modules. But by default Rollup doesn't support importing CommonJS modules without a plugin.

Importing CommonJS

Rollup can import existing CommonJS modules through a plugin.

Therefore, require in the entry file packages/main/src/index.js didn't work and the output file packages/main/dist/index.cjs failed to bundle foo.js.

This root cause also meant the original path ./foo.js was correct. The change from require("./foo.js") to require("../src/foo.js") was a tentative solution only for development. I had to make Rollup bundle foo.js with another permanent solution for production.

Solution: Rewrite CommonJS modules to ES modules or Add @rollup/plugin-commonjs

There are two solutions for the root cause that Rollup doesn't support importing CommonJS modules by default.

  1. Rewrite CommonJS modules to ES modules
  2. Add @rollup/plugin-commonjs to vite.config.js

The former is to replace module.exports with export and replace require with import. The latter is as follows.

import { commonjs } from "@rollup/plugin-commonjs"

const config = {

    rollupOptions: {
      output: {
        entryFileNames: '[name].cjs',
      },
     plugins: [ commonjs() ] 
    },
	
}

The former solution only supports ES modules. The latter solution allows mixed ES and CommonJS modules.

I rewrote foo.js and converted it to an ES module. The solution fixed the problems of both development and production.

I'm not good at path-related problems. They often arise and waste much time in resolving paths. In this case, the tentative solution for development is quite different from the permanent solution for production.

Path-related problems will never end...