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 parentpackage.json
file contains a top-level"type"
field with a value of"module"
.Strings passed in as an argument to
--eval
, or piped tonode
viaSTDIN
, 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.