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.
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
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
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
]
};
Detailed interface information can be found under docs/
.
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:
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.
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.
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
.
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
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
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
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
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
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
- remark-capitalize β predecessor to this package.
- remark-ignore β use comments to exclude one or more nodes from transformation.
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.
See the table of contributors.