Skip to content

Commit 82095d2

Browse files
authored
feat(plugins): allow disabling headings title/description extraction (#188)
1 parent ef2921e commit 82095d2

3 files changed

Lines changed: 59 additions & 16 deletions

File tree

docs/content/4.plugins/1.built-in/headings.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,32 @@ The description check always looks at the node **immediately after** the title
6060

6161
| Option | Type | Default | Description |
6262
|---|---|---|---|
63-
| [`titleTag`](#options-code-titletag) | `string` | `'h1'` | Element tag to extract as the page title |
64-
| [`descriptionTag`](#options-code-descriptiontag) | `string` | `'p'` | Element tag to extract as the page description |
63+
| [`titleTag`](#options-code-titletag) | `string \| false` | `'h1'` | Element tag to extract as the page title, or `false` to disable |
64+
| [`descriptionTag`](#options-code-descriptiontag) | `string \| false` | `'p'` | Element tag to extract as the page description, or `false` to disable |
6565
| [`remove`](#options-code-remove) | `boolean` | `false` | Remove extracted nodes from the tree after extraction |
6666

6767
### `titleTag`
6868

69-
The element tag to read the title from.
69+
The element tag to read the title from. Set to `false` to disable title extraction entirely.
7070

7171
```typescript
7272
headings({ titleTag: 'h2' })
73+
74+
// Disable title extraction
75+
headings({ titleTag: false })
7376
```
7477

7578
**Default:** `'h1'`
7679

7780
### `descriptionTag`
7881

79-
The element tag to read the description from. Use `'blockquote'` if you prefer a block quote as the lead-in description.
82+
The element tag to read the description from. Use `'blockquote'` if you prefer a block quote as the lead-in description. Set to `false` to disable description extraction entirely.
8083

8184
```typescript
8285
headings({ descriptionTag: 'blockquote' })
86+
87+
// Disable description extraction
88+
headings({ descriptionTag: false })
8389
```
8490

8591
**Default:** `'p'`

packages/comark/src/plugins/headings.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { defineComarkPlugin } from '../utils/helpers.ts'
44
export interface HeadingsOptions {
55
/**
66
* Tag to extract as title and set to `tree.meta.title`.
7+
* Set to `false` to disable title extraction.
78
* @default 'h1'
89
*/
9-
titleTag?: string
10+
titleTag?: string | false
1011
/**
1112
* Tag to extract as description and set to `tree.meta.description`.
1213
* Useful alternatives: `'blockquote'`
14+
* Set to `false` to disable description extraction.
1315
* @default 'p'
1416
*/
15-
descriptionTag?: string
17+
descriptionTag?: string | false
1618
/**
1719
* Whether to remove the extracted nodes from the tree.
1820
* @default false
@@ -72,6 +74,12 @@ function flattenNodeText(node: ComarkNode): string {
7274
*
7375
* // Extract metadata and remove the matched nodes from the tree
7476
* headings({ remove: true })
77+
*
78+
* // Disable title extraction, only extract description
79+
* headings({ titleTag: false })
80+
*
81+
* // Disable description extraction, only extract title
82+
* headings({ descriptionTag: false })
7583
* ```
7684
*/
7785
export default defineComarkPlugin((options: HeadingsOptions = {}) => {
@@ -88,18 +96,23 @@ export default defineComarkPlugin((options: HeadingsOptions = {}) => {
8896
let titleNodeIndex = -1
8997
let descriptionNodeIndex = -1
9098

91-
const first = contentNodes[0]
92-
if (first && getTag(first) === titleTag) {
93-
titleNodeIndex = nodes.indexOf(first)
94-
state.tree.meta.title = flattenNodeText(first)
99+
let nextContentIndex = 0
100+
101+
if (titleTag !== false) {
102+
const first = contentNodes[0]
103+
if (first && getTag(first) === titleTag) {
104+
titleNodeIndex = nodes.indexOf(first)
105+
state.tree.meta.title = flattenNodeText(first)
106+
nextContentIndex = 1
107+
}
95108
}
96109

97-
// Description is the first content node after the (optional) title
98-
const afterTitle = titleNodeIndex !== -1 ? contentNodes.slice(1) : contentNodes
99-
const second = afterTitle[0]
100-
if (second && getTag(second) === descriptionTag) {
101-
descriptionNodeIndex = nodes.indexOf(second)
102-
state.tree.meta.description = flattenNodeText(second)
110+
if (descriptionTag !== false) {
111+
const candidate = contentNodes[nextContentIndex]
112+
if (candidate && getTag(candidate) === descriptionTag) {
113+
descriptionNodeIndex = nodes.indexOf(candidate)
114+
state.tree.meta.description = flattenNodeText(candidate)
115+
}
103116
}
104117

105118
if (remove) {

packages/comark/test/headings.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ describe('headings plugin', () => {
5555
expect(tree.meta.description).toBe('Just a paragraph.')
5656
})
5757

58+
it('disables title extraction with titleTag: false', async () => {
59+
const md = 'Just a paragraph.\n'
60+
const tree = await parse(md, { plugins: [headings({ titleTag: false })] })
61+
62+
expect(tree.meta.title).toBeUndefined()
63+
expect(tree.meta.description).toBe('Just a paragraph.')
64+
})
65+
66+
it('disables description extraction with descriptionTag: false', async () => {
67+
const tree = await parse(CONTENT, { plugins: [headings({ descriptionTag: false })] })
68+
69+
expect(tree.meta.title).toBe('My Page Title')
70+
expect(tree.meta.description).toBeUndefined()
71+
})
72+
73+
it('disables both with titleTag: false and descriptionTag: false', async () => {
74+
const tree = await parse(CONTENT, {
75+
plugins: [headings({ titleTag: false, descriptionTag: false })],
76+
})
77+
78+
expect(tree.meta.title).toBeUndefined()
79+
expect(tree.meta.description).toBeUndefined()
80+
})
81+
5882
it('uses custom titleTag and descriptionTag', async () => {
5983
const md = `## Custom Title
6084

0 commit comments

Comments
 (0)