LoginSignup
199
161

HTMLにTypeScriptを書いてブラウザで直接実行できるって知ってた?

Posted at

百聞は一見に如かず

空のHTMLファイルにコピペしてウェブブラウザで開いてみてください。

ts.html
<!doctype html>
<meta charset="utf-8">

<script async type="module">
    import {transpile, ScriptTarget} from "https://esm.sh/typescript@5.3.3?bundle&target=esnext";
    import {minify_sync} from "https://esm.sh/terser@5.27.0?bundle&target=esnext";

    for(const {type, textContent} of document.getElementsByTagName("script")){
        if(type !== "text/typescript" || !textContent){
            continue;
        }

        const js = transpile(textContent, {
            target: ScriptTarget.ESNext
        });

        const {code} = minify_sync(js, {
            module: true
        });

        await import(`data:text/javascript;base64,${btoa([...new TextEncoder().encode(code)].map(v => String.fromCharCode(v)).join(""))}`);
    }
</script>

<script type="text/typescript">
    interface Test{
        a: string;
        b: number;
        c: boolean;
    }

    const test:Test = {
        a: "aaa",
        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です。
新進気鋭ということで高機能かつ使いやすく、旧来の jsdelivrunpkg などの上位互換となり得そうです。

1. TypeScript記述

ウェブブラウザは読み込み時に script 要素の type 属性を見てJavaScriptかどうかを識別し、実行する仕様となっています。
(無い場合は暗黙的にJavaScriptと見なす)

この挙動を利用し、明示的にメディアタイプを指定することでTypeScriptを記述しつつ実行を阻止しています。

2. トランスパイル

typescript をインポートしています。
これは本家本元のTypeScriptパッケージです。

script 要素のうち、メディアタイプがTypeScriptな要素のみを抽出しJavaScriptにトランスパイルします。

3. ソースコード圧縮

terser をインポートしています。
これはソースコード圧縮、いわゆる「ミニファイ」を行うパッケージです。

トランスパイルしたJavaScriptを圧縮します。

4. 動的インポート

実はESMの import はDataURLも読み込めます。
つまりスクリプトを直接埋め込めるわけです。

圧縮したJavaScriptをDataURLへ変換し埋め込み実行します。

export は存在せず、ただスクリプトの内容を上から実行できれば良いので、特に代入はしません。

このとき、スクリプトはUTF-8でエンコードしBASE64へ変換しています。
マルチバイト文字や空白をURLとして認識できないためです。
また、スクリプト(DataURL)の肥大化によりインポート処理が不安定になる可能性を低減するため、前項で圧縮しています。

まとめ

ちなみに同じ理論で SASS/SCSS も行けます。
が、こちらは既に CSS Nesting があるので...
是非 ECMAScript Type Annotations も何卒よろしくお願い致します...

意外とそのままウェブブラウザで動くnpmパッケージは多いので、ダメ元でesm.shに打ち込んで試してみるのはアリ。
Node.jsやOS固有の機能を呼び出してる場合は当然ながら動かないです。

199
161
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
199
161