Denoのimport.metaの使い方
JSの import
に加えDenoにおけるimportの補強をするような機能がいくつかあります。今回は実際にそれらを使ってみたいと思います。
import.meta.main
import.meta.main
はエントリーポイントかどうかのフラグがあり、true
の場合はエントリーポイントで、false
の場合はモジュールとして読み込まれたとわかります。
Node.jsの場合は require.main === module
で判定していたかと思いますがDenoでは真偽値で判定可能です。
使い道としては例えば基本ライブラリとして提供するものの、もしファイルがそのまま実行されたときにはライブラリを利用した初期の処理を行うといったことが可能です。
以下はBase64のライブラリだがコマンドとして実行されたら引数をエンコードして表示するサンプルです。
export function encodeBase64(str: string) {
return btoa(str);
}
if (import.meta.main) {
if (Deno.args.length <= 0) {
console.log('Need arguments.');
} else {
for (const str of Deno.args) {
console.log(encodeBase64(str));
}
}
}
import { encodeBase64 } from './lib.ts'
console.log(encodeBase64('Hello, World!'));
# 引数なし実行なのでエラーメッセージを出力
$ deno run lib.ts
Need arguments.
# 引数を与える。今回はスペースで区切られて2つの値が渡される。
$ deno run lib.ts Hello, World!
SGVsbG8=
V29ybGQh
# ライブラリとして使用。今回は一つの文字列が渡される。
$ deno run main.ts
SGVsbG8sIFdvcmxkIQ==
import.meta.url
これは現在のファイルのパスをURL形式で取得します。URL形式は file://
から始まるので、Windowsだと file:///C:/dir1/dir2/filename.ts
のようになります。
こちらもライブラリとして読み込んだ場合の挙動を見てみます。
export function getURL() {
return import.meta.url;
}
import { getURL } from './dir/lib.ts'
console.log(import.meta.url);
console.log(getURL());
$ deno run main.ts
file:///C:/project_dir/main.ts
file:///C:/project_dir/dir/lib.ts
これとDeno標準ライブラリを使うとその環境でのファイルパスを取得したり、ファイルパスの合成が可能です。
import { fromFileUrl, dirname, join } from 'https://deno.land/std/path/mod.ts';
// 生の値
console.log(import.meta.url);
// URL形式→実行環境の形式でファイルの絶対パスを取得
const filePath = fromFileUrl(import.meta.url);
console.log(filePath);
// 実行環境の形式で親ディレクトリの絶対パスを取得
const dirName = dirname(filePath);
console.log(dirName);
// 実行環境の形式で親ディレクトリ/dir/libs.tsの絶対パスを取得
const joinedPath = join(dirName, 'dir/lib.ts');
console.log(joinedPath);
実行結果は以下です。
file:///C:/project_dir/main.ts
C:\project_dir\main.ts
C:\project_dir
C:\project_dir\dir\lib.ts
Denoの標準ライブラリの path
は環境に応じて /
や \
を使い分けてくれます。
import.meta.url
からこれを使っているソースファイルの絶対パスをURL形式で入手可能なので、 fromFileUrl()
を使ってローカルの絶対パスに書き換えます。
この後 dirName()
や join()
を使うことで目的のファイルの絶対パスを取得することができます。
import.meta.dirname / import.meta.filename
import.meta.url
から自分のパスを取得することも可能ですが、便利なものもあります。それが import.meta.dirname
と import.meta.filename
です。前者はディレクトリ、後者はフルパスを取得できます。
console.log(import.meta.dirname);
console.log(import.meta.filename);
C:\project_dir
C:\project_dir\main.ts
上で紹介したやり方よりかなり楽に取得できますね。
import.meta.resolve()
import.meta.resolve()
は第一引数に渡したモジュールのパスをURL形式で返してくれる関数です。
これだけなら諸々のモジュール解決に使うくらいかと思いますが、Denoでは重要な使い方があります。
Denoには compile
という対象のプログラムを単一実行可能な実行可能プログラムとして出力する機能があります。
エントリーポイントだけで完結するなら特に問題はないのですが、Workerは別のファイルを読み込ませて実行します。このままではbundleで出力されたもの以外にWorkerのソースファイルを用意しなければいけなくなります。しかもそれが他のモジュールに依存してたらどうするんだ?など、様々な問題が発生します。
ですが、import.meta.resolve()
経由でWorker側のファイルパスを指定し、compile
時にWorkerのファイルも一緒に指定すると一緒に単一ファイルに混ぜてもらえる他、実行ファイル内に取り込まれたWorkerの実行も可能です。
declare const self: Worker;
self.onmessage = (event) => {
console.log('worker received:', event.data);
self.postMessage('hello from worker!');
};
const worker1 = new Worker(import.meta.resolve('./worker.ts'), {
type: 'module'
});
worker1.postMessage('Hello from main!');
worker1.onmessage = (event) => {
console.log('main received', event.data);
worker1.terminate();
};
$ deno run --allow-read main.ts
worker received: Hello from main!
main received hello from worker!
$ deno compile --include worker.ts -o example main.ts
Check file:///C:/project_dir/main.ts
Check file:///C:/project_dir/worker.ts
Compile file:///C:/project_dir/main.ts to example.exe
$ ./example.exe
worker received: Hello from main!
main received hello from worker!
無事単純実行でも compile
で単一ファイルにしてもWorkerを実行することができました。
まとめ
今回はDenoの import.meta
周りのまとめでした。Denoで色々したいなという場合にちょくちょく使うことになると思います。
参考
-
https://deno.com/manual/runtime/import_meta_api
-
import.meta
の説明
-
-
https://twitter.com/deno_land/status/1660794789862965249
- 公式TwitterにおけるTips
Discussion