Node.jsとgulpをアップデートして、Dart Sassにしたお話

# はじめに

私が担当している弊社サービスでは、大きな問題がなかったため、長い間Node.jsをアップデートしないまま開発を行っていました。（Node.jsは、gulpを使用（Sassをコンパイル・画像圧縮など）するために入れています）
そんな中、新メンバーが加入することになり、PCのセットアップをする際、旧バージョンのNode.jsはさすがに入れられないということが発覚し、急遽アップデートしないといけない事態になってしまいました :sweat_drops:
今回は急いで対応を迫られ、ようやく重い腰を上げて対応したアップデートのお話になります。
（ちなみに、対応した私は黒画面が苦手な部類の人間です:skull:）


![nimotsu_hakobu_woman.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54595/802e5f1c-747a-173f-3f66-ef63c475b55a.png)


# チャレンジ :one:

| | before | after |
|:-:|:-:|:-:|
| **Node.js** | v10.15.3  | v14.21.3 |


※ 一気に`v18.x`までアップデートしようとしたのですが、Sassの記述エラーを修正する必要があったため、段階を踏むことになりました。

## Node.jsのバージョンアップ
### 【悲報】nodistの開発停止

Windows環境では、`nodist`でNode.jsのバージョンを管理していたのですが、新しいバージョンをインストールしたところ、`npm does not support Node.js`というエラーが出てしまいました。
調べてみたところ、`nodist`はすでに開発が停止していたようで、`npm`のバージョンが正常にアップデートできていないために出ていたエラーでした。
そこで、新たなバージョン管理として、WindowsでもMacでも使用できる`nvm`に変更することにし、無事にアップデートすることができました。

**:pencil2: 参考記事**

https://qiita.com/SakuraiYuki/items/cca5365e5cad02d24d83

https://qiita.com/akuden/items/a88630de9624039c4135

https://zenn.dev/ymasaoka/articles/note-uninstall-nodish-windows

## package.jsonの内容見直し

Node.jsのアップデートに伴い、`package.json`の各プラグインのバージョンを確認し、アップデートしました。

### バージョンの確認方法① npm-outdated
- [npm-outdated](https://docs.npmjs.com/cli/v10/commands/npm-outdated)

```bash
# 確認する
$ npm outdated
```

```bash
# 出力結果
Package                            Current  Wanted  Latest  Location                                        Depended by
gulp-imagemin                        7.1.0   7.1.0   9.0.0  node_modules/gulp-imagemin                      moppy
imagemin-mozjpeg                     9.0.0   9.0.0  10.0.0  node_modules/imagemin-mozjpeg                   moppy
stylelint-config-recommended-scss   13.0.0  13.1.0  13.1.0  node_modules/stylelint-config-recommended-scss  moppy
stylelint-scss                       5.3.0   5.3.1   5.3.1  node_modules/stylelint-scss                     moppy
（一部省略）
```
:::note info
出力される結果 
- Current：現在インストールされているバージョン
- Wanted：存在するバージョンのうち、package.json に記載された semver の条件を満たす最新のバージョン
- Latest：最新バージョン
:::

必要な情報を確認することができます。

### バージョンの確認方法② npm-check-updates

- [npm-check-updates](https://weev.media/article/296)

```bash
# npm-check-updatesをインストールする
$ npm install -g npm-check-updates

# 確認する
$ ncu

# アップデートする
$ ncu -u
```

```bash
# 出力結果
 gulp-imagemin                        ^7.1.0  →    ^9.0.0
 imagemin-mozjpeg                     ^9.0.0  →   ^10.0.0
 prettier                             ^3.0.3  →    ^3.1.0
 sass                                ^1.68.0  →   ^1.69.5
 stylelint                          ^15.10.3  →  ^15.11.0
 stylelint-config-recommended-scss   ^13.0.0  →   ^13.1.0
 stylelint-scss                       ^5.2.1  →    ^5.3.1
 （一部省略）
 ```

必要な情報を確認することができ、コマンドでアップデートすることもできます。
こちらのコマンドの方が便利かもしれませんね。

**:pencil2: 参考記事**

https://zenn.dev/yoshii0110/articles/820187fd237b44

### browserslist

サービス内の推奨環境に合わせて、内容を見直しました。

- [Browserslist（last 2 versions）](https://browsersl.ist/#q=last+2+versions&region=JP)

```package.json
  "browserslist": [
    "last 2 versions",
    "Android >= 9",
    "ios_saf >= 14" 
  ],
```

## gulpfile.jsの内容見直し

gulpのバージョンが、`3.9.1` → `4.0.2`に。アップデートに伴い、書き換えを行いました。
（自力では難しかったため、エンジニアさんの力をお借りしました）

**:pencil2: 参考記事**

https://qiita.com/hibikikudo/items/493fbfbbea183c94b38b


## Sassをコンパイル（CSSに変換する）

gulpのコマンドを叩き、Sassをコンパイルします。
差分内容を確認し、表示に問題ないか確認後、リリースを行いました。
browserslistの変更に伴い、細かい部分で差分が発生しました。

:::note warn
主な差分内容
- flex系の記述の`-webkit-`が削除される変更
- グラデーションの記述の`-webkit-`が削除される変更
- `position: sticky;`の`-webkit-`が削除される変更
- `:placeholder`にベンダープレフィックスが追加される変更
:::

# チャレンジ :two:

Node.jsを`v18.x`にアップデートする前に、事前に把握できたSass側の2つのエラーを修正することにしました。


## 除算の`/`

Sassにおける除算で `/`（スラッシュ） は非推奨となり、将来的に廃止されるため、記述を変更しました。
まだ、Dart Sassではないので`calc(100% / 2)`の形で修正しました。

```
Deprecation Warning: Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0.
Recommendation: math.div(100%, 2) or calc(100% / 2)
More info and automated migrator: https://sass-lang.com/d/slash-div
```

```Scss
// NG
width: 100% / 2;

// OK
width: calc(100% / 2);
```

## 変数でのカラー管理

変数で使用しているkeyをダブルクォーテーションで囲んでいないとエラーが出てしまっていたので、ダブルクォーテーションで囲むように修正しました。

```
Warning: You probably don't mean to use the color value red in interpolation here.
It may end up represented as red, which will likely produce invalid CSS.
Always quote color names when using them as strings or map keys (for example, "red").
If you really want to use the color value here, use '"" + $color'.
```


```Scss
// NG
$pattern1-colors: red, yellow, green;

// OK
$pattern1-colors: "red", "yellow", "green";
```

# チャレンジ :three:

| | before | after |
|:-:|:-:|:-:|
| **Node.js** | v14.21.3  | v18.18.0 |

いよいよ、`v18.x`までバージョンをアップデートし、`LibSass`を`Dart Sass`に変更していきます。

## Node.jsのバージョンアップ

最新のバージョンを確認して、バージョンアップを行いました。

```bash
# 最新のバージョンを確認する
$ nvm list available

|   CURRENT    |     LTS      |  OLD STABLE  | OLD UNSTABLE |
|--------------|--------------|--------------|--------------|
|    20.8.0    |   18.18.0    |   0.12.18    |   0.11.16    |
|    20.7.0    |   18.17.1    |   0.12.17    |   0.11.15    |
|    20.6.1    |   18.17.0    |   0.12.16    |   0.11.14    |
|    20.6.0    |   18.16.1    |   0.12.15    |   0.11.13    |
|    20.5.1    |   18.16.0    |   0.12.14    |   0.11.12    |
|    20.5.0    |   18.15.0    |   0.12.13    |   0.11.11    |
|    20.4.0    |   18.14.2    |   0.12.12    |   0.11.10    |
|    20.3.1    |   18.14.1    |   0.12.11    |    0.11.9    |
|    20.3.0    |   18.14.0    |   0.12.10    |    0.11.8    |
|    20.2.0    |   18.13.0    |    0.12.9    |    0.11.7    |
|    20.1.0    |   18.12.1    |    0.12.8    |    0.11.6    |
|    20.0.0    |   18.12.0    |    0.12.7    |    0.11.5    |
|    19.9.0    |   16.20.2    |    0.12.6    |    0.11.4    |
|    19.8.1    |   16.20.1    |    0.12.5    |    0.11.3    |
|    19.8.0    |   16.20.0    |    0.12.4    |    0.11.2    |
|    19.7.0    |   16.19.1    |    0.12.3    |    0.11.1    |
|    19.6.1    |   16.19.0    |    0.12.2    |    0.11.0    |
|    19.6.0    |   16.18.1    |    0.12.1    |    0.9.12    |
|    19.5.0    |   16.18.0    |    0.12.0    |    0.9.11    |
|    19.4.0    |   16.17.1    |   0.10.48    |    0.9.10    |

# バージョンをインストールする
$ nvm install 18.18.0

# 指定のバージョンに切り替える
$ nvm use 18.18.0

# 確認する
$ node -v
```

## package.jsonの内容見直し

チャレンジ :one:と同様にコマンドを叩いて確認し、バージョンを変更しました。
一部のプラグインで`Could not resolve dependency`というエラーが出てしまっため、npmをインストールする際にオプションをつけて対応しました。

```bash
$ npm i --save --legacy-peer-deps stylelint-config-prettier
```

**:pencil2: 参考記事**

https://qiita.com/koh97222/items/c46d1ef2a63b92bb6c15

## gulpfile.jsの内容見直し

gulp-sassのバージョンが、`4.1.1` → `5.1.0`に。アップデートに伴い、書き換えを行いました。

```diff_javascript:gulpfile.js
- const sass = require('gulp-sass');
+ const sass = require('gulp-sass')(require('sass'));
```

## Dart Sassに変更

いよいよDart Sassの書き方に変更していきます。
サイトの構造に合わせて、`@forward` `@use`に書き換えを行っていきます。
（今回の変更は、既存のサイト構造に合わせて、ミニマムでできる内容にしました）

## `@forward`

元々、サイト共通の変数・mixinなどをまとめたSassがあったので、このファイルを`@forward`を用いて修正しました。


```diff_sass:_core.scss
// 構成
- @import "_core/_config";
+ @forward "_core/_config";

// 変数
- @import "_core/_variable";
+ @forward "_core/_variable";

// mixin
- @import "_core/_mixin";
+ @forward "_core/_mixin";

// function
- @import "_core/_function";
+ @forward "_core/_function";
```

## `@use`

元々、必要なページでは上記の`_core.scss`を読み込ませていたので、全てのファイルを`@use`の書き方に修正しました。

```diff_sass:index.scss
// # コアファイル
// ============================================================
- @import "_core";
+ @use "_core" as *;
```

- 名前空間を指定した方が望ましいのですが、既存ファイルが膨大なため、名前空間はなしの意味の`as *`を採用しました。

各パーツも`@use`の書き方に修正しました。

```diff_sass:index.scss
// アイコン
- @import "_widget/_icon";
+ @use "_widget/_icon";
```

以上で書き換え作業は完了です！

## コンパイルエラー

作業中によく出たエラーです。
エラーの内容から対処方法を確認し、エラーが出なくなるまで作業しました。

| エラー | 内容 | 対処方法 |
|:--|:--|:--|
| ![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54595/64f6c77f-3e5f-4c80-43ad-623d6aa3f5f9.png) | 変数に値がないですよというエラー | コアファイルを読み込むように対応しました  |
| ![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54595/21546603-f5ce-3506-8904-a4afdd204fd6.png) | `@use`ルールは他のルールよりも前に記述する必要がありますよというエラー  | `@use`を先に記述し、`@import`（Webフォント）はあとに記述するように対応しました |
| ![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54595/85be290c-899e-b7c6-d630-d2d40808a622.png) | モジュールがループしてしまっていますよというエラー | モジュール内のパーツファイルは、必要なモジュールのみ読み込むように対応しました |

**:pencil2: 参考記事**

https://qiita.com/h-iwamori/items/23a7b015881e35d5c9da

https://qiita.com/super-mana-chan/items/fbfa5447ae893bed3b4c

https://qiita.com/ryo_hisano/items/250364999319b802decb


## Sassをコンパイル（CSSに変換する）

最後にgulpのコマンドを叩き、Sassをコンパイルします。
差分内容を確認し、表示に問題ないか確認後、リリースを行いました。
Node.jsのアップデート・Dart Sass変更に伴い、細かい部分で差分が発生しました。

:::note warn
主な差分内容
- CSSのセレクタの指定方法で、前方・後方・部分一致内の`""`が削除される変更
- 空行が詰まる変更
- コンパイル後のクラス名の順番変更
- calcの計算のタイミングの変更
- 小数点以下の取り扱いが10桁に変更
- `@extend`の書き出しの変更
:::

以上で対応は完了です！

# まとめ

いかがでしたでしょうか？
何度もエラーでつまずき、しばらくgulpでうなされる夢を見ましたが、なんとかバージョンアップすることができました :relaxed:

実際に作業してみて気がついたことは、**「バージョンの互換性が大事！」** **「バージョンによって書き方が異なる場合がある！」** ということです。

基本的にはエラーの内容を見て、調べて、修正して、チームメンバーに助けを求めて・・・の繰り返しだったかと思います。
最後に簡単にアップデート手順をまとめました。

:::note info
アップデート手順
- 最新のNode.jsのバージョンを確認する
- 各プラグインの最新バージョンを確認する
- 検証・テスト・表示が崩れないか確認する
- レビュー・リリースをする

:::

本記事を読んでみて、アップデートする際の参考になりましたら幸いです :bow_tone1: