#javascript #node.js #typescript #tsc #dynamic-import
#javascript #node.js #typescript #tsc #динамический импорт
Вопрос:
Я создаю discord.js
Discord-бота. Теперь по какой-то причине discord.js
не работает с ESM
модулями (совершенно отдельная проблема), поэтому мое приложение-бот использует CommonJS
модули. Теперь у меня есть другой проект в моей системе Lib
, который имеет множество служебных функций, которые я планирую использовать в нескольких разных проектах, поэтому мне не нужно их переписывать. В этом Lib
проекте используются ESM
модули. Поскольку мне приходится импортировать Lib
из DiscordBot
, я использую синтаксис динамического импорта в typescript. Теперь, всякий раз, когда я переношу свой DiscordBot
проект, динамический импорт преобразуется в какой-то уродливый код модуля javascript, и этот уродливый код модуля в конечном итоге заканчивается использованием require() . Поскольку require() не может импортировать модули ESM, мой бот завершает сбой.
Однако я попытался остановить свой компилятор ts, скопировать код из моего файла ts, который импортирует Lib
, затем вставить этот код в соответствующий файл JS вручную (и удалить функции, исключающие TS, такие как аннотации типов и интерфейсы). Затем я запустил свое приложение-бот, и оно отлично работало. Но я не хочу делать это каждый раз. Так что tsc
проблема в компиляции. Как мне это исправить?
Комментарии:
1. Вы изучили этот
module
вариант ?2. Ну, да, конечно. Вот как я настроил
Lib
модуль ESM и модульDiscordBot
CommonJS.3. Можете ли вы поделиться своим
tsconfig.json
здесь?
Ответ №1:
В настоящее время это невозможно. На GitHub появилась очень новая проблема (https://github.com/microsoft/TypeScript/issues/43329 ), но это еще не реализовано. Итак, все, что вы можете сделать сейчас, это переключиться с ESM на CommonJS в вашем Lib
проекте.
Обновление 2022
Проблема была закрыта, и теперь есть новая опция для "module"
вызова node12
. Это должно решить проблему
Комментарии:
1. Это возможно. Смотрите мой ответ ниже. Это некрасиво, но работает.
Ответ №2:
Итак, я понимаю, что цель:
- Разработайте код на TypeScript
- Запустите скомпилированный код в пакете CommonJS
- Импортируйте и используйте модуль ES
Вариант 1:
Если "module"
in tsconfig.json
имеет значение "commonjs"
, в настоящее время нет способа запретить TypeScript переносить динамический import()
в require()
— за исключением того, что вы скрываете код в строке и используете eval
его для выполнения. Вот так:
async function body (pMap:any){
// do something with module pMap here
}
eval ("import('p-map').then(body)");
TypeScript ни в коем случае не переносит строку!
Вариант 2
Установите "module"
в tsconfig.json
значение "es2020"
. При этом динамический импорт не будет перенесен в require()
, и вы можете использовать динамический импорт для импорта модуля CommonJS или ES. Или вы можете использовать const someModule = require("someModule")
синтаксис для импорта модуля CommonJS (не будет перенесен в синтаксис импорта ES6). Вы не можете использовать синтаксис импорта ES6, такой как import * as someModule from "someModule"
или import someModule from "someModule"
. Эти синтаксисы будут генерировать импорт синтаксиса модуля ES (для «module» установлено значение «es2020») и не могут быть запущены в пакете CommonJS.
Ниже приведена небольшая информация:
- Если
"module"
установлено значение"es2020"
: динамический импортimport()
не переносится. - Если
"module"
установлено значение `»es2015″: ошибка:
TS1323: Динамический импорт поддерживается только в том случае, если для флага ‘—module’ установлено значение ‘es2020’, ‘esnext’, ‘commonjs’, ‘amd’, ‘system’ или ‘umd’.
- Если
"module"
установлено значение"commonjs"
: динамический импорт переносится.
tsconfig.json
Ссылка на цитату для "module"
поля:
Если вам интересно, в чем разница между ES2015 и ES2020, ES2020 добавляет поддержку динамического импорта и import.meta.
Комментарии:
1. Я попросил OP для a
tsconfig.json
, но теперь я понимаю, насколько стар этот вопрос, и он возник у меня только потому, что вы назначили награду за него. Вы могли бы поделитьсяtsconfig.json
? Еще лучше было бы использовать минимальный тестовый пример. Возможно, в вставке или, может быть, даже в новом вопросе.2. Мой ответ не имеет ничего общего с tsconfig.json. Если параметр в tsconfig.json может помешать typescript переносить динамический импорт в require() , это было бы прекрасным ответом. Мой ответ уродлив — он скрывает синтаксис динамического импорта в строке и использует ‘eval’ для его выполнения. Typescript не будет передавать строку.
Ответ №3:
node12
Настройка, о которой говорят другие, не сработала для меня, но они compilerOptions
сработали, используя Typescript 4.7.2:
"module": "CommonJS",
"moduleResolution": "Node16",
Это спасло меня, мне не нужно было переносить все import require
файлы в imports, чтобы иметь возможность использовать ESM npm lib.
Источник ввода Typescript:
import Redis = require('redis');
import * as _ from 'lodash';
export async function main() {
const fileType = await import('file-type');
console.log(fileType, _.get, Redis);
}
Вывод CommonJS:
...
const Redis = require("redis");
const _ = __importStar(require("lodash"));
async function main() {
const fileType = await import('file-type');
console.log(fileType, _.get, Redis);
}
exports.main = main;
Ответ №4:
Какой компилятор / пакет вы используете? Я предполагаю, что tsc зависит от контекста.
Я рекомендую использовать esbuild для компиляции и объединения ваших TS. Вы также можете использовать его просто для преобразования после использования tsc. У него есть опция под названием «формат», которая может удалять любой импорт в стиле модуля. См. https://esbuild.github.io/api/#format .
Вот простой пример использования.
build.js
const esbuild = require("esbuild");
esbuild.build({
allowOverwrite: true,
write: true,
entryPoints: ["my-main-file.ts"],
outfile: "some-file.bundle.js",
format: "cjs", //format option set to cjs makes all imports common-js style
bundle: true,
}).then(() => {
console.log("Done!");
});
Затем вы можете добавить что-то подобное в свой package.json
"scripts": {
"build": "node build.js",
...rest of scripts
Вот дополнительная ссылка о некоторых предостережениях при использовании esbuild с typescript. Ничто из этого не должно быть для вас проблемой. https://esbuild.github.io/content-types/#typescript-caveats
Ответ №5:
Это было исправлено с добавлением node12
опции для module
настройки. Из документации:
Доступные в ночных сборках экспериментальные режимы node12 и nodenext интегрируются с поддержкой встроенного модуля ECMAScript в Node. В исходящем JavaScript используется либо вывод CommonJS, либо вывод ES2020 в зависимости от расширения файла и значения параметра типа в ближайшем package.json. Разрешение модуля также работает по-другому. Вы можете узнать больше в руководстве.
Однако, если вы используете этот параметр без ночной сборки, в настоящее время он выдает следующую ошибку:
ошибка TS4124: параметр компилятора ‘module’ со значением ‘node12’ нестабилен. Используйте nightly TypeScript, чтобы отключить эту ошибку. Попробуйте выполнить обновление с помощью «npm install -D typescript @next».