Svelteノート(2):コンパイラは君よりずっと賢いんだ
今日はSvelteのソースコードを読みに行き、アーキテクチャの理解を深めつつ練習になりそうな手頃なissueがないか探してみた。そしてこれ — emits warningを見つけた。
このissueの説明を見る限り、href のない aタグは本来不正なはず(少なくともa11yには準拠していない)だと僕は思ったのだが、調べてみると StackOverflow で現在の仕様には一部統一されていない部分があると言及されていた。
だが基本的に href は空でもよい。ただその場合は a を使わずに button などのタグで代替することを検討すべきであり、その方がスクリーンリーダーにとっても親切だ。
その後 eslint-plugin-jsx-a11y を確認してみたところ、このESLintルールが href の値が妥当かどうかを判定してくれることが分かった。不正とされる値には以下のようなものがある:
<a onClick={foo} /> // 可以用 button 取代
<a href="#" /> // 只有 # 沒有 id
<a href={"#"} /> // 字面值
<a href={`#`} /> // 字面值
<a href="javascript:void(0)" /> // 不應該用 javascript:void(0)
<a href={"javascript:void(0)"} /> // 字面值
<a href={`javascript:void(0)`} /> // 字面值
また、href が空の場合もある:
<a />
<a href={undefined} />
<a href={null} />
Svelte は missing href や href value invalid など、基本的な a11y のチェックを行ってくれる。Svelte 自体がまずコンパイラを通す仕組みになっているため、チェックはすべてコンパイル段階で行われるのだ:
// compiler/compile/nodes/Element.ts
// L425
if (this.name === 'a') {
const attribute = attribute_map.get('href') || attribute_map.get('xlink:href');
if (attribute) {
const value = attribute.get_static_value();
if (value === '' || value === '#') {
component.warn(attribute, {
code: `a11y-invalid-attribute`,
message: `A11y: '${value}' is not a valid ${attribute.name} attribute`
});
}
} else {
component.warn(this, {
code: `a11y-missing-attribute`,
message: `A11y: <a> element should have an href attribute`
});
}
}
この段階で、Svelte は構文を構文木(AST)に変換してくれているので、チェックの処理も非常にやりやすい。
しかしよく見てみると、あれ? javascript:void(0) のケースが処理されていないようだ。現状は value が空または # の場合のみ処理されている。そこで、ついでにチェックを1行追加し(if (value === '' || value === '#' || /^\\W*javascript:/.test(value)))、Pull Request を提出した。
また記録として残しておくと、やや厄介なのは、現在の Svelte は値が is_static であるものしかチェックしないという点だ。そのため、例えば以下のようなコード:
<a href={undefined} />
<a href={null} />
<a href={`#`} />
<a href={"javascript:void(0)"} />
<a href={`javascript:void(0)`} />
{} の中はすべて Expression になるため、Svelte 側でチェックできない(is_static が直接 false にマークされる)。これらをチェックしたい場合は、それらの Expression に対して個別に処理を行う必要がある。時間があるとき(もうすぐゴールデンウィークだし)、まとめて修正を試みてみようと思う。
そこまで実用的な機能というわけでもないが、どうせコンパイル段階で行われるのでバンドルサイズに影響を与えることもない。最近、コンパイラというアプローチは実に筋が良いとますます感じるようになってきた。どんな書き方を好むかは自分で決めればよく、コンパイルして最終的に JavaScript になりさえすればいいのだ。
もちろんこれは新しいアイデアというわけではなく、LiveScript や Elm なども多少なりともそうしたコンセプトを持っている。だが僕が思うに、Svelte が注目を集めている(あるいは導入しやすい)理由は、JavaScript の構文を大部分そのまま残している点にある。テンプレートの学習コストは(React や Vue を知っていれば)ほぼゼロであり、書き心地としては HTML、CSS、JavaScript を1箇所にまとめた Vue に近い。
もう一つは Svelte のリアクティブな仕組みやアニメーション制御だ。これらはフロントエンドの実装において常に考慮が必要な要素であり、他のコンパイラでは提供できないものでもある。誰もが関数型プログラミング(Functional Programming)を求めているが、実際に開発者が直面している真の課題を解決できなければ、それは一部の愛好家のこだわり(執念)に過ぎない。
関連記事
- フロントエンドで画像を扱う際に注意すべきこと Jake Archibaldの記事を起点に、現代のレスポンシブ画像の書き方を整理する。なぜwidth/heightを付ける必要があるのか、CSSのaspect-ratioはいつ使うべきか、AVIFとWebPの選び方、そしてpicture/source/srcsetを使ったモバイル向け画像の切り替えについて。
- CSS field-sizing — たった1行のCSSでフォーム要素を自動リサイズする かつてtextareaの自動高さ調整は、JavaScriptでscrollHeightを監視するしかなかった。しかしCSSのfield-sizing: contentなら、わずか1行で代替でき、textarea、input、selectに対応している。本記事では従来のやり方のペインポイントと、field-sizingの使い方をまとめる。
- リンクの下線をもっと見栄え良くする:text-underline-offset デフォルトでは下線と文字が近すぎて、このスタイルを好まないデザイナーもいるし、僕自身もあまり綺麗ではないと感じていた。
- なぜウェブで Pixel Perfect を追求すべきではないのか Pixel Perfect はそれが本当に重要な場合にのみ意識すべきであり、そうでなければ往々にしてお互い損をする結果に陥ってしまう。