Webpack + Babel + core-js な環境でビルドしたJavaScriptをPerlサーバーで配信するプロジェクトで、あるときPerlサーバーで配信しようとしたときに Internal Server Error がでてしまった。
ログを確認すると、Wide character in subroutine entry at /path/to/Compress/Zlib.pm
というエラーがでていた。どうやら、配信しようとしていたJavaScriptにUnicodeが含まれていたため、Perlの内部文字列のutf8フラグが立ってしまい、そのままgzip圧縮をする Compress::Zlib
モジュールに渡ってしまったのが原因のようだ。そのため、$content = encode("UTF-8", $content);
としてUTF-8バイト文字列に変換すればよいのだが、そもそも元のJavaScriptにはUnicodeは含まれていない。しかし、ビルドされたJavaScriptを確認すると、たしかにUnicodeが含まれている。
いろいろ調査したところ、どうやら Terser は ascii_only
オプションが false
の時(デフォルト)は、"\u2028"
といった Unicode Escape Sequence を展開してしまうことがあるらしいということがわかった。そして、core-js によって挿入された whitespaces.js
というモジュールには Unicode Escape Sequence が含まれており、Terser がこれを展開してしまったため、Perlで読み込まれたときにutf8フラグが立ってしまい問題が引き起こされたようだ。
そのため、webpack.config.js
で以下のように Terser の ascii_only
オプションを true
とすることで問題を解決した。
const TerserPlugin = require('terser-webpack-plugin'); module.exports = { ... optimization: { minimizer: [new TerserPlugin({ terserOptions: { format: { ascii_only: true } } })] } }
今回のような状況下でなくても、Unicode Escape Sequence が勝手に展開されるのはバグの温床になるので、ascii_only
オプションは常に true
としておいたほうがよさそうである。
なお、このときの環境は以下の通り。
- Node.js: 16.13.2
- Webpack: 5.51.1
- Babel: 7.15.0
- core-js: 3.16.3
- terser: 5.7.1
- terser-webpack-plugin: 5.1.4