1.3.0 • Published 9 months ago

@maxmmyron/rehype-section-headings v1.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

🎁 @maxmmyron/rehype-section-headings

A fork of Tim Perkins' great rehype package rehype-section-headings. I added a few customizable options that make it more suitable for my personal use cases.

CI

License: MIT

rehype plugin to wrap all heading elements and any following content with <section> tags

Installation

# npm
npm install @maxmmyron/rehype-section-headings

# yarn
yarn add @maxmmyron/rehype-section-headings

# pnpm
pnpm add @maxmmyron/rehype-section-headings

Usage

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

rehype().use(rehypeSectionHeadings).process(/* html */);

Example

The following script..

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<span>This shouldn't <i>span</i> the whole page</span>
<h2>Heading level 2</h2>
<p>Hello again, world!</p>
`;

rehype().use(rehypeSectionHeadings).process(html);

...results in the following output

<section>
  <h1>Heading level 1</h1>
  <p>Hey, World!</p>
  <span>This shouldn't <i>span</i> the whole page</span>
</section>
<section>
  <h2>Heading level 2</h2>
  <p>Hello again, world!</p>
</section>

API

rehype().use(rehypeSectionHeadings, [options])

options

options.sectionDataAttribute

Type: string. Default: undefined.

If any heading elements have an id attribute, this plugin will take the data-* attribute name specified here and add it against any <section> tags.

The value of the data-* attribute will be the same as the heading elements id attribute.

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1 id="heading-level-1">Heading level 1</h1>
<p>Hey, World!</p>
<span>This shouldn't <i>span</i> the whole page</span>
<h2 id="heading-level-2">Heading level 2</h2>
<p>Hello again, world!</p>
`;

rehype()
  .use(rehypeSectionHeadings, { sectionDataAttribute: "data-heading-id" })
  .process(html);

...results in the following output

<section data-heading-id="heading-level-1">
  <h1 id="heading-level-1">Heading level 1</h1>
  <p>Hey, World!</p>
  <span>This shouldn't <i>span</i> the whole page</span>
</section>
<section data-heading-id="heading-level-2">
  <h2 id="heading-level-2">Heading level 2</h2>
  <p>Hello again, world!</p>
</section>

options.maxHeadingLevel

Type: (1 | 2 | 3 | 4 | 5 | 6). Default: 6.

The maximum heading <h*> level to wrap in section tags. Any heading elements with a level higher than the specified value will be ignored and wrapped within the last section tag.

For example, a maxHeadingLevel of 2 will wrap all <h1> and <h2> elements in <section> tags, but any <h3> or higher elements will be ignored and wrapped within their corresponding <h2> section tag:

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<h2>Heading level 2</h2>
<p>What is the meaning of life?</p>
<h3>Heading level 3</h3>
<p>Lorem ipsum means nothing to me.</p>
<h2>Heading level 2 (again)</h2>
<p>A bit more content</p>
`;

rehype().use(rehypeSectionHeadings, { maxHeadingLevel: 2 }).process(html);

...results in the following output

<section>
  <h1>Heading level 1</h1>
  <p>Hey, World!</p>
  <p>This is a bit of content.</p>
</section>
<section>
  <h2>Heading level 2</h2>
  <p>What is the meaning of life?</p>
  <h3>Heading level 3</h3>
  <p>Lorem ipsum means nothing to me.</p>
</section>
<section>
  <h2>Heading level 2 (again)</h2>
  <p>A bit more content</p>
</section>

options.headerWrap

Type: Partial<Record<"h1" | "h2" | "h3" | "h4" | "h5" | "h6", string | Hast.Element>>. Default: {}.

An optional element to wrap a particular header in. Useful as a jumping-off point when you need to insert extra content with the header, while keeping the header itself as the first child of the section. Can be specified as a string (i.e. "div" or "aside"), or as a hast element (which provides more granular element property control).

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<h2>Heading level 2</h2>
<p>What is the meaning of life?</p>
`;

rehype()
  .use(rehypeSectionHeadings, { headerWrap: { h1: "aside" } })
  .process(html);

...results in the following output

<section>
  <aside>
    <h1>Heading level 1</h1>
  </aside>
  <p>Hey, World!</p>
  <p>This is a bit of content.</p>
</section>
<section>
  <h2>Heading level 2</h2>
  <p>What is the meaning of life?</p>
</section>

You can also use hast elements to describe the wrapping element:

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<h2>Heading level 2</h2>
<p>What is the meaning of life?</p>
`;

rehype()
  .use(rehypeSectionHeadings, {
    headerWrap: {
      h1: {
        type: "element",
        tagName: "aside",
        properties: { className: ["aside"] },
        children: [],
      },
    },
  })
  .process(html);

...results in the following output

<section>
  <aside class="aside">
    <h1>Heading level 1</h1>
  </aside>
  <p>Hey, World!</p>
  <p>This is a bit of content.</p>
</section>
<section>
  <h2>Heading level 2</h2>
  <p>What is the meaning of life?</p>
</section>

options.contentWrap

Type: string | Hast.Element. default: null.

An optional element to wrap the content of a section in. Useful as a jumping-off point when you need to further customize a section's content. Accepts either a string (i.e. a tag name like "div") or a hast Element.

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<img src="LK-99.png" alt="A supposed room-temperature superconductor." />
<h2>Heading level 2</h2>
<p>I want to believe!!!</p>
`;

rehype().use(rehypeSectionHeadings, { contentWrap: "main" }).process(html);

...results in the following output

<section>
  <h1>Heading level 1</h1>
  <main>
    <p>Hey, World!</p>
    <p>This is a bit of content.</p>
    <img src="LK-99.png" alt="A supposed room-temperature superconductor." />
  </main>
</section>
<section>
  <h2>Heading level 2</h2>
  <main>
    <p>I want to believe!!!</p>
  </main>
</section>

We can also provide this as a hast element, to get more granular control over the element's properties:

import rehype from "rehype";
import rehypeSectionHeadings from "@maxmmyron/rehype-section-headings";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<img src="LK-99.png" alt="A supposed room-temperature superconductor." />
<h2>Heading level 2</h2>
<p>I want to believe!!!</p>
`;

rehype()
  .use(rehypeSectionHeadings, {
    contentWrap: {
      type: "element",
      tagName: "main",
      properties: { className: ["content"] },
      children: [],
    },
  })
  .process(html);

...results in the following output

<section>
  <h1>Heading level 1</h1>
  <main class="content">
    <p>Hey, World!</p>
    <p>This is a bit of content.</p>
    <img src="LK-99.png" alt="A supposed room-temperature superconductor." />
  </main>
</section>
<section>
  <h2>Heading level 2</h2>
  <main class="content">
    <p>I want to believe!!!</p>
  </main>
</section>

We can even combine this with options.headerWrap to get even more control over the output:

import rehype from "rehype";

const html = `
<h1>Heading level 1</h1>
<p>Hey, World!</p>
<p>This is a bit of content.</p>
<img src="LK-99.png" alt="A supposed room-temperature superconductor." />
<h2>Heading level 2</h2>
<p>I want to believe!!!</p>
`;

rehype()
  .use(rehypeSectionHeadings, {
    headerWrap: {
      h1: "header",
      h2: {
        type: "element",
        tagName: "aside",
        properties: { className: ["aside"] },
        children: [],
      },
    },
    contentWrap: {
      type: "element",
      tagName: "main",
      properties: { className: ["content"] },
      children: [],
    },
  })
  .process(html);

...results in the following output

<section>
  <header>
    <h1>Heading level 1</h1>
  </header>
  <main class="content">
    <p>Hey, World!</p>
    <p>This is a bit of content.</p>
    <img src="LK-99.png" alt="A supposed room-temperature superconductor." />
  </main>
</section>
<section>
  <aside class="aside">
    <h2>Heading level 2</h2>
  </aside>
  <main class="content">
    <p>I want to believe!!!</p>
  </main>
</section>

License

MIT

1.3.0

9 months ago

1.2.2

9 months ago

1.2.1

9 months ago

1.2.0

9 months ago