无法启动 Node.js 使用 ES 模块的应用程序

Unable to start Node.js application that uses ES module

我有一个简单的 Node.js 16.x 应用程序。它使用 ES 模块,所以我在 package.json 中有 "type": "module"。每当我使用 npm 脚本时一切正常。

现在我尝试使用 Docker 部署它,我不再需要 npm 脚本,所以我直接使用 node 二进制文件启动应用程序,就像我在 package.json 中声明它一样: node --require dotenv/config main.js ...但这不起作用,它失败并出现围绕 ES 模块的典型错误消息,例如:

(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

我也尝试了围绕此命令的设置组合:node --input-type=module --experimental-modules --require dotenv/config main.js by following this guide,但我仍然收到相同的错误消息。

我想我在这里遗漏了一些微不足道的东西,所以我的问题是:如何在不使用 package.json(因为此时我不再拥有它)或重命名 .js 文件到 .mjs,仅使用 node 二进制文件?


这是应用程序在文件系统中的样子(如果它有任何不同):

/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");
});

这是 package.json 文件的(节选):

{
  "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/*"
    ]
  }
}

很遗憾,您尝试的操作是不可能的。来自 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.

如果可能,您应该只复制 Docker 文件中的 package.json - 这正是 Node 所期望的。

否则,如果您绝对不能在 Docker 图像中使用 package.json,那么 node --input-type module --require dotenv/config < main.js 应该可以解决问题。