😊
import.meta.url を esbuild で CJS に変換する
メモ
ES2020 の機能で import.meta というのがあります。
import.meta はモジュールのメターデータを含むオブジェクトです。ブラウザや Node.js では import.meta.url というプロパティが生えています。
これは import.meta.url が評価されたモジュールのファイルの URL を表すプロパティです。
Node.js では、ESM 環境下で createRequire をしたり __filename や __dirname に相当するものを取得する用途等でよく使われます。
import module from "node:module";
const require = module.createRequire(import.meta.url);
import path from "node:path";
import url from "node:url";
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dir(__filename);
こんな感じで import.meta.url を使うプログラムを、esbuild を使って CJS に変換すると、実は空オブジェクトになります。
変換前のコードがこちら。ただ import.meta.url を console.log します。
console.log(import.meta.url)
変換後のコードがこちら。import.meta が {} になって、{} の url を console.log しています。
var import_meta = {};
console.log(import_meta.url);
これでは困っちゃうので、なんとかしましょう。
なんとかする方法の一つとして、define と inject があります。
こんな感じで import.meta.url を CJS 環境で再現するコードを用意しておきます。
import-meta-url.js
export var import_meta_url = require("url").pathToFileURL(__filename);
で、esbuild の define で import.meta.url を import_meta_url に置き換え、inject で import-meta-url.js を読むことで import_meta_url という変数を import.meta.url 相当のものに置き換えます。
import { build } from "esbuild";
import path from "path";
/** @type {import('esbuild').BuildOptions} */
const options = {
entryPoints: ["./src/index.js"],
minify: true,
bundle: true,
outfile: "./dist/index.cjs",
format: "cjs",
define: {
"import.meta.url": "import_meta_url",
},
inject: [
// このへんのパスは各々いい感じで
path.join(process.cwd(), "import-meta-url.js"),
],
};
build(options).catch((err) => {
process.stderr.write(err.stderr);
process.exit(1);
});
これで良い感じになる。
Discussion