AST に変換して HTML の構造を変形する

小ネタです。例えば以下のような HTML の <h3> ではじまるコンテンツを <section> で囲みたい時、正規表現だとちょっとやりづらかったりします。

<h2>H2</h2>
<p>...</p>
<h3>H3</h3>
<p>...</p>
<h3>H3</h3>
<p>...</p>
<h2>H2</h2>
<p>...</p>
<section>
  <h3>H3</h3>
  <p>...</p>
</section>
<section>
  <h3>H3</h3>
  <p>...</p>
</section>

そんな時は AST に変換する Parser を利用すると簡単に構造変形をすることができたりします。

AST explorer を使えば簡単に試してみることができます。 以下コードは posthtml-parser 用です。ヘッダーの Transform メニューから posthtml を選択すると実行できます。

export default function (tree) {
  return groupByHeader(tree, 3).map((idx) => {
    if (!Array.isArray(idx)) {
      return tree[idx];
    }
    return {
      tag: "section",
      content: [idx.map((i) => tree[i])]
    };
  });
}

function groupByHeader(tree, level) {
  return tree.reduce((acc, node, i) => {
    // If the tag's header level is equal,
    if (node.tag === `h${level}`) {
      acc.push([i]);
      return acc;
    }
    // If the tag's header level is higher,
    if (/^h\d$/.test(node.tag) && Number(node.tag.slice(1)) < level) {
      acc.push(i);
      return acc;
    }
    const last = acc[acc.length - 1];
    if (Array.isArray(last)) {
      last.push(i);
      return acc;
    }
    acc.push(i);
    return acc;
  }, []);
}