Fable から JavaScript のコードを実行したくなることがある。 刹那的なデバッグをするために、Fable の正しいやり方を置いておいて、ひとまず JavaScript の alert を呼びたいときとか。 きちんとしたことは Fable のドキュメント (link) あたり見るべきなのは分かっているけど……。

やりたいことは Fable.Core.JS.eval を使ってできた。 alert くらいはこれで動く。 試したのは SAFE Stack の環境だけど、通常の Fable のアプリケーションでも同じだと期待している。 このコードがブラウザにロードされると、アラートのダイアログが出て “test jsAlert” というメッセージが表示される。

注意したのは JavaScript を実行するために eval を使っていること。 意図しないコードが eval に渡らないようにした。

例1 (雑に文字列を安全化)

文字列を安全にするため、英数字、空白、ピリオド、カンマでない文字は削除している。 使える文字を過剰なくらい制限しているけど、このような縛りをしたくないなら安直に eval を使わないで、真っ当なやり方を調べて使うのがよい。

元の F# コード

open System.Text.RegularExpressions
let jsAlert (msg: string) =
    let msg = Regex.Replace(msg, "[^ 0-9A-Za-z.,]", "")
    Fable.Core.JS.eval $"alert('{msg}')" |> ignore

jsAlert "test jsAlert"

Fable で変換後の JavaScript コード

export function jsAlert(msg) {
    const msg_1 = replace(msg, "[^ 0-9A-Za-z.,+\\-*/()]", "");
    void eval(toText(interpolate("alert(\u0027%P()\u0027)", [msg_1])));
}

jsAlert("test jsAlert");

例2 (あからさまに危険)

もし任意に文字列を eval に渡せると、 alert にメッセージを渡すだけでは済まず、コードの実行を許してしまう。 以下では F# の jsAlert_Danger 関数には文字列を一つ渡しているけど、JavaScritp を実行すると alert が呼ばれてから、後続のコード (ここでは confirm('Do you see another behavior that is not an alert?')) が実行される。

元の F# コード

let jsAlert_Danger (msg: string) =
    Fable.Core.JS.eval $"alert('{msg}')" |> ignore

jsAlert_Danger "test jsAlert_Danger'); confirm('Do you see another behavior that is not an alert?"

Fable で変換後の JavaScript コード

export function jsAlert_Danger(msg) {
    void eval(toText(interpolate("alert(\u0027%P()\u0027)", [msg])));
}

jsAlert_Danger("test jsAlert_Danger\u0027); confirm(\u0027Do you see another behavior that is not an alert?");