Home > front end >  Unable to start Node.js application that uses ES module
Unable to start Node.js application that uses ES module

Time:01-28

I have a simple Node.js 16.x application. It uses ES modules, so I have "type": "module" in package.json. Everything works fine whenever I use npm scripts.

Now I'm trying to deploy it using Docker and I don't need the npm scripts anymore, so I'm starting the application directly using the node binary, in the same way I declared it within package.json: node --require dotenv/config main.js ...but that doesn't work, it fails with a typical error message around ES modules and such:

(node:9) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/opt/application/main.js:1
import { app } from "./application.js";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1031:15)
    at Module._compile (node:internal/modules/cjs/loader:1065:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

I've also tried a combination of settings around this command: node --input-type=module --experimental-modules --require dotenv/config main.js by following this guide, but still, I'm getting the same error message.

I think I'm missing something trivial here, so my question is: how can I start this application without using package.json (because I won't have it anymore at this point) or renaming .js files to .mjs, by using only the node binary?


This is how the application looks like in the filesystem (if it makes any difference):

/opt/application # ls -ltr 
total 72
-rw-r--r--    1 root     root          2158 Oct 10 13:47 application.js
drwxr-xr-x    2 root     root          4096 Jan 27 14:57 support
-rw-r--r--    1 root     root           318 Jan 27 14:57 main.js
drwxr-xr-x    2 root     root          4096 Jan 27 14:57 handler
drwxr-xr-x    2 root     root          4096 Jan 27 14:57 config
-rw-r--r--    1 root     root          1448 Jan 27 14:58 knexfile.js
drwxr-xr-x    2 root     root          4096 Jan 27 16:57 service
drwxr-xr-x    2 root     root          4096 Jan 27 16:57 model
-rw-r--r--    1 root     root         28672 Jan 27 16:57 production.sqlite3
drwxr-xr-x  228 root     root         12288 Jan 27 16:57 node_modules
/opt/application # cat main.js 
import { app } from "./application.js";
import { logger } from "./config/winston.js";
import { SERVER_PORT } from "./support/constants.js";

const port = process.env.PORT || SERVER_PORT;

app.listen(port, () => {
  logger.debug(`Server running at http://localhost:${port}`);
  logger.info("Press CTRL-C to stop");
});

And this is (an excerpt) of the package.json file:

{
  "name": "node-express",
  "private": true,
  "type": "module",
  "scripts": {
    "develop": "nodemon --require dotenv/config src/server.js",
    "start": "node --require dotenv/config src/server.js",
    "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
  },
  "dependencies": {
    "compression": "1.7.4",
    "dotenv": "10.0.0",
    "errorhandler": "1.5.1",
    "express": "4.17.1",
    "helmet": "4.6.0",
    "http-status-codes": "2.1.4",
    "lusca": "1.7.0",
    "knex": "0.95.11",
    "morgan": "1.10.0",
    "objection": "2.2.16",
    "sqlite3": "5.0.2",
    "uuid": "8.3.2",
    "winston": "3.3.3"
  },
  "devDependencies": {
    "@jest/globals": "27.2.1",
    "cross-env": "7.0.3",
    "eslint-config-prettier": "8.3.0",
    "eslint-config-recommended": "4.1.0",
    "eslint-plugin-jest": "24.4.2",
    "eslint-plugin-prettier": "4.0.0",
    "eslint": "7.32.0",
    "jest": "27.2.1",
    "npm-run-all": "4.1.5",
    "nodemon": "2.0.12",
    "prettier": "2.4.1",
    "supertest": "6.1.6"
  },
  "engines": {
    "node": "16.x"
  },
  "jest": {
    "collectCoverage": true,
    "clearMocks": true,
    "setupFiles": [
      "dotenv/config"
    ],
    "verbose": true
  },
  "nodemonConfig": {
    "ignore": [
      "*.test.js"
    ],
    "watch": [
      "src/*"
    ]
  }
}

CodePudding user response:

Unfortunately, what you're trying to do isn't possible. From the Node documentation:

Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code:

  • Files ending in .mjs.

  • Files ending in .js when the nearest parent package.json file contains a top-level "type" field with a value of "module".

  • Strings passed in as an argument to --eval, or piped to node via STDIN, with the flag --input-type=module.

If possible, you should just copy over the package.json in your Dockerfile - that's what Node expects.

Otherwise, if you absolutely can't have a package.json in your Docker image, then node --input-type module --require dotenv/config < main.js should do the trick.

  •  Tags:  
  • Related