7

I'm trying to launch my NodeJS API on a production server using Phusion Passenger. I used the last ECM syntax to import/export my modules. However when trying to reach my api, Passenger returns this error message :

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/***/api/server.mjs
    at Module.load (internal/modules/cjs/loader.js:948:11)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at Module.require (/opt/passenger/src/helper-scripts/node-loader.js:80:25)
    at require (internal/modules/cjs/helpers.js:92:18)
    at loadApplication (/opt/passenger/src/helper-scripts/node-loader.js:243:2)
    at setupEnvironment (/opt/passenger/src/helper-scripts/node-loader.js:214:2)
    at Object.<anonymous> (/opt/passenger/src/helper-scripts/node-loader.js:133:1)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) {
  code: 'ERR_REQUIRE_ESM'

I have the "type": "module" line in my package.json and I tried to change the extension from .js to .mjs without effect.

I noticed that passenger uses CJS with "require" syntax to load my server.js as we can see here: https://github.com/phusion/passenger/blob/stable-6.0/src/helper-scripts/node-loader.js.

function loadApplication() {
    var appRoot = PhusionPassenger.options.app_root || process.cwd();
    var startupFile = PhusionPassenger.options.startup_file || (appRoot + '/' + 'app.js');
    require(startupFile);
}

So my question is: Is there any way not to change all my code from ECM to CJS to match with Passenger?

Deltrak
  • 71
  • 1
  • 4

3 Answers3

9

An update to the code above - this would not work for me unless the dynamic import is called asynchronously:

Create an entry.cjs file and use an async/await to dynamically import your applications entry file that you would normally set in plesk:

// entry.cjs
async function loadApp() {
    const { app } = await import("./app.js"); // this is your normal entry file - (index.js, main.js, app.mjs etc.)
}
loadApp()

Then in Plesk set your application entry file to ./entry.cjs

LessBorders
  • 151
  • 1
  • 3
8

There's a way: Considering all your files have .mjs extension, you just need to create a .js to be the entry file for Passenger, and in this file (which is in CommonJS format) you can dynamically import the ESM one.

So, instead of running node server.mjs through Passenger, create a entry.js with the following content:

import("./server.mjs");

And then call node entry.js through Passenger.

Note: you can use any other name for the entry.js file.

Felladrin
  • 106
  • 1
  • 5
-1

Just in case you are working with typescript (that's my case), I managed to get rid of this error by compiling my code to commonJs module in two simple steps

  1. change "type": "module" in your packeage.json file to "type": "commonjs"
  2. change your tsconfig.json module to "module": "CommonJS"

Rebuild and now it should work :)

TOPKAT
  • 6,667
  • 2
  • 44
  • 72