🧗‍♂️

演算子 ?? が使えて ??= が使えない理由と Nuxt.js でSSRのときだけ ??= がこける理由

2023/03/22に公開

環境

$ node -v
v19.8.1

$ npm i @babel/cli
$ babel -V
7.21.0 (@babel/core 7.21.3)

$ nuxt -v
@nuxt/cli v2.16.3

?? (Nullish Coalescing Operator - ES2020)

https://babeljs.io/docs/babel-plugin-proposal-nullish-coalescing-operator

$ echo "null ?? 1; let a; a ??= 1" | babel --no-babelrc --plugins @babel/plugin-proposal-nullish-coalescing-operator -f -
var _ref;
(_ref = null) !== null && _ref !== void 0 ? _ref : 1;
let a;
a ??= 1;
  • @babel/plugin-proposal-nullish-coalescing-operator で有効になる
  • ?? はトランスパイルしてくれているが ??= はそのままなことがわかる

??= (Logical Assignment Operators - ES2021)

https://babeljs.io/docs/babel-plugin-proposal-logical-assignment-operators

echo "let a; a ||= 1; a ??= 1" | babel --no-babelrc --plugins @babel/plugin-proposal-logical-assignment-operators -f -
let a;
a || (a = 1);
a ?? (a = 1);
  • @babel/plugin-proposal-logical-assignment-operators で有効になる
  • 本来は ||= と書けるようにするためのもの
  • ついでに ??= にも対応しているというだけ
  • つまり ????= は別ものだった

Nuxt.js で SSR すると ??= でこける問題

Nuxt.js で SSR するとサーバーサイドでのみ ??= が失敗する。当初 ?? が有効なら ??= も使えると考えていたし、SSR時のクライアントサイドどサーバーサイドの環境が異なっているとは思わなかったので、サーバーサイドかつ ??= のときだけおかしくなるという条件に気づくまでが本当に長かった。

こちらを見ると、

https://github.com/nuxt/nuxt/blob/4b6a504d66c5dc0f98831675b018e7991d7e915a/packages/babel-preset-app/src/index.js#L173-L177

サーバーサイドで中途半端に ?? こと @babel/plugin-proposal-nullish-coalescing-operator だけを有効にしているのがわかったので .babelrcplugins に追加する。

.babelrc
{
  "presets": [
    "@babel/preset-env",
  ],
  "plugins": [
    "@babel/plugin-proposal-logical-assignment-operators",
  ],
}

ところがぜんぜん反映されないのでドキュメントを見ると、

https://develop365.gitlab.io/nuxtjs-2.8.X-doc/ja/api/configuration-build/#babel

JavaScript や Vue ファイルのために Babel の設定をカスタマイズします。 .babelrc はデフォルトで無視されます。

とのこと。そういうときに .babelrc があれば警告を出してあげるぐらいの思いやりはほしい。不親切にもほどがある。

@nuxt/babel-preset-app のデフォルトターゲットは client ビルドでは ie: '9'、server ビルドでは node: 'current' になります。

これは微調整していた package.json の browserslist もまったく効いていなかったということなのだろう。

ちなみに @babel/cli で入れた babel コマンドは nuxt.config.js の方なんか見るわけもなく .babelrc の方を見るし、package.json の browserslist も見る。もう、ややこしすぎて未来の自分がこの記事を読んで理解できそうに思えない。

もともと複雑なもの @babel/preset-app をより複雑なもの @nuxt/babel-preset-app でラップしてはいけない(切実)

結局、次のようにするとサーバーサイドでも ??= が効くようになった。

nuxt.config.js
...
  build: {
    babel: {
      plugins: [
        "@babel/plugin-proposal-logical-assignment-operators",
      ],
    },
  },
...

なお、クライアントサイドでこけなかった理由はわからない。

まとめ

Discussion