Ошибка: функция require() модулей ES не поддерживается при импорте node-fetch

#javascript #node.js #node-fetch

Вопрос:

Я создаю программу для анализа потоков камер видеонаблюдения и застрял на самой первой строке. На данный момент в моем файле .js нет ничего, кроме импорта node-fetch, и он выдает мне сообщение об ошибке. Что я делаю не так?

Запуск Ubuntu 20.04.2 LTS в подсистеме Windows для Linux.

Версия узла:

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

версия пакета для извлечения узла:

 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$ 
 

Комментарии:

1. Это два типа синтаксиса модулей для nodejs. Синтаксис CommonJS , который использует require и module.exports , и синтаксис ES , которые используют import * from "path" стиль модуля. По умолчанию nodejs попытается загрузить модули с синтаксисом CommonJS . Если вы хотите использовать ES синтаксис, вы должны указать "type":"module" в своем файле package.json. Но вы не можете их перепутать. Вы можете использовать один синтаксис, но не оба.

2. @Ariart Я использую синтаксис ES в своем коде, и в node-fetch есть «тип»: модуль в пакете.json

3. Указано ли это в вашем пакете проекта.json ?

4. @Ariart У меня даже не было пакета.json, я создал его, в котором не было ничего, кроме «типа»: «модуль», и это сработало! Спасибо! Есть ли способ установить это встроенное значение при запуске узла вместо определения его в файле package.json?

5. Да, вы можете запустить узел с флагом. По памяти, это что-то вроде --input-type=module

Ответ №1:

Из Руководства по обновлению

node-fetch был преобразован в пакет только для ESM в версии 3.0.0-beta.10. node-fetch-это модуль только для ESM-вы не можете импортировать его с помощью require.

В качестве альтернативы вы можете использовать функцию асинхронного импорта() из CommonJS для асинхронной загрузки выборки узлов:

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

или вы остаетесь на v2, как говорится в README

node-fetch это модуль только для ESM-вы не можете импортировать его с помощью require. Мы рекомендуем вам остановиться на версии v2, которая построена с использованием CommonJS, если вы сами не используете ESM. Мы продолжим публиковать критические исправления ошибок для него.

Редактировать

Я видел это в документе, использовал его, и сценарий вылетает с :

ошибка TS2556: Аргумент spread должен либо иметь тип кортежа, либо быть передан параметру rest.

Поскольку вы используете typescript, вам следует сделать что-то вроде этого

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

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

Комментарии:

1. Я увидел это в документе, использовал его, и сценарий вылетает с : error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter. Node.js 14.17.1

2. @JeremyThille Я обновил свой ответ. Я еще не тестировал его, поэтому, пожалуйста, дайте мне знать, работает ли он.

3. Большое спасибо. Я пробовал именно это, но я понимаю Error [ERR_REQUIRE_ESM]: Must use import to load ES Module. require() of ES modules is not supported. index.ts 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 node_modules/node-fetch/package.json. , что Он продолжает говорить, что я использую require , но я использую import . Запускаю свой файл TS с ts-node-dev index.ts помощью .

4. @JeremyThille ты когда-нибудь выяснял, почему? У меня то же самое, что и у вас — я уже использую импорт.

5. Это не работает из-за инструкции «импорт nodeFetch…». Вы должны импортировать только типы, а не реализацию. это должно сработать: import { RequestInfo, RequestInit } from 'node-fetch'; const fetch = (url: RequestInfo, init?: RequestInit) => import('node-fetch').then(({ default: fetch }) => fetch(url, init));

Ответ №2:

node-fetch v3 предназначен только для ESM: https://github.com/node-fetch/node-fetch#loading-and-configuring-the-module. esm Модуль, который вы добавляете, предназначен для добавления совместимости с ESM, но теперь в этом нет необходимости, поскольку узел 12 изначально поддерживает ESM; и он не работает с пакетами только для ESM, такими как node-fetch 3 .

Чтобы устранить вашу проблему:

  1. Снимите esm упаковку.
  2. Добавьте "type": "module" к своему package.json .

И это все. Тогда, когда вы запустите node server.js , это должно сработать.

Комментарии:

1. Я думаю, что по-прежнему рекомендуется использовать .mjs расширение, когда вы пишете ESM-код для Node, но у меня возникли проблемы с поиском ссылки.

2. .mjs это вариант, но не обязательный. nodejs.org/api/packages.html#determining-module-system

Ответ №3:

Есть несколько способов сделать это:

  1. укажите "type":"module" в package.json
  2. Используйте этот флаг --input-type=module при запуске файла
  3. использовать .mjs расширение файла

Ответ №4:

Поскольку вопрос действительно касался требования выборки узлов, и определенно есть причины, по которым разработчику может потребоваться использовать require vs import, ответ на импорт модуля с помощью import() несколько точен, но неполон, потому что он игнорирует тот факт, что использование импорта является асинхронным, и если вы используете выборку узлов в своем коде в нескольких местах, у вас будет немного асинхронного беспорядка повсюду, когда вы хотите использовать выборку узлов, поэтому ответ очень неполный IMHO. Более полный ответ включал бы лучшее руководство. Что-то вроде следующего:

Вам нужно использовать 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, а затем использовать модуль позже, взяв его из вашего промежуточного модуля следующим образом:

 "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, и вы знаете, что как только они будут загружены, вы сможете использовать их по своему усмотрению во всем остальном коде простым и понятным способом, который не заставляет вас добавлять дополнительную асинхронную логику повсюду, когда вам нужен модуль.