動機🤔
AsciiDoc で prism.js
の Command Line プラグイン を使ってみたかった。
上記プラグインではカスタムデータ属性の指定が必要だが、標準の Asciidoctor.js
では任意のカスタムデータ属性に対応していない。
よって、テンプレートコンバーターを使ってカスタムデータ属性に対応したHTML出力をさせてみる。
……やってみた結果としては、カスタムコンバーターを使うほうがよさそうだった🥺。
ツール | バージョン |
---|---|
Node |
12.19.0 |
@asciidoctor/core |
2.2.0 |
本記事でやったこと
JavaScript の関数でもテンプレート作成できるが、今回は割愛。 |
テンプレート🥪
HTMLコードを生成するJSテンプレートエンジン用のソースファイルを指す。
asciidoctor.js
の各ノード(paragraph ブロックとか table ブロックとか)ごとのHTML出力を、このテンプレートから生成されるHTMLコードで置き換える。
- メリット
-
-
各ノードの出力をファイル単位で独立して記述できるので、上書きしたいノードに対してのみテンプレートを作成すればよい
-
適用するファイルまたはディレクトリを変更するだけでHTML出力を変更できる
-
- デメリット
-
-
テンプレートエンジンの使い方を学習する必要がある
-
ブラウザ環境では使えない(はず)
-
JSテンプレートエンジン
yarn add nunjucks
ライブラリをインストールすれば、あとは自動的に Asciidoctor.js
の方で読み込まれる。
テンプレートの作成
-
テンプレートを配置するディレクトリを作成(出力時に
template_dirs
で指定) -
置き換えたいノード名と一致する名前でファイル作成
ノード名の一覧は こちら を参照。たとえば置き換えたいノード名が listing
ならば、作成するテンプレートはlisting.njk
というように同じ名前にする。 -
テンプレートのコードを書く
ノード名とテンプレート名を同じにすることだけ注意すれば、あとはがんばってテンプレートを書くだけ。
以下はその一例で、prism.js
の Command Line プラグインに対応させるためにカスタムデータ属性の出力などを行っている。
テンプレートのサンプル
{%- import "macros/sourcecode.njk" as func -%}
<div {{ func.roles(node) }}>
{% if node.getTitle() -%}
<div class="title">{{ node.getTitle() }}</div>
{%- endif %}
<div class="content">
{% if node.getStyle() === 'source' -%}
<pre class="highlight {{ 'command-line' if node.getRoles().includes('command-line') -}}" {{ func.customdata(node.getAttributes()) }}><code {{ func.language(node) }}>{{ node.getContent() | safe }}</code></pre>
{% else -%}
<pre {{- func.customdata(node.getAttributes()) }}>{{ node.getContent() | safe }}</pre>
{%- endif -%}
</div>
</div>
{# set id and class attributes #}
{%- macro roles(node) -%}
{% if node.getId() -%}
id="{{ node.getId() }}"
{%- endif %}
class="{{ ['listingblock', node.getRole()] | join(' ') | trim }}"
{%- endmacro -%}
{# <code> language class and attribute #}
{%- macro language(node) -%}
{%- set lang = node.getAttribute('language') -%}
{%- if lang -%}
class="language-{{ lang }}"
data-lang="{{ lang }}"
{%- endif -%}
{%- endmacro -%}
{# custom data attributes #}
{% macro customdata(attrs) -%}
{%- set regExp = r/^data-.*/ -%}
{% for key, value in attrs -%}
{% if regExp.test(key) -%}
{{ key }}="{{ value }}"
{% endif %}
{%- endfor %}
{%- endmacro %}
safe フィルター
nunjucks では文字列を自動エスケープするが、エスケープ処理は asciidoctor で行いたいので停止しておく。
|
node オブジェクト テンプレートには AbstractNode 型の node オブジェクトが引数として渡される。テンプレート内ではこの node からノードのクラスや属性、テキストなどを参照する。
|
テンプレートを適用して出力💻
CLIまたはAPIにおいて、先ほど作成したテンプレートファイルのあるディレクトリを template_dirs
オプションで指定して呼び出す。
import Processor from '@asciidoctor/core'
const processor = Processor()
const doc = processor.loadFile(
'path/to/adoc_file', { template_dirs: ['path/to/template/dir'] })
console.log(doc.convert())
おわりに😎
テンプレートコンバーターの使い方についてあまり情報がなくて戦々恐々としてたけど、やってみたら簡単だった。
ただテンプレートエンジンの使い方も学習する必要があるのが難点。
JavaScript コードで完結するカスタムコンバーターのほうを使ったほうがいいかもしれない。