意見募集(RFC):モジュールシステム

投稿日:2018年11月27日 Natalie Weizenbaum

Sassで最も頻繁にリクエストされる機能の多くは、インポートに関連しています。Sassの初期リリースから存在するインポートシステムは、端的に言って、あまり良くありません。Sassファイルをテキストとして別のファイルに含める以上のことはほとんど行いません。そのため、mixin、関数、変数がどこで定義されているかを追跡することが難しく、新しい追加がプロジェクトの他の部分と競合しないことを確認することも困難です。さらに悪いことに、CSS組み込みの`@import`ルールと重複しているため、多くのヒューリスティックを使用してどちらであるかを判断する必要があります。

これらの問題やその他の問題のため、私たちは長い間、Sassファイル同士の関連付け方を全面的に見直したいと考えてきました。過去数年間、SassのコアチームとSassフレームワークのメンテナーと協力して、`@import`に代わる適切なモジュールシステムの提案を作成してきました。その提案は、少なくとも出発点としては、コアチームがかなり満足できる状態になったので、コミュニティからのフィードバックを募集したいと思います。

提案全文をお読みになりたい場合は、GitHubでご覧いただけます。ご意見がございましたら、Issueを作成してください。提案の本文は仕様書として書かれているため、非常に詳細ですが、「目標」、「概要」、および「FAQ」セクション(以下に再掲)は、Sassに精通している方であれば誰でもアクセスできるはずです。

目標目標へのパーマリンク

高レベル高レベルへのパーマリンク

これらは、モジュールシステム全体に関する哲学的な設計目標です。システムを一意に指定するものではありませんが、多くの低レベルの設計決定の根底にある動機を表しています。

  • 局所性。モジュールシステムは、そのファイルだけを見ればSassファイルを理解できるようにするべきです。この重要な側面は、ファイル内の名前が、コンパイルのグローバル状態ではなく、ファイルの内容に基づいて解決されるべきであるということです。これはオーサリングにも当てはまります。作成者は、ファイル内で可視の名前と競合しない限り、名前の使用は安全であると確信できるはずです。

  • カプセル化。モジュールシステムは、特にライブラリの作成者が、公開するAPIを選択できるようにするべきです。外部ユーザーがアクセスまたは変更できないように、内部使用のためのエンティティを定義できるはずです。ライブラリの実装のファイルへの編成は、ユーザーに見えるAPIを変更することなく、柔軟に変更できるはずです。

  • 設定。Sassは、副作用、特にCSSを出力することを唯一の目的とするファイルの使用につながる設計になっているという点で、他の言語とは異なっています。CSSを直接出力しない場合でも、他のトップレベル変数の値の計算を含む計算で使用される設定変数を定義する、より広範なクラスのライブラリもあります。モジュールシステムは、ユーザーが副作用を持つモジュールを柔軟に使用および設定できるようにするべきです。

低レベル低レベルへのパーマリンク

これらは、哲学よりも実用性に基づいた目標です。ほとんどの場合、これらは、長年にわたって`@import`について収集してきたユーザーフィードバックから導き出されています。

  • 一度だけインポートする。`@import`はリテラルなテキストのインクルードであるため、コンパイルのスコープ内で同じSassファイルを複数回`@import`すると、そのファイルが複数回コンパイルおよび実行されます。せいぜい、コンパイル時間が無駄に長くなるだけで、スタイル自体が重複している場合は、CSS出力の肥大化にもつながります。新しいモジュールシステムは、ファイルを一度だけコンパイルするべきです。

  • 後方互換性。新しいモジュールシステムへの移行をできるだけ容易にしたいと考えており、そのためには、`@import`を使用する既存のスタイルシートと連携させる必要があります。`@import`のみを使用する既存のスタイルシートは、以前のバージョンのSassと同じインポート動作をする必要があり、スタイルシートは全体を変更することなく、部分的に`@use`に変更できる必要があります。

非目標非目標へのパーマリンク

