Skip to content

Commit 8499c82

Browse files
committed
Add firstLineBlank option
Closes GH-1.
1 parent 29748a5 commit 8499c82

File tree

4 files changed

+119
-36
lines changed

4 files changed

+119
-36
lines changed

index.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
export {gfmFootnoteFromMarkdown, gfmFootnoteToMarkdown} from './lib/index.js'
22

3+
export interface ToMarkdownOptions {
4+
// To do: next major: change default.
5+
/**
6+
* Use a blank line for the first line of footnote definitions
7+
* (`boolean`, default: `false`).
8+
*/
9+
firstLineBlank?: boolean | null | undefined
10+
}
11+
312
declare module 'mdast-util-to-markdown' {
413
interface ConstructNameMap {
514
/**

lib/index.js

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Extension as FromMarkdownExtension,
55
* Handle as FromMarkdownHandle
66
* } from 'mdast-util-from-markdown'
7+
* @import {ToMarkdownOptions} from 'mdast-util-gfm-footnote'
78
* @import {
89
* Handle as ToMarkdownHandle,
910
* Map,
@@ -96,31 +97,6 @@ function exitFootnoteDefinition(token) {
9697
this.exit(token)
9798
}
9899

99-
/**
100-
* @type {ToMarkdownHandle}
101-
* @param {FootnoteDefinition} node
102-
*/
103-
function footnoteDefinition(node, _, state, info) {
104-
const tracker = state.createTracker(info)
105-
let value = tracker.move('[^')
106-
const exit = state.enter('footnoteDefinition')
107-
const subexit = state.enter('label')
108-
value += tracker.move(
109-
state.safe(state.associationId(node), {before: value, after: ']'})
110-
)
111-
subexit()
112-
value += tracker.move(
113-
']:' + (node.children && node.children.length > 0 ? ' ' : '')
114-
)
115-
tracker.shift(4)
116-
value += tracker.move(
117-
state.indentLines(state.containerFlow(node, tracker.current()), map)
118-
)
119-
exit()
120-
121-
return value
122-
}
123-
124100
/** @type {ToMarkdownHandle} */
125101
function footnoteReferencePeek() {
126102
return '['
@@ -172,22 +148,65 @@ export function gfmFootnoteFromMarkdown() {
172148
* Create an extension for `mdast-util-to-markdown` to enable GFM footnotes
173149
* in markdown.
174150
*
151+
* @param {ToMarkdownOptions | null | undefined} [options]
152+
* Configuration (optional).
175153
* @returns {ToMarkdownExtension}
176154
* Extension for `mdast-util-to-markdown`.
177155
*/
178-
export function gfmFootnoteToMarkdown() {
156+
export function gfmFootnoteToMarkdown(options) {
157+
// To do: next major: change default.
158+
let firstLineBlank = false
159+
160+
if (options && options.firstLineBlank) {
161+
firstLineBlank = true
162+
}
163+
179164
return {
180165
handlers: {footnoteDefinition, footnoteReference},
181166
// This is on by default already.
182167
unsafe: [{character: '[', inConstruct: ['label', 'phrasing', 'reference']}]
183168
}
169+
170+
/**
171+
* @type {ToMarkdownHandle}
172+
* @param {FootnoteDefinition} node
173+
*/
174+
function footnoteDefinition(node, _, state, info) {
175+
const tracker = state.createTracker(info)
176+
let value = tracker.move('[^')
177+
const exit = state.enter('footnoteDefinition')
178+
const subexit = state.enter('label')
179+
value += tracker.move(
180+
state.safe(state.associationId(node), {before: value, after: ']'})
181+
)
182+
subexit()
183+
184+
value += tracker.move(']:')
185+
186+
if (node.children && node.children.length > 0) {
187+
tracker.shift(4)
188+
189+
value += tracker.move(
190+
(firstLineBlank ? '\n' : ' ') +
191+
state.indentLines(
192+
state.containerFlow(node, tracker.current()),
193+
firstLineBlank ? mapAll : mapExceptFirst
194+
)
195+
)
196+
}
197+
198+
exit()
199+
200+
return value
201+
}
184202
}
185203

186204
/** @type {Map} */
187-
function map(line, index, blank) {
188-
if (index === 0) {
189-
return line
190-
}
205+
function mapExceptFirst(line, index, blank) {
206+
return index === 0 ? line : mapAll(line, index, blank)
207+
}
191208

209+
/** @type {Map} */
210+
function mapAll(line, index, blank) {
192211
return (blank ? '' : ' ') + line
193212
}

readme.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
* [Use](#use)
1919
* [API](#api)
2020
* [`gfmFootnoteFromMarkdown()`](#gfmfootnotefrommarkdown)
21-
* [`gfmFootnoteToMarkdown()`](#gfmfootnotetomarkdown)
21+
* [`gfmFootnoteToMarkdown(options?)`](#gfmfootnotetomarkdownoptions)
22+
* [`ToMarkdownOptions`](#tomarkdownoptions)
2223
* [HTML](#html)
2324
* [Syntax](#syntax)
2425
* [Syntax tree](#syntax-tree)
@@ -126,7 +127,9 @@ const tree = fromMarkdown(value, {
126127

127128
console.log(tree)
128129

129-
const result = toMarkdown(tree, {extensions: [gfmFootnoteToMarkdown()]})
130+
const result = toMarkdown(tree, {
131+
extensions: [gfmFootnoteToMarkdown({firstLineBlank: true})]
132+
})
130133

131134
console.log(result)
132135
```
@@ -187,10 +190,12 @@ console.log(result)
187190
```markdown
188191
In the Solar System, Mercury[^mercury] and Venus[^venus] have very small tilts.
189192

190-
[^mercury]: **Mercury** is the first planet from the Sun and the smallest
193+
[^mercury]:
194+
**Mercury** is the first planet from the Sun and the smallest
191195
in the Solar System.
192196

193-
[^venus]: **Venus** is the second planet from
197+
[^venus]:
198+
**Venus** is the second planet from
194199
the Sun.
195200
```
196201

@@ -200,6 +205,7 @@ This package exports the identifiers
200205
[`gfmFootnoteFromMarkdown`][api-gfmfootnotefrommarkdown] and
201206
[`gfmFootnoteToMarkdown`][api-gfmfootnotetomarkdown].
202207
There is no default export.
208+
It exports the type [`ToMarkdownOptions`][api-to-markdown-options].
203209

204210
### `gfmFootnoteFromMarkdown()`
205211

@@ -212,17 +218,31 @@ to enable GFM footnotes in markdown.
212218
Extension for `mdast-util-from-markdown`
213219
([`FromMarkdownExtension`][frommarkdownextension]).
214220

215-
### `gfmFootnoteToMarkdown()`
221+
### `gfmFootnoteToMarkdown(options?)`
216222

217223
Create an extension for
218224
[`mdast-util-to-markdown`][mdast-util-to-markdown]
219225
to enable GFM footnotes in markdown.
220226

227+
###### Parameters
228+
229+
* `options` ([`ToMarkdownOptions`][api-to-markdown-options], optional)
230+
— configuration
231+
221232
###### Returns
222233

223234
Extension for `mdast-util-to-markdown`
224235
([`ToMarkdownExtension`][tomarkdownextension]).
225236

237+
### `ToMarkdownOptions`
238+
239+
Configuration (TypeScript type).
240+
241+
###### Fields
242+
243+
* `firstLineBlank` (`boolean`, default: `false`)
244+
— use a blank line for the first line of footnote definitions
245+
226246
## HTML
227247

228248
This utility does not handle how markdown is turned to HTML.
@@ -381,7 +401,9 @@ abide by its terms.
381401

382402
[api-gfmfootnotefrommarkdown]: #gfmfootnotefrommarkdown
383403

384-
[api-gfmfootnotetomarkdown]: #gfmfootnotetomarkdown
404+
[api-gfmfootnotetomarkdown]: #gfmfootnotetomarkdownoptions
405+
406+
[api-to-markdown-options]: #tomarkdownoptions
385407

386408
[author]: https://wooorm.com
387409

test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,37 @@ test('gfmFootnoteToMarkdown', async function (t) {
361361
)
362362
}
363363
)
364+
365+
await t.test('should support `firstLineBlank`', async function () {
366+
assert.deepEqual(
367+
toMarkdown(
368+
{
369+
type: 'footnoteDefinition',
370+
identifier: 'a',
371+
children: [
372+
{type: 'paragraph', children: [{type: 'text', value: 'b'}]}
373+
]
374+
},
375+
{extensions: [gfmFootnoteToMarkdown({firstLineBlank: true})]}
376+
),
377+
'[^a]:\n b\n'
378+
)
379+
})
380+
381+
await t.test(
382+
'should support `firstLineBlank` w/o children',
383+
async function () {
384+
assert.deepEqual(
385+
toMarkdown(
386+
{
387+
type: 'footnoteDefinition',
388+
identifier: 'a',
389+
children: []
390+
},
391+
{extensions: [gfmFootnoteToMarkdown({firstLineBlank: true})]}
392+
),
393+
'[^a]:\n'
394+
)
395+
}
396+
)
364397
})

0 commit comments

Comments
 (0)