[LibSass非推奨化] node-sassとのお別れ ~ Dart Sassへ移行する


2020/11/09

このほど唐突に
事実上のLibSassの開発断念がアナウンスされ、コアなnode-sassユーザーにとってが悲しいニュースが飛び込んできました。

後髪を引かれる思いはしますが、仕方がないので公式で推奨されている
dart-sass cliをサクッと移行してみます。


LibSass非推奨の経緯

Sassのコンパイラで圧倒的なシェアを誇っていたnode-sassだけに今回のLibSass非推奨の理由がなんとなく気になってきます。

公式のアナウンスを端的に掘り下げると、以下のような内容で説明されています。

長らくSassではLibSassを公式にサポートされているというポジションだったものの、この2年ほどLibSassの機能は更新されておらず、最近ではSassユーザーの意図としない問題が増加していたようです。

たとえば、ここ数年でも本家CSSなどは
min/maxなどの新しい機能が盛り込まれてきました。

LibSassにはその機能をサポートしていないため、それだけでも十分ユーザーにストレスを与えていたことでしょう。

また最近では新しいSassのモジュールシステムをサポートしていないため、LibSassが今後さらに時代遅れになってしまうことは火を見るより明らかな情勢になってきました。

公式がすべてのSassユーザーにLibSassから移行する必要があること示すことで、ユーザーに移行計画して対処して欲しいという警告のような意味合いを持たせるための処置のようです。

しかしながらLibSassのバグやセキュリティ問題の修正や最新nodeとの互換性などは定期的にメンテナンスを継続されるそうです。

SassのリソースをC++ APIを介して多言語に移植したり、高速コンパイルを実現する必要のある開発者はこれからもLibSassを選択することは可能です。


node環境で移行する手順

Dart Sassのインストールの手順説明によるといくつかインストール方法があります。

ここではより簡単なのはnpmパッケージをグローバルインストールしてコマンドラインから叩けるようにする方法を試してみます。

            
            $ npm install -g sass
        
とこれだけでDart Sass Cliが導入されます。

インストールされた時の現状の手元のバーションは、

            
            $ sass --version
1.26.10 compiled with dart2js 2.8.4
        
ちなみにAngular Cli 8.0以降ではすでにnode-sassからdart-sassへ(いつの間にかという感じでひっそりと)移行されています。

Sass単体を叩くことの少ない方は特にdart-sassの単独インストールを意識することは無いようです。


@importから@useへ

node-sassから移行した場合、これまで作り込まれたアセットをコンパイルした時に大きく影響を受けるのが@importの扱いだと思います。

すでに
@importは非推奨の用法であり、よりモダンに@useに置き換えることが求められます。

LibSass時代には制限されていた
@useを活用することで、node-sassユーザーにはいままで出来なかったことが色々と出来るようになります。

なお、大きなプロジェクト内のSassコードをまるごとアップグレードするのであれば、
コマンドラインから使える移行ユーティリティーを利用すると一括で移行できるようです。

もちろん
@import@useだけが移行のすべてではありませんが、折角なので公式の@useガイドのサンプルを試してみます。

@use

@import@useは一見すると同じような作用があるように思えますが、仕組みは全く別物になります。

@importは外部のSassコードを宣言された位置に内容まるごと挿入してくれる、c言語でいうところのinclude的な役割だったのに対して、@useは外部のコードをカプセル化した一つのオブジェクトのように扱えるようです。

この作用をSassの
モジュールと呼んで区別しています。

ちなみに組込の
ビルドイン・モジュールというものがいくつかデフォルトで用意されているので、これも@useで呼び出すことで利用できます。

たとえばSassのプロジェクトでディレクトリ構造が以下のようになっているとすると、

            
            $ tree
.
├── style.scss
└── foundation
     ├── _code.scss
     └── _lists.scss
        
プロジェクトルートにあるstyle.scssは下位のフォルダのscssコードを以下のようにモジュールインポートが可能になります。

            
            @use 'foundation/code';
@use 'foundation/lists';
        
ここらへんは@import時代と変わりません。

@importはソースコードの任意の位置で呼び出しが可能だったのに対して、@useはファイルの冒頭で呼び出される必要がある点に注意してください。

foundation以下の
_code.scssおよび_lists.scssはcode要素とlist要素それぞれ付与したいスタイルを保持しているようなファイルだとして、

            
            code {
    padding: .25em;
    line-height: 0;
}
        

            
            ul, ol {
    text-align: left;

    & & {
        padding: {
        bottom: 0;
        left: 0;
        }
    }
}
        
のような内容になっています。

これをコンパイルしてみると、以下のcssコードに変換されます。

            
            code {
    padding: .25em;
    line-height: 0;
}
ul, ol {
    text-align: left;
}
ul ul, ol ol {
    padding-bottom: 0;
    padding-left: 0;
}
        
この結果は単なるcssの合成になりました。

モジュールのメンバを呼び出す

もっとモダンなSassらしいモジュールを用法をやってみます。

たとえば、モジュール内の変数を呼び出したい場合には
<名前空間名>.<変数名>、関数は<名前空間名>.<関数名>()、ミクスインだと@include <名前空間名>.<ミクスイン名>()、となります。

ここでの
名前空間名はScssのファイル名からプレフィックス(_など)を抜いた文字列のことです。

今度はプロジェクト構造が以下になっているとすると、

            
            $ tree
.
├── style.scss
└── src
     └── _corners.scss
        
まず_corners.scssには$radiusという変数とroundedというミクスインが記述されているとすると、

            
            $radius: 3px;

@mixin rounded {
    border-radius: $radius;
}
        
これをstyle.scssからモジュールとして呼び出し、そのメンバにアクセスする方法は以下のようになります。

            
            @use "src/corners";

.button {
    @include corners.rounded;
    padding: 5px + corners.$radius;
}
        
呼び出されたモジュールの名前空間名はcornersになり、変数はcorners.$radiusで、ミクスインは@include corners.rounded;で利用することができます。

またモジュールの名前空間名はエイリアス名やワイルドカードも利用できます。

エイリアス名を利用する場合には、

            
            @use "src/corners" as c;

.button {
    @include c.rounded;
    padding: 5px + c.$radius;
}
        
のようにasを使ってモジュールを修飾します。

このとき
as *にしてしまうことで、モジュールの中身全てを展開することも可能です。

            
            @use "src/corners" as *;

.button {
    @include rounded;
    padding: 5px + $radius;
}
        
この場合、ほぼ@importのような作用で複数のモジュールが呼び出されたら容易にメンバー名が衝突する問題に陥りやすく、あまり推奨される使い方ではないようです。

プライベートメンバー化

モジュール内のメンバをプライベートに設定し、アクセスを制限することも可能です。

やり方としては簡単で、モジュールのメンバ名で
$に続く文字を-_で始めればプライベートメンバにすることができます。

たとえば先程の
_corners.scssを以下のように変えてみます。

            
            $-radius: 3px;

@mixin rounded {
    border-radius: $-radius;
}
        
このとき$-radiusでプライベートに設定されます。

こうすると
style.scssから呼び出しても、

            
            @use "src/corners";

.button {
    @include corners.rounded;
    //👇$-radiusは外部から呼び出し禁止でコンパイルエラー
    padding: 5px + corners.$-radius;
}
        
となります。


まとめ

node-sassからdart-sassへの簡単な移行を試してみました。

個人的に複雑なSassライブラリなどのプロジェクトは持っていないので、
@import@useに変えるだけでほぼ移行完了でした。

それ自体は良いのですが、dart-nodeを取り入れることでSassの様々な最新機能がどっと利用可能になったので、これまで自作してきたライブラリの数々が用済みになって少し寂しい限りです。

参考サイト

LibSass is Deprecated

Sassのモジュールシステムを@importから@useに移行する方法を考えてみた

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。