remark-acroterm
v1.0.0
Published
LaTeX acroterm package (\term/\acro/\tacro/\aterm) reimplemented as a remark-directive plugin for Astro, using a two-pass build to resolve first-use expansion across pages.
Downloads
160
Maintainers
Readme
remark-acroterm
LaTeX の acroterm パッケージ(\term / \acro / \tacro / \aterm)を
Astro (remark-directive) 向けに再実装したプラグインです。
CTAN の acroterm パッケージそのものを移植したものではなく、その挙動を再設計・再実装したものです (LaTeXの組版エンジンと静的サイトジェネレータでは仕組みが根本的に異なるため)。
LaTeXコマンドとの対応
| LaTeX | このプラグイン | 備考 |
|---|---|---|
| \term{X} | :term[X] | 常に全文表示。索引にも登録 |
| \acro{ABBR} | :acro[ABBR] | 中央辞書 (acroterm.config.json) を引いて展開判定 |
| \tacro{full}{ABBR} | :tacro[ABBR]{full="full"} | その場で定義しつつ使用。初出は full(ABBR) |
| \aterm{ABBR}{full} | :aterm[ABBR]{full="full"} | \tacroと語順が逆。初出は ABBR(full) |
| \Term \Acro \Tacro \Aterm | :Term[...] :Acro[...] ... | ディレクティブ名の先頭を大文字にすると出力の先頭も大文字化 |
設計上の要点:なぜ2段階ビルドか
LaTeX は \label/\ref の解決に「2回コンパイルして .aux ファイルを介す」という方式を取ります。
acroterm の「初出だけ展開する」機能も同様に、まだレンダリングしていない他のページの情報を先に知る必要がある問題です。
このプラグインも同じ考え方で2段階に分けています。
- Pass 1 (
src/scan.mjs) — 全 Markdown/MDX を(レンダリングはせず)事前スキャンし、 「どの語がどのファイルで何番目に初めて登場したか」をregistry.jsonに確定させる。 LaTeX の1回目のコンパイル(.aux生成)に相当。 - Pass 2 (
src/plugin.mjs) — 実際の remark 変換時にregistry.jsonを参照し、 自分が処理しているのが「初出ファイル」かどうかを判定して展開/省略を出し分ける。 LaTeX の2回目のコンパイル(.auxを読んで解決)に相当。
文書順は「コンテンツディレクトリ内のファイルパス昇順」とみなしています。章立てのあるサイトでは
01-intro.md, 02-body.md のように番号を振っておくと直感通りの「初出」判定になります。
セットアップ
yarn add remark-acroterm remark-directive unified unist-util-visit mdast-util-to-stringacroterm.config.json(中央辞書。\newacronym的な事前登録に相当。任意):
{
"acronyms": { "SNAFU": "situation normal: all fouled up" },
"terms": { "Potrzebie System": "Potrzebie System of Weights and Measures" }
}astro.config.mjs(example/astro.config.mjs 参照):
import acrotermIntegration from 'remark-acroterm/astro-integration.mjs';
import acrotermPlugin from 'remark-acroterm/src/plugin.mjs';
const acrotermOptions = {
contentDir: './src/content/docs',
configPath: './acroterm.config.json',
registryPath: './.acroterm/registry.json',
};
export default defineConfig({
integrations: [acrotermIntegration(acrotermOptions)],
markdown: { remarkPlugins: [[acrotermPlugin, acrotermOptions]] },
});Integration が astro:config:setup フックで自動的に Pass 1 を実行するため、
astro dev / astro build を叩くだけで動きます(手動で scan.mjs を呼ぶ必要はありません)。
用語集ページは example/glossary.astro と src/glossary.mjs を参照してください。
出力されるHTML
:tacro[SNAFU]{full="situation normal: all fouled up"} → 初出
:acro[SNAFU] → 2回目以降<abbr class="acroterm-acro" title="situation normal: all fouled up">
situation normal: all fouled up(SNAFU)
</abbr>
<abbr class="acroterm-acro" title="situation normal: all fouled up">SNAFU</abbr><abbr> / <dfn> を使っているので、CSSを当てなくてもスクリーンリーダーやブラウザの
ツールチップ表示(title属性)で展開形が伝わります。見た目の装飾はCSSで自由に追加できます。
既知の制約
- 「初出」の判定はファイルパスのアルファベット順に依存します。ナビゲーション上の実際の閲覧順と
一致させたい場合はファイル名にプレフィックスを振るか、
scan.mjsのcollectFilesを content collection の順序(getCollection()の結果順)に差し替えてください。 astro devの増分ビルドでは、編集したファイル1つだけが再コンパイルされても Pass 1 は毎回全文書をスキャンし直します(サイト規模が大きい場合はキャッシュの検討余地あり)。- 大文字小文字は区別します(
SNAFUとsnafuは別語として扱われます)。
テスト
example/content/ に検証用のサンプルを同梱しています。
node src/scan.mjs example/content example/acroterm.config.json .acroterm/registry.json
node test-run.mjs # Pass2まで通してHTML出力を確認