remark-capitalize-headings
TypeScript icon, indicating that this package has built-in type declarations

2.0.1 • Public • Published

Black Lives Matter! Last commit timestamp Codecov Source license Monthly Downloads NPM version Uses Semantic Release!

remark-capitalize-headings

This is a unified (remark) plugin that selectively transforms headings using vercel's title package, which adheres to The Chicago Manual of Style. Comes with full unicode support (reducible to title's unicode support) too.

While you can get a similar effect by using remark-capitalize (this plugin's abandoned? inspiration), remark-capitalize-headings comes with the following changes:

The end result is a less bothersome experience, where you're more likely to use a single simple configuration across your projects without constant tweaking.

You might also be interested in remark-ignore, which lets you instruct remark not to transform parts of your Markdown documents. It can be used as a last resort to ignore individual headers when this plugin's options just will not do.


Install

Due to the nature of the unified ecosystem, this package is ESM only and cannot be require'd.

npm install --save-dev remark-capitalize-headings

Usage

Via API

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings)
  .process(await read('example.md'));

console.log(String(file));

Via remark-cli

remark -o --use capitalize-headings README.md

Via unified configuration

In package.json:

  /* … */
  "remarkConfig": {
    "plugins": [
      "remark-capitalize-headings"
      /* … */
    ]
  },
  /* … */

In .remarkrc.js:

module.exports = {
  plugins: [
    // …
    'capitalize-headings'
  ]
};

In .remarkrc.mjs:

import remarkCapitalizeHeadings from 'remark-capitalize-headings';

export default {
  plugins: [
    // …
    remarkCapitalizeHeadings
  ]
};

API

Detailed interface information can be found under docs/.

Options

All "RegExp" values below are assumed to be strings, and will be transformed into regular expression objects via the following expression: RegExp(value, 'gu').

This plugin recognizes the following options:

excludeHeadingLevel

Valid values: { [level: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"]: boolean }
Default: {}

Headings of the specified level in { [level]: true } will be excluded from capitalization entirely, where h1 corresponds to <h1>…</h1>/# …, h2 to <h2>…</h2>/## …, etc.

Excludes with false values are treated as if they were commented out.

excludeSectionRegExp

Valid values: [RegExp]
Default: []

Entire sections with a stringified heading matching at least one of the given regular expression strings will be excluded from capitalization entirely.

replaceHeadingRegExp

Valid values: { [regExp: RegExp]: string }
Default: { "(?<=\\s)a(?=\\p{P})": "A" }

This option lets you manipulate non-excluded headers in their stringified form after they've been transformed by title. This extra context is useful for tasks like capitalizing a word only if it appears at the end of a heading, or as part of a phrase, or to prevent a word from being capitalized. This option also supports using matching groups during replacement.

The only limitation is that any manipulations must not change the length of the (stringified) header. If they do, an error will be thrown. Since this plugin is meant for capitalization, there isn't much reason to add or remove characters anyway.

By default, a is replaced with A when it appears alone before a single punctuation character; e.g., # Section a: Raised By Wolves becomes # Section A: Raised By Wolves. This diverges from title's default behavior at time of writing but is compliant with the CMOS.

For example: in the title # Evaluating the Notation of the Associated Press and the Style of the New York Times, you may want to capitalize the the that occurs before Associated Press and before New York Times, but not anywhere else. This could be achieved with the following:

{
  replaceHeadingRegExp: {
    'the Associated Press': 'The Associated Press',
    'the New York Times': 'The New York Times',
  }
}

Which would yield: # Evaluating the Notation of The Associated Press and the Style of The New York Times.

Examples

Suppose we have the following Markdown file example.md:

# my documentation

## Section 1 is [the best](https://google.com)

### Subsection a

### Subsection _a_

### Subsection \_a

### Subsection a: be see

### Subsection b

### Subsection C

## section 2 is the test

### subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

#### Additional option: `options.opt3`

## Section 3 has the rest {#section-three}

### Subsection [a][1]

#### Sci-fi title generator

##### children of celeste

##### the bionic oblivion

##### snows Of arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Using the Default Configuration

Running the following JavaScript:

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings)
  .process(await read('example.md'));

console.log(String(file));

Would output the following compared to example.md:

-# my documentation
+# My Documentation

-## Section 1 is [the best](https://google.com)
+## Section 1 Is [the best](https://google.com)

-### Subsection a
+### Subsection A

-### Subsection _a_
+### Subsection _A_

### Subsection \_a

-### Subsection a: be see
+### Subsection A: Be See

-### Subsection b
+### Subsection B

### Subsection C

-## section 2 is the test
+## Section 2 Is the Test

-### subsection 1
+### Subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

-#### Additional option: `options.opt3`
+#### Additional Option: `options.opt3`

-## Section 3 has the rest {#section-three}
+## Section 3 Has the Rest {#section-Three}

### Subsection [a][1]

-#### Sci-fi title generator
+#### Sci-Fi Title Generator

-##### children of celeste
+##### Children of Celeste

-##### the bionic oblivion
+##### The Bionic Oblivion

-##### snows Of arrakis
+##### Snows of Arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Using excludeHeadingLevel

Running the following JavaScript:

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings, {
    // Do not capitalize any H3 and H4 headings
    excludeHeadingLevel: { h3: true, h4: true }
  })
  .process(await read('example.md'));

console.log(String(file));

Would output the following compared to example.md:

-# my documentation
+# My Documentation

-## Section 1 is [the best](https://google.com)
+## Section 1 Is [the best](https://google.com)

### Subsection a

### Subsection _a_

### Subsection \_a

### Subsection a: be see

### Subsection b

### Subsection C

-## section 2 is the test
+## Section 2 Is the Test

### subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

#### Additional option: `options.opt3`

-## Section 3 has the rest {#section-three}
+## Section 3 Has the Rest {#section-Three}

### Subsection [a][1]

#### Sci-fi title generator

-##### children of celeste
+##### Children of Celeste

-##### the bionic oblivion
+##### The Bionic Oblivion

-##### snows Of arrakis
+##### Snows of Arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Using excludeHeadingText

Running the following JavaScript:

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings, {
    // Don't mess with {#custom-headers} from remark-heading-id
    // See: https://github.com/Xunnamius/unified-utils/issues/95
    excludeHeadingText: ['\\{\\s*#.*?\\}\\s*$']
  })
  .process(await read('example.md'));

console.log(String(file));

Would output the following compared to example.md:

-# my documentation
+# My Documentation

-## Section 1 is [the best](https://google.com)
+## Section 1 Is [the best](https://google.com)

### Subsection a

### Subsection _a_

### Subsection \_a

### Subsection a: be see

### Subsection b

### Subsection C

-## section 2 is the test
+## Section 2 Is the Test

### subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

#### Additional option: `options.opt3`

-## Section 3 has the rest {#section-three}
+## Section 3 Has the Rest {#section-three}

### Subsection [a][1]

#### Sci-fi title generator

-##### children of celeste
+##### Children of Celeste

-##### the bionic oblivion
+##### The Bionic Oblivion

-##### snows Of arrakis
+##### Snows of Arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Using excludeSectionRegExp

Running the following JavaScript:

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings, {
    // Do not capitalize headings with "subsection" in their text, nor any of the
    // headings below them
    excludeSectionRegExp: ['(s|S)ubsection']
  })
  .process(await read('example.md'));

console.log(String(file));

Would output the following compared to example.md:

-# my documentation
+# My Documentation

-## Section 1 is [the best](https://google.com)
+## Section 1 Is [the best](https://google.com)

### Subsection a

### Subsection _a_

### Subsection \_a

### Subsection a: be see

### Subsection b

### Subsection C

-## section 2 is the test
+## Section 2 Is the Test

### subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

#### Additional option: `options.opt3`

-## Section 3 has the rest {#section-three}
+## Section 3 Has the Rest {#section-Three}

### Subsection [a][1]

#### Sci-fi title generator

##### children of celeste

##### the bionic oblivion

##### snows Of arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Using replaceHeadingRegExp

Running the following JavaScript:

import { read } from 'to-vfile';
import { remark } from 'remark';
import remarkCapitalizeHeadings from 'remark-capitalize-headings';

const file = await remark()
  .use(remarkCapitalizeHeadings, {
    // Make some last-minute adjustments
    replaceHeadingRegExp: {
      '\\s(_?)(a|A)$': ' $1Y',
      'Has the Rest': 'Has The Rest'
    }
  })
  .process(await read('example.md'));

console.log(String(file));

Would output the following compared to example.md:

-# my documentation
+# My Documentation

-## Section 1 is [the best](https://google.com)
+## Section 1 Is [the best](https://google.com)

-### Subsection a
+### Subsection Y

-### Subsection _a_
+### Subsection _Y_

-### Subsection \_a
+### Subsection \_Y

-### Subsection a: be see
+### Subsection a: Be See

-### Subsection b
+### Subsection B

### Subsection C

-## section 2 is the test
+## Section 2 Is the Test

-### subsection 1
+### Subsection 1

### Subsection 2

#### `options.opt1`

#### `options.opt2`

-#### Additional option: `options.opt3`
+#### Additional Option: `options.opt3`

-## Section 3 has the rest {#section-three}
+## Section 3 Has The Rest {#section-Three}

### Subsection [a][1]

-#### Sci-fi title generator
+#### Sci-Fi Title Generator

-##### children of celeste
+##### Children of Celeste

-##### the bionic oblivion
+##### The Bionic Oblivion

-##### snows Of arrakis
+##### Snows of Arrakis

[1]: https://www.youtube.com/watch?v=dFs4yX4V7NQ

Related

Contributing and Support

New issues and pull requests are always welcome and greatly appreciated! 🤩 Just as well, you can star 🌟 this project to let me know you found it useful! ✊🏿 Thank you!

See CONTRIBUTING.md and SUPPORT.md for more information.

Contributors

See the table of contributors.

Package Sidebar

Install

npm i remark-capitalize-headings

Weekly Downloads

42

Version

2.0.1

License

MIT

Unpacked Size

27.4 kB

Total Files

5

Last publish

Collaborators

  • xunnamius