百聞は一見に如かず
空のHTMLファイルにコピペしてウェブブラウザで開いてみてください。
<!doctype html>
<meta charset="utf-8">
<script async type="module">
import {transpile, ScriptTarget} from "https://esm.sh/typescript@5.6.3?target=esnext";
import {minify} from "https://esm.sh/terser@5.36.0?target=esnext";
// esnext-polyfill
Uint8Array.prototype.toBase64 ??= function() {
return btoa(Array.from(this, (v) => String.fromCharCode(v)).join(""));
}
for(const {type, textContent} of document.getElementsByTagName("script")) {
if(type !== "text/typescript" || !textContent) {
continue;
}
const js = transpile(textContent, {
target: ScriptTarget.ESNext
});
const {code} = await minify(js, {
module: true
});
await import(`data:text/javascript;base64,${new TextEncoder().encode(code).toBase64()}`);
}
</script>
<script type="text/typescript">
interface Test {
a: string;
b: number;
c: boolean;
}
const test: Test = {
a: "foo",
b: 111,
c: true
};
await new Promise<void>(done => setTimeout(done, 100));
console.log(test);
document.getElementById("status").textContent = "Complete!";
</script>
<title>TypeScript on Web</title>
<h1>TypeScript on Web</h1>
<p>TypeScript realtime transpile and execution demo.</p>
<p>[Status] <span id="status">Processing...</span></p>
何が起きている?
まず esm.sh はESMを配信するCDNです。
新進気鋭ということで高機能かつ使いやすく、旧来の jsdelivr や unpkg などの上位互換となり得そうです。
1. TypeScript記述
ウェブブラウザは読み込み時に script
要素の type
属性を見てJavaScriptかどうかを識別し、実行する仕様となっています。
(無い場合は暗黙的にJavaScriptと見なす)
この挙動を利用し、明示的にメディアタイプを指定することでTypeScriptを記述しつつ実行を阻止しています。
2. トランスパイル
typescript をインポートしています。
これは本家本元のTypeScriptパッケージです。
script
要素のうち、メディアタイプがTypeScriptな要素のみを抽出しJavaScriptにトランスパイルします。
3. ソースコード圧縮
terser をインポートしています。
これはソースコード圧縮、いわゆる「ミニファイ」を行うパッケージです。
トランスパイルしたJavaScriptを圧縮します。
4. 動的インポート
実はESMの import
はDataURLも読み込めます。
つまりスクリプトを埋め込めるわけです。
圧縮したJavaScriptをUTF-8でエンコードしBase64のDataURLにして埋め込み、実行します。
export
は存在せず、ただスクリプトの中身を上から実行できれば良いので、特に代入はしません。
また、スクリプト(DataURL)の肥大化によりインポート処理が不安定になる可能性を低減するため、前項で圧縮しています。
まとめ
ちなみに同じ理論で SASS/SCSS も行けます。
が、こちらは既に Nesting や Scope があるので...
是非 ECMAScript Type Annotations も何卒よろしくお願い致します...
意外とそのままウェブブラウザで動くnpmパッケージは多いので、ダメ元でesm.shに打ち込んで試してみるのはアリ。
Node.jsやOS固有の機能を呼び出してる場合は当然ながら動かないです。