😊
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