Web開発
MicroLighterの紹介
Introducing MicroLighter (daverupert.com)
要約
MicroLighterは、CSS Custom Highlights APIを利用した軽量なクライアントサイドのシンタックスハイライターです。依存関係がなく、約2KBのサイズで、DOM操作を避け、CSSのみでコードのトークンをハイライトします。Textmateの言語定義を利用し、オンデマンドで言語定義をロードすることで、バンドルサイズを削減し、柔軟なテーマ設定とWebコンポーネントによる機能拡張を可能にします。
全文翻訳
MicroLighterという、CSS Custom Highlights APIを利用した軽量なクライアントサイドのシンタックスハイライターを作成しました。
なぜこれを作ったのかについては後ほど説明しますが、まずはその特徴をいくつか紹介したいと思います。
依存関係なし
ミニファイ・gzip圧縮後で約2KB
spanではなくCSSの::highlight(token-name)を利用
Textmateの言語定義を活用
人間が読めるlight-dark()テーマ
すべての言語/定義はオンデマンドでロード
ハイライト以外の全機能を<micro-lighter>カスタム要素に移行
ある時、Jekyllブログのシンタックスハイライトが壊れてしまいました。
長年、いくつかのシンタックスハイライター(Highlight.js、PrismJS、Rouge、Shikiなど)を使ってきましたが、クライアントサイドとサーバーサイドの実装の間にはトレードオフがあると感じていました。
別のものを選ぶことになり、Bramus氏のCSS Custom Highlights APIを使ったシンタックスハイライトの手法を試したいと思いました。
CSSの::highlight()疑似要素にはいくつかの制限があります。イタリック、太字、フォントの切り替えはできません。しかし、それ以外は、いたるところにspanを挿入するのではなく、CSSを通じて「このトークンをハイライトしたい」という意図を表現するのに pretty cool な構文です。
Highlight APIを使用することで、DOMの変更を避けられます。ライブラリのスコープは、正規表現パターンを使ってコードブロックをスキャンし、CSS.highlights.set(category, textRanges) を送信してコードブロックをハイライトすることに限定されます。
このサイトでは、それほど多くのシンタックスハイライトは必要ありません。すべての投稿にコードがあるわけではなく、コードサンプルはせいぜい15行程度です。
私の課題は、言語を頻繁に切り替えることです。同じ投稿でHTML、CSS、JavaScriptをすべて使用します。少しbash、少しruby、そしておまけにmarkdown。
これらすべてを使用すると、正規表現の限界を超えて複雑さが増したので、VS Codeで使われているTextmateの確立されたパターンのコレクションに頼ることにしました。
そして気づいた時には、私の小さなハイライターはほとんどすべての言語に対応できるようになっていました。
異なる言語を頻繁に使用することを考えると、私が確立した原則の一つは、設定とバンドルサイズを削減するために、すべての言語定義をオンデマンドで自動ロードすることでした。そうすれば、使用したものに対してのみ料金が発生します。
PrismJSの簡略化されたトークンカテゴリに触発されて、Textmateの細かいトークンカテゴリを、より人間が理解しやすいセットにフラット化し、スタイリングを容易にしました。
それに加えて、コードブロックのスタイリングに関する私の大きな不満の一つは、ライトテーマとダークテーマが別々のエンティティであることです。そこで、light-dark() を使ってそれらを1つのテーマに統合しました。
私が組み込んだ最後の大きな意見は、シンタックスハイライターに一つの仕事をしてほしいということでした。それは、言語を推測し、その言語でコードをハイライトすることです。
これをガイドラインとして、行番号などの追加機能はすべてWebコンポーネントに移動しました。
バニラのWebコンポーネントは約1KiBのサイズを追加しますが、UIをネイティブカスタム要素のようなUIプリミティブに共置することは適切であり、ShadowDOMのカプセル化により、コードとプレゼンテーションUIを簡単に分離できます。
明らかに、私はWebコンポーネントの変態ですが、すべてをコアライブラリに詰め込もうとするよりも、関心の分離として優れているように感じます。
試してみてください
あなたのサイトで始めるには、自己初期化するミニファイされたバンドルを使用するのが良いでしょう。しかし、ESMとWebコンポーネントも提供しています。
npm install microlighter
<script type="module" src="path/to/microlighter/microlighter.min.js"></script>
前述したように、私の投稿のすべてにシンタックスハイライトがあるわけではないので、コードがあるページだとわかっている場合以外は、スクリプトのインポートを待っています。
if(document.querySelector('pre>code').length) {
import('path/to/microlighter/microlighter.min.js');
}
何か派手なことを自分でやるつもりなら、ESMバージョンも使えます。
import { highlightAll } from 'microlighter'
highlightAll({ selector: 'pre.onlyTheseGetHighlights' })
そして、前述した追加機能が必要な場合は、Webコンポーネントも使用できます。
<micro-lighter data-syntax-theme="github" line-numbers controls="copy">
<pre><code>コードはここに</code></pre>
</micro-lighter>
Webコンポーネントのクラスは拡張性も高いので、もし私がサポートしていないものがあれば、ベースクラスを拡張して独自の機能を追加することで「フォーク」できます。
最後に、提供されているテーマのいずれかを使用するか、独自のテーマを作成できます。
基本的な構造は次のとおりです。
/**
* セマンティックな--syntax-*トークンを設定します
* @value background | foreground | comment | keyword |
* operator |string | constant | function | type | variable |
* property | tag | selector | inserted | deleted
*/
[data-syntax-theme="my-theme-name"] {
color-scheme: light dark;
/* コードブロックのトークン */
--syntax-background: light-dark(#f8f8f8, #3a3a3a);
--syntax-foreground: light-dark(#3a3a3a, #f8f8f8);
/* ハイライトトークン */
--syntax-comment: light-dark(#6e7781, #8b949e);
--syntax-function: light-dark(#8250df, #d2a8ff);
/* ...など... */
}
[data-syntax-theme="my-theme-name"] pre:has(code) {
background-color:var(--syntax-background);
color:var(--syntax-foreground)
}
::highlight(comment) {
color: var(--syntax-comment)
}
::highlight(function) {
color: var(--syntax-function)
}
/* ...など... */
これがMicroLighterです。
もしあなたがそれを使って試してみたら、どう思ったか教えてください。