#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.12. @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 .
Чтобы устранить вашу проблему:
- Снимите
esm
упаковку. - Добавьте
"type": "module"
к своемуpackage.json
.
И это все. Тогда, когда вы запустите node server.js
, это должно сработать.
Комментарии:
1. Я думаю, что по-прежнему рекомендуется использовать
.mjs
расширение, когда вы пишете ESM-код для Node, но у меня возникли проблемы с поиском ссылки.2.
.mjs
это вариант, но не обязательный. nodejs.org/api/packages.html#determining-module-system
Ответ №3:
Есть несколько способов сделать это:
- укажите
"type":"module"
вpackage.json
- Используйте этот флаг
--input-type=module
при запуске файла - использовать
.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, и вы знаете, что как только они будут загружены, вы сможете использовать их по своему усмотрению во всем остальном коде простым и понятным способом, который не заставляет вас добавлять дополнительную асинхронную логику повсюду, когда вам нужен модуль.