コメント募集: 新しいJS API

2021年8月5日投稿者: Natalie Weizenbaum

長い間開発中だったものを正式に発表できることを嬉しく思います。Sassのための全く新しいJavaScript API(提案)です。このAPIは、Node Sass APIと、長年にわたる他の言語における様々なSass APIから得られた教訓に基づき、ゼロから設計し直されており、既存の APIの多くの欠点を解消しています。

このAPIには4つの主要なコンポーネントがあり、それら全てをこの記事で説明します。

読み進めていく中で、このAPIはまだ提案段階であることを覚えておいてください。正式リリース前に、皆様のニーズを満たしているかどうか、そしてどのように改善できるかについて、ユーザーである皆様からのご意見をお聞きしたいと考えています。ですので、イシュートラッカーでぜひご意見をお聞かせください!

なぜ新しいAPIなのか?なぜ新しいAPIなのか? パーマリンク

既存のJavaScript APIは時代遅れになりつつあります。Dart Sassよりも前に存在しており、現在は非推奨となっているLibSass実装をラップしたnode-sassパッケージのために元々設計されました。(そのため、「Node Sass API」と呼ばれています!) LibSassと共に有機的に、そしてしばしば混乱を招く形で成長し、いくつかの厄介なレガシー動作が残ってしまいました。これらの動作の多くは実装よりもむしろ問題であり、しかし、そのうちのいくつかは非常に困難な状況を招いていました。

  • インポーター APIは、URLではなくファイルパスに基づいて構築されており、物理的なファイルシステムと密接に結びついていました。そのため、すべてのファイルベースの読み込みを上書きして完全に仮想的なファイルシステムを提供することが不可能であり、新しいモジュール システムとカスタムのNodeインポーターの相互作用が不十分でした。

  • 関数 APIは、Sassの不変の性質に反する、変更可能な値オブジェクトに基づいて構築されていました。また、慣習的なカスタム関数の作成を容易にするユーティリティメソッド(マップ内のキーの検索など)も提供されておらず、文字列が引用符で囲まれているかどうかなどの値に関する重要な情報へのアクセスも提供されていませんでした。

  • 非同期関数はすべて、promiseベースではなく、コールバックベースでした。

新しいAPIはこれらの問題などを、SassとJSから作業するのを容易にする最新の慣習的なAPIで解決します。

コンパイルコンパイル パーマリンク

このAPIの中心となるのは、実際のSassコンパイルを行う4つの関数、2つの同期関数と2つの非同期関数です。正確に何を取得して返すかを明確にするためにTypeScript構文で示していますが、プレーン JSから常に呼び出すことができます。

function compile(
  path: string,
  options?: Options<'sync'>
): CompileResult;

function compileString(
  source: string,
  options?: StringOptions<'sync'>
): CompileResult;

function compileAsync(
  path: string,
  options?: Options<'async'>
): Promise<CompileResult>;

function compileStringAsync(
  source: string,
  options?: StringOptions<'async'>
): Promise<CompileResult>;

compile()compileAsync()関数は、ディスク上のパスからSassファイルを読み込みます。一方、compileString()compileStringAsync()関数は、文字列として渡されたSassソースコードをコンパイルします。これらはすべて以下のオプションを受け付けます。

  • alertAscii: エラーと警告が(Unicodeのボックス描画文字などではなく)ASCII文字のみを使用するかどうか。
  • alertColor: エラーと警告がターミナル カラーを使用するかどうか。
  • loadPaths: ファイルの検索に使用するファイルパスのリスト。古い APIincludePathsと同じです。
  • importers: Sassソース ファイルの読み込みに使用するカスタムインポーターのリスト。
  • functions: キーがSass関数シグネチャで、値がカスタム 関数であるオブジェクト。
  • quietDeps: 依存関係における非推奨警告を抑制するかどうか。
  • logger: 警告とデバッグ メッセージの出力に使用するカスタムロガー
  • sourceMap: コンパイル中にソースマップを生成するかどうか。
  • style: 出力スタイル、'compressed'または'expanded'
  • verbose: 遭遇したすべての非推奨警告を出力するかどうか。

compileString()compileStringAsync()関数は、いくつかの追加オプションを受け付けます。

  • syntax: ファイルの構文、'scss'(デフォルト)、'indented'、または'css'
  • url: ファイルの標準URL
  • importer: ファイルのソースとして扱うカスタムインポーター。これが渡された場合、このインポーターはこの スタイルシートからの相対的な読み込みの解決に使用されます。

これらの関数はすべて、以下のフィールドを持つオブジェクトを返します。

  • css: コンパイルされたCSS(文字列)。
  • loadedUrls: コンパイル中に読み込まれたすべてのURL(特定の順序はありません)。
  • sourceMap: sourceMap: trueが渡された場合のファイルのソースマップ(デコードされたオブジェクト)。

Node Sass APIと同様に、同期関数は非同期関数よりも大幅に高速です。fibersパッケージが 廃止されたため、残念ながら新しいAPIは非同期コンパイルを高速化するfibersオプションをサポートしません。

ロガーロガー パーマリンク

ロガー APIを使用すると、警告とデバッグメッセージの出力方法とタイミングをより詳細に制御できます。この提案の他の側面とは異なり、古い APIにもloggerオプションが追加され、新しいAPI にすぐにアップグレードしなくてもメッセージを制御できます。

ロガーは以下のインターフェースを実装します。

interface Logger {
  warn?(
    message: string,
    options: {
      deprecation: boolean;
      span?: SourceSpan;
      stack?: string;
    }
  ): void;

  debug?(
    message: string,
    options: {span: SourceSpan}
  ): void;
}

warn関数は、コンパイラ自体からの警告と@warnルールからの警告の両方を含む警告を処理します。以下が渡されます。

  • 警告メッセージ
  • それが具体的に非推奨 警告であるかどうかを示すフラグ
  • 特定の 場所から来た場合、警告があった場所を示すスパン
  • 警告が発生した時点で発生した実行中のSassスタックトレース

debug関数は、@debugルールのみを処理し、メッセージとルールのスパンのみが渡されます。SourceSpan型の詳細については、ロガー 提案を参照してください。

Sassは、メッセージをまったく出力しない組み込みロガーLogger.silentも提供します。これにより、警告がまったく 表示されない「静音モード」でSassを簡単に実行できます。

インポーターインポーター パーマリンク

新しいAPIでは、インポーターを単一関数のコールバックとしてモデル化するのではなく、URL正規化するメソッドと、正規化された URL読み込むメソッドの2つのメソッドを公開するオブジェクトとしてモデル化します。

// Importers for compileAsync() and compileStringAsync() are the same, except
// they may return Promises as well.
interface Importer {
  canonicalize(
    url: string,
    options: {fromImport: boolean}
  ): URL | null;

  load(canonicalUrl: URL): ImporterResult | null;
}

compile()またはloadPathsを介してファイルシステムから直接読み込まれるスタイルシートでさえ、インポーターによって読み込まれたかのように扱われることに注意してください。この組み込みファイルシステムインポーターは、すべてのパスをfile: URLに正規化し、それらのURLを物理的な ファイルシステムから読み込みます。

正規化正規化 パーマリンク

最初のステップでは、スタイルシートの標準URLを決定します。各スタイルシートには、正確に1つの標準URLがあり、それは正確に1つのスタイルシートを参照します。標準URLはスキームを含めて絶対でなければなりませんが、具体的な構造はインポーター次第です。ほとんどの場合、対象のスタイルシートはディスク上に存在し、インポーターは単にそのためのfile: URLを返します。

canonicalize()メソッドは、相対または絶対のいずれかのURL文字列を受け取ります。インポーターがそのURLを認識した場合、対応する絶対URL(スキームを含む)を返します。これが、対象のスタイルシートの標準URLです。入力URLでファイル拡張子や先頭のアンダースコアを省略しても、標準URLは完全に 解決されなければなりません。

ファイルシステムから読み込まれるスタイルシートの場合、標準URLはディスク上の物理ファイルの絶対file: URLになります。メモリ内で生成される場合は、インポーターがカスタムURLスキームを選択して、その標準URLが他の インポーターのURLと競合しないようにする必要があります。

たとえば、データベースからSassファイルを読み込んでいる場合は、db:スキームを使用できます。データベースのキーstylesに関連付けられたスタイルシートの標準URLdb:stylesになる可能性があります。

この関数は、インポーターが@importルール(@use@forward、またはmeta.load-css()ではなく)から呼び出されているかどうかを示すfromImportオプションも受け取ります。

各スタイルシートに正規のURLを持たせることで、Sassは新しいモジュール システムで同じスタイルシートが複数回ロードされないようにすることができます。

相対パスの正規化相対パスの正規化 permalink

スタイルシートが、@use "variables"のような相対URLをロードしようとすると、それがスタイルシートを基準としたファイルを参照しているのか、別のインポーターまたはロードパスを参照しているのか、ドキュメント自体からは明確ではありません。インポーターAPIがその曖昧さをどのように解決するのかを以下に示します 

  • まず、相対URLは、@use(または@forward@import)を含んでいたスタイルシートの正規のURLを基準にして解決されます。たとえば、正規のURLfile:///path/to/my/_styles.scssの場合、解決されたURLfile:///path/to/my/variablesになります。

  • このURLは、古いスタイルシートをロードしたインポーターのcanonicalize()メソッドに渡されます。(つまり、インポーターが絶対URLをサポートすることが重要になります!)インポーターが認識した場合、正規の値を返し、それがインポーターのload()に渡されます。認識しない場合は、nullを返します。

  • 古いスタイルシートのインポーターがURLを認識しなかった場合、optionsに表示される順序で、すべてのimportersのcanonicalize関数に渡され、すべてのloadPathsで確認されます。それらのいずれも認識しない場合、ロードは 失敗します。

ローカルの相対パスが他のインポーターまたはロードパスよりも優先されることが重要です。そうでないと、依存関係が競合する名前のファイルを追加することで、ローカルのスタイルシートが予期せず破損する可能性があります 

ロードロード permalink

2番目のステップでは、実際にスタイルシートのテキストをロードします。load()メソッドは、canonicalize()によって返された正規のURLを受け取り、そのURLにあるスタイルシートの内容を返します。これは、各正規URLごとにコンパイルごとに1回だけ呼び出されます。同じURLを将来ロードする場合、既存のモジュール(@use@forwardの場合)または構文木(@importの場合)が再利用されます。

load()メソッドは、次の フィールドを持つオブジェクトを返します。

  • css: ロードされた スタイルシートのテキスト。
  • syntax: ファイルの構文:'scss''indented'、または'css'
  • sourceMapUrl: この ファイルを参照する際にソースマップに含める、ブラウザからアクセス可能なオプションのURL

FileImporterFileImporter permalink

この提案では、FileImporterとして知られる特殊なタイプのインポーターも追加されます。このインポーターは、ロードを物理ファイルシステム上のどこかにリダイレクトするという一般的なケースを容易にします。ディスク上のファイルでは常に同じになるため、呼び出し元がload()を実装する必要はありません 

interface FileImporter {
  findFileUrl(
    url: string,
    options: {fromImport: boolean}
  ): FileImporterResult | null;
}

findFileUrl()メソッドは相対URLを受け取り、次の フィールドを持つオブジェクトを返します。

  • url: ロードするファイルの絶対file: URL。このURLは完全に正規化されている必要はありません。Sassコンパイラは、部分ファイル、ファイル拡張子、インデックスファイルなどを解決します 
  • sourceMapUrl: この ファイルを参照する際にソースマップに含める、ブラウザからアクセス可能なオプションのURL

関数関数 permalink

新しい関数APIの関数型は、古い APIと非常によく似ています。

type CustomFunctionCallback = (args: Value[]) => Value;

違いは 次のとおりです。

  • 非同期関数は、コールバックを呼び出すのではなく、Promise<Value>を返します 
  • 値の型自体が 異なります。

ただし、2点目は非常に重要です!新しい値の型は、古いバージョンよりもはるかに詳細に記述されています。親 クラスから始めましょう。

abstract class Value {
  /**
   * Returns the values of `this` when interpreted as a list.
   *
   * - For a list, this returns its elements.
   * - For a map, this returns each of its key/value pairs as a `SassList`.
   * - For any other value, this returns a list that contains only that value.
   */
  get asList(): List<Value>;

  /** Whether `this` is a bracketed Sass list. */
  get hasBrackets(): boolean;

  /** Whether `this` is truthy (any value other than `null` or `false`). */
  get isTruthy(): boolean;

  /** Returns JS's null if this is `sassNull`, or `this` otherwise. */
  get realNull(): null | Value;

  /** If `this` is a list, return its separator. Otherwise, return `null`. */
  get separator(): ListSeparator;

  /**
   * Converts the Sass index `sassIndex` to a JS index into the array returned
   * by `asList`.
   *
   * Sass indices start counting at 1, and may be negative in order to index
   * from the end of the list.
   */
  sassIndexToListIndex(sassIndex: Value): number;

  /**
   * Returns `this` if it's a `SassBoolean`, and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertBoolean(name?: string): SassBoolean;

  /**
   * Returns `this` if it's a `SassColor`, and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertColor(name?: string): SassColor;

  /**
   * Returns `this` if it's a `SassFunction`, and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of the parameter passed to the custom function (without the `$`).
   */
  assertFunction(name?: string): SassFunction;

  /**
   * Returns `this` if it's a `SassMap` (or converts it to a `SassMap` if it's
   * an empty list), and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of the parameter passed to the custom function (without the `$`).
   */
  assertMap(name?: string): SassMap;

  /**
   * Returns `this` if it's a `SassNumber`, and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertNumber(name?: string): SassNumber;

  /**
   * Returns `this` if it's a `SassString`, and throws an error otherwise.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertString(name?: string): SassString;

  /**
   * Returns the value of `this` if it can be interpreted as a map.
   *
   * - If this is a map, returns its contents.
   * - If this is an empty list, returns an empty map.
   * - Otherwise, returns `null`.
   */
  tryMap(): OrderedMap<Value, Value> | null;

  /** Returns whether `this == other` in SassScript. */
  equals(other: Value): boolean;
}

ここで注意すべき重要な点がいくつかあります 

  • CSSには、単一要素とそれを含むリストとの間に強い構文上の違いがないため、すべてのSass値はリストであるかのように扱うことができます。Valueは、すべてのValueに対してasList()hasBrackets()separator()ゲッターを提供することで、この規則に従いやすくします。

  • asList()によって返されるリストとasMap()によって返されるマップは、immutableパッケージの不変型です。これは、Sassのすべての型の組み込みの不変性を反映しています。これらの値は直接変更できませんが、それらのAPIを使用すると、変更を適用した新しい値を簡単に効率的に作成できます 

  • Sassのリストインデックス規則はJavaScriptのものとは異なります。sassIndexToListIndex()関数は、SassインデックスからJS インデックスへの変換を容易にします。

  • Sassでは、ブールコンテキストで任意の値を使用でき、falsenullは「falsy」値としてカウントされます。isTruthyゲッターを使用すると、この規則に従いやすくなります 

  • assert*()関数は、期待どおりの引数が渡されていることを確認し、そうでない場合は慣習的なエラーをスローすることを容易にします。これらは、Valueの型を自動的に絞り込むため、TypeScriptユーザーにとって特に役立ちます。

ほとんどのSass値には独自のサブクラスがありますが、定数として使用できる3つのシングルトン値があります。sassTruesassFalsesassNullは、それぞれSassのtruefalsenull値を表します。

カラーカラー permalink

新しいAPISassColorクラスは、RGBHSLHWB形式の色にアクセスできます。組み込みのSassカラー関数と同様に、最初にどのように作成されたかに関係なく、任意の色で任意の属性にアクセスできます 

class SassColor extends Value {
  /** Creates an RGB color. */
  static rgb(
    red: number,
    green: number,
    blue: number,
    alpha?: number
  ): SassColor;

  /** Creates an HSL color. */
  static hsl(
    hue: number,
    saturation: number,
    lightness: number,
    alpha?: number
  ): SassColor;

  /** Creates an HWB color. */
  static hwb(
    hue: number,
    whiteness: number,
    blackness: number,
    alpha?: number
  ): SassColor;

  /** The color's red channel. */
  get red(): number;

  /** The color's green channel. */
  get green(): number;

  /** The color's blue channel. */
  get blue(): number;

  /** The color's hue. */
  get hue(): number;

  /** The color's saturation. */
  get saturation(): number;

  /** The color's lightness. */
  get lightness(): number;

  /** The color's whiteness. */
  get whiteness(): number;

  /** The color's blackeness. */
  get blackness(): number;

  /** The color's alpha channel. */
  get alpha(): number;

  /**
   * Returns a copy of `this` with the RGB channels updated to match `options`.
   */
  changeRgb(options: {
    red?: number;
    green?: number;
    blue?: number;
    alpha?: number;
  }): SassColor;

  /**
   * Returns a copy of `this` with the HSL values updated to match `options`.
   */
  changeHsl(options: {
    hue?: number;
    saturation?: number;
    lightness?: number;
    alpha?: number;
  }): SassColor;

  /**
   * Returns a copy of `this` with the HWB values updated to match `options`.
   */
  changeHwb(options: {
    hue?: number;
    whiteness?: number;
    blackness?: number;
    alpha?: number;
  }): SassColor;

  /** Returns a copy of `this` with `alpha` as its alpha channel. */
  changeAlpha(alpha: number): SassColor;
}

数値数値 permalink

SassNumberクラスは、分子と分母の単位を文字列ではなく配列として格納します。さらに、特定の単位を持っていることを確認するためのメソッド(assertNoUnits()assertUnit())と、特定の単位に変換するためのメソッド(convert()convertToMatch()convertValue()convertValueToMatch()coerce()coerceValue()coerceValueToMatch())を提供します。

Sassの数値ロジックもJSとは微妙に異なり、Sassは10番目の少数点以下の桁より少ない値の差を持つ数値を同一とみなします。このAPIは、これとJavaScriptの数値 ロジック間の変換に役立つ多くのメソッドを提供します。

class SassNumber extends Value {
  /** Creates a Sass number with no units or a single numerator unit. */
  constructor(value: number, unit?: string);

  /** Creates a Sass number with multiple numerator and/or denominator units. */
  static withUnits(
    value: number,
    options?: {
      numeratorUnits?: string[] | List<string>;
      denominatorUnits?: string[] | List<string>;
    }
  ): SassNumber;

  /** This number's value. */
  get value(): number;

  /**
   * Whether `value` is an integer according to Sass's numeric logic.
   *
   * The integer value can be accessed using `asInt`.
   */
  get isInt(): boolean;

  /**
   * If `value` is an integer according to Sass's numeric logic, returns the
   * corresponding JS integer, or `null` if `value` isn't an integer.
   */
  get asInt(): number | null;

  /** This number's numerator units. */
  get numeratorUnits(): List<string>;

  /** This number's denominator units. */
  get denominatorUnits(): List<string>;

  /** Whether `this` has numerator or denominator units. */
  get hasUnits(): boolean;

  /**
   * If `value` is an integer according to Sass's numeric logic, returns the
   * corresponding JS integer, or throws an error if `value` isn't an integer.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of the parameter passed to the custom function (without the `$`).
   */
  assertInt(name?: string): number;

  /**
   * If `value` is between `min` and `max` according to Sass's numeric logic,
   * returns it clamped to that range. Otherwise, throws an error.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of the parameter passed to the custom function (without the `$`).
   */
  assertInRange(min: number, max: number, name?: string): number;

  /**
   * Returns `this` if it has no units. Otherwise, throws an error.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertNoUnits(name?: string): SassNumber;

  /**
   * Returns `this` if it has `unit` as its single (numerator) unit. Otherwise,
   * throws an error.
   *
   * The `name` parameter is used for error reporting. It should match the name
   * of a parameter passed to the custom function (without the `$`).
   */
  assertUnit(name?: stringunit: string): SassNumber;

  /** Returns whether `this` has the single numerator unit `unit`. */
  hasUnit(unit: string): boolean;

  /** Returns whether this number's units are compatible with `unit`. */
  compatibleWithUnit(unit: string): boolean;

  /**
   * If this number's units are compatible with `newNumerators` and
   * `newDenominators`, returns a new number with those units that's equal to
   * `this`. Otherwise, throws an error.
   *
   * Note that unitless numbers are only compatible with other unitless numbers.
   */
  convert(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>
  ): SassNumber;

  /**
   * If this number's units are compatible with `other`'s, returns a new number
   * with `other`'s units that's equal to `this`. Otherwise, throws an error.
   *
   * Note that unitless numbers are only compatible with other unitless numbers.
   */
  convertToMatch(other: SassNumber): SassNumber;

  /** Equivalent to `convert(newNumerators, newDenominators).value`. */
  convertValue(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>
  ): number;

  /** Equivalent to `convertToMatch(other).value`. */
  convertValueToMatch(other: SassNumber): number;

  /**
   * Like `convert()`, but if `this` is unitless returns a copy of it with the
   * same value and the given units.
   */
  coerce(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>
  ): SassNumber;

  /**
   * Like `convertToMatch()`, but if `this` is unitless returns a copy of it
   * with the same value and `other`'s units.
   */
  coerceToMatch(other: SassNumber): SassNumber;

  /** Equivalent to `coerce(newNumerators, newDenominators).value`. */
  coerceValue(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>
  ): number;

  /** Equivalent to `coerceToMatch(other).value`. */
  coerceValueToMatch(other: SassNumber): number;
}

文字列文字列 permalink

SassStringクラスは、文字列が引用されているかどうかに関する情報にアクセスできます。リストと同様に、JSのインデックスの概念はSassのものとは異なるため、JSインデックスをSass インデックスに変換するsassIndexToStringIndex()メソッドも提供します。

class SassString extends Value {
  /** Creates a string with the given `text`. */
  constructor(
    text: string,
    options?: {
      /** @default true */
      quotes: boolean;
    }
  );

  /** Creates an empty string`. */
  static empty(options?: {
    /** @default true */
    quotes: boolean;
  }): SassString;

  /** The contents of `this`. */
  get text(): string;

  /** Whether `this` has quotes. */
  get hasQuotes(): boolean;

  /** The number of Unicode code points in `text`. */
  get sassLength(): number;

  /**
   * Converts the Sass index `sassIndex` to a JS index into `text`.
   *
   * Sass indices start counting at 1, and may be negative in order to index
   * from the end of the list. In addition, Sass indexes strings by Unicode code
   * point, while JS indexes them by UTF-16 code unit.
   */
  sassIndexToStringIndex(sassIndex: Value): number;
}

リストリスト permalink

前述のように、ほとんどのリスト関数はValueスーパークラスにあり、すべての値をリストとして扱うSassの規則に従いやすくしています。ただし、SassListクラスは、新しい リストを作成するためにまだ構築できます。

class SassList extends Value {
  /** Creates a Sass list with the given `contents`. */
  constructor(
    contents: Value[] | List<Value>,
    options?: {
      /** @default ',' */
      separator?: ListSeparator;
      /** @default false */
      brackets?: boolean;
    }
  );

  /** Creates an empty Sass list. */
  static empty(options?: {
    /** @default null */
    separator?: ListSeparator;
    /** @default false */
    brackets?: boolean;
  }): SassList;
}

マップマップ permalink

SassMapクラスは、その内容をimmutableパッケージOrderedMapとして公開します。

class SassMap extends Value {
  /** Creates a Sass map with the given `contents`. */
  constructor(contents: OrderedMap<Value, Value>);

  /** Creates an empty Sass map. */
  static empty(): SassMap;

  /** Returns this map's contents. */
  get contents(): OrderedMap<Value, Value>;
}

関数関数 permalink

SassFunctionクラスは非常に制限されています。同期コールバックを使用して新しいファーストクラス関数を生成することしかできません。これらの関数はカスタム関数によって呼び出すことはできませんが、それでも古い APIよりも強力です!

class SassFunction extends Value {
  /**
   * Creates a Sass function value with the given `signature` that calls
   * `callback` when it's invoked.
   */
  constructor(
    signature: string,
    callback: CustomFunctionCallback
  );
}

詳細情報詳細情報 permalink

これらの提案の詳細を知りたい場合、および最新の形式を確認したい場合は、GitHubで全文を確認できます 

フィードバックを熱心に待っていますので、ご意見をお聞かせください!問題の提案は、このブログ投稿が公開されてから少なくとも1か月間、そしてそれに関する議論がどれくらい活発かによって、さらに長く公開されます