211
173

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-01-29

百聞は一見に如かず

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

ts.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です。
新進気鋭ということで高機能かつ使いやすく、旧来の jsdelivrunpkg などの上位互換となり得そうです。

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 も行けます。
が、こちらは既に NestingScope があるので...
是非 ECMAScript Type Annotations も何卒よろしくお願い致します...

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

211
173
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
211
173

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?