mjs で __dirname が使えない
◆ EsModules だと __dirname などモジュール実行時に自動で定義される変数が存在しない
◆ import.meta.url で URL として自身のファイルのパスが取得できる
◆ URL なので new URL(url).pathname でパスを取得してから dirname 関数を通す
◆ import.meta.url で URL として自身のファイルのパスが取得できる
◆ URL なので new URL(url).pathname でパスを取得してから dirname 関数を通す
Node.js 10 で Es Modules が実験的にですが使えるようになったらしいので使ってみました
[test.mjs]
実行時にフラグが必須なのと拡張子を mjs にしないといけないところに注意です
js 拡張子だと http のところで Unexpected identifier とエラーになりました
ブラウザでいう type="module" 忘れと同じで非モジュール扱いみたいですね
これが __dirname が未定義というエラーになりました
Node.js では __dirname で今実行中のファイルが存在するフォルダのパスが取得できます
それが使えないです
--experimental-modules フラグを外して実行すると取得できていたので モジュールが原因みたいです
ロードの方法が変わったことで Node.js 固有の用意されていた変数が使えなくなったのでしょうか
こういう関数が作られて この関数の中身に require するコードがそのまま貼り付けられています
この関数が実行されるので モジュール内から __dirname などが使えていました
読み込み方法がこの方法とは違うのなら未定義なのもわかります
これまでは CommonJS から来ていた Node.js 独自のものでしたが EcmaScript の仕様にあわせるならこういう余計な変数を勝手に追加することはできないでしょうし
調べてみると StackOverflow で import.meta を使えば良いと書いてる人がいました
こういう情報が入っていました
なので dirname を作るには
でできます
Web の機能なので url というプロパティ通り URL 形式でしか取得できないです
なのでローカルのものにするため URL の pathname を取得します
あとはフォルダ名にするために dirname 関数を通します
import はオブジェクトではなくインポートのためのキーワードなので meta プロパティにアクセスすればオブジェクトが取得できますが import 自体をオブジェクトのように扱ってもエラーになります
console.log もできません
super みたいなものです
JavaScript はクラスもなく全部オブジェクトという単純なものでよかったので こういう特殊なキーワードはやめてほしかったのですけどねー
import.meta を使うにせよ 毎回こういうのを書くのは結構面倒です
でも import.meta というワードを書かずに済ませたいです
ですが ライブラリに dirname 関数を作るとすると そのライブラリの中で import.meta を参照してもライブラリのファイルのパスに対するものになります
その関数を呼び出したファイルのパスではありません
呼び出し元のパスというとエラー時の stack です
とりあえずこんなものを作ってみました
[test.mjs]
[dirname.mjs]
一応ちゃんと動きます……がちゃんとしたところでは使いたくないものです
まだ実験的なサポート段階ですし 簡単に扱える機能ができることを期待しましょう
EsModules
[test.mjs]
import http from "http"
import fs from "fs"
import path from "path"
$ node --experimental-modules test.mjs
実行時にフラグが必須なのと拡張子を mjs にしないといけないところに注意です
js 拡張子だと http のところで Unexpected identifier とエラーになりました
ブラウザでいう type="module" 忘れと同じで非モジュール扱いみたいですね
__dirname
ここさえ気をつければブラウザ側と同じ感じで EsModules のエクスポートインポートが使えると思ったのですが 意外なところで問題が起きましたconsole.log(__dirname)
これが __dirname が未定義というエラーになりました
Node.js では __dirname で今実行中のファイルが存在するフォルダのパスが取得できます
それが使えないです
--experimental-modules フラグを外して実行すると取得できていたので モジュールが原因みたいです
ロードの方法が変わったことで Node.js 固有の用意されていた変数が使えなくなったのでしょうか
require
これまでの require だと(function (exports, require, module, __filename, __dirname) { });
こういう関数が作られて この関数の中身に require するコードがそのまま貼り付けられています
この関数が実行されるので モジュール内から __dirname などが使えていました
読み込み方法がこの方法とは違うのなら未定義なのもわかります
これまでは CommonJS から来ていた Node.js 独自のものでしたが EcmaScript の仕様にあわせるならこういう余計な変数を勝手に追加することはできないでしょうし
対処
でも今のファイルの場所がわからないのは不便です調べてみると StackOverflow で import.meta を使えば良いと書いてる人がいました
console.log(import.meta)
{ url: 'file:///home/user/js/test.js' }
こういう情報が入っていました
なので dirname を作るには
const dirname = path.dirname(new URL(import.meta.url).pathname)
// /home/user/js
でできます
Web の機能なので url というプロパティ通り URL 形式でしか取得できないです
なのでローカルのものにするため URL の pathname を取得します
あとはフォルダ名にするために dirname 関数を通します
import はオブジェクトではなくインポートのためのキーワードなので meta プロパティにアクセスすればオブジェクトが取得できますが import 自体をオブジェクトのように扱ってもエラーになります
console.log もできません
super みたいなものです
JavaScript はクラスもなく全部オブジェクトという単純なものでよかったので こういう特殊なキーワードはやめてほしかったのですけどねー
import.meta を使うにせよ 毎回こういうのを書くのは結構面倒です
関数にしたい
import.meta のオブジェクトを渡せば dirname を返す関数を作るのは簡単ですでも import.meta というワードを書かずに済ませたいです
ですが ライブラリに dirname 関数を作るとすると そのライブラリの中で import.meta を参照してもライブラリのファイルのパスに対するものになります
その関数を呼び出したファイルのパスではありません
呼び出し元のパスというとエラー時の stack です
とりあえずこんなものを作ってみました
[test.mjs]
import {dirname} from "./dirname"
console.log(dirname())
[dirname.mjs]
import path from "path"
export function dirname() {
const stack = new Error().stack
const lines = stack.split("\n")
const idx = lines.findIndex(e => e.trim().startsWith("at dirname ")) + 1
if (idx) {
const matched = lines[idx].match(/(file:.+):\d+:\d+$/)
if (matched) return path.dirname(new URL(matched[1]).pathname)
}
return false
}
user@localhost ~> n/bin/node --experimental-modules js/test.mjs
(node:7359) ExperimentalWarning: The ESM module loader is experimental.
/home/user/js
一応ちゃんと動きます……がちゃんとしたところでは使いたくないものです
まだ実験的なサポート段階ですし 簡単に扱える機能ができることを期待しましょう