とあるプロジェクトである日突然にそれは訪れました。
デプロイ時のassets:precompile
でエラーが発生し、デプロイに失敗するようになったのです。
config系の設定値は変えてないし、特にバージョンアップなどしてないし、変更したことといえばいくつかライブラリを追加した(nodeのパッケージ)のとReactのファイルをいくつか追加したぐらい🤔
同じような現象でハマる人が減りますようにという期待を込めて、情報共有として残しておきます。
TL;DR
- sprockets + gemのuglifierを使用してjsのcompileをしている場合は、compile用のgemをterserに変更して、assets.rbも変更したほうが良さそう
- uglifierが使えるのはES6まで
- railsもterser推奨っぽい
何が起こった?
冒頭で概略は説明しましたが、デプロイ時に実際に出たエラーは以下のような感じです(プロジェクト名・ブランチ名の部分は伏せてあります)
# ...(略)
rake stdout: Nothing written
rake stderr: rake aborted!
Uglifier::Error:
/var/www/xxx/app/assets/config/manifest.js:2
/var/www/xxx/shared/bundle/ruby/3.0.0/gems/uglifier-4.2.0/lib/uglifier.rb:291:in `parse_result'
/var/www/xxx/shared/bundle/ruby/3.0.0/gems/uglifier-4.2.0/lib/uglifier.rb:221:in `run_uglifyjs'
/var/www/xxx/shared/bundle/ruby/3.0.0/gems/uglifier-4.2.0/lib/uglifier.rb:166:in `compile'
どうやらUglifier
のエラーのようです。しかしそれ以上のことはほとんどわかりません。。
調べてみる
uglifierのエラーでググってみると、ES6構文を使うにはjs_compressorの設定を書き変えるといいよーという記事がいくつかヒットします。
config.assets.js_compressor = Uglifier.new(harmony: true)
「いやこれはやってるっちゅーねん😅」
という想いを押し殺して根気よくいろいろ記事を漁ってみます。
そうすると同じような現象のissueが出てきました。↓
The Safe Navigation / Optional Chaining operator (?.) was first introduced in ECMAScript 2020 (ES2020). For various reasons, Uglifier supports up to ES5 (ES6 with harmony: true). #43610 leads to (rails/sprockets#713), which mentions to use terser gem instead.
なにやらES6構文まではuglifierが使えるけど、ES2020とかになるとterserを使えとのことでした。
回答のリンクを辿ってみると、rails自体terserを推奨に変わっているようでした。↓
この通り設定を変えて祈るようにデプロイしたところ無事デプロイ成功できました。
どうやら新しく追加したReactのファイル内にES7以降の構文が使われていたんでしょう。
(ただReactはTypeScriptで書いており、tsconfigのtargetはes5にしてあったので、sprocketsでcomplileされるときはes5の構文で渡されるのでは?とか思う部分もあるので、詳しい原因とか挙動については検証しきれていないです。)
具体的な設定
一応どのように設定したら良いか書いておきます(リンクのissueを見ればすぐわかりますが)
- gem 'uglifier'
+ gem 'terser'
config.assets.js_compressor = :terser
さいごに
フロントエンドのビルドにwebpackerやwebpackだけを使っている場合はこのような問題は起こらないはずですが、レガシープロジェクトでどうしてもsprocketsをやめられない事情がある場合はterserに移行したほうが良さそうです。
もう少し詳しい事情をご存知のかたおりましたらコメントいただけると助かります