remark-capitalize-headings
v2.0.1
Published
remark plugin that selectively transforms headings using vercel's title package
Downloads
332
Maintainers
Readme
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:
- Does not capitalize inline code blocks or links.
- Doesn't erroneously trim whitespace between nodes.
- Allows for a deeper level of customization, letting you to easily ignore individual words, headings by level, or even entire sections.
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
- remark-capitalize — predecessor to this package.
- remark-ignore — use comments to exclude one or more nodes from transformation.
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.