@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

ESモジュールをCommonJSモジュールにバベル

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 | Mediumjavascript - 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を使ってもmjscjsに変更できそうな気がします。 Babelプラグインを書いたあとで気がついきました。

参考