内部計測モジュールに `span` と `track` APIを追加
Tailwind CSSの内部計測モジュール(Instrumentation)に、span と track という2つの新しい計測APIが追加されました。既存の start/end マーカー方式を補完し、計測コードの記述漏れを構造的に防ぐ設計です。
背景
既存の I.start(label) / I.end(label) 方式には、計測漏れのリスクがありました。複数の return 文を持つ関数では、すべての出口に I.end(label) を記述しなければならず、追加・削除の際に対応する呼び出しを2か所修正する必要がありました。
PRでは、この問題を解決する2つのユースケースが整理されています。1つ目は、toCss(ast) のような1行で完結する処理を計測したいケース。2つ目は、複数行にまたがるブロックを計測したいケースです。前者は関数全体をコールバックで包む span API、後者はブロックの終了時に自動で end を呼ぶ track APIがそれぞれ対応します。
技術的な変更
instrumentation.ts に track メソッドと span メソッドの2つが追加されました。いずれも既存の start/end の薄いラッパーとして実装されており、内部の計測ロジックは変更されていません。
track メソッド は、using 宣言と組み合わせて使用するDisposableオブジェクトを返します。Symbol.dispose と Symbol.asyncDispose の両方を実装しており、ブロックのあらゆる出口(正常終了・例外・非同期)で end が呼ばれることを保証します。done フラグにより二重呼び出しも防止されています。
track(label: string) {
this.start(label)
let done = false
return {
[Symbol.dispose]: () => {
if (!done) {
this.end(label)
done = true
}
},
[Symbol.asyncDispose]: () => {
if (!done) {
this.end(label)
done = true
}
},
}
}
使用例は以下のとおりです:
{
using _ = I.track('label')
// ブロック終了時に自動で I.end('label') が呼ばれる
…
}
span メソッド は、コールバックの結果をそのまま返すジェネリクス T で型付けされており、計測対象の処理をインラインで記述できます。同期・非同期の両方に対応しており、result.then の存在確認でPromiseを検出した場合は finally フックで end を呼ぶ設計です。
span<T>(label: string, fn: () => T): T {
this.start(label)
let isPromise = false
try {
let result = fn()
isPromise = result && typeof (result as any).then === 'function'
// @ts-expect-error — TS can't infer that result is a Promise here
return isPromise ? result.finally(() => this.end(label)) : result
} finally {
if (!isPromise) this.end(label)
}
}
1行で計測を完結させる典型的な使い方は次のとおりです:
let css = I.span('toCss(…)', () => toCss(ast))
また、コンストラクタに shouldReport パラメータが追加されました。デフォルト値は既存の env.DEBUG ですが、環境変数を変更せずにコード上で計測の有効・無効を切り替えられるようになっています。
設計判断
track と span を別々のAPIとして提供した点に、明確なトレードオフの整理が見られます。span はコールバックで包む記述コストと引き換えに、インラインで戻り値を利用できる簡潔さを提供します。一方 track は using 宣言によるインデントへの影響を最小限に抑えつつ、既存コードをほぼそのままに計測を追加できます。
span のPromise検出に instanceof Promise ではなく .then の有無を確認するduck typingを採用している点も注目です。これにより、ネイティブのPromise以外のthenable(例: ライブラリが返すPromise-like オブジェクト)も正しく扱えます。
using キーワードへの依存については、PRでは「計測モジュールはすでにこれに依存していた」と明示されており、既存の前提を踏まえた一貫した拡張といえます。
まとめ
本PRは、既存の start/end APIをベースに、計測漏れを構造的に排除する2つのAPIを追加した変更です。span と track の使い分けにより、1行の処理から複数行のブロックまで、コードスタイルを大きく変えることなく計測を挿入できるようになりました。