これらは、さまざまな理由から、この提案の一部として追求しないことを明示的に決定した潜在的な目標です。将来の作業で検討される可能性のあるものもありますが、モジュールシステムを阻害するとは考えていません。

  • **動的インポート**。変数を含めたり、条件付きブロックに含めたりして、モジュールへのパスを動的に定義できるようにすると、宣言型ではなくなります。スタイルシートの可読性が低下するだけでなく、あらゆる種類の静的分析がより困難になります(実際、一般的なケースでは不可能です)。また、将来の実装の最適化の可能性も制限されます。

  • **複数のファイルを一度にインポートする**。これがサポートされていない長年の理由(作成者が、発見が難しく、デバッグが困難な順序付けのバグに陥る可能性があるため)に加えて、これは、どのファイルがインポートされ、名前がどこから来ているかを曖昧にすることで、局所性の原則に違反します。

  • **拡張のみのインポート**。`@extend`されない限り生成されるCSSが出力されないようにファイルをインポートするというアイデアは素晴らしいですが、かなりの追加作業が必要です。これは将来のリリースに含まれる可能性が最も高い機能ですが、初期モジュールシステムに含めるほど中心的ではありません。

  • **コンテキストに依存しないモジュール**。生成されるCSSとすべての変数の解決された値を含む、ロードされたモジュールの形式を、ロードの原因となったエントリポイントとは完全に独立させたいと思うかもしれません。これにより、ロードされたモジュールを複数のコンパイル間で共有し、インクリメンタルコンパイルのためにファイルシステムにシリアル化することさえ可能になります。

    しかし、実際には実現不可能です。CSSを生成するモジュールは、ほとんどの場合、何らかの設定に基づいてCSSを生成しますが、これは異なるエントリポイントによって変更される可能性があり、キャッシュは役に立ちません。さらに、複数のモジュールが同じ共有モジュールに依存し、一方が他方が使用する前にその設定を変更する可能性があります。このケースを一般的に禁止することは、モジュールが変数に基づいてCSSを生成することを事実上禁止することに等しくなります。

    幸いなことに、実装には、ソースツリーや、場合によっては定数に折りたたまれた変数の値やCSSツリーなど、コンテキストに依存しないと静的に判断できる情報をキャッシュするための多くの自由度があります。完全なコンテキスト独立性は、それらに加えて多くの価値を提供する可能性は低いでしょう。

  • **厳格さの向上**。多くの人が参加する大規模なチームは、ベストプラクティスを強制し、ミスをすばやくキャッチするために、Sassスタイルシートの記述方法に関するより厳格なルールを必要とする場合があります。新しいモジュールシステムを、厳格さをさらに押し進めるための手段として使用したいと思うかもしれません。たとえば、パーシャルが直接CSSを生成することを難しくしたり、人々に使用を避けたい関数を新しい組み込みモジュールに移動することを拒否したりすることができます。

    しかし、魅力的ではありますが、*たとえ避けるべきだと思っていても*、既存のすべてのユースケースを新しいシステムでできるだけ簡単にしたいと考えています。このモジュールシステムは、既存の動作からの大きな変更であり、Sassユーザーはサポートするためにかなりの量の作業が必要になります。この移行をできるだけ容易にしたいと考えており、その一部として、ユーザーが既存のスタイルシートを新しいモジュールシステムで動作させるために、不必要なハードルを追加することを避けています。

    `@use`がエコシステムで完全に採用されたら、lintやTypeScriptスタイルの`--strict-*`フラグの形で厳格さを高めることを検討できます。

  • **コード分割**。モノリシックなCSSを、遅延的に提供できる個別のチャンクに分割する機能は、非常に大規模なアプリケーションの読み込み時間を短縮するために重要です。ただし、これは、このモジュールシステムが解決しようとしている問題とは直交しています。このシステムは、主に、生成されたCSSのチャンク間の依存関係を宣言するのではなく、Sass API(mixin、関数、プレースホルダー)のスコープ設定に関心があります。

    このモジュールシステムは、外部コード分割システムと連携して動作できると考えています。たとえば、モジュールシステムを使用して、個々のコンポーネントのスタイルを設定するために使用されるライブラリをロードできます。各コンポーネントは独自のCSSファイルにコンパイルされます。これらのCSSファイルは、特別なコメントまたはカスタムのat-ruleを使用して相互に依存関係を宣言し、コード分割ポストプロセッサによってまとめてステッチすることができます。

概要概要へのパーマリンク

