Error: require() of ES modules is not supported when importing node-fetch

Error: require() of ES modules is not supported when importing node-fetch

我正在创建一个程序来分析安全摄像头流,但卡在了第一行。目前我的 .js 文件只有 node-fetch 的导入,它给了我一条错误消息。我做错了什么?

运行 Ubuntu Windows 子系统中的 20.04.2 LTS Linux.

节点版本:

user@MYLLYTIN:~/CAMSERVER$ node -v
v14.17.6

node-fetch 包版本:

user@MYLLYTIN:~/CAMSERVER$ npm v node-fetch

node-fetch@3.0.0 | MIT | deps: 2 | versions: 63
A light-weight module that brings Fetch API to node.js
https://github.com/node-fetch/node-fetch

keywords: fetch, http, promise, request, curl, wget, xhr, whatwg

dist
.tarball: https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz
.shasum: 79da7146a520036f2c5f644e4a26095f17e411ea
.integrity: sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==
.unpackedSize: 75.9 kB

dependencies:
data-uri-to-buffer: ^3.0.1 fetch-blob: ^3.1.2         

maintainers:
- endless <jimmy@warting.se>
- bitinn <bitinn@gmail.com>
- timothygu <timothygu99@gmail.com>
- akepinski <npm@kepinski.ch>

dist-tags:
latest: 3.0.0        next: 3.0.0-beta.10  

published 3 days ago by endless <jimmy@warting.se>

esm 包版本:

user@MYLLYTIN:~/CAMSERVER$ npm v esm

esm@3.2.25 | MIT | deps: none | versions: 140
Tomorrow's ECMAScript modules today!
https://github.com/standard-things/esm#readme

keywords: commonjs, ecmascript, export, import, modules, node, require

dist
.tarball: https://registry.npmjs.org/esm/-/esm-3.2.25.tgz
.shasum: 342c18c29d56157688ba5ce31f8431fbb795cc10
.integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
.unpackedSize: 308.6 kB

maintainers:
- jdalton <john.david.dalton@gmail.com>

dist-tags:
latest: 3.2.25  

published over a year ago by jdalton <john.david.dalton@gmail.com>

.js 文件的内容(实际上除了导入什么都没有):

user@MYLLYTIN:~/CAMSERVER$ cat server.js 
import fetch from "node-fetch";

结果:

user@MYLLYTIN:~/CAMSERVER$ node -r esm server.js 
/home/user/CAMSERVER/node_modules/node-fetch/src/index.js:1
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/user/CAMSERVER/node_modules/node-fetch/src/index.js
require() of ES modules is not supported.
require() of /home/user/CAMSERVER/node_modules/node-fetch/src/index.js from /home/user/CAMSERVER/server.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/user/CAMSERVER/node_modules/node-fetch/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1089:13) {
  code: 'ERR_REQUIRE_ESM'
}
user@MYLLYTIN:~/CAMSERVER$ 

使用 ESM 语法,也在 运行 文件之前使用其中一种方法。

  1. package.json
  2. 中指定"type":"module"
  3. 或者在 运行 文件
  4. 时使用此标志 --input-type=module
  5. 或使用.mjs文件扩展名

node-fetch v3 仅支持 ESM:https://github.com/node-fetch/node-fetch#loading-and-configuring-the-module。您要添加的 esm 模块是为了添加 ESM 兼容性,但现在 Node 12+ 本身就支持 ESM,因此没有必要;它不适用于 node-fetch 3+.

等纯 ESM 软件包

解决您的问题:

  1. 删除 esm 包。
  2. "type": "module" 添加到您的 package.json

就是这样。然后当你 运行 node server.js 它应该工作。

来自Upgrade Guide

node-fetch was converted to be a ESM only package in version 3.0.0-beta.10. node-fetch is an ESM-only module - you are not able to import it with require.

Alternatively, you can use the async import() function from CommonJS to load node-fetch asynchronously:

// mod.cjs
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));

或者你继续 v2 正如他们在 README

中所说

node-fetch is an ESM-only module - you are not able to import it with require. We recommend you stay on v2 which is built with CommonJS unless you use ESM yourself. We will continue to publish critical bug fixes for it.

编辑

I saw that in the doc, used it, and the script crashes with :

error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

既然你使用的是打字稿,你应该这样做

import { RequestInfo, RequestInit } from "node-fetch";

const fetch = (url: RequestInfo, init?: RequestInit) =>  import("node-fetch").then(({ default: fetch }) => fetch(url, init));

由于问题实际上是关于需要节点获取的,并且开发人员可能需要使用 require 与 import 肯定有原因,使用 import() 导入模块的答案有些准确但不完整,因为它忽略了使用 import 是异步的事实,如果你在代码中的多个地方使用 node-fetch,那么每当你想使用 node-fetch 时,到处都会有一些异步混乱,所以答案是非常不完整的恕我直言。更完整的答案将包括更好的操作方法。类似于以下内容:

您需要使用 import() 而不是 require 因为这是一个 ES 模块。为避免异步混乱,您需要等待一次该模块的导入,然后才能在任何需要的地方使用它。为此,创建一个模块来导入任何仅 ES 模块,如下所示:

"use strict";

let got;
let fetch;

module.exports.load = async function() {
    queueMicrotask(function() {
        // any ESM-only module that needs to be loaded can be loaded here, just add import for each using specific structure of each
        import("got").then(({default: Got}) => got = Got );
        fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
    });

    while(
        // check each module to see if it's been loaded here
        !got || !got.get || !fetch || typeof fetch !== "function"
         ) {
        // waiting for modules to load
        console.log("Waiting for ES-Only modules to load...");
        await new Promise((resolve)=>setTimeout(resolve, 1000));
    }
    module.exports.got = got;
    module.exports.fetch = fetch;
    console.log("ES-Only modules finished loading!");
}

这样您就可以调用等待加载 ES-Only 模块一次,然后稍后通过从中间模块中获取该模块来使用该模块,如下所示:

"use strict";

const esmModules = require("esm_modules"); // or whatever you called the intermiary module
async function doMyFetching(url, options) {
   const fetch = esmModules.fetch;
   const result = await fetch(url, options)
}

这样做的好处是,您知道您的 ES-Only 模块已加载,并且您知道一旦它们被加载,您就可以在其余代码中随意使用它们干净的方式,不会强迫您在需要模块时到处添加额外的异步逻辑。

为了解决这个问题,我将 node-fetch 降级到最新版本 2,现在是 2.6.6。这是我的脚本 运行:

yarn add node-fetch@^2.6.6

npm install node-fetch@^2.6.6

我还添加了这些编译器选项:

{
  "compilerOptions": { "allowJs": true, "outDir": "./dist" },
}

当前版本的 fetch 多次发生错误,我们可以做的是安装以前的版本,我试过了,它对我有用。

要安装以前的版本: npm i node-fetch@2.6.1 或您喜欢的任何内容。

我以前的解决方案总是有错误,这对我来说每次都是有效的

const _importDynamic = new Function('modulePath', 'return import(modulePath)');

export const fetch = async function (...args: any) {
    const {default: fetch} = await _importDynamic('node-fetch');
    return fetch(...args);
}