【Rails】アセットプリコンパイルが ExecJS::ProgramError: Unexpected token で詰まった


サーバにRailsのプロジェクトをデプロイしてアセットプリコンパイルをした時に少し躓いたので備忘録程度に残しておく。

コマンドなどは[Rails Asset Pipelineがうまくいかないときの問題の切り分けかた](http://qiita.com/metheglin/items/c5c756246b7afbd34ae2)を参考にさせていただきました。

## 環境
 - centos-6.7
 - Rails4.2.1


## 何が起きたのか

```
[hoge@huga project_dir]$ rake assets:precompile RAILS_ENV=production
...
rake aborted!
ExecJS::ProgramError: Unexpected token: punc ()) (line: 18, col: 1, pos: 347)

Error
    at new JS_Parse_Error (<eval>:2659:11936)
    at js_error (<eval>:2659:12155)
    at croak (<eval>:2659:20622)
    at token_error (<eval>:2659:20759)
    at unexpected (<eval>:2659:20847)
    at semicolon (<eval>:2659:21320)
    at simple_statement (<eval>:2659:24179)
    at <eval>:2659:22067
    at <eval>:2659:21493
    at <eval>:2660:1595new JS_Parse_Error ((execjs):2659:11936)   
```
 
とエラーが出た。

```
ExecJS::ProgramError: Unexpected token: punc ()) (line: 18, col: 1, pos: 347)
```
とあるので、どこかのファイルの18行目（line: 18）によくわからない")"が入っているよう。

bootswatchかbootstrapzeroか何かのテンプレートを使っていたので、jsのファイルも大量にあり、どのファイルがエラーを起こしているか分からない状態。

## どのファイルか分からない

```
$ rake assets:precompile RAILS_ENV=production
```
上のコマンドを叩くとプリコンパイルされたファイルが順番に出力されるのだが、エラー出た時にスペル間違えたかなとか考えて上のコマンドを複数回叩いてしまうと、既にプリコンパイルが通ったファイルは出てこなくなってしまうので、どのファイルでエラーが吐き出されたか分からなくなってしまう。
### 確認方法
```
$ rake assets:clobber RAILS_ENV=production
```

上のコマンドで一度プリコンパイル済みのファイルを消す。
そのうえで、

```
$ rake assets:precompile RAILS_ENV=production
```

を再度実行すると、再び順番にファイルが表示されるので、改めて確認できる。

#### 例

```txt:
[hoge@huga project_dir]# rake assets:clobber RAILS_ENV=production
I, [2016-03-13T11:17:21.016229 #15048]  INFO -- : Removed /path/to/project_dir/public/assets
rm -rf /path/to/project_dir/tmp/cache/assets
[hoge@huga project_dir]# rake assets:precompile RAILS_ENV=production
I, [2016-03-13T11:17:44.155293 #15050]  INFO -- : Writing /path/to/project_dir/public/assets/bootstrap-6f46133c56bb1d0c175c4aa13a4b92eaf1698d0099f9f7883e02cc46.css
I, [2016-03-13T11:17:44.460988 #15050]  INFO -- : Writing /path/to/project_dir/public/assets/main-dda09b290fa83f87fb61c84141abe63649f04a5631ee4c13961f.css
（省略）
I, [2016-03-13T11:17:47.325617 #15050]  INFO -- : Writing /path/to/project_dir/public/assets/retina-2a5fee7353499948abdb51cfab12b599fa47d.js
I, [2016-03-13T11:17:47.398177 #15050]  INFO -- : Writing /path/to/project_dir/public/assets/jquery.easing.1.3-710024002e0292a08d24791d0e0186db6f8ba68e74025c89b3bf2.js
I, [2016-03-13T11:17:47.466105 #15050]  INFO -- : Writing /path/to/project_dir/public/assets/smoothscroll-a8b941cc3911cf557ca8e2cd17ad3ddd144f128752be16144c.js
rake aborted!
ExecJS::ProgramError: Unexpected token: punc ()) (line: 18, col: 1, pos: 347)
（以下省略）
```

上の場合では、smoothscroll.jsの後のファイルのプリコンパイルでエラーが出ていることがわかる。

## プリコンパイルするファイルの順番
先ほどの例で、smoothscroll.jsの後のファイルのプリコンパイルで失敗していることがわかった。しかし肝心のsmoothscroll.jsの後のファイルが何かわかっていない。

### 確認方法
rails consoleで以下のコマンドを叩くことで調べることができる

```
[hoge@huga project_dir]# rails c -e production
Loading production environment (Rails 4.2.1)
2.2.1 :001 > Rails.application.config.assets.precompile
```

#### 例

```
[hoge@huga project_dir]# rails c -e production
Loading production environment (Rails 4.2.1)
2.2.1 :001 > Rails.application.config.assets.precompile
 => [#<Proc:0x000000037963e8@/usr/local/rvm/gems/ruby-2.2.1/gems/sprockets-rails-2.3.3/lib/sprockets/railtie.rb:60 (lambda)>, /(?:\/|\\|\A)application\.(css|js)$/, "admin/*.css", "admin/*.js", "bootstrap.css", "main.css",（省略）, "retina.js", "jquery.easing.1.3.js", "smoothscroll.js", "jquery-func.js", （省略）]
```

Rails.application.config.assets.precompileで得られるファイルの順にプリコンパイルされるようなので、smoothscroll.jsの後のjquery-func.jsでエラーが出ていることがわかる。

## ファイルの中身
jquery-func.jsを見てみると

```js:jquery-func.js
!function($) {
（省略）
});
```

もともとのエラー

```
ExecJS::ProgramError: Unexpected token: punc ()) (line: 18, col: 1, pos: 347)
```

によると18行目に不要な")"があると言うことだったが、このjquery-func.jsの最終行が18行目であり、セミコロン直前の")"がいらなかった。これを消してprecompileしたらエラーは消えた。

結局自前で用意したファイルだった。