Как запретить typescript переносить динамический импорт в require()?

#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:

Итак, я понимаю, что цель:

  1. Разработайте код на TypeScript
  2. Запустите скомпилированный код в пакете CommonJS
  3. Импортируйте и используйте модуль 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».