Cannot find module package.json with Electron + Strapi v4

I am experiencing a problem creating a boilerplate with Electron and Strapi CMS, with the Strapi v4 only.
A similar boilerplate already exist for Strapi v3 at the following url https://github.com/afdallismen/electron-strapi-boilerplate and it works fine.

So I made some changes and I made a similar one with Strapi v4: https://github.com/AsoStrife/Strapi-V4-Electron-Boilerplate

Here my two main files:

package.json:

{
  "name": "Strapi-V4-Electron-Boilerplate",
  "version": "1.0.0",
  "description": "A minimal Electron application with Strapi v4",
  "main": "main.js",
  "scripts": {
    "strapi-start": "strapi start",
    "strapi-develop": "strapi develop",
    "strapi-build": "strapi build",
    "electron": "electron .",
    "electron-build": "electron-builder",
    "build": "strapi build && electron-builder"
  },
  "keywords": [
    "Electron",
    "Strapi",
    "Boilerplate",
    "CMS"
  ],
  "author": "Andrea Corriga",
  "license": "MIT",
  "dependencies": {
    "@strapi/plugin-i18n": "4.3.0",
    "@strapi/plugin-users-permissions": "4.3.0",
    "@strapi/strapi": "4.3.2",
    "better-sqlite3": "7.4.6",
    "electron-is-dev": "^2.0.0",
    "electron-is-packaged": "^1.0.2"
  },
  "devDependencies": {
    "electron": "^19.0.8",
    "electron-builder": "^22.14.5"
  },
  "strapi": {
    "uuid": "23376639-3e73-4487-812b-a57332ff6859"
  },
  "engines": {
    "node": ">=12.x.x <=16.x.x",
    "npm": ">=6.0.0"
  }
}

and main.js

const { app, BrowserWindow } = require('electron')
const path = require('path')
const Strapi = require('@strapi/strapi')
const isPackaged = require('electron-is-packaged')
const strapi = Strapi()

if (isPackaged) {
    const fs = require('fs')
    const path = require('path')
  
    const appPath = path.join(app.getPath('home'), app.getName())
  
    const requiredFolderPaths = {
        database: path.join(appPath, 'database'),
        public: path.join(appPath, 'public'),
        uploads: path.join(appPath, 'public', 'uploads'),
    }
  
    Object.values(requiredFolderPaths).forEach((folderPath) => {
        if (!fs.existsSync(folderPath)) {
            fs.mkdirSync(folderPath, { recursive: true });
        }
    })
}

process.env.NODE_ENV = isPackaged ? 'production' : 'development';
process.env.BROWSER = 'none';

function createWindow () {
    const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            preload: path.join(__dirname, 'preload.js')
        }
    })

    win.maximize()
    win.webContents.openDevTools()
    strapi
        .start()
        .then(() => {
            win.loadURL('http://localhost:1337/admin');
            //win.loadFile('index.html')
        })
        .catch((err) => {
            console.error(err)
        })

        win.on('closed', () => {
            app.quit();
        })
}

app.whenReady().then(() => {
    createWindow()

    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow()
        }
    })
})

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

So where’s the problem?
If I run npm run electron it works perfectly, Electron opens correctly, with Strapi UI, a working DB and API accessible by localhost.
But if I run npm run build or npm run electron-build I generate a Strapi-V4-Electron-Boilerplate.exe but it doesn’t work.

It gives me this error and I don’t know how to debug. All dependencies are correct (I suppose, since with the npm run electron command everything works.

enter image description here

Has anyone experienced similar problems? Do you have any idea how to debug this problem to figure out what is not working?

I’ve been having a similar problem. Been working on it for days. Finally found a solution.
Thought I share:

The problem seems to be (at least in my case) that strapi (and some dependecies) rely on process.cwd(). Packaging the application and starting it changed the current working directory since node has been invoked from a different source.

Usually one would fix that by changing process.cwd() to __dirname when it comes to setting paths.
But since it’s used in different node_modules that doesn’t seem to be an option.

What you can do though is setting the current working directory programmatically:

process.chdir(__dirname);

(you might wanna try...catch that, at least for debugging)

That did the trick for me!

But doing that raised another problem: Strapi wouldn’t recognice any Environmental Variables set in ./.env anymore !

Logging process.env, it was clear that not only the cwd changed but the environment as well.
So… sadly no variables from the .env file here.

I tried copiing the .env file to the same location the executable was, but that didn’t seem to work.

So I thought maybe I could read the .env file line by line and (re)set the variables programmatically.

The .env file should be easily accessible via path.join(process.cwd(), '.env') since cwd has been (re)set.

I used an example of the official node documentation found here.

FULL CODE: (main.js)

const { app, BrowserWindow, dialog } = require('electron')
const path = require("path");
const fs = require('fs');
const readline = require('readline');
const strapi = require('@strapi/strapi');

function createWindow() {

    const win = new BrowserWindow({
        maximizable: true,
        title: "Your title",
        webPreferences: {
            nodeIntegration: true
        },
        show: false
    })
   
    try {
      process.chdir(__dirname);
    }
    catch (err) {
      dialog.showErrorBox('CWD Error', err);
      app.quit();
    }

    prepareAndStart();

    async function prepareAndStart(){

      const rl = readline.createInterface({
        input: fs.createReadStream(path.join(process.cwd(), '.env')),
        crlfDelay: Infinity
      });

      for await (const line of rl) {
        let split = line.split('=');
        let key = split[0];
        let val = split[1];
        process.env[key] = val;
      }

      // console.log(process.env); // debug

      strapi({
        dir: __dirname + '/'
      }).start().then(() => {

          win.loadURL('http://localhost:1337/admin/');

          win.once('ready-to-show', () => {
            win.show()
          } );

      }).catch((err) => {
          dialog.showErrorBox('Startup Error', err);
          app.quit();
      });

    }

    win.on('closed', () => {
        app.quit();
    });

//...

}

I don’t know if this is a safe method or has any unknown consequences (regarding strapi), also it doesn’t make use of electron-is-packaged but for me it did the trick and … maybe it helps anyway.

Or it might help somebody else, landing here in an endless search for a solution ( like I was :wink: ).

EDIT:

  • platform: darwin
  • strapi@4.3.8
  • electron@19.0.16