· 4分で読了

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 になりさえすればいいのだ。

もちろんこれは新しいアイデアというわけではなく、LiveScriptElm なども多少なりともそうしたコンセプトを持っている。だが僕が思うに、Svelte が注目を集めている(あるいは導入しやすい)理由は、JavaScript の構文を大部分そのまま残している点にある。テンプレートの学習コストは(React や Vue を知っていれば)ほぼゼロであり、書き心地としては HTML、CSS、JavaScript を1箇所にまとめた Vue に近い。

もう一つは Svelte のリアクティブな仕組みやアニメーション制御だ。これらはフロントエンドの実装において常に考慮が必要な要素であり、他のコンパイラでは提供できないものでもある。誰もが関数型プログラミング(Functional Programming)を求めているが、実際に開発者が直面している真の課題を解決できなければ、それは一部の愛好家のこだわり(執念)に過ぎない。

関連記事

他のトピックを探索