npmにESモジュールとCommonJSモジュールを両方公開すること - @ledsun blog の続きです。 npmにESモジュールとCommonJSモジュールを両方公開するために、ESモジュールからCommonJSモジュールを自動生成する方法を考えます。
先行事例
世の中にはいくつかの方法があるようです。
Publishing NPM Packages as Native ES Modules | by Bill Beesley | DAZN Engineering | Medium では、TypeScriptのモジュールからBabelで、ESモジュールとCommonJSモジュールを生成しています。
How to Create a Hybrid NPM Module for ESM and CommonJS. | SenseDeepでは、TypeScriptのモジュールからtsc(TypeScriptのコンパイラ)で、ESモジュールとCommonJSモジュールを生成しています。
How To Publish An ES6 Module To NPM | by Alec Mather | The Startup | Mediumとjavascript - How to publish a module written in ES6 to NPM? - Stack Overflowでは、CommonJSモジュールからBabelで、ESモジュールを生成しています。
方法
ESモジュールからCommonJSモジュールを生成する方法が見つかりませんでした。
Babelを使って、ESモジュールからCommonJSモジュールを生成してみましょう。 @babel/plugin-transform-modules-commonjs · Babelを使うとESモジュールをCommonJSモジュールに変換できます。 これだけでは
import defaultOption from './defaultOption.mjs';
が
var _defaultOption = _interopRequireDefault(require("./defaultOption.mjs"));
のように変換されます。 import/export分は良い感じで変換できます。 import対象のファイルの拡張子はmjsのままです。
そこでこの部分を変換するBabelプラグインを書きます。
export default function (babel) { const { types } = babel; return { visitor: { Program: { enter: (programPath, state) => { programPath.traverse( { ImportDeclaration: (declaration) => { const source = declaration.get("source"); source.replaceWith( types.stringLiteral( source.node.value.replace(/\.mjs$/, ".cjs") ) ); }, }, state ); }, }, }, }; }
babel-plugin-transform-import-extension.mjs
というファイル名で保存します。
すると.babelrc
は次のようになります。
{ "ignore": [ "test", "babel-plugin-transform-import-extension.mjs" ], "plugins": [ "@babel/plugin-transform-modules-commonjs", "./babel-plugin-transform-import-extension.mjs" ] }
あとは
npm i -D @babel/cli @babel/core @babel/plugin-transform-modules-commonjs npx babel . --extensions '.mjs' -d . --out-file-extension '.cjs'
を実行すればESモジュールの隣にCommonJSモジュールが生成されます。
Babelプラグインを書かなくても、babel-plugin-replace-import-extension - npmを使ってもmjs
をcjs
に変更できそうな気がします。
Babelプラグインを書いたあとで気がついきました。