Deno1.17でついにJSONインポートが導入されます!!
もともとDenoにはバージョン0時代にJSONインポートが実装されていました(#1065)。当時の構文はimport json from "./foo.json"
という単純なものでした。
しかし、JSONインポートがECMAScript仕様に入るにあたりassert { type: "json" }
という構文(Import Assertions)への対応が必要になったため、バージョン1.0.0リリース時にJSONインポートは削除されていました(#5037)。
先日TypeScript4.5がリリースされたことで、Import Assertionsへの対応が全て完了したため、再びDenoにJSONインポートが導入されました。(swcやv8などは既に対応しており、TSが最後のブロッカーでした。)
JSONインポートを試す
JSONインポートは12月16日リリースのDeno1.17で導入されるのですが、アドベントカレンダーの担当日的な意味でギリギリ間に合わないので、canary版を使って試していきます。
※canary版のリリースもギリギリ間に合わなかったので(日本時間で12/16の朝4時)、投稿が遅れました。ごめんなさい。
> deno upgrade --canary
先ほど述べた通り、JSONインポートはImport Assertionsと組み合わせて使います。
{ "Hello": "World" }
import data from "./test.json" assert { type: "json" };
console.log(data); //=> { Hello: "World" }
ちなみに、型推論も効きます。
複数回importした時の挙動
1つのJSONファイルを複数回importした時、===
で比較するとtrueになります。
import data1 from "./test.json" assert { type: "json" };
import data2 from "./test.json" assert { type: "json" };
console.log(data1 === data2); //=> true
プロパティを書き換えた時の挙動
上で実験した通り、複数回importすると参照が同一になります。
importしたモジュールの一部を書き換えた場合、他の場所でimportしたモジュールも書き換わってしまうのか試してみます。
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インポートを利用することができます。
const { default: data } = await import("./test.json", {
assert: { type: "json" },
});
console.log(data);
JSONインポートはdefault export
として扱われるため、代入時にdefault
で受ける必要があります。
data URLとの組み合わせも可能です。
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など、いくつかのブラウザでは既に利用可能です。
import sheet from './styles.css' assert { type: 'css' };
Denoにはまだ導入されていません(そもそも導入すべきか決まっていないような状況)。個人的には、将来フロントエンド開発にDenoが使われるようになり、ブラウザのCSSモジュールとよく連携できるならば、導入するのもアリだと思います。
HTMLインポートについては仕様がまだ議論中のようです。
アセット参照
importに似た構文で、assetという構文の導入が検討されています。
asset Logo from "./logo.gif";
「このファイルはimportするわけではないけど後で使うよ」というのを宣言するための構文です。asset
で宣言しておくとバンドラーが画像やHTMLをまとめてバンドルしてくれる、みたいな事も可能になります。あとはDenoでdllファイルなどを呼び出すときに、asset
で読み込むことで--allow-read
,--allow-net
フラグが不要になり--allow-ffi
フラグのみでOKになるといった効果が期待できます。
もちろん、TSやリンターのような静的解析にも役立つでしょう。
Import Reflection
こちらはImport Assertionの姉妹のような構文で、importしたモジュールの評価方法を指定する構文です。
import FooModule from "./foo.wasm" as "wasm-module";
例に出した通り、これはWasmモジュールをimportするための構文です。
(今知ったのですが)Wasmを実行するには型付き配列をWebAssembly.Module
に一旦変換してからWebAssembly.Instance
に変換しないと実行できないそうです。
WebAssembly.Module
とWebAssembly.Instance
のどちらの形式でimportするかを選べるようにするため、この構文が必要なようです。
実はこのproposal、DenoコアチームのLuca Casonatoさんが主導しています。先日発表された「DenoがJavaScriptの仕様策定に参加する」というニュースでもBetter support for non-JS assets in the ES module graph
と書かれているとおり、DenoとしてもJSのモジュールシステムを改善していきたいと考えているみたいです。
まとめ
Deno1.17で導入されるJSONインポートを試してみました。
そもそもNode.jsのエコシステムではrequire
や拡張子無しimportがある上、ライブラリやバンドラーが勝手に独自でCSSやJSONのimportをサポートしていたりする現状があります。
はやくこの辺の構文が統一されて、普通に動くようになってほしいですね。