Dart Sassへの移行でつまづいたこととか

[公式ブログ](https://sass-lang.com/blog/the-module-system-is-launched#future-plans)によると新しいモジュールシステム導入に伴い、これまで使用されていた`@import`ルールは2021年10月には非推奨に、2022年10月にはサポートが終了する予定になっています。
これにより新しいモジュールシステムをサポートしていない[LibSassは非推奨になり](https://sass-lang.com/blog/libsass-is-deprecated)、Dart Sassへの移行が推奨されるようになりました。
今回はDart Sassへの移行の際につまづいたことや気づいたことをまとめたいと思います。


## 除算演算子（/）はsass:mathモジュールに変更する
除算演算子をそのままコンパイルすると警告が出力されます。

```scss
.hoge {
  width: (100% / 3); // Deprecation Warning: Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0.
}
```

CSSではスラッシュを演算子ではなくセパレータ（区切り文字）として使用するものが増えてきており、これに倣ってSassもスラッシュをセパレータとして再定義することになったようです。
そのため演算子として記述していたスラッシュは非推奨となり、sass:mathモジュールのdiv関数に変更する必要があります。
ただし`calc()`関数の内部にあるスラッシュは非推奨の対象となりません。

### 変更後

```scss
@use "sass:math";

.hoge {
  width: math.div(100%, 3);
}
```


## map-get関数はsass:mapモジュールに変更する
Dart Sassではすべての組み込み関数が組み込みモジュールに移行されました。
それぞれ名前も変更され、`map-get`は`map.get`の形式で呼び出す必要があります。

```scss
$breakpoints: (
  sm: 768px,
  md: 960px,
  lg: 1200px,
);

@mixin max-screen($breakpoint) {
  @media (max-width: #{map-get($breakpoints, $breakpoint)}) {
    @content;
  }
}

.hoge {
  margin-top: 80px;
  @include max-screen(sm) {
    margin-top: 160px;
  }
}
```

### 変更後

```scss
@use "sass:map";

$breakpoints: (...);

@mixin max-screen($breakpoint) {
  @media (max-width: #{map.get($breakpoints, $breakpoint)}) {
    @content;
  }
}

.hoge {...}
```

また、Dart Sassでは2つ以上の引数での`map.get`の呼び出しをサポートしています。
第二引数、第三引数と記述していくことで、ネストされた値を取得できます。

```scss
$easing: (
  "ease-in": (
    "easeInQuad": cubic-bezier(0.550, 0.085, 0.680, 0.530),
    "easeInCubic": cubic-bezier(0.550, 0.055, 0.675, 0.190)
  )
);

@debug map.get($easing, ease-in, easeInCubic); // cubic-bezier(0.55, 0.055, 0.675, 0.19)
```


## `@forward`と`@use`を併記して読み込みを簡潔にする
以下のようなファイル構成があるとします。

```scss:_variables.scss
$color-main: #f88 ;

$breakpoints: (
  sm: 768px,
  md: 960px,
  lg: 1200px,
);
```

```scss:_mixin.scss
@use "sass:map";
@use 'variables' as *;

@mixin max-screen($breakpoint) {
  @media (max-width: #{map.get($breakpoints, $breakpoint)}) {
    @content;
  }
}
```

```scss:style.scss
@use 'variables' as *;
@use 'mixin' as *;

.un_heading {
  color: $color-main;
  font-weight: 700;
  @include max-screen(sm) {
    font-weight: 400;
  }
}
```

`_variables.scss`に定義されている変数を利用する場合、記述したいファイルの先頭で`_variables.scss`を読み込む必要があります。
ただし今回の場合`_mixin.scss`に`@forward`の記述を追加することで、`_mixin.scss`を読み込んでいる`style.scss`で変数の使用が可能になります。
注意点として`@forward`は`@use`より前に記述しなければなりません。

### 変更後

```scss:_mixin.scss
@use "sass:map";
@forward 'variables';
@use 'variables' as *;

@mixin max-screen($breakpoint) {...}
```

```scss:style.scss
@use 'mixin' as *;

.un_heading {
  color: $color-main;
  font-weight: 700;
  @include max-screen(sm) {
    font-weight: 400;
  }
}
```


## Globを使う
これまでは`gulp-sass-glob`というプラグインで対応していましたが、このプラグインは`@use`ルールに対応していないのでGlobが使えません。

```
├ src
│ └ scss
│   ├ _partials
│   │ ├ _function.scss
│   │ ├ _mixin.scss
│   │ └ _variables.scss
│   └ style.scss
└ dest
  └ css
    └ style.css
```

```js:gulpgile.js
const gulp = require('gulp');
const sass = require('gulp-dart-sass');
const sassGlob = require('gulp-sass-glob');

const styles = () => {
  return gulp.src('src/scss/**/*.scss')
    .pipe(sassGlob())
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dest/css'));
};
```

```scss:style.scss
@use '_partials/*' as *; // Error: Can't find stylesheet to import.
```

対象法として`gulp-sass-glob`の代わりに`gulp-sass-glob-use-forward`を使うことで、Globが利用できます。

### 変更後

```js:gulpgile.js
const gulp = require('gulp');
const sass = require('gulp-dart-sass');
const sassGlob = require('gulp-sass-glob-use-forward');

const styles = () => {...};
```


## 最後に
Dart Sassでは名前空間をはじめ、細かなルールの変更・追加が行われました。
新しい手法なので慣れの問題もあると思いますが、個人的には使いづらくなった印象です。
2022年10月にサポートが終了するまでは移行期間とも受け取れるので、より快適な開発環境の構築を模索したいと思います。