軽くSvelteにコントリビュートした

4 min read読了の目安(約3900字

Svelte 聞いたことがありますか?React や Vue と違って、コードをコンパイルしてコードを生成する。例えばこの Svelte コンポネントは:

<script>
  import { createEventDispatcher } from 'svelte';
  export let label;
	let a = 1;
	
	$: b = a * 2
  const dispatch = createEventDispatcher();
  function handleClick() {
    dispatch('toggle', {
      value
    });
  }
</script>

<button class="button" class:active on:click={handleClick}>{label}</button>

Svelte をコンパイルしたらこんなふうになります:

import { createEventDispatcher } from "svelte";

function create_fragment(ctx) {
	let button;
	let t;
	let dispose;

	return {
		c() {
			button = element("button");
			t = text(/*label*/ ctx[0]);
			attr(button, "class", "button");
		},
		m(target, anchor, remount) {
			insert(target, button, anchor);
			append(button, t);
			if (remount) dispose();
			dispose = listen(button, "click", /*handleClick*/ ctx[1]);
		},
		p(ctx, [dirty]) {
			if (dirty & /*label*/ 1) set_data(t, /*label*/ ctx[0]);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(button);
			dispose();
		}
	};
}

let a = 1;

function instance($$self, $$props, $$invalidate) {
	let { label } = $$props;
	const dispatch = createEventDispatcher();

	function handleClick() {
		dispatch("toggle", { value });
	}

	$$self.$set = $$props => {
		if ("label" in $$props) $$invalidate(0, label = $$props.label);
	};

	let b;
	$: b = a * 2;
	return [label, handleClick];
}

class App extends SvelteComponent {...}

コンパイルすることでランタイムで Virtual DOM みたいな Diff アルゴリズムしなくても依頼をトラッキングすることができ、bundle size やオーバーヘッドなど React よりはるかに少ないです。

そして、Svelte コンポネントは一目で見ると普通の JavaScript、HTML、CSS に近いので、初心者でも簡単に使うことができます。React hooks などのコンセプトを事前に学ぶ必要もないのでとてもおすすめです。

Svelte に関して別で記事を書く予定なので、ここで Svelte にコントリビュートしたことだけを書きます。

きっかけ

Svelteを使っていてすぐ一目惚れになった私は、少しでも貢献したい気持ちになってコードベースやイシューリストをざっくり見ました。そのなかで一番注目しているのがアクシスビィティです。

Svelteはコンパイラーなので、例えばこんなことを検知することができます:REPL

截圖 2020-11-04 13.34.38

  • <a> はhrefがあるべきだ
  • <a>は子要素が必要

こんなwarningが出ます。それは eslint ではなく、コンパイラーはコードを分析し、hrefはないことを検知した。

公式のREPLだけでなく、VS Codeも表示されます(プラグインが必要)

截圖 2020-11-04 13.41.05

ミスを事前に防ぐことができるのがコンパイラーの強さだと思います。Svelteはコンパイラーを活用し、a11yのチェックを入れました。

Svelteはその制御をしていなかったことをいろいろ試した結果気づいた。

<a href="javascript:void(0)">Test</a>

onclickできない場合はそれを使う場合が多いですが大嫌いです。

スペックにより問題ありませんが、a11y視点ではとても迷惑です。Screen Readerは <a>を読み上げるとき、リンクだと思って押したら、全然違う挙動になり、ユーザーが困るでしょう。

我々はウェブ開発者であり、ウェブのアクシスビィティを守るべきです。そんな意識を持っていただきたくて、Svelteはその信念でもありとても好きです。

それを発見してコードベースをチェックしました。Element.tsでいろんなチェックが入っていて、これもその中の一つです:

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`
				});
			}
		}

やることは本当に簡単で、conditionを追加するだけです。

if (value === '' || value === '#' || /^\W*javascript:/i.test(value))

プルリクも出して見て、無事にマージされました。そして公式サイトもアバターが出てきましたね。

截圖 2020-11-04 13.58.50

そして何よりも <a href="javascript:void(0)"></a>を制御できるようになりました。

截圖 2020-11-04 15.15.11

こんなしつこい warning が出ます。よりいいサイトになるためにjavascript:void(0)をやめましょう。

まとめ

とても小さな修正に見えてますが、この warning により修正を入れるエンジニアがいたらそれでいいとは思います。何よりもこの後 Svelte をもっと貢献したい気持ちが強くなりました!