`shadow-cljs release` の最適化で使わない引数が最適化されてしまいエラーに

hata published on
4 min, 632 words

Categories: programming

詳細はまた追記するかも。

ローカルでは動くのに Firebase にリリースすると動かない。

shadow-cljs release コマンドで生成される minify された JS を見て気づいた。

仮実装で使っていない引数が全て消えてしまって、結果引数なしで呼び出され、その場合 casualdefine が関数ではなく Property として機能を生やす仕様のため、関数呼び出しがエラーになるようになってしまった。

casualdefine の引数ありとなしの場合の、生成される関数/プロパティの仕様はこのドキュメントを見ると分かりやすい。

https://github.com/boo1ean/casual#define

実際の僕のコードはこんなの。

(.define casual
         "hoge"
         (fn [arg1 arg2] "仮の return value"))

shadow-cljs の最適化の問題というよりは、ClojureScript が中で使っている Google Closure Compiler の ADVANCED_OPTIMIZATIONS コンパイルの挙動なんだろうな。

Closure Compiler Compilation Levels  |  Google Developers


(追記) コンパイル結果の違い🔗

(.define casual
         "hoge"
         (fn [arg1 arg2] "仮の return value"))

上記のコードと同様のこのコードが shadow-cljs.edn:compiler-options {:optimizations :simple} の有無で shadow-cljs release の結果がどう変わるか試してみた。

この設定のドキュメントはこちら → 13.1. Release Configuration | Shadow CLJS User’s Guide

指定できる値はソースコードのこのあたり https://github.com/thheller/shadow-cljs/blob/086ee94f9a4e5fac66a9c4fa985364a40ab41ce4/src/main/shadow/build/closure.clj#L116-L121

(case level-kw
  :advanced CompilationLevel/ADVANCED_OPTIMIZATIONS
  :whitespace CompilationLevel/WHITESPACE_ONLY
  :simple CompilationLevel/SIMPLE_OPTIMIZATIONS
  :none nil
  (throw (ex-info "invalid :optimizations level" opts)))]

:optimizations :simple ありの場合🔗

shadow.js.shim.module$casual.define("hoge",function(a,b){return"仮の return value"});

この場合の呼び出しは

// js
casual.hoge(null, null)
; cljs
(.hoge casual nil nil)

のような感じ。関数としてコールする。

:optimizations :simple なし → なので :optimizations :advanced が暗黙的に使われた場合🔗

Ue.define("hoge",function(){return"仮の return value"});

変数名が短くされているのは関係ないのでおいておく。 これだと、呼び出し時は

// js
casual.hoge
; cljs
(.-hoge casual)

とプロパティ参照で使わないといけなくなってしまう。

感想🔗

  1. Google Closure Compoiler の ADVANCED_OPTIMIZATIONS が使ってない引数をシグネチャからも削除してしまう。
  2. casual が関数呼び出しじゃなくてプロパティ参照がデフォルト、という変わった API なこと。

2つ合わさってのはまりどころでした。

Google Closure Compoiler のコンパイルオプションなどで挙動を制御できそうな気もする。