30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

ES2020 でリリースされた import.meta について

ES2020 でimport.metaがリリースされた。
この機能を使うと、モジュールのメタ情報を取得することができる。
例えば、当該モジュールのパスを取得することができる。

モジュールのメタ情報を取得する機能であるため、CommonJS や Script モードで使用するとエラーになる。

import.metaでどのような情報を取得できるか、Node.js、Deno、ブラウザで試してみた。

それぞれのバージョンは以下の通り。

  • Node.js: 14.7.0
  • Deno: 1.2.2
  • Google Chrome: 84.0.4147.105

Node.js

前述の通り CommonJS だとエラーになるため、ES Modules で使う必要がある。

// SyntaxError: Cannot use 'import.meta' outside a module
console.log(import.meta);

Node.js で ES Modules を使う方法については、以下の記事に書いた。

numb86-tech.hatenablog.com

ES Modules として書かれたファイルなら、import.metaを使える。

// [Object: null prototype] {
//   url: 'file:///Users/numb/index.mjs'
// }
console.log(import.meta);

ES Modules では__dirname__filenameを使えないが、import.meta.urlでファイルパスを取得することができる。

// console.log(__dirname); // ReferenceError: __dirname is not defined
// console.log(__filename); // ReferenceError: __filename is not defined
console.log(import.meta.url); // file:///Users/numb/index.mjs

Deno

Deno では、urlの他にmainという名前の値を取得することができる。

console.log(import.meta); // { url: "file:///Users/numb/index.ts", main: true }

mainは、プログラムの起点として Deno に渡されたモジュールかどうかを、真偽値で持っている。

例えば、以下のモジュールがあったとする。

// meta-sub.ts
console.log(import.meta.main);
export const x = 1;
// meta.ts
import { x } from "./meta-sub.ts";
console.log(x);
console.log(import.meta.main);

この状況でmeta.tsを実行すると、meta.tsがメインのスクリプトとなるため(meta-sub.tsmeta.tsに呼ばれているだけ)、meta-sub.tsimport.meta.mainfalseになる。

$ deno run meta.ts
false
1
true

meta-sub.tsを実行すれば、meta-sub.tsimport.meta.maintrueになる。

$ deno run meta-sub.ts
true

ブラウザ

以下のコードを Deno で実行して、サーバを立てる。

import {
  listenAndServe,
} from "https://deno.land/std@0.63.0/http/mod.ts";

listenAndServe(
  { port: 8080 },
  async (req) => {
    if (req.method !== "GET") {
      req.respond({
        status: 405,
      });
      return;
    }

    switch (req.url) {
      case "/":
        req.respond({
          status: 200,
          headers: new Headers({
            "content-type": "text/html",
          }),
          body: await Deno.readFile("./index.html"),
        });
        break;

      case "/module.js":
        req.respond({
          status: 200,
          headers: new Headers({
            "content-type": "text/javascript",
          }),
          body: await Deno.readFile("./module.js"),
        });
        break;

      default:
        req.respond({
          status: 404,
          headers: new Headers({
            "content-type": "text/plain",
          }),
          body: "Not found\n",
        });
        break;
    }
  },
);

console.log("Server running on localhost:8080");

index.htmlmodule.jsはそれぞれ、以下の内容。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>ImportMeta</title>
</head>
<body>
  <script type="module" src="/module.js"></script>
</body>
</html>
console.log(Object.keys(import.meta));
console.log(import.meta.url);

この状態でhttp://localhost:8080/にアクセスするとindex.htmlが表示され、そのなかでmodule.jsを読み込む。
scriptタグにはtype="module"をつけているが、これがないと Script モードになってしまい、import.metaを使ったときにエラーになるので注意。

ブラウザのログには以下のように表示されており、import.meta.urlで当該ファイル(今回の場合はmodule.js)の URL を取得できることが分かる。

["url"]
http://localhost:8080/module.js