この提案では、`@use`と`@forward`の2つのat-ruleが追加されます。これらは、スタイルシートのトップレベルで、ルール(`@charset`以外)の前にのみ表示できます。これらは一緒に、`@import`を完全に置き換えることを目的としています。`@import`は最終的に非推奨となり、さらに最終的には言語から削除されます。

`@use` @useへのパーマリンク

`@use`は、別のスタイルシートからのCSS、変数、mixin、関数を現在のスタイルシートでアクセスできるようにします。デフォルトでは、変数、mixin、関数は、URLのベース名に基づく名前空間で利用できます。

@use "bootstrap";

.element {
  @include bootstrap.float-left;
}

名前空間の他に、`@use`と`@import`の間にはいくつかの重要な違いがあります

  • `@use`は、スタイルシートが何回使用されても、スタイルシートを一度だけ実行し、そのCSSを含めます。
  • `@use`は、グローバルではなく、現在のスタイルシートでのみ名前を使用できるようにします。
  • `-`または`_`で始まる名前のメンバーは、`@use`では現在のスタイルシートに対してプライベートです。
  • スタイルシートに`@extend`が含まれている場合、その拡張は、それをインポートするスタイルシートにのみ適用され、それをインポートするスタイルシートには適用されません。

プレースホルダーセレクターは*名前空間化されませんが*、*プライバシーは尊重されます*。

名前空間の制御 名前空間の制御へのパーマリンク

`@use`ルールのデフォルトの名前空間はURLのベース名によって決定されますが、`as`を使用して明示的に設定することもできます。

@use "bootstrap" as b;

.element {
  @include b.float-left;
}

特殊な構文`as *`を使用して、トップレベルの名前空間にすべてを含めることもできます。複数のモジュールが同じ名前のメンバーを公開し、`as *`で使用されている場合、Sassはエラーを生成することに注意してください。

@use "bootstrap" as *;

.element {
  @include float-left;
}

ライブラリの構成 ライブラリの構成へのパーマリンク

`@import`では、ライブラリは多くの場合、これらのライブラリで定義されている`!default`変数をオーバーライドするグローバル変数を設定することによって構成されます。`@use`では変数はグローバルではなくなったため、ライブラリを構成するためのより明示的な方法である`with`句がサポートされています。

// bootstrap.scss
$paragraph-margin-bottom: 1rem !default;

p {
  margin-top: 0;
  margin-bottom: $paragraph-margin-bottom;
}
@use "bootstrap" with (
  $paragraph-margin-bottom: 1.2rem
);

これは、評価前にBootstrapの$paragraph-margin-bottom変数を1.2remに設定します。with句は、インポートされるモジュールで定義された(または転送された)変数のみを許可し、それらが!defaultで定義されている場合のみ許可するため、ユーザーはタイプミスから保護されます。

@forward@forward パーマリンク

@forwardルールは、別のモジュールの変数、mixin、および関数を、現在のモジュール内でコードに表示することなく、現在のモジュールによって公開されるAPIの一部として含めます。これにより、ライブラリの作成者は、ファイル内の局所性を損なうことなく、ライブラリを多くの異なるソースファイルに分割できます。@useとは異なり、forwardは名前に名前空間を追加しません。

// bootstrap.scss
@forward "functions";
@forward "variables";
@forward "mixins";

可視性制御可視性制御 パーマリンク

@forwardルールは、特定の名前のみを表示することを選択できます。

@forward "functions" show color-yiq;

また、ライブラリ専用にすることを目的とした名前を非表示にすることもできます。

@forward "functions" hide assert-ascending;

追加のプレフィックス追加のプレフィックス パーマリンク

オールインワンモジュールを介して子モジュールを転送する場合、そのモジュールに手動で名前空間を追加することをお勧めします。これは、as句を使用して行うことができます。これは、転送されるすべてのメンバー名にプレフィックスを追加します。

// material/_index.scss
@forward "theme" as theme-*;

このようにして、ユーザーはテーマ変数に適切にスコープされた名前を持つオールインワンモジュールを使用できます。

@use "material" with ($theme-primary: blue);

または、より単純な名前で子モジュールを使用できます。

@use "material/theme" with ($primary: blue);

@importとの互換性@importとの互換性 パーマリンク

Sassエコシステムは一夜にして@useに切り替わるわけではないため、当面は@importと適切に相互運用する必要があります。これは両方向でサポートされています。

  • @importを含むファイルが@useされる場合、そのグローバル名前空間内のすべてが単一のモジュールとして扱われます。このモジュールのメンバーは、通常どおり名前空間を使用して参照されます。

  • @useを含むファイルが@importされる場合、そのパブリックAPIのすべてがインポートするスタイルシートのグローバルスコープに追加されます。これにより、ライブラリは、@importするユーザーに対しても、エクスポートする特定の名前を制御できます。 @useするユーザーだけでなく。

ライブラリが既存の@import指向のAPIを、必要に応じて明示的な名前空間を使用して維持できるようにするために、この提案では、@useではなく@importに対してのみ表示されるファイルのサポートも追加されます。それらは"file.import.scss"と記述され、ユーザーが@import "file"と記述するとインポートされます。

組み込みモジュール組み込みモジュール パーマリンク

新しいモジュールシステムは、7つの組み込みモジュールも追加します:mathcolorstringlistmapselector、およびmeta。これらには、既存のすべての組み込みSass関数が含まれます。これらのモジュールは(通常)名前空間でインポートされるため、プレーンCSS関数と競合することなくSass関数を使用することがはるかに容易になります。

これにより、Sassが新しい関数を追加することがはるかに安全になります。今後、これらのモジュールに多くの便利な関数を追加する予定です。

meta.load-css()meta.load-css() パーマリンク

この提案では、新しい組み込みmixinであるmeta.load-css($url, $with: ())も追加されます。このmixinは、指定されたURLを持つモジュールを動的にロードし、そのCSSを含めます(ただし、その関数、変数、およびmixinは使用できません)。これはネストされたインポートの代替であり、新しいメンバーを動的にロードできる場合に発生する多くの問題なしに、動的インポートのユースケースに対処するのに役立ちます。

よくある質問よくある質問 パーマリンク

  • **なぜこのプライバシーモデルなのですか?**メンバーをプライベートとして宣言するための多くのモデルを検討しました。モジュールから明示的にエクスポートされたメンバーのみが表示されるJSのようなモデルや、明示的な@privateキーワードを持つC#のようなモデルなどです。ただし、これらのモデルにははるかに多くの定板が含まれており、プライバシーが単一のスタイルルール内で混在する可能性のあるプレースホルダーセレクターでは特にうまく機能しません。名前ベースのプライバシーは、ライブラリがすでに使用している規則とのある程度の互換性も提供します。

  • **メンバーをライブラリ専用にすることはできますか?**「ライブラリ」という言語レベルの概念はないため、ライブラリのプライバシーも組み込まれていません。ただし、1つのモジュールで使用されるメンバーは、ダウンストリームモジュールに自動的に表示されるわけではありません。モジュールがライブラリのメインスタイルシートを介して@forwardされない場合、ダウンストリームのコンシューマーには表示されず、事実上ライブラリ専用になります。

    慣例として、ライブラリは、ユーザーがsrcという名前のディレクトリで直接使用することを意図していないライブラリ専用のスタイルシートを作成することをお勧めします。

  • **ライブラリを設定可能にするにはどうすればよいですか?**すべてのコア!defaultベースの設定を共有する多くのソースファイルで構成される大規模なライブラリがある場合は、その設定をライブラリのエントリポイントから転送され、ライブラリのファイルで使用されるファイルで定義することをお勧めします。例えば

// bootstrap.scss
@forward "variables";
@use "reboot";
// _variables.scss
$paragraph-margin-bottom: 1rem !default;
// _reboot.scss
@use "variables" as *;

p {
  margin-top: 0;
  margin-bottom: $paragraph-margin-bottom;
}
// User's stylesheet
@use "bootstrap" with (
  $paragraph-margin-bottom: 1.2rem
);

フィードバックの送信フィードバックの送信 パーマリンク

これはまだ単なる提案です。モジュールシステムの全体的な形状にはかなり満足していますが、それは決して確定されておらず、あなたのようなユーザーからのフィードバックがあれば何でも変更できます。ご意見がある場合は、GitHubで問題を提起するか、@SassCSSにツイートしてください。「素晴らしい」から「ひどい」まで何でも受け取りますが、具体的であればあるほど、作業できる情報が多くなります!