Deno1.17で導入されるJSONインポートを試す

Deno1.17でついにJSONインポートが導入されます！！

https://twitter.com/biwanczuk/status/1470933296792145921

もともとDenoにはバージョン0時代にJSONインポートが実装されていました（[#1065](https://github.com/denoland/deno/pull/1065)）。当時の構文は`import json from "./foo.json"`という単純なものでした。
しかし、JSONインポートがECMAScript仕様に入るにあたり`assert { type: "json" }`という構文（**Import Assertions**）への対応が必要になったため、バージョン1.0.0リリース時にJSONインポートは削除されていました（[#5037](https://github.com/denoland/deno/pull/5037)）。
先日TypeScript4.5がリリースされたことで、Import Assertionsへの対応が全て完了したため、再びDenoにJSONインポートが導入されました。（swcやv8などは既に対応しており、TSが最後のブロッカーでした。）

## JSONインポートを試す

JSONインポートは12月16日リリースのDeno1.17で導入されるのですが、アドベントカレンダーの担当日的な意味でギリギリ間に合わないので、canary版を使って試していきます。

※canary版のリリースもギリギリ間に合わなかったので（日本時間で12/16の朝4時）、投稿が遅れました。**ごめんなさい。**

```shell:canary版へアップデート
> deno upgrade --canary
```

先ほど述べた通り、JSONインポートはImport Assertionsと組み合わせて使います。

```json:test.json
{ "Hello": "World" }
```

```ts:JSONインポート
import data from "./test.json" assert { type: "json" };
console.log(data); //=> { Hello: "World" }
```

ちなみに、型推論も効きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/73a06e47-1a6e-adf8-3967-8504584fe453.png)


### 複数回importした時の挙動

1つのJSONファイルを複数回importした時、`===`で比較するとtrueになります。

```ts
import data1 from "./test.json" assert { type: "json" };
import data2 from "./test.json" assert { type: "json" };
console.log(data1 === data2); //=> true
```

### プロパティを書き換えた時の挙動

上で実験した通り、複数回importすると参照が同一になります。
importしたモジュールの一部を書き換えた場合、他の場所でimportしたモジュールも書き換わってしまうのか試してみます。

```ts
import data1 from "./test.json" assert { type: "json" };
import data2 from "./test.json" assert { type: "json" };

data1.Hello = "Konnichiwa";
console.log(data2.Hello); //=> "Konnichiwa"
// data1の内容を変更したら、data2の内容も変更される！
```

無事(？)、他の場所でimportしたモジュールの内容も書き換わりました。
JSONモジュールを複数回importする時は、プロパティを書き換えると意図せず他の場所に影響を及ぼすかもしれないので、気を付けましょう。

### 動的import

動的importでもJSONインポートを利用することができます。

```ts
const { default: data } = await import("./test.json", {
  assert: { type: "json" },
});
console.log(data);
```

JSONインポートは`default export`として扱われるため、代入時に`default`で受ける必要があります。
data URLとの組み合わせも可能です。

```ts
function JSONParse(code: string) {
  return import("data:application/json," + encodeURIComponent(code), {
    assert: { type: "json" },
  });
}
console.log(await JSONParse('{"Hello":"World"}'));
```

まあこれはただの`JSON.parse`なので、使い道はありませんが…

## JSのimport構文の今後

Import Assertionsの導入により、JavaScript以外のリソースをimportできるようになりました。まず手始めにJSONのimportが導入されたわけですが、JSON以外のimportも導入が検討されています。
なお、以下はDenoに限った話ではなく、ブラウザ含むJavaScript・Web標準についての話になります。

#### CSS / HTMLのimport

CSSについては`CSS Module Script`という名前で標準化が進んでいます。
Chromeなど、いくつかのブラウザでは既に利用可能です。

```ts
import sheet from './styles.css' assert { type: 'css' };
```

https://developer.chrome.com/blog/new-in-chrome-93/#css-modules

Denoには[まだ導入されていません](https://github.com/denoland/deno/issues/11961)（そもそも導入すべきか決まっていないような状況）。個人的には、将来フロントエンド開発にDenoが使われるようになり、ブラウザのCSSモジュールとよく連携できるならば、導入するのもアリだと思います。

HTMLインポートについては仕様がまだ議論中のようです。

#### アセット参照

importに似た構文で、assetという構文の導入が検討されています。

```ts
asset Logo from "./logo.gif";
```

https://github.com/tc39/proposal-asset-references

「このファイルはimportするわけではないけど後で使うよ」というのを宣言するための構文です。`asset `で宣言しておくとバンドラーが画像やHTMLをまとめてバンドルしてくれる、みたいな事も可能になります。あとはDenoでdllファイルなどを呼び出すときに、`asset`で読み込むことで`--allow-read`,`--allow-net`フラグが不要になり`--allow-ffi`フラグのみでOKになるといった効果が期待できます。
もちろん、TSやリンターのような静的解析にも役立つでしょう。

#### Import Reflection

こちらはImport Assertionの姉妹のような構文で、importしたモジュールの評価方法を指定する構文です。

```ts
import FooModule from "./foo.wasm" as "wasm-module";
```

https://github.com/tc39/proposal-import-reflection

例に出した通り、これはWasmモジュールをimportするための構文です。
（今知ったのですが）Wasmを実行するには型付き配列を`WebAssembly.Module`に一旦変換してから`WebAssembly.Instance`に変換しないと実行できないそうです。
`WebAssembly.Module`と`WebAssembly.Instance`のどちらの形式でimportするかを選べるようにするため、この構文が必要なようです。

実はこのproposal、DenoコアチームのLuca Casonatoさんが主導しています。先日発表された「[DenoがJavaScriptの仕様策定に参加する](https://deno.com/blog/deno-joins-tc39)」というニュースでも`Better support for non-JS assets in the ES module graph`と書かれているとおり、DenoとしてもJSのモジュールシステムを改善していきたいと考えているみたいです。

## まとめ

Deno1.17で導入されるJSONインポートを試してみました。

そもそもNode.jsのエコシステムでは`require`や拡張子無しimportがある上、ライブラリやバンドラーが勝手に独自でCSSやJSONのimportをサポートしていたりする現状があります。
はやくこの辺の構文が統一されて、普通に動くようになってほしいですね。