HTML以外につかう
プラグインをつかうことで、テンプレートエンジンやフレームワークなどのHTML以外の構文にも適用できます。
プラグインのインストール
npmもしくはYarnでパーサプラグインをインストールします。
- npm
- Yarn
- pnpm
- Bun
npm install -D @markuplint/pug-parser
yarn add --dev @markuplint/pug-parser
pnpm add -D @markuplint/pug-parser
bun add --dev @markuplint/pug-parser
HTMLを含むタグ付きテンプレートリテラル(lit-htmlなど)を使用する場合は、タグ付きテンプレートリテラルパーサをインストールします。
- npm
- Yarn
- pnpm
- Bun
npm install -D @markuplint/tagged-template-literal-parser
yarn add --dev @markuplint/tagged-template-literal-parser
pnpm add -D @markuplint/tagged-template-literal-parser
bun add --dev @markuplint/tagged-template-literal-parser
構文に独自の仕様がある場合は、パーサプラグインと一緒にスペックプラグインをインストールする必要があります。
- npm
- Yarn
- pnpm
- Bun
npm install -D @markuplint/jsx-parser @markuplint/react-spec
yarn add --dev @markuplint/jsx-parser @markuplint/react-spec
pnpm add -D @markuplint/jsx-parser @markuplint/react-spec
bun add --dev @markuplint/jsx-parser @markuplint/react-spec
- npm
- Yarn
- pnpm
- Bun
npm install -D @markuplint/vue-parser @markuplint/vue-spec
yarn add --dev @markuplint/vue-parser @markuplint/vue-spec
pnpm add -D @markuplint/vue-parser @markuplint/vue-spec
bun add --dev @markuplint/vue-parser @markuplint/vue-spec
サポートしている構文
| テンプレートエンジンまたは構文 | パーサ | スペック |
|---|---|---|
| JSX | @markuplint/jsx-parser | @markuplint/react-spec |
| Vue | @markuplint/vue-parser | @markuplint/vue-spec |
| Svelte | @markuplint/svelte-parser | @markuplint/svelte-spec |
| SvelteKit | @markuplint/svelte-parser/kit | - |
| Astro | @markuplint/astro-parser | - |
| Alpine.js | @markuplint/alpine-parser | @markuplint/alpine-spec |
| HTMX | - | @markuplint/htmx-spec |
| タグ付きテンプレートリテラル(lit-html等) | @markuplint/tagged-template-literal-parser | - |
| Markdown | @markuplint/markdown-parser | - |
| MDX | @markuplint/mdx-parser | @markuplint/react-spec |
| Pug | @markuplint/pug-parser | - |
| PHP | @markuplint/php-parser | - |
| Smarty | @markuplint/smarty-parser | - |
| eRuby | @markuplint/erb-parser | - |
| EJS | @markuplint/ejs-parser | - |
| Mustache or Handlebars | @markuplint/mustache-parser | - |
| Nunjucks | @markuplint/nunjucks-parser | - |
| Liquid | @markuplint/liquid-parser | - |
@markuplint/html-parserというパッケージが存在しますが、コアパッケージに含まれており、インストールや設定ファイルへの指定は必要ありません。
プラグインの適用
設定ファイルのparserプロパティに適用するプラグインを指定します。また、スペックが存在する場合はspecsプロパティにも追加します。parserプロパティのキーに対象ファイル名を特定できる正規表現を設定します。
{
"parser": {
"\\.jsx$": "@markuplint/jsx-parser"
},
"specs": {
"\\.jsx$": "@markuplint/react-spec"
}
}
{
"parser": {
"\\.vue$": "@markuplint/vue-parser"
},
"specs": {
"\\.vue$": "@markuplint/vue-spec"
}
}
{
"parser": {
"\\.ts$": "@markuplint/tagged-template-literal-parser"
}
}
{
"parser": {
"\\.md$": "@markuplint/markdown-parser"
}
}
{
"parser": {
"\\.mdx$": "@markuplint/mdx-parser"
},
"specs": {
"\\.mdx$": "@markuplint/react-spec"
}
}
なぜスペックプラグインが必要なのですか
例えば、ネイティブのHTML要素にはkey属性は存在しませんが、ReactやVueを使うときにはその固有の属性をつかうことがとても多いです。そこで、@markuplint/react-specや@markuplint/vue-specを指定する必要があります。
const Component = ({ list }) => {
return (
<ul>
{list.map(item => (
<li key={item.key}>{item.text}</li>
))}
</ul>
);
};
<template>
<ul>
<li v-for="item in list" :key="item.key">{{ item.text }}</li>
</ul>
</template>
これ以外にもスペックプラグインは、それぞれが持つ固有の属性やディレクティブを含んでいます。
プリテンダー(偽装機能)
ReactやVueなどでは、カスタムコンポーネントをHTML要素として評価ができません。つまり、markuplintのコンテンツモデルルール — permitted-contentsなど — は、コンポーネントが実際に何をレンダリングするか知る手段がありません。この情報がないと、<button>要素をレンダリングする<Button>コンポーネントは未知の要素として扱われ、<a><Button /></a>(インタラクティブコンテンツの中にインタラクティブコンテンツ)のような不正なネストが検出されません。
<List>{/* ネイティブのHTML要素として評価できない */}
<Item />{/* ネイティブのHTML要素として評価できない */}
<Item />{/* ネイティブのHTML要素として評価できない */}
<Item />{/* ネイティブのHTML要素として評価できない */}
</List>
プリテンダー機能は、各コンポーネントが何としてレンダリングされるかをmarkuplintに伝えることでこれを解決します。
手動設定
コンポーネントとマッチするセレクタと、コンポーネントが公開する要素を手動で指定できます。
{
"pretenders": [
{
"selector": "List",
"as": "ul"
},
{
"selector": "Item",
"as": "li"
}
]
}
<List>{/* <ul>として評価 */}
<Item />{/* <li>として評価 */}
<Item />{/* <li>として評価 */}
<Item />{/* <li>として評価 */}
</List>
小規模プロジェクトではこの方法で十分ですが、コンポーネントライブラリが大きくなるにつれて手動でリストを管理するのは面倒になります。そこで動的スキャンが活躍します。
必要であれば、設定のpretendersプロパティの詳細を参照してください。
動的スキャン
この機能は実験的であり、将来のリリースで変更される可能性があります。
すべてのコンポーネントを手動でリスト化する代わりに、markuplintにコンポーネントのソースファイルをスキャンさせ、プリテンダーマッピングを自動的に発見させることができます。
{
"pretenders": {
"scan": [
{
"files": "./src/components/**/*.tsx"
}
]
}
}
この1つの設定で、数十の手動プリテンダー定義を置き換えることができます。markuplintは実行時にコンポーネントファイルを解析し、以下を判定します:
- コンポーネントがルート要素としてレンダリングするHTML要素
- コンポーネントが子要素を受け入れるかどうか(スロット検出)
- ルート要素の静的な属性
対応ファイルタイプ
ファイル拡張子によって使用するスキャナーが自動的に決定されます:
| 拡張子 | スキャナー | フレームワーク |
|---|---|---|
.js, .jsx, .ts, .tsx | JSXスキャナー | React, Preact, Solid等 |
.vue | テンプレートスキャナー | Vue |
.svelte | テンプレートスキャナー | Svelte |
.astro | テンプレートスキャナー | Astro |
複数のファイルタイプを同時にスキャンできます:
{
"pretenders": {
"scan": [
{
"files": "./src/components/**/*.tsx"
},
{
"files": "./src/components/**/*.vue",
"ignoreComponentNames": ["BaseLayout"]
}
]
}
}
スキャナーが検出するもの
以下のようなReactコンポーネントを例に考えます:
const ProfileCard = ({ children }) => {
return <article className="profile">{children}</article>;
};
スキャナーは、ProfileCardが<article>としてレンダリングされ、子要素を受け入れることを自動的に発見します。これは以下の手動定義と同等です:
{
"selector": "ProfileCard",
"as": {
"element": "article",
"slots": true
}
}
これにより、markuplintは<ProfileCard>がフローコンテンツのみを含むことを正しく検証でき(<article>と同様)、<p>の中に<ProfileCard>をネストすることが不正であると判定できます。
スキャンと手動定義の併用
scanと手動のdata定義を併用できます。スキャナーが特定のコンポーネントに対して正しいマッピングを判定できない場合や、スキャン結果をオーバーライドしたい場合に便利です:
{
"pretenders": {
"scan": [
{
"files": "./src/components/**/*.tsx"
}
],
"data": [
{
"selector": "SpecialComponent",
"as": {
"element": "nav",
"aria": { "name": { "fromAttr": "label" } }
}
}
]
}
}
pretenders.scanで設定リファレンスの全体を参照してください。
as属性について
コンポーネントにas属性が指定されている場合、その属性の値として指定された要素として評価されます。
<x-ul as="ul"><!-- <ul> として評価される -->
<x-li as="li"></x-li><!-- <li> として評価される -->
<x-li as="li"></x-li><!-- <li> として評価される -->
<x-li as="li"></x-li><!-- <li> として評価される -->
</x-ul>
これは、コンポーネントから継承された属性に対しても同様に評価されます。
<!-- <img src="image.png" alt="image"> として評価される -->
<x-img src="image.png" alt="image">