Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.
This commit is contained in:
164
node_modules/micromark-extension-gfm-footnote/dev/index.d.ts
generated
vendored
Normal file
164
node_modules/micromark-extension-gfm-footnote/dev/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
export {gfmFootnoteHtml, defaultBackLabel} from './lib/html.js'
|
||||
export {gfmFootnote} from './lib/syntax.js'
|
||||
|
||||
/**
|
||||
* Generate a back label dynamically.
|
||||
*
|
||||
* For the following markdown:
|
||||
*
|
||||
* ```markdown
|
||||
* Alpha[^micromark], bravo[^micromark], and charlie[^remark].
|
||||
*
|
||||
* [^remark]: things about remark
|
||||
* [^micromark]: things about micromark
|
||||
* ```
|
||||
*
|
||||
* This function will be called with:
|
||||
*
|
||||
* * `0` and `0` for the backreference from `things about micromark` to
|
||||
* `alpha`, as it is the first used definition, and the first call to it
|
||||
* * `0` and `1` for the backreference from `things about micromark` to
|
||||
* `bravo`, as it is the first used definition, and the second call to it
|
||||
* * `1` and `0` for the backreference from `things about remark` to
|
||||
* `charlie`, as it is the second used definition
|
||||
*
|
||||
* @param referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns
|
||||
* Back label to use when linking back from definitions to their reference.
|
||||
*/
|
||||
export type BackLabelTemplate = (
|
||||
referenceIndex: number,
|
||||
rereferenceIndex: number
|
||||
) => string
|
||||
|
||||
/**
|
||||
* Configuration.
|
||||
*/
|
||||
export interface HtmlOptions {
|
||||
/**
|
||||
* Prefix to use before the `id` attribute on footnotes to prevent them from
|
||||
* *clobbering* (default: `'user-content-'`).
|
||||
*
|
||||
* Pass `''` for trusted markdown and when you are careful with
|
||||
* polyfilling.
|
||||
* You could pass a different prefix.
|
||||
*
|
||||
* DOM clobbering is this:
|
||||
*
|
||||
* ```html
|
||||
* <p id="x"></p>
|
||||
* <script>alert(x) // `x` now refers to the `p#x` DOM element</script>
|
||||
* ```
|
||||
*
|
||||
* The above example shows that elements are made available by browsers, by
|
||||
* their ID, on the `window` object.
|
||||
* This is a security risk because you might be expecting some other variable
|
||||
* at that place.
|
||||
* It can also break polyfills.
|
||||
* Using a prefix solves these problems.
|
||||
*/
|
||||
clobberPrefix?: string | null | undefined
|
||||
/**
|
||||
* Textual label to use for the footnotes section (default: `'Footnotes'`).
|
||||
*
|
||||
* Change it when the markdown is not in English.
|
||||
*
|
||||
* This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass different attributes with the `labelAttributes` option.
|
||||
*/
|
||||
label?: string | null | undefined
|
||||
/**
|
||||
* Attributes to use on the footnote label (default: `'class="sr-only"'`).
|
||||
*
|
||||
* Change it to show the label and add other attributes.
|
||||
*
|
||||
* This label is typically hidden visually (assuming an `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass an empty string.
|
||||
* You can also add different attributes.
|
||||
*
|
||||
* > 👉 **Note**: `id="footnote-label"` is always added, because footnote
|
||||
* > calls use it with `aria-describedby` to provide an accessible label.
|
||||
*/
|
||||
labelAttributes?: string | null | undefined
|
||||
/**
|
||||
* HTML tag name to use for the footnote label element (default: `'h2'`).
|
||||
*
|
||||
* Change it to match your document structure.
|
||||
*
|
||||
* This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass different attributes with the `labelAttributes` option.
|
||||
*/
|
||||
labelTagName?: string | null | undefined
|
||||
/**
|
||||
* Textual label to describe the backreference back to references (default:
|
||||
* `defaultBackLabel`).
|
||||
*
|
||||
* The default value is:
|
||||
*
|
||||
* ```js
|
||||
* function defaultBackLabel(referenceIndex, rereferenceIndex) {
|
||||
* return (
|
||||
* 'Back to reference ' +
|
||||
* (referenceIndex + 1) +
|
||||
* (rereferenceIndex > 1 ? '-' + rereferenceIndex : '')
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Change it when the markdown is not in English.
|
||||
*
|
||||
* This label is used in the `aria-label` attribute on each backreference
|
||||
* (the `↩` links).
|
||||
* It affects users of assistive technology.
|
||||
*/
|
||||
backLabel?: BackLabelTemplate | string | null | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment types.
|
||||
*/
|
||||
declare module 'micromark-util-types' {
|
||||
/**
|
||||
* Compile data.
|
||||
*/
|
||||
interface CompileData {
|
||||
gfmFootnoteDefinitions?: Record<string, string>
|
||||
gfmFootnoteDefinitionStack?: Array<string>
|
||||
gfmFootnoteCallCounts?: Record<string, number>
|
||||
gfmFootnoteCallOrder?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse context.
|
||||
*/
|
||||
interface ParseContext {
|
||||
gfmFootnotes?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Token types.
|
||||
*/
|
||||
interface TokenTypeMap {
|
||||
gfmFootnoteCall: 'gfmFootnoteCall'
|
||||
gfmFootnoteCallLabelMarker: 'gfmFootnoteCallLabelMarker'
|
||||
gfmFootnoteCallMarker: 'gfmFootnoteCallMarker'
|
||||
gfmFootnoteCallString: 'gfmFootnoteCallString'
|
||||
gfmFootnoteDefinition: 'gfmFootnoteDefinition'
|
||||
gfmFootnoteDefinitionIndent: 'gfmFootnoteDefinitionIndent'
|
||||
gfmFootnoteDefinitionLabel: 'gfmFootnoteDefinitionLabel'
|
||||
gfmFootnoteDefinitionLabelMarker: 'gfmFootnoteDefinitionLabelMarker'
|
||||
gfmFootnoteDefinitionLabelString: 'gfmFootnoteDefinitionLabelString'
|
||||
gfmFootnoteDefinitionMarker: 'gfmFootnoteDefinitionMarker'
|
||||
gfmFootnoteDefinitionWhitespace: 'gfmFootnoteDefinitionWhitespace'
|
||||
}
|
||||
}
|
3
node_modules/micromark-extension-gfm-footnote/dev/index.js
generated
vendored
Normal file
3
node_modules/micromark-extension-gfm-footnote/dev/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Note: types are exported from `dev/index.d.ts`.
|
||||
export {gfmFootnote} from './lib/syntax.js'
|
||||
export {gfmFootnoteHtml, defaultBackLabel} from './lib/html.js'
|
25
node_modules/micromark-extension-gfm-footnote/dev/lib/html.d.ts
generated
vendored
Normal file
25
node_modules/micromark-extension-gfm-footnote/dev/lib/html.d.ts
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Generate the default label that GitHub uses on backreferences.
|
||||
*
|
||||
* @param {number} referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param {number} rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns {string}
|
||||
* Default label.
|
||||
*/
|
||||
export function defaultBackLabel(referenceIndex: number, rereferenceIndex: number): string;
|
||||
/**
|
||||
* Create an extension for `micromark` to support GFM footnotes when
|
||||
* serializing to HTML.
|
||||
*
|
||||
* @param {Options | null | undefined} [options={}]
|
||||
* Configuration (optional).
|
||||
* @returns {HtmlExtension}
|
||||
* Extension for `micromark` that can be passed in `htmlExtensions` to
|
||||
* support GFM footnotes when serializing to HTML.
|
||||
*/
|
||||
export function gfmFootnoteHtml(options?: Options | null | undefined): HtmlExtension;
|
||||
import type { HtmlOptions as Options } from 'micromark-extension-gfm-footnote';
|
||||
import type { HtmlExtension } from 'micromark-util-types';
|
226
node_modules/micromark-extension-gfm-footnote/dev/lib/html.js
generated
vendored
Normal file
226
node_modules/micromark-extension-gfm-footnote/dev/lib/html.js
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @import {HtmlOptions as Options} from 'micromark-extension-gfm-footnote'
|
||||
* @import {HtmlExtension} from 'micromark-util-types'
|
||||
*/
|
||||
|
||||
import {ok as assert} from 'devlop'
|
||||
import {normalizeIdentifier} from 'micromark-util-normalize-identifier'
|
||||
import {sanitizeUri} from 'micromark-util-sanitize-uri'
|
||||
|
||||
const own = {}.hasOwnProperty
|
||||
|
||||
/** @type {Options} */
|
||||
const emptyOptions = {}
|
||||
|
||||
/**
|
||||
* Generate the default label that GitHub uses on backreferences.
|
||||
*
|
||||
* @param {number} referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param {number} rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns {string}
|
||||
* Default label.
|
||||
*/
|
||||
export function defaultBackLabel(referenceIndex, rereferenceIndex) {
|
||||
return (
|
||||
'Back to reference ' +
|
||||
(referenceIndex + 1) +
|
||||
(rereferenceIndex > 1 ? '-' + rereferenceIndex : '')
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an extension for `micromark` to support GFM footnotes when
|
||||
* serializing to HTML.
|
||||
*
|
||||
* @param {Options | null | undefined} [options={}]
|
||||
* Configuration (optional).
|
||||
* @returns {HtmlExtension}
|
||||
* Extension for `micromark` that can be passed in `htmlExtensions` to
|
||||
* support GFM footnotes when serializing to HTML.
|
||||
*/
|
||||
export function gfmFootnoteHtml(options) {
|
||||
const config = options || emptyOptions
|
||||
const label = config.label || 'Footnotes'
|
||||
const labelTagName = config.labelTagName || 'h2'
|
||||
const labelAttributes =
|
||||
config.labelAttributes === null || config.labelAttributes === undefined
|
||||
? 'class="sr-only"'
|
||||
: config.labelAttributes
|
||||
const backLabel = config.backLabel || defaultBackLabel
|
||||
const clobberPrefix =
|
||||
config.clobberPrefix === null || config.clobberPrefix === undefined
|
||||
? 'user-content-'
|
||||
: config.clobberPrefix
|
||||
return {
|
||||
enter: {
|
||||
gfmFootnoteDefinition() {
|
||||
const stack = this.getData('tightStack')
|
||||
stack.push(false)
|
||||
},
|
||||
gfmFootnoteDefinitionLabelString() {
|
||||
this.buffer()
|
||||
},
|
||||
gfmFootnoteCallString() {
|
||||
this.buffer()
|
||||
}
|
||||
},
|
||||
exit: {
|
||||
gfmFootnoteDefinition() {
|
||||
let definitions = this.getData('gfmFootnoteDefinitions')
|
||||
const footnoteStack = this.getData('gfmFootnoteDefinitionStack')
|
||||
assert(footnoteStack, 'expected `footnoteStack`')
|
||||
const tightStack = this.getData('tightStack')
|
||||
const current = footnoteStack.pop()
|
||||
const value = this.resume()
|
||||
|
||||
assert(current, 'expected to be in a footnote')
|
||||
|
||||
if (!definitions) {
|
||||
this.setData('gfmFootnoteDefinitions', (definitions = {}))
|
||||
}
|
||||
|
||||
if (!own.call(definitions, current)) definitions[current] = value
|
||||
|
||||
tightStack.pop()
|
||||
this.setData('slurpOneLineEnding', true)
|
||||
// “Hack” to prevent a line ending from showing up if we’re in a definition in
|
||||
// an empty list item.
|
||||
this.setData('lastWasTag')
|
||||
},
|
||||
gfmFootnoteDefinitionLabelString(token) {
|
||||
let footnoteStack = this.getData('gfmFootnoteDefinitionStack')
|
||||
|
||||
if (!footnoteStack) {
|
||||
this.setData('gfmFootnoteDefinitionStack', (footnoteStack = []))
|
||||
}
|
||||
|
||||
footnoteStack.push(normalizeIdentifier(this.sliceSerialize(token)))
|
||||
this.resume() // Drop the label.
|
||||
this.buffer() // Get ready for a value.
|
||||
},
|
||||
gfmFootnoteCallString(token) {
|
||||
let calls = this.getData('gfmFootnoteCallOrder')
|
||||
let counts = this.getData('gfmFootnoteCallCounts')
|
||||
const id = normalizeIdentifier(this.sliceSerialize(token))
|
||||
/** @type {number} */
|
||||
let counter
|
||||
|
||||
this.resume()
|
||||
|
||||
if (!calls) this.setData('gfmFootnoteCallOrder', (calls = []))
|
||||
if (!counts) this.setData('gfmFootnoteCallCounts', (counts = {}))
|
||||
|
||||
const index = calls.indexOf(id)
|
||||
const safeId = sanitizeUri(id.toLowerCase())
|
||||
|
||||
if (index === -1) {
|
||||
calls.push(id)
|
||||
counts[id] = 1
|
||||
counter = calls.length
|
||||
} else {
|
||||
counts[id]++
|
||||
counter = index + 1
|
||||
}
|
||||
|
||||
const reuseCounter = counts[id]
|
||||
|
||||
this.tag(
|
||||
'<sup><a href="#' +
|
||||
clobberPrefix +
|
||||
'fn-' +
|
||||
safeId +
|
||||
'" id="' +
|
||||
clobberPrefix +
|
||||
'fnref-' +
|
||||
safeId +
|
||||
(reuseCounter > 1 ? '-' + reuseCounter : '') +
|
||||
'" data-footnote-ref="" aria-describedby="footnote-label">' +
|
||||
String(counter) +
|
||||
'</a></sup>'
|
||||
)
|
||||
},
|
||||
null() {
|
||||
const calls = this.getData('gfmFootnoteCallOrder') || []
|
||||
const counts = this.getData('gfmFootnoteCallCounts') || {}
|
||||
const definitions = this.getData('gfmFootnoteDefinitions') || {}
|
||||
let index = -1
|
||||
|
||||
if (calls.length > 0) {
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag(
|
||||
'<section data-footnotes="" class="footnotes"><' +
|
||||
labelTagName +
|
||||
' id="footnote-label"' +
|
||||
(labelAttributes ? ' ' + labelAttributes : '') +
|
||||
'>'
|
||||
)
|
||||
this.raw(this.encode(label))
|
||||
this.tag('</' + labelTagName + '>')
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag('<ol>')
|
||||
}
|
||||
|
||||
while (++index < calls.length) {
|
||||
// Called definitions are always defined.
|
||||
const id = calls[index]
|
||||
const safeId = sanitizeUri(id.toLowerCase())
|
||||
let referenceIndex = 0
|
||||
/** @type {Array<string>} */
|
||||
const references = []
|
||||
|
||||
while (++referenceIndex <= counts[id]) {
|
||||
references.push(
|
||||
'<a href="#' +
|
||||
clobberPrefix +
|
||||
'fnref-' +
|
||||
safeId +
|
||||
(referenceIndex > 1 ? '-' + referenceIndex : '') +
|
||||
'" data-footnote-backref="" aria-label="' +
|
||||
this.encode(
|
||||
typeof backLabel === 'string'
|
||||
? backLabel
|
||||
: backLabel(index, referenceIndex)
|
||||
) +
|
||||
'" class="data-footnote-backref">↩' +
|
||||
(referenceIndex > 1
|
||||
? '<sup>' + referenceIndex + '</sup>'
|
||||
: '') +
|
||||
'</a>'
|
||||
)
|
||||
}
|
||||
|
||||
const reference = references.join(' ')
|
||||
let injected = false
|
||||
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag('<li id="' + clobberPrefix + 'fn-' + safeId + '">')
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag(
|
||||
definitions[id].replace(/<\/p>(?:\r?\n|\r)?$/, function ($0) {
|
||||
injected = true
|
||||
return ' ' + reference + $0
|
||||
})
|
||||
)
|
||||
|
||||
if (!injected) {
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag(reference)
|
||||
}
|
||||
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag('</li>')
|
||||
}
|
||||
|
||||
if (calls.length > 0) {
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag('</ol>')
|
||||
this.lineEndingIfNeeded()
|
||||
this.tag('</section>')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
node_modules/micromark-extension-gfm-footnote/dev/lib/syntax.d.ts
generated
vendored
Normal file
9
node_modules/micromark-extension-gfm-footnote/dev/lib/syntax.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Create an extension for `micromark` to enable GFM footnote syntax.
|
||||
*
|
||||
* @returns {Extension}
|
||||
* Extension for `micromark` that can be passed in `extensions` to
|
||||
* enable GFM footnote syntax.
|
||||
*/
|
||||
export function gfmFootnote(): Extension;
|
||||
import type { Extension } from 'micromark-util-types';
|
558
node_modules/micromark-extension-gfm-footnote/dev/lib/syntax.js
generated
vendored
Normal file
558
node_modules/micromark-extension-gfm-footnote/dev/lib/syntax.js
generated
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
/**
|
||||
* @import {Event, Exiter, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
|
||||
*/
|
||||
|
||||
import {ok as assert} from 'devlop'
|
||||
import {blankLine} from 'micromark-core-commonmark'
|
||||
import {factorySpace} from 'micromark-factory-space'
|
||||
import {markdownLineEndingOrSpace} from 'micromark-util-character'
|
||||
import {normalizeIdentifier} from 'micromark-util-normalize-identifier'
|
||||
import {codes, constants, types} from 'micromark-util-symbol'
|
||||
|
||||
const indent = {tokenize: tokenizeIndent, partial: true}
|
||||
|
||||
// To do: micromark should support a `_hiddenGfmFootnoteSupport`, which only
|
||||
// affects label start (image).
|
||||
// That will let us drop `tokenizePotentialGfmFootnote*`.
|
||||
// It currently has a `_hiddenFootnoteSupport`, which affects that and more.
|
||||
// That can be removed when `micromark-extension-footnote` is archived.
|
||||
|
||||
/**
|
||||
* Create an extension for `micromark` to enable GFM footnote syntax.
|
||||
*
|
||||
* @returns {Extension}
|
||||
* Extension for `micromark` that can be passed in `extensions` to
|
||||
* enable GFM footnote syntax.
|
||||
*/
|
||||
export function gfmFootnote() {
|
||||
/** @type {Extension} */
|
||||
return {
|
||||
document: {
|
||||
[codes.leftSquareBracket]: {
|
||||
name: 'gfmFootnoteDefinition',
|
||||
tokenize: tokenizeDefinitionStart,
|
||||
continuation: {tokenize: tokenizeDefinitionContinuation},
|
||||
exit: gfmFootnoteDefinitionEnd
|
||||
}
|
||||
},
|
||||
text: {
|
||||
[codes.leftSquareBracket]: {
|
||||
name: 'gfmFootnoteCall',
|
||||
tokenize: tokenizeGfmFootnoteCall
|
||||
},
|
||||
[codes.rightSquareBracket]: {
|
||||
name: 'gfmPotentialFootnoteCall',
|
||||
add: 'after',
|
||||
tokenize: tokenizePotentialGfmFootnoteCall,
|
||||
resolveTo: resolveToPotentialGfmFootnoteCall
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To do: remove after micromark update.
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizePotentialGfmFootnoteCall(effects, ok, nok) {
|
||||
const self = this
|
||||
let index = self.events.length
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = [])
|
||||
/** @type {Token} */
|
||||
let labelStart
|
||||
|
||||
// Find an opening.
|
||||
while (index--) {
|
||||
const token = self.events[index][1]
|
||||
|
||||
if (token.type === types.labelImage) {
|
||||
labelStart = token
|
||||
break
|
||||
}
|
||||
|
||||
// Exit if we’ve walked far enough.
|
||||
if (
|
||||
token.type === 'gfmFootnoteCall' ||
|
||||
token.type === types.labelLink ||
|
||||
token.type === types.label ||
|
||||
token.type === types.image ||
|
||||
token.type === types.link
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return start
|
||||
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
assert(code === codes.rightSquareBracket, 'expected `]`')
|
||||
|
||||
if (!labelStart || !labelStart._balanced) {
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
const id = normalizeIdentifier(
|
||||
self.sliceSerialize({start: labelStart.end, end: self.now()})
|
||||
)
|
||||
|
||||
if (id.codePointAt(0) !== codes.caret || !defined.includes(id.slice(1))) {
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
effects.enter('gfmFootnoteCallLabelMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteCallLabelMarker')
|
||||
return ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
// To do: remove after micromark update.
|
||||
/** @type {Resolver} */
|
||||
function resolveToPotentialGfmFootnoteCall(events, context) {
|
||||
let index = events.length
|
||||
/** @type {Token | undefined} */
|
||||
let labelStart
|
||||
|
||||
// Find an opening.
|
||||
while (index--) {
|
||||
if (
|
||||
events[index][1].type === types.labelImage &&
|
||||
events[index][0] === 'enter'
|
||||
) {
|
||||
labelStart = events[index][1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert(labelStart, 'expected `labelStart` to resolve')
|
||||
|
||||
// Change the `labelImageMarker` to a `data`.
|
||||
events[index + 1][1].type = types.data
|
||||
events[index + 3][1].type = 'gfmFootnoteCallLabelMarker'
|
||||
|
||||
// The whole (without `!`):
|
||||
/** @type {Token} */
|
||||
const call = {
|
||||
type: 'gfmFootnoteCall',
|
||||
start: Object.assign({}, events[index + 3][1].start),
|
||||
end: Object.assign({}, events[events.length - 1][1].end)
|
||||
}
|
||||
// The `^` marker
|
||||
/** @type {Token} */
|
||||
const marker = {
|
||||
type: 'gfmFootnoteCallMarker',
|
||||
start: Object.assign({}, events[index + 3][1].end),
|
||||
end: Object.assign({}, events[index + 3][1].end)
|
||||
}
|
||||
// Increment the end 1 character.
|
||||
marker.end.column++
|
||||
marker.end.offset++
|
||||
marker.end._bufferIndex++
|
||||
/** @type {Token} */
|
||||
const string = {
|
||||
type: 'gfmFootnoteCallString',
|
||||
start: Object.assign({}, marker.end),
|
||||
end: Object.assign({}, events[events.length - 1][1].start)
|
||||
}
|
||||
/** @type {Token} */
|
||||
const chunk = {
|
||||
type: types.chunkString,
|
||||
contentType: 'string',
|
||||
start: Object.assign({}, string.start),
|
||||
end: Object.assign({}, string.end)
|
||||
}
|
||||
|
||||
/** @type {Array<Event>} */
|
||||
const replacement = [
|
||||
// Take the `labelImageMarker` (now `data`, the `!`)
|
||||
events[index + 1],
|
||||
events[index + 2],
|
||||
['enter', call, context],
|
||||
// The `[`
|
||||
events[index + 3],
|
||||
events[index + 4],
|
||||
// The `^`.
|
||||
['enter', marker, context],
|
||||
['exit', marker, context],
|
||||
// Everything in between.
|
||||
['enter', string, context],
|
||||
['enter', chunk, context],
|
||||
['exit', chunk, context],
|
||||
['exit', string, context],
|
||||
// The ending (`]`, properly parsed and labelled).
|
||||
events[events.length - 2],
|
||||
events[events.length - 1],
|
||||
['exit', call, context]
|
||||
]
|
||||
|
||||
events.splice(index, events.length - index + 1, ...replacement)
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeGfmFootnoteCall(effects, ok, nok) {
|
||||
const self = this
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = [])
|
||||
let size = 0
|
||||
/** @type {boolean} */
|
||||
let data
|
||||
|
||||
// Note: the implementation of `markdown-rs` is different, because it houses
|
||||
// core *and* extensions in one project.
|
||||
// Therefore, it can include footnote logic inside `label-end`.
|
||||
// We can’t do that, but luckily, we can parse footnotes in a simpler way than
|
||||
// needed for labels.
|
||||
return start
|
||||
|
||||
/**
|
||||
* Start of footnote label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
assert(code === codes.leftSquareBracket, 'expected `[`')
|
||||
effects.enter('gfmFootnoteCall')
|
||||
effects.enter('gfmFootnoteCallLabelMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteCallLabelMarker')
|
||||
return callStart
|
||||
}
|
||||
|
||||
/**
|
||||
* After `[`, at `^`.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callStart(code) {
|
||||
if (code !== codes.caret) return nok(code)
|
||||
|
||||
effects.enter('gfmFootnoteCallMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteCallMarker')
|
||||
effects.enter('gfmFootnoteCallString')
|
||||
effects.enter('chunkString').contentType = 'string'
|
||||
return callData
|
||||
}
|
||||
|
||||
/**
|
||||
* In label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callData(code) {
|
||||
if (
|
||||
// Too long.
|
||||
size > constants.linkReferenceSizeMax ||
|
||||
// Closing brace with nothing.
|
||||
(code === codes.rightSquareBracket && !data) ||
|
||||
// Space or tab is not supported by GFM for some reason.
|
||||
// `\n` and `[` not being supported makes sense.
|
||||
code === codes.eof ||
|
||||
code === codes.leftSquareBracket ||
|
||||
markdownLineEndingOrSpace(code)
|
||||
) {
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
if (code === codes.rightSquareBracket) {
|
||||
effects.exit('chunkString')
|
||||
const token = effects.exit('gfmFootnoteCallString')
|
||||
|
||||
if (!defined.includes(normalizeIdentifier(self.sliceSerialize(token)))) {
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
effects.enter('gfmFootnoteCallLabelMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteCallLabelMarker')
|
||||
effects.exit('gfmFootnoteCall')
|
||||
return ok
|
||||
}
|
||||
|
||||
if (!markdownLineEndingOrSpace(code)) {
|
||||
data = true
|
||||
}
|
||||
|
||||
size++
|
||||
effects.consume(code)
|
||||
return code === codes.backslash ? callEscape : callData
|
||||
}
|
||||
|
||||
/**
|
||||
* On character after escape.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b\c] d
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callEscape(code) {
|
||||
if (
|
||||
code === codes.leftSquareBracket ||
|
||||
code === codes.backslash ||
|
||||
code === codes.rightSquareBracket
|
||||
) {
|
||||
effects.consume(code)
|
||||
size++
|
||||
return callData
|
||||
}
|
||||
|
||||
return callData(code)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeDefinitionStart(effects, ok, nok) {
|
||||
const self = this
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = [])
|
||||
/** @type {string} */
|
||||
let identifier
|
||||
let size = 0
|
||||
/** @type {boolean | undefined} */
|
||||
let data
|
||||
|
||||
return start
|
||||
|
||||
/**
|
||||
* Start of GFM footnote definition.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
assert(code === codes.leftSquareBracket, 'expected `[`')
|
||||
effects.enter('gfmFootnoteDefinition')._container = true
|
||||
effects.enter('gfmFootnoteDefinitionLabel')
|
||||
effects.enter('gfmFootnoteDefinitionLabelMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteDefinitionLabelMarker')
|
||||
return labelAtMarker
|
||||
}
|
||||
|
||||
/**
|
||||
* In label, at caret.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelAtMarker(code) {
|
||||
if (code === codes.caret) {
|
||||
effects.enter('gfmFootnoteDefinitionMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteDefinitionMarker')
|
||||
effects.enter('gfmFootnoteDefinitionLabelString')
|
||||
effects.enter('chunkString').contentType = 'string'
|
||||
return labelInside
|
||||
}
|
||||
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
/**
|
||||
* In label.
|
||||
*
|
||||
* > 👉 **Note**: `cmark-gfm` prevents whitespace from occurring in footnote
|
||||
* > definition labels.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelInside(code) {
|
||||
if (
|
||||
// Too long.
|
||||
size > constants.linkReferenceSizeMax ||
|
||||
// Closing brace with nothing.
|
||||
(code === codes.rightSquareBracket && !data) ||
|
||||
// Space or tab is not supported by GFM for some reason.
|
||||
// `\n` and `[` not being supported makes sense.
|
||||
code === codes.eof ||
|
||||
code === codes.leftSquareBracket ||
|
||||
markdownLineEndingOrSpace(code)
|
||||
) {
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
if (code === codes.rightSquareBracket) {
|
||||
effects.exit('chunkString')
|
||||
const token = effects.exit('gfmFootnoteDefinitionLabelString')
|
||||
identifier = normalizeIdentifier(self.sliceSerialize(token))
|
||||
effects.enter('gfmFootnoteDefinitionLabelMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('gfmFootnoteDefinitionLabelMarker')
|
||||
effects.exit('gfmFootnoteDefinitionLabel')
|
||||
return labelAfter
|
||||
}
|
||||
|
||||
if (!markdownLineEndingOrSpace(code)) {
|
||||
data = true
|
||||
}
|
||||
|
||||
size++
|
||||
effects.consume(code)
|
||||
return code === codes.backslash ? labelEscape : labelInside
|
||||
}
|
||||
|
||||
/**
|
||||
* After `\`, at a special character.
|
||||
*
|
||||
* > 👉 **Note**: `cmark-gfm` currently does not support escaped brackets:
|
||||
* > <https://github.com/github/cmark-gfm/issues/240>
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a\*b]: c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelEscape(code) {
|
||||
if (
|
||||
code === codes.leftSquareBracket ||
|
||||
code === codes.backslash ||
|
||||
code === codes.rightSquareBracket
|
||||
) {
|
||||
effects.consume(code)
|
||||
size++
|
||||
return labelInside
|
||||
}
|
||||
|
||||
return labelInside(code)
|
||||
}
|
||||
|
||||
/**
|
||||
* After definition label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelAfter(code) {
|
||||
if (code === codes.colon) {
|
||||
effects.enter('definitionMarker')
|
||||
effects.consume(code)
|
||||
effects.exit('definitionMarker')
|
||||
|
||||
if (!defined.includes(identifier)) {
|
||||
defined.push(identifier)
|
||||
}
|
||||
|
||||
// Any whitespace after the marker is eaten, forming indented code
|
||||
// is not possible.
|
||||
// No space is also fine, just like a block quote marker.
|
||||
return factorySpace(
|
||||
effects,
|
||||
whitespaceAfter,
|
||||
'gfmFootnoteDefinitionWhitespace'
|
||||
)
|
||||
}
|
||||
|
||||
return nok(code)
|
||||
}
|
||||
|
||||
/**
|
||||
* After definition prefix.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function whitespaceAfter(code) {
|
||||
// `markdown-rs` has a wrapping token for the prefix that is closed here.
|
||||
return ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeDefinitionContinuation(effects, ok, nok) {
|
||||
/// Start of footnote definition continuation.
|
||||
///
|
||||
/// ```markdown
|
||||
/// | [^a]: b
|
||||
/// > | c
|
||||
/// ^
|
||||
/// ```
|
||||
//
|
||||
// Either a blank line, which is okay, or an indented thing.
|
||||
return effects.check(blankLine, ok, effects.attempt(indent, ok, nok))
|
||||
}
|
||||
|
||||
/** @type {Exiter} */
|
||||
function gfmFootnoteDefinitionEnd(effects) {
|
||||
effects.exit('gfmFootnoteDefinition')
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeIndent(effects, ok, nok) {
|
||||
const self = this
|
||||
|
||||
return factorySpace(
|
||||
effects,
|
||||
afterPrefix,
|
||||
'gfmFootnoteDefinitionIndent',
|
||||
constants.tabSize + 1
|
||||
)
|
||||
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
function afterPrefix(code) {
|
||||
const tail = self.events[self.events.length - 1]
|
||||
return tail &&
|
||||
tail[1].type === 'gfmFootnoteDefinitionIndent' &&
|
||||
tail[2].sliceSerialize(tail[1], true).length === constants.tabSize
|
||||
? ok(code)
|
||||
: nok(code)
|
||||
}
|
||||
}
|
164
node_modules/micromark-extension-gfm-footnote/index.d.ts
generated
vendored
Normal file
164
node_modules/micromark-extension-gfm-footnote/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
export {gfmFootnoteHtml, defaultBackLabel} from './lib/html.js'
|
||||
export {gfmFootnote} from './lib/syntax.js'
|
||||
|
||||
/**
|
||||
* Generate a back label dynamically.
|
||||
*
|
||||
* For the following markdown:
|
||||
*
|
||||
* ```markdown
|
||||
* Alpha[^micromark], bravo[^micromark], and charlie[^remark].
|
||||
*
|
||||
* [^remark]: things about remark
|
||||
* [^micromark]: things about micromark
|
||||
* ```
|
||||
*
|
||||
* This function will be called with:
|
||||
*
|
||||
* * `0` and `0` for the backreference from `things about micromark` to
|
||||
* `alpha`, as it is the first used definition, and the first call to it
|
||||
* * `0` and `1` for the backreference from `things about micromark` to
|
||||
* `bravo`, as it is the first used definition, and the second call to it
|
||||
* * `1` and `0` for the backreference from `things about remark` to
|
||||
* `charlie`, as it is the second used definition
|
||||
*
|
||||
* @param referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns
|
||||
* Back label to use when linking back from definitions to their reference.
|
||||
*/
|
||||
export type BackLabelTemplate = (
|
||||
referenceIndex: number,
|
||||
rereferenceIndex: number
|
||||
) => string
|
||||
|
||||
/**
|
||||
* Configuration.
|
||||
*/
|
||||
export interface HtmlOptions {
|
||||
/**
|
||||
* Prefix to use before the `id` attribute on footnotes to prevent them from
|
||||
* *clobbering* (default: `'user-content-'`).
|
||||
*
|
||||
* Pass `''` for trusted markdown and when you are careful with
|
||||
* polyfilling.
|
||||
* You could pass a different prefix.
|
||||
*
|
||||
* DOM clobbering is this:
|
||||
*
|
||||
* ```html
|
||||
* <p id="x"></p>
|
||||
* <script>alert(x) // `x` now refers to the `p#x` DOM element</script>
|
||||
* ```
|
||||
*
|
||||
* The above example shows that elements are made available by browsers, by
|
||||
* their ID, on the `window` object.
|
||||
* This is a security risk because you might be expecting some other variable
|
||||
* at that place.
|
||||
* It can also break polyfills.
|
||||
* Using a prefix solves these problems.
|
||||
*/
|
||||
clobberPrefix?: string | null | undefined
|
||||
/**
|
||||
* Textual label to use for the footnotes section (default: `'Footnotes'`).
|
||||
*
|
||||
* Change it when the markdown is not in English.
|
||||
*
|
||||
* This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass different attributes with the `labelAttributes` option.
|
||||
*/
|
||||
label?: string | null | undefined
|
||||
/**
|
||||
* Attributes to use on the footnote label (default: `'class="sr-only"'`).
|
||||
*
|
||||
* Change it to show the label and add other attributes.
|
||||
*
|
||||
* This label is typically hidden visually (assuming an `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass an empty string.
|
||||
* You can also add different attributes.
|
||||
*
|
||||
* > 👉 **Note**: `id="footnote-label"` is always added, because footnote
|
||||
* > calls use it with `aria-describedby` to provide an accessible label.
|
||||
*/
|
||||
labelAttributes?: string | null | undefined
|
||||
/**
|
||||
* HTML tag name to use for the footnote label element (default: `'h2'`).
|
||||
*
|
||||
* Change it to match your document structure.
|
||||
*
|
||||
* This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
* is defined that does that) and so affects screen readers only.
|
||||
* If you do have such a class, but want to show this section to everyone,
|
||||
* pass different attributes with the `labelAttributes` option.
|
||||
*/
|
||||
labelTagName?: string | null | undefined
|
||||
/**
|
||||
* Textual label to describe the backreference back to references (default:
|
||||
* `defaultBackLabel`).
|
||||
*
|
||||
* The default value is:
|
||||
*
|
||||
* ```js
|
||||
* function defaultBackLabel(referenceIndex, rereferenceIndex) {
|
||||
* return (
|
||||
* 'Back to reference ' +
|
||||
* (referenceIndex + 1) +
|
||||
* (rereferenceIndex > 1 ? '-' + rereferenceIndex : '')
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Change it when the markdown is not in English.
|
||||
*
|
||||
* This label is used in the `aria-label` attribute on each backreference
|
||||
* (the `↩` links).
|
||||
* It affects users of assistive technology.
|
||||
*/
|
||||
backLabel?: BackLabelTemplate | string | null | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment types.
|
||||
*/
|
||||
declare module 'micromark-util-types' {
|
||||
/**
|
||||
* Compile data.
|
||||
*/
|
||||
interface CompileData {
|
||||
gfmFootnoteDefinitions?: Record<string, string>
|
||||
gfmFootnoteDefinitionStack?: Array<string>
|
||||
gfmFootnoteCallCounts?: Record<string, number>
|
||||
gfmFootnoteCallOrder?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse context.
|
||||
*/
|
||||
interface ParseContext {
|
||||
gfmFootnotes?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Token types.
|
||||
*/
|
||||
interface TokenTypeMap {
|
||||
gfmFootnoteCall: 'gfmFootnoteCall'
|
||||
gfmFootnoteCallLabelMarker: 'gfmFootnoteCallLabelMarker'
|
||||
gfmFootnoteCallMarker: 'gfmFootnoteCallMarker'
|
||||
gfmFootnoteCallString: 'gfmFootnoteCallString'
|
||||
gfmFootnoteDefinition: 'gfmFootnoteDefinition'
|
||||
gfmFootnoteDefinitionIndent: 'gfmFootnoteDefinitionIndent'
|
||||
gfmFootnoteDefinitionLabel: 'gfmFootnoteDefinitionLabel'
|
||||
gfmFootnoteDefinitionLabelMarker: 'gfmFootnoteDefinitionLabelMarker'
|
||||
gfmFootnoteDefinitionLabelString: 'gfmFootnoteDefinitionLabelString'
|
||||
gfmFootnoteDefinitionMarker: 'gfmFootnoteDefinitionMarker'
|
||||
gfmFootnoteDefinitionWhitespace: 'gfmFootnoteDefinitionWhitespace'
|
||||
}
|
||||
}
|
3
node_modules/micromark-extension-gfm-footnote/index.js
generated
vendored
Normal file
3
node_modules/micromark-extension-gfm-footnote/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Note: types are exported from `dev/index.d.ts`.
|
||||
export { gfmFootnote } from './lib/syntax.js';
|
||||
export { gfmFootnoteHtml, defaultBackLabel } from './lib/html.js';
|
25
node_modules/micromark-extension-gfm-footnote/lib/html.d.ts
generated
vendored
Normal file
25
node_modules/micromark-extension-gfm-footnote/lib/html.d.ts
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Generate the default label that GitHub uses on backreferences.
|
||||
*
|
||||
* @param {number} referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param {number} rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns {string}
|
||||
* Default label.
|
||||
*/
|
||||
export function defaultBackLabel(referenceIndex: number, rereferenceIndex: number): string;
|
||||
/**
|
||||
* Create an extension for `micromark` to support GFM footnotes when
|
||||
* serializing to HTML.
|
||||
*
|
||||
* @param {Options | null | undefined} [options={}]
|
||||
* Configuration (optional).
|
||||
* @returns {HtmlExtension}
|
||||
* Extension for `micromark` that can be passed in `htmlExtensions` to
|
||||
* support GFM footnotes when serializing to HTML.
|
||||
*/
|
||||
export function gfmFootnoteHtml(options?: Options | null | undefined): HtmlExtension;
|
||||
import type { HtmlOptions as Options } from 'micromark-extension-gfm-footnote';
|
||||
import type { HtmlExtension } from 'micromark-util-types';
|
154
node_modules/micromark-extension-gfm-footnote/lib/html.js
generated
vendored
Normal file
154
node_modules/micromark-extension-gfm-footnote/lib/html.js
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @import {HtmlOptions as Options} from 'micromark-extension-gfm-footnote'
|
||||
* @import {HtmlExtension} from 'micromark-util-types'
|
||||
*/
|
||||
|
||||
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
||||
import { sanitizeUri } from 'micromark-util-sanitize-uri';
|
||||
const own = {}.hasOwnProperty;
|
||||
|
||||
/** @type {Options} */
|
||||
const emptyOptions = {};
|
||||
|
||||
/**
|
||||
* Generate the default label that GitHub uses on backreferences.
|
||||
*
|
||||
* @param {number} referenceIndex
|
||||
* Index of the definition in the order that they are first referenced,
|
||||
* 0-indexed.
|
||||
* @param {number} rereferenceIndex
|
||||
* Index of calls to the same definition, 0-indexed.
|
||||
* @returns {string}
|
||||
* Default label.
|
||||
*/
|
||||
export function defaultBackLabel(referenceIndex, rereferenceIndex) {
|
||||
return 'Back to reference ' + (referenceIndex + 1) + (rereferenceIndex > 1 ? '-' + rereferenceIndex : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an extension for `micromark` to support GFM footnotes when
|
||||
* serializing to HTML.
|
||||
*
|
||||
* @param {Options | null | undefined} [options={}]
|
||||
* Configuration (optional).
|
||||
* @returns {HtmlExtension}
|
||||
* Extension for `micromark` that can be passed in `htmlExtensions` to
|
||||
* support GFM footnotes when serializing to HTML.
|
||||
*/
|
||||
export function gfmFootnoteHtml(options) {
|
||||
const config = options || emptyOptions;
|
||||
const label = config.label || 'Footnotes';
|
||||
const labelTagName = config.labelTagName || 'h2';
|
||||
const labelAttributes = config.labelAttributes === null || config.labelAttributes === undefined ? 'class="sr-only"' : config.labelAttributes;
|
||||
const backLabel = config.backLabel || defaultBackLabel;
|
||||
const clobberPrefix = config.clobberPrefix === null || config.clobberPrefix === undefined ? 'user-content-' : config.clobberPrefix;
|
||||
return {
|
||||
enter: {
|
||||
gfmFootnoteDefinition() {
|
||||
const stack = this.getData('tightStack');
|
||||
stack.push(false);
|
||||
},
|
||||
gfmFootnoteDefinitionLabelString() {
|
||||
this.buffer();
|
||||
},
|
||||
gfmFootnoteCallString() {
|
||||
this.buffer();
|
||||
}
|
||||
},
|
||||
exit: {
|
||||
gfmFootnoteDefinition() {
|
||||
let definitions = this.getData('gfmFootnoteDefinitions');
|
||||
const footnoteStack = this.getData('gfmFootnoteDefinitionStack');
|
||||
const tightStack = this.getData('tightStack');
|
||||
const current = footnoteStack.pop();
|
||||
const value = this.resume();
|
||||
if (!definitions) {
|
||||
this.setData('gfmFootnoteDefinitions', definitions = {});
|
||||
}
|
||||
if (!own.call(definitions, current)) definitions[current] = value;
|
||||
tightStack.pop();
|
||||
this.setData('slurpOneLineEnding', true);
|
||||
// “Hack” to prevent a line ending from showing up if we’re in a definition in
|
||||
// an empty list item.
|
||||
this.setData('lastWasTag');
|
||||
},
|
||||
gfmFootnoteDefinitionLabelString(token) {
|
||||
let footnoteStack = this.getData('gfmFootnoteDefinitionStack');
|
||||
if (!footnoteStack) {
|
||||
this.setData('gfmFootnoteDefinitionStack', footnoteStack = []);
|
||||
}
|
||||
footnoteStack.push(normalizeIdentifier(this.sliceSerialize(token)));
|
||||
this.resume(); // Drop the label.
|
||||
this.buffer(); // Get ready for a value.
|
||||
},
|
||||
gfmFootnoteCallString(token) {
|
||||
let calls = this.getData('gfmFootnoteCallOrder');
|
||||
let counts = this.getData('gfmFootnoteCallCounts');
|
||||
const id = normalizeIdentifier(this.sliceSerialize(token));
|
||||
/** @type {number} */
|
||||
let counter;
|
||||
this.resume();
|
||||
if (!calls) this.setData('gfmFootnoteCallOrder', calls = []);
|
||||
if (!counts) this.setData('gfmFootnoteCallCounts', counts = {});
|
||||
const index = calls.indexOf(id);
|
||||
const safeId = sanitizeUri(id.toLowerCase());
|
||||
if (index === -1) {
|
||||
calls.push(id);
|
||||
counts[id] = 1;
|
||||
counter = calls.length;
|
||||
} else {
|
||||
counts[id]++;
|
||||
counter = index + 1;
|
||||
}
|
||||
const reuseCounter = counts[id];
|
||||
this.tag('<sup><a href="#' + clobberPrefix + 'fn-' + safeId + '" id="' + clobberPrefix + 'fnref-' + safeId + (reuseCounter > 1 ? '-' + reuseCounter : '') + '" data-footnote-ref="" aria-describedby="footnote-label">' + String(counter) + '</a></sup>');
|
||||
},
|
||||
null() {
|
||||
const calls = this.getData('gfmFootnoteCallOrder') || [];
|
||||
const counts = this.getData('gfmFootnoteCallCounts') || {};
|
||||
const definitions = this.getData('gfmFootnoteDefinitions') || {};
|
||||
let index = -1;
|
||||
if (calls.length > 0) {
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('<section data-footnotes="" class="footnotes"><' + labelTagName + ' id="footnote-label"' + (labelAttributes ? ' ' + labelAttributes : '') + '>');
|
||||
this.raw(this.encode(label));
|
||||
this.tag('</' + labelTagName + '>');
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('<ol>');
|
||||
}
|
||||
while (++index < calls.length) {
|
||||
// Called definitions are always defined.
|
||||
const id = calls[index];
|
||||
const safeId = sanitizeUri(id.toLowerCase());
|
||||
let referenceIndex = 0;
|
||||
/** @type {Array<string>} */
|
||||
const references = [];
|
||||
while (++referenceIndex <= counts[id]) {
|
||||
references.push('<a href="#' + clobberPrefix + 'fnref-' + safeId + (referenceIndex > 1 ? '-' + referenceIndex : '') + '" data-footnote-backref="" aria-label="' + this.encode(typeof backLabel === 'string' ? backLabel : backLabel(index, referenceIndex)) + '" class="data-footnote-backref">↩' + (referenceIndex > 1 ? '<sup>' + referenceIndex + '</sup>' : '') + '</a>');
|
||||
}
|
||||
const reference = references.join(' ');
|
||||
let injected = false;
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('<li id="' + clobberPrefix + 'fn-' + safeId + '">');
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag(definitions[id].replace(/<\/p>(?:\r?\n|\r)?$/, function ($0) {
|
||||
injected = true;
|
||||
return ' ' + reference + $0;
|
||||
}));
|
||||
if (!injected) {
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag(reference);
|
||||
}
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('</li>');
|
||||
}
|
||||
if (calls.length > 0) {
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('</ol>');
|
||||
this.lineEndingIfNeeded();
|
||||
this.tag('</section>');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
9
node_modules/micromark-extension-gfm-footnote/lib/syntax.d.ts
generated
vendored
Normal file
9
node_modules/micromark-extension-gfm-footnote/lib/syntax.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Create an extension for `micromark` to enable GFM footnote syntax.
|
||||
*
|
||||
* @returns {Extension}
|
||||
* Extension for `micromark` that can be passed in `extensions` to
|
||||
* enable GFM footnote syntax.
|
||||
*/
|
||||
export function gfmFootnote(): Extension;
|
||||
import type { Extension } from 'micromark-util-types';
|
485
node_modules/micromark-extension-gfm-footnote/lib/syntax.js
generated
vendored
Normal file
485
node_modules/micromark-extension-gfm-footnote/lib/syntax.js
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
/**
|
||||
* @import {Event, Exiter, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
|
||||
*/
|
||||
|
||||
import { blankLine } from 'micromark-core-commonmark';
|
||||
import { factorySpace } from 'micromark-factory-space';
|
||||
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
||||
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
||||
const indent = {
|
||||
tokenize: tokenizeIndent,
|
||||
partial: true
|
||||
};
|
||||
|
||||
// To do: micromark should support a `_hiddenGfmFootnoteSupport`, which only
|
||||
// affects label start (image).
|
||||
// That will let us drop `tokenizePotentialGfmFootnote*`.
|
||||
// It currently has a `_hiddenFootnoteSupport`, which affects that and more.
|
||||
// That can be removed when `micromark-extension-footnote` is archived.
|
||||
|
||||
/**
|
||||
* Create an extension for `micromark` to enable GFM footnote syntax.
|
||||
*
|
||||
* @returns {Extension}
|
||||
* Extension for `micromark` that can be passed in `extensions` to
|
||||
* enable GFM footnote syntax.
|
||||
*/
|
||||
export function gfmFootnote() {
|
||||
/** @type {Extension} */
|
||||
return {
|
||||
document: {
|
||||
[91]: {
|
||||
name: 'gfmFootnoteDefinition',
|
||||
tokenize: tokenizeDefinitionStart,
|
||||
continuation: {
|
||||
tokenize: tokenizeDefinitionContinuation
|
||||
},
|
||||
exit: gfmFootnoteDefinitionEnd
|
||||
}
|
||||
},
|
||||
text: {
|
||||
[91]: {
|
||||
name: 'gfmFootnoteCall',
|
||||
tokenize: tokenizeGfmFootnoteCall
|
||||
},
|
||||
[93]: {
|
||||
name: 'gfmPotentialFootnoteCall',
|
||||
add: 'after',
|
||||
tokenize: tokenizePotentialGfmFootnoteCall,
|
||||
resolveTo: resolveToPotentialGfmFootnoteCall
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// To do: remove after micromark update.
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizePotentialGfmFootnoteCall(effects, ok, nok) {
|
||||
const self = this;
|
||||
let index = self.events.length;
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
|
||||
/** @type {Token} */
|
||||
let labelStart;
|
||||
|
||||
// Find an opening.
|
||||
while (index--) {
|
||||
const token = self.events[index][1];
|
||||
if (token.type === "labelImage") {
|
||||
labelStart = token;
|
||||
break;
|
||||
}
|
||||
|
||||
// Exit if we’ve walked far enough.
|
||||
if (token.type === 'gfmFootnoteCall' || token.type === "labelLink" || token.type === "label" || token.type === "image" || token.type === "link") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
if (!labelStart || !labelStart._balanced) {
|
||||
return nok(code);
|
||||
}
|
||||
const id = normalizeIdentifier(self.sliceSerialize({
|
||||
start: labelStart.end,
|
||||
end: self.now()
|
||||
}));
|
||||
if (id.codePointAt(0) !== 94 || !defined.includes(id.slice(1))) {
|
||||
return nok(code);
|
||||
}
|
||||
effects.enter('gfmFootnoteCallLabelMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteCallLabelMarker');
|
||||
return ok(code);
|
||||
}
|
||||
}
|
||||
|
||||
// To do: remove after micromark update.
|
||||
/** @type {Resolver} */
|
||||
function resolveToPotentialGfmFootnoteCall(events, context) {
|
||||
let index = events.length;
|
||||
/** @type {Token | undefined} */
|
||||
let labelStart;
|
||||
|
||||
// Find an opening.
|
||||
while (index--) {
|
||||
if (events[index][1].type === "labelImage" && events[index][0] === 'enter') {
|
||||
labelStart = events[index][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Change the `labelImageMarker` to a `data`.
|
||||
events[index + 1][1].type = "data";
|
||||
events[index + 3][1].type = 'gfmFootnoteCallLabelMarker';
|
||||
|
||||
// The whole (without `!`):
|
||||
/** @type {Token} */
|
||||
const call = {
|
||||
type: 'gfmFootnoteCall',
|
||||
start: Object.assign({}, events[index + 3][1].start),
|
||||
end: Object.assign({}, events[events.length - 1][1].end)
|
||||
};
|
||||
// The `^` marker
|
||||
/** @type {Token} */
|
||||
const marker = {
|
||||
type: 'gfmFootnoteCallMarker',
|
||||
start: Object.assign({}, events[index + 3][1].end),
|
||||
end: Object.assign({}, events[index + 3][1].end)
|
||||
};
|
||||
// Increment the end 1 character.
|
||||
marker.end.column++;
|
||||
marker.end.offset++;
|
||||
marker.end._bufferIndex++;
|
||||
/** @type {Token} */
|
||||
const string = {
|
||||
type: 'gfmFootnoteCallString',
|
||||
start: Object.assign({}, marker.end),
|
||||
end: Object.assign({}, events[events.length - 1][1].start)
|
||||
};
|
||||
/** @type {Token} */
|
||||
const chunk = {
|
||||
type: "chunkString",
|
||||
contentType: 'string',
|
||||
start: Object.assign({}, string.start),
|
||||
end: Object.assign({}, string.end)
|
||||
};
|
||||
|
||||
/** @type {Array<Event>} */
|
||||
const replacement = [
|
||||
// Take the `labelImageMarker` (now `data`, the `!`)
|
||||
events[index + 1], events[index + 2], ['enter', call, context],
|
||||
// The `[`
|
||||
events[index + 3], events[index + 4],
|
||||
// The `^`.
|
||||
['enter', marker, context], ['exit', marker, context],
|
||||
// Everything in between.
|
||||
['enter', string, context], ['enter', chunk, context], ['exit', chunk, context], ['exit', string, context],
|
||||
// The ending (`]`, properly parsed and labelled).
|
||||
events[events.length - 2], events[events.length - 1], ['exit', call, context]];
|
||||
events.splice(index, events.length - index + 1, ...replacement);
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeGfmFootnoteCall(effects, ok, nok) {
|
||||
const self = this;
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
|
||||
let size = 0;
|
||||
/** @type {boolean} */
|
||||
let data;
|
||||
|
||||
// Note: the implementation of `markdown-rs` is different, because it houses
|
||||
// core *and* extensions in one project.
|
||||
// Therefore, it can include footnote logic inside `label-end`.
|
||||
// We can’t do that, but luckily, we can parse footnotes in a simpler way than
|
||||
// needed for labels.
|
||||
return start;
|
||||
|
||||
/**
|
||||
* Start of footnote label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
effects.enter('gfmFootnoteCall');
|
||||
effects.enter('gfmFootnoteCallLabelMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteCallLabelMarker');
|
||||
return callStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* After `[`, at `^`.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callStart(code) {
|
||||
if (code !== 94) return nok(code);
|
||||
effects.enter('gfmFootnoteCallMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteCallMarker');
|
||||
effects.enter('gfmFootnoteCallString');
|
||||
effects.enter('chunkString').contentType = 'string';
|
||||
return callData;
|
||||
}
|
||||
|
||||
/**
|
||||
* In label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b] c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callData(code) {
|
||||
if (
|
||||
// Too long.
|
||||
size > 999 ||
|
||||
// Closing brace with nothing.
|
||||
code === 93 && !data ||
|
||||
// Space or tab is not supported by GFM for some reason.
|
||||
// `\n` and `[` not being supported makes sense.
|
||||
code === null || code === 91 || markdownLineEndingOrSpace(code)) {
|
||||
return nok(code);
|
||||
}
|
||||
if (code === 93) {
|
||||
effects.exit('chunkString');
|
||||
const token = effects.exit('gfmFootnoteCallString');
|
||||
if (!defined.includes(normalizeIdentifier(self.sliceSerialize(token)))) {
|
||||
return nok(code);
|
||||
}
|
||||
effects.enter('gfmFootnoteCallLabelMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteCallLabelMarker');
|
||||
effects.exit('gfmFootnoteCall');
|
||||
return ok;
|
||||
}
|
||||
if (!markdownLineEndingOrSpace(code)) {
|
||||
data = true;
|
||||
}
|
||||
size++;
|
||||
effects.consume(code);
|
||||
return code === 92 ? callEscape : callData;
|
||||
}
|
||||
|
||||
/**
|
||||
* On character after escape.
|
||||
*
|
||||
* ```markdown
|
||||
* > | a [^b\c] d
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function callEscape(code) {
|
||||
if (code === 91 || code === 92 || code === 93) {
|
||||
effects.consume(code);
|
||||
size++;
|
||||
return callData;
|
||||
}
|
||||
return callData(code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeDefinitionStart(effects, ok, nok) {
|
||||
const self = this;
|
||||
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
|
||||
/** @type {string} */
|
||||
let identifier;
|
||||
let size = 0;
|
||||
/** @type {boolean | undefined} */
|
||||
let data;
|
||||
return start;
|
||||
|
||||
/**
|
||||
* Start of GFM footnote definition.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function start(code) {
|
||||
effects.enter('gfmFootnoteDefinition')._container = true;
|
||||
effects.enter('gfmFootnoteDefinitionLabel');
|
||||
effects.enter('gfmFootnoteDefinitionLabelMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteDefinitionLabelMarker');
|
||||
return labelAtMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* In label, at caret.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelAtMarker(code) {
|
||||
if (code === 94) {
|
||||
effects.enter('gfmFootnoteDefinitionMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteDefinitionMarker');
|
||||
effects.enter('gfmFootnoteDefinitionLabelString');
|
||||
effects.enter('chunkString').contentType = 'string';
|
||||
return labelInside;
|
||||
}
|
||||
return nok(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* In label.
|
||||
*
|
||||
* > 👉 **Note**: `cmark-gfm` prevents whitespace from occurring in footnote
|
||||
* > definition labels.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelInside(code) {
|
||||
if (
|
||||
// Too long.
|
||||
size > 999 ||
|
||||
// Closing brace with nothing.
|
||||
code === 93 && !data ||
|
||||
// Space or tab is not supported by GFM for some reason.
|
||||
// `\n` and `[` not being supported makes sense.
|
||||
code === null || code === 91 || markdownLineEndingOrSpace(code)) {
|
||||
return nok(code);
|
||||
}
|
||||
if (code === 93) {
|
||||
effects.exit('chunkString');
|
||||
const token = effects.exit('gfmFootnoteDefinitionLabelString');
|
||||
identifier = normalizeIdentifier(self.sliceSerialize(token));
|
||||
effects.enter('gfmFootnoteDefinitionLabelMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('gfmFootnoteDefinitionLabelMarker');
|
||||
effects.exit('gfmFootnoteDefinitionLabel');
|
||||
return labelAfter;
|
||||
}
|
||||
if (!markdownLineEndingOrSpace(code)) {
|
||||
data = true;
|
||||
}
|
||||
size++;
|
||||
effects.consume(code);
|
||||
return code === 92 ? labelEscape : labelInside;
|
||||
}
|
||||
|
||||
/**
|
||||
* After `\`, at a special character.
|
||||
*
|
||||
* > 👉 **Note**: `cmark-gfm` currently does not support escaped brackets:
|
||||
* > <https://github.com/github/cmark-gfm/issues/240>
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a\*b]: c
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelEscape(code) {
|
||||
if (code === 91 || code === 92 || code === 93) {
|
||||
effects.consume(code);
|
||||
size++;
|
||||
return labelInside;
|
||||
}
|
||||
return labelInside(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* After definition label.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function labelAfter(code) {
|
||||
if (code === 58) {
|
||||
effects.enter('definitionMarker');
|
||||
effects.consume(code);
|
||||
effects.exit('definitionMarker');
|
||||
if (!defined.includes(identifier)) {
|
||||
defined.push(identifier);
|
||||
}
|
||||
|
||||
// Any whitespace after the marker is eaten, forming indented code
|
||||
// is not possible.
|
||||
// No space is also fine, just like a block quote marker.
|
||||
return factorySpace(effects, whitespaceAfter, 'gfmFootnoteDefinitionWhitespace');
|
||||
}
|
||||
return nok(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* After definition prefix.
|
||||
*
|
||||
* ```markdown
|
||||
* > | [^a]: b
|
||||
* ^
|
||||
* ```
|
||||
*
|
||||
* @type {State}
|
||||
*/
|
||||
function whitespaceAfter(code) {
|
||||
// `markdown-rs` has a wrapping token for the prefix that is closed here.
|
||||
return ok(code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeDefinitionContinuation(effects, ok, nok) {
|
||||
/// Start of footnote definition continuation.
|
||||
///
|
||||
/// ```markdown
|
||||
/// | [^a]: b
|
||||
/// > | c
|
||||
/// ^
|
||||
/// ```
|
||||
//
|
||||
// Either a blank line, which is okay, or an indented thing.
|
||||
return effects.check(blankLine, ok, effects.attempt(indent, ok, nok));
|
||||
}
|
||||
|
||||
/** @type {Exiter} */
|
||||
function gfmFootnoteDefinitionEnd(effects) {
|
||||
effects.exit('gfmFootnoteDefinition');
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @type {Tokenizer}
|
||||
*/
|
||||
function tokenizeIndent(effects, ok, nok) {
|
||||
const self = this;
|
||||
return factorySpace(effects, afterPrefix, 'gfmFootnoteDefinitionIndent', 4 + 1);
|
||||
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
function afterPrefix(code) {
|
||||
const tail = self.events[self.events.length - 1];
|
||||
return tail && tail[1].type === 'gfmFootnoteDefinitionIndent' && tail[2].sliceSerialize(tail[1], true).length === 4 ? ok(code) : nok(code);
|
||||
}
|
||||
}
|
22
node_modules/micromark-extension-gfm-footnote/license
generated
vendored
Normal file
22
node_modules/micromark-extension-gfm-footnote/license
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2021 Titus Wormer <tituswormer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
132
node_modules/micromark-extension-gfm-footnote/package.json
generated
vendored
Normal file
132
node_modules/micromark-extension-gfm-footnote/package.json
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"name": "micromark-extension-gfm-footnote",
|
||||
"version": "2.1.0",
|
||||
"description": "micromark extension to support GFM footnotes",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"micromark",
|
||||
"micromark-extension",
|
||||
"gfm",
|
||||
"footnote",
|
||||
"note",
|
||||
"definition",
|
||||
"markdown",
|
||||
"unified"
|
||||
],
|
||||
"repository": "micromark/micromark-extension-gfm-footnote",
|
||||
"bugs": "https://github.com/micromark/micromark-extension-gfm-footnote/issues",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
},
|
||||
"author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
|
||||
"contributors": [
|
||||
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dev/",
|
||||
"lib/",
|
||||
"index.d.ts",
|
||||
"index.js"
|
||||
],
|
||||
"exports": {
|
||||
"development": "./dev/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-core-commonmark": "^2.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"c8": "^10.0.0",
|
||||
"create-gfm-fixtures": "^1.0.0",
|
||||
"micromark": "^4.0.0",
|
||||
"micromark-build": "^2.0.0",
|
||||
"prettier": "^3.0.0",
|
||||
"remark-cli": "^12.0.0",
|
||||
"remark-preset-wooorm": "^10.0.0",
|
||||
"type-coverage": "^2.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"xo": "^0.58.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepack": "npm run build && npm run format",
|
||||
"build": "tsc --build --clean && tsc --build && type-coverage && micromark-build",
|
||||
"format": "remark . -qfo && prettier . -w --log-level warn && xo --fix",
|
||||
"test-api-prod": "node --conditions production test/index.js",
|
||||
"test-api-dev": "node --conditions development test/index.js",
|
||||
"test-api": "npm run test-api-dev && npm run test-api-prod",
|
||||
"test-coverage": "c8 --100 --reporter lcov npm run test-api",
|
||||
"test": "npm run build && npm run format && npm run test-coverage"
|
||||
},
|
||||
"prettier": {
|
||||
"bracketSpacing": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false
|
||||
},
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"remark-preset-wooorm"
|
||||
]
|
||||
},
|
||||
"typeCoverage": {
|
||||
"atLeast": 100,
|
||||
"detail": true,
|
||||
"ignoreCatch": true,
|
||||
"strict": true
|
||||
},
|
||||
"xo": {
|
||||
"prettier": true,
|
||||
"rules": {
|
||||
"logical-assignment-operators": "off",
|
||||
"unicorn/no-this-assignment": "off",
|
||||
"unicorn/prefer-at": "off",
|
||||
"unicorn/prefer-string-replace-all": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/array-type": [
|
||||
"error",
|
||||
{
|
||||
"default": "generic"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
"extendDefaults": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/consistent-type-definitions": [
|
||||
"error",
|
||||
"interface"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"test/**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"no-await-in-loop": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
656
node_modules/micromark-extension-gfm-footnote/readme.md
generated
vendored
Normal file
656
node_modules/micromark-extension-gfm-footnote/readme.md
generated
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
# micromark-extension-gfm-footnote
|
||||
|
||||
[![Build][build-badge]][build]
|
||||
[![Coverage][coverage-badge]][coverage]
|
||||
[![Downloads][downloads-badge]][downloads]
|
||||
[![Size][size-badge]][size]
|
||||
[![Sponsors][sponsors-badge]][collective]
|
||||
[![Backers][backers-badge]][collective]
|
||||
[![Chat][chat-badge]][chat]
|
||||
|
||||
[micromark][] extensions to support GFM [footnotes][post].
|
||||
|
||||
## Contents
|
||||
|
||||
* [What is this?](#what-is-this)
|
||||
* [When to use this](#when-to-use-this)
|
||||
* [Install](#install)
|
||||
* [Use](#use)
|
||||
* [API](#api)
|
||||
* [`defaultBackLabel(referenceIndex, rereferenceIndex)`](#defaultbacklabelreferenceindex-rereferenceindex)
|
||||
* [`gfmFootnote()`](#gfmfootnote)
|
||||
* [`gfmFootnoteHtml(options?)`](#gfmfootnotehtmloptions)
|
||||
* [`BackLabelTemplate`](#backlabeltemplate)
|
||||
* [`HtmlOptions`](#htmloptions)
|
||||
* [Bugs](#bugs)
|
||||
* [Authoring](#authoring)
|
||||
* [HTML](#html)
|
||||
* [CSS](#css)
|
||||
* [Syntax](#syntax)
|
||||
* [Types](#types)
|
||||
* [Compatibility](#compatibility)
|
||||
* [Security](#security)
|
||||
* [Related](#related)
|
||||
* [Contribute](#contribute)
|
||||
* [License](#license)
|
||||
|
||||
## What is this?
|
||||
|
||||
This package contains extensions that add support for footnotes as enabled by
|
||||
GFM to [`micromark`][micromark].
|
||||
|
||||
GitHub announced footnotes [on September 30, 2021][post] but did not specify
|
||||
them in their GFM spec.
|
||||
As they are implemented in their parser and supported in all places where
|
||||
other GFM features work, they can be considered part of GFM.
|
||||
GitHub employs several other features (such as mentions or frontmatter) that
|
||||
are either not in their parser, or not in all places where GFM features work,
|
||||
which should not be considered GFM.
|
||||
|
||||
The implementation of footnotes on github.com is currently buggy.
|
||||
The bugs have been reported on [`cmark-gfm`][cmark-gfm].
|
||||
This micromark extension matches github.com except for its bugs.
|
||||
|
||||
## When to use this
|
||||
|
||||
This project is useful when you want to support footnotes in markdown.
|
||||
|
||||
You can use these extensions when you are working with [`micromark`][micromark].
|
||||
To support all GFM features, use
|
||||
[`micromark-extension-gfm`][micromark-extension-gfm] instead.
|
||||
|
||||
When you need a syntax tree, combine this package with
|
||||
[`mdast-util-gfm-footnote`][mdast-util-gfm-footnote].
|
||||
|
||||
All these packages are used in [`remark-gfm`][remark-gfm], which focusses on
|
||||
making it easier to transform content by abstracting these internals away.
|
||||
|
||||
## Install
|
||||
|
||||
This package is [ESM only][esm].
|
||||
In Node.js (version 16+), install with [npm][]:
|
||||
|
||||
```sh
|
||||
npm install micromark-extension-gfm-footnote
|
||||
```
|
||||
|
||||
In Deno with [`esm.sh`][esmsh]:
|
||||
|
||||
```js
|
||||
import {gfmFootnote, gfmFootnoteHtml} from 'https://esm.sh/micromark-extension-gfm-footnote@2'
|
||||
```
|
||||
|
||||
In browsers with [`esm.sh`][esmsh]:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import {gfmFootnote, gfmFootnoteHtml} from 'https://esm.sh/micromark-extension-gfm-footnote@2?bundle'
|
||||
</script>
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
Say our document `example.md` contains:
|
||||
|
||||
````markdown
|
||||
Using footnotes is fun![^1] They let you reference relevant information without disrupting the flow of what you’re trying to say.[^bignote]
|
||||
|
||||
[^1]: This is the first footnote.
|
||||
[^bignote]: Here’s one with multiple paragraphs and code.
|
||||
|
||||
Indent paragraphs to include them in the footnote.
|
||||
|
||||
```
|
||||
my code
|
||||
```
|
||||
|
||||
Add as many paragraphs as you like.
|
||||
|
||||
Text here and here and here.
|
||||
[Learn more about markdown and footnotes in markdown](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes)
|
||||
````
|
||||
|
||||
…and our module `example.js` looks as follows:
|
||||
|
||||
```js
|
||||
import fs from 'node:fs/promises'
|
||||
import {micromark} from 'micromark'
|
||||
import {gfmFootnote, gfmFootnoteHtml} from 'micromark-extension-gfm-footnote'
|
||||
|
||||
const output = micromark(await fs.readFile('example.md'), {
|
||||
extensions: [gfmFootnote()],
|
||||
htmlExtensions: [gfmFootnoteHtml()]
|
||||
})
|
||||
|
||||
console.log(output)
|
||||
```
|
||||
|
||||
…now running `node example.js` yields:
|
||||
|
||||
```html
|
||||
<p>Using footnotes is fun!<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup> They let you reference relevant information without disrupting the flow of what you’re trying to say.<sup><a href="#user-content-fn-bignote" id="user-content-fnref-bignote" data-footnote-ref="" aria-describedby="footnote-label">2</a></sup></p>
|
||||
<p>Text here and here and here.
|
||||
<a href="https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes">Learn more about markdown and footnotes in markdown</a></p>
|
||||
<section data-footnotes="" class="footnotes"><h2 id="footnote-label" class="sr-only">Footnotes</h2>
|
||||
<ol>
|
||||
<li id="user-content-fn-1">
|
||||
<p>This is the first footnote. <a href="#user-content-fnref-1" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content">↩</a></p>
|
||||
</li>
|
||||
<li id="user-content-fn-bignote">
|
||||
<p>Here’s one with multiple paragraphs and code.</p>
|
||||
<p>Indent paragraphs to include them in the footnote.</p>
|
||||
<pre><code>my code
|
||||
</code></pre>
|
||||
<p>Add as many paragraphs as you like. <a href="#user-content-fnref-bignote" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content">↩</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
This package exports the identifiers
|
||||
[`defaultBackLabel`][api-default-back-label],
|
||||
[`gfmFootnote`][api-gfm-footnote], and
|
||||
[`gfmFootnoteHtml`][api-gfm-footnote-html].
|
||||
There is no default export.
|
||||
|
||||
The export map supports the [`development` condition][development].
|
||||
Run `node --conditions development module.js` to get instrumented dev code.
|
||||
Without this condition, production code is loaded.
|
||||
|
||||
### `defaultBackLabel(referenceIndex, rereferenceIndex)`
|
||||
|
||||
Generate the default label that GitHub uses on backreferences
|
||||
([`BackLabelTemplate`][api-back-label-template]).
|
||||
|
||||
### `gfmFootnote()`
|
||||
|
||||
Create an extension for `micromark` to enable GFM footnote syntax.
|
||||
|
||||
###### Returns
|
||||
|
||||
Extension for `micromark` that can be passed in `extensions` to enable GFM
|
||||
footnote syntax ([`Extension`][micromark-extension]).
|
||||
|
||||
### `gfmFootnoteHtml(options?)`
|
||||
|
||||
Create an extension for `micromark` to support GFM footnotes when serializing
|
||||
to HTML.
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `options` ([`HtmlOptions`][api-html-options], optional)
|
||||
— configuration
|
||||
|
||||
###### Returns
|
||||
|
||||
Extension for `micromark` that can be passed in `htmlExtensions` to support GFM
|
||||
footnotes when serializing to HTML
|
||||
([`HtmlExtension`][micromark-html-extension]).
|
||||
|
||||
### `BackLabelTemplate`
|
||||
|
||||
Generate a back label dynamically (TypeScript type).
|
||||
|
||||
For the following markdown:
|
||||
|
||||
```markdown
|
||||
Alpha[^micromark], bravo[^micromark], and charlie[^remark].
|
||||
|
||||
[^remark]: things about remark
|
||||
[^micromark]: things about micromark
|
||||
```
|
||||
|
||||
This function will be called with:
|
||||
|
||||
* `0` and `0` for the backreference from `things about micromark` to
|
||||
`alpha`, as it is the first used definition, and the first call to it
|
||||
* `0` and `1` for the backreference from `things about micromark` to
|
||||
`bravo`, as it is the first used definition, and the second call to it
|
||||
* `1` and `0` for the backreference from `things about remark` to
|
||||
`charlie`, as it is the second used definition
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `referenceIndex` (`number`)
|
||||
— index of the definition in the order that they are first referenced,
|
||||
0-indexed
|
||||
* `rereferenceIndex` (`number`)
|
||||
— index of calls to the same definition, 0-indexed
|
||||
|
||||
###### Returns
|
||||
|
||||
Back label to use when linking back from definitions to their reference
|
||||
(`string`).
|
||||
|
||||
### `HtmlOptions`
|
||||
|
||||
Configuration (TypeScript type).
|
||||
|
||||
##### Fields
|
||||
|
||||
###### `clobberPrefix`
|
||||
|
||||
Prefix to use before the `id` attribute on footnotes to prevent them from
|
||||
*clobbering* (`string`, default: `'user-content-'`).
|
||||
|
||||
Pass `''` for trusted markdown and when you are careful with polyfilling.
|
||||
You could pass a different prefix.
|
||||
|
||||
DOM clobbering is this:
|
||||
|
||||
```html
|
||||
<p id="x"></p>
|
||||
<script>alert(x) // `x` now refers to the `p#x` DOM element</script>
|
||||
```
|
||||
|
||||
The above example shows that elements are made available by browsers, by their
|
||||
ID, on the `window` object.
|
||||
This is a security risk because you might be expecting some other variable at
|
||||
that place.
|
||||
It can also break polyfills.
|
||||
Using a prefix solves these problems.
|
||||
|
||||
###### `label`
|
||||
|
||||
Textual label to use for the footnotes section (`string`, default:
|
||||
`'Footnotes'`).
|
||||
|
||||
Change it when the markdown is not in English.
|
||||
|
||||
This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
is defined that does that) and so affects screen readers only.
|
||||
|
||||
###### `labelAttributes`
|
||||
|
||||
Attributes to use on the footnote label (`string`, default:
|
||||
`'class="sr-only"'`).
|
||||
|
||||
Change it to show the label and add other attributes.
|
||||
|
||||
This label is typically hidden visually (assuming an `sr-only` CSS class
|
||||
is defined that does that) and so affects screen readers only.
|
||||
If you do have such a class, but want to show this section to everyone,
|
||||
pass an empty string.
|
||||
You can also add different attributes.
|
||||
|
||||
> 👉 **Note**: `id="footnote-label"` is always added, because footnote
|
||||
> calls use it with `aria-describedby` to provide an accessible label.
|
||||
|
||||
###### `labelTagName`
|
||||
|
||||
HTML tag name to use for the footnote label element (`string`, default:
|
||||
`'h2'`).
|
||||
|
||||
Change it to match your document structure.
|
||||
|
||||
This label is typically hidden visually (assuming a `sr-only` CSS class
|
||||
is defined that does that) and so affects screen readers only.
|
||||
|
||||
###### `backLabel`
|
||||
|
||||
Textual label to describe the backreference back to footnote calls
|
||||
([`BackLabelTemplate`][api-back-label-template] or `string`,
|
||||
default: [`defaultBackLabel`][api-default-back-label]).
|
||||
|
||||
Change it when the markdown is not in English.
|
||||
|
||||
This label is used in the [`aria-label`][aria-label] attribute on each
|
||||
backreference (the `↩` links).
|
||||
It affects users of assistive technology.
|
||||
|
||||
## Bugs
|
||||
|
||||
GitHub’s own algorithm to parse footnote definitions contains several bugs.
|
||||
These are not present in this project.
|
||||
The issues relating to footnote definitions are:
|
||||
|
||||
* [Footnote reference call identifiers are trimmed, but definition
|
||||
identifiers aren’t](https://github.com/github/cmark-gfm/issues/237)\
|
||||
— initial and final whitespace in labels causes them not to match
|
||||
* [Footnotes are matched case-insensitive, but links keep their casing,
|
||||
breaking them](https://github.com/github/cmark-gfm/issues/239)\
|
||||
— using uppercase (or any character that will be percent encoded) in
|
||||
identifiers breaks links
|
||||
* [Colons in footnotes generate links w/o
|
||||
`href`](https://github.com/github/cmark-gfm/issues/250)\
|
||||
— colons in identifiers generate broken links
|
||||
* [Character escape of `]` does not work in footnote
|
||||
identifiers](https://github.com/github/cmark-gfm/issues/240)\
|
||||
— some character escapes don’t work
|
||||
* [Footnotes in links are
|
||||
broken](https://github.com/github/cmark-gfm/issues/249)\
|
||||
— while `CommonMark` prevents links in links, GitHub does not prevent
|
||||
footnotes (which turn into links) in links
|
||||
* [Footnote-like brackets around image, break that
|
||||
image](https://github.com/github/cmark-gfm/issues/275)\
|
||||
— images can’t be used in what looks like a footnote call
|
||||
* [GFM footnotes: line ending in footnote definition label causes text to
|
||||
disappear](https://github.com/github/cmark-gfm/issues/282)\
|
||||
— line endings in footnote definitions cause text to disappear
|
||||
|
||||
## Authoring
|
||||
|
||||
When authoring markdown with footnotes it’s recommended to use words instead
|
||||
of numbers (or letters or anything with an order) as identifiers.
|
||||
That makes it easier to reuse and reorder footnotes.
|
||||
|
||||
It’s recommended to place footnotes definitions at the bottom of the document.
|
||||
|
||||
## HTML
|
||||
|
||||
GFM footnotes do not, on their own, relate to anything in HTML.
|
||||
When a footnote reference matches with a definition, they each relate to several
|
||||
elements in HTML.
|
||||
|
||||
The reference relates to `<sup>` and `<a>` elements in HTML:
|
||||
|
||||
```html
|
||||
<sup><a href="#user-content-fn-x" id="user-content-fnref-x" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup></p>
|
||||
```
|
||||
|
||||
…where `x` is the identifier used in the markdown source and `1` the number of
|
||||
corresponding, listed, definition.
|
||||
|
||||
See [*§ 4.5.19 The `sub` and `sup` elements*][html-sup],
|
||||
[*§ 4.5.1 The `a` element*][html-a], and
|
||||
[*§ 3.2.6.6 Embedding custom non-visible data with the `data-*`
|
||||
attributes*][html-data]
|
||||
in the HTML spec, and
|
||||
[*§ 6.8 `aria-describedby` property*][aria-describedby]
|
||||
in WAI-ARIA, for more info.
|
||||
|
||||
When one or more definitions are referenced, a footnote section is generated at
|
||||
the end of the document, using `<section>`, `<h2>`, and `<ol>` elements:
|
||||
|
||||
```html
|
||||
<section data-footnotes="" class="footnotes"><h2 id="footnote-label" class="sr-only">Footnotes</h2>
|
||||
<ol>…</ol>
|
||||
</section>
|
||||
```
|
||||
|
||||
Each definition is generated as a `<li>` in the `<ol>` in the order they were
|
||||
first referenced:
|
||||
|
||||
```html
|
||||
<li id="user-content-fn-1">…</li>
|
||||
```
|
||||
|
||||
Backreferences are injected at the end of the first paragraph, or, when there
|
||||
is no paragraph, at the end of the definition.
|
||||
When a definition is referenced multiple times, multiple backreferences are
|
||||
generated.
|
||||
Further backreferences use an extra counter in the `href` attribute and
|
||||
visually in a `<span>` after `↩`.
|
||||
|
||||
```html
|
||||
<a href="#user-content-fnref-1" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content">↩</a> <a href="#user-content-fnref-1-2" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content">↩<sup>2</sup></a>
|
||||
```
|
||||
|
||||
See
|
||||
[*§ 4.5.1 The `a` element*][html-a],
|
||||
[*§ 4.3.6 The `h1`, `h2`, `h3`, `h4`, `h5`, and `h6` elements*][html-h],
|
||||
[*§ 4.4.8 The `li` element*][html-li],
|
||||
[*§ 4.4.5 The `ol` element*][html-ol],
|
||||
[*§ 4.4.1 The `p` element*][html-p],
|
||||
[*§ 4.3.3 The `section` element*][html-section], and
|
||||
[*§ 4.5.19 The `sub` and `sup` elements*][html-sup]
|
||||
in the HTML spec, and
|
||||
[*§ 6.8 `aria-label` property*][aria-label]
|
||||
in WAI-ARIA, for more info.
|
||||
|
||||
## CSS
|
||||
|
||||
The following CSS is needed to make footnotes look a bit like GitHub (and fixes
|
||||
a bug).
|
||||
For the complete actual CSS see
|
||||
[`sindresorhus/github-markdown-css`](https://github.com/sindresorhus/github-markdown-css).
|
||||
|
||||
```css
|
||||
/* Style the footnotes section. */
|
||||
.footnotes {
|
||||
font-size: smaller;
|
||||
color: #8b949e;
|
||||
border-top: 1px solid #30363d;
|
||||
}
|
||||
|
||||
/* Hide the section label for visual users. */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
word-wrap: normal;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* Place `[` and `]` around footnote references. */
|
||||
[data-footnote-ref]::before {
|
||||
content: '[';
|
||||
}
|
||||
|
||||
[data-footnote-ref]::after {
|
||||
content: ']';
|
||||
}
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
Footnotes form with, roughly, the following BNF:
|
||||
|
||||
```bnf
|
||||
gfm_footnote_reference ::= gfm_footnote_label
|
||||
|
||||
gfm_footnote_definition_start ::= gfm_footnote_label ':' *space_or_tab
|
||||
; Restriction: blank line allowed.
|
||||
gfm_footnote_definition_cont ::= 4(space_or_tab)
|
||||
|
||||
; Restriction: maximum `999` codes between `^` and `]`.
|
||||
gfm_footnote_label ::= '[' '^' 1*(gfm_footnote_label_byte | gfm_footnote_label_escape) ']'
|
||||
gfm_footnote_label_byte ::= text - '[' - '\\' - ']'
|
||||
gfm_footnote_label_escape ::= '\\' ['[' | '\\' | ']']
|
||||
|
||||
; Any byte (u8)
|
||||
byte ::= 0x00..=0xFFFF
|
||||
space_or_tab ::= '\t' | ' '
|
||||
eol ::= '\n' | '\r' | '\r\n'
|
||||
line ::= byte - eol
|
||||
text ::= line - space_or_tab
|
||||
```
|
||||
|
||||
Further lines after `gfm_footnote_definition_start` that are not prefixed with
|
||||
`gfm_footnote_definition_cont` cause the footnote definition to be exited,
|
||||
except when those lines are lazy continuation or blank.
|
||||
Like so many things in markdown, footnote definition too are complex.
|
||||
See [*§ Phase 1: block structure* in `CommonMark`][commonmark-block] for more
|
||||
on parsing details.
|
||||
|
||||
The identifiers in the `label` parts are interpreted as the
|
||||
[string][micromark-content-types] content type.
|
||||
That means that character escapes and character references are allowed.
|
||||
|
||||
Definitions match to references through identifiers.
|
||||
To match, both labels must be equal after normalizing with
|
||||
[`normalizeIdentifier`][micromark-normalize-identifier].
|
||||
One definition can match to multiple calls.
|
||||
Multiple definitions with the same, normalized, identifier are ignored: the
|
||||
first definition is preferred.
|
||||
To illustrate, the definition with the content of `x` wins:
|
||||
|
||||
```markdown
|
||||
[^a]: x
|
||||
[^a]: y
|
||||
|
||||
[^a]
|
||||
```
|
||||
|
||||
Importantly, while labels *can* include [string][micromark-content-types]
|
||||
content (character escapes and character references), these are not considered
|
||||
when matching.
|
||||
To illustrate, neither definition matches the reference:
|
||||
|
||||
```markdown
|
||||
[^a&b]: x
|
||||
[^a\&b]: y
|
||||
|
||||
[^a&b]
|
||||
```
|
||||
|
||||
Because footnote definitions are containers (like block quotes and list items),
|
||||
they can contain more footnote definitions.
|
||||
They can even include references to themselves.
|
||||
|
||||
## Types
|
||||
|
||||
This package is fully typed with [TypeScript][].
|
||||
It exports the additional types [`BackLabelTemplate`][api-back-label-template]
|
||||
and [`HtmlOptions`][api-html-options].
|
||||
|
||||
## Compatibility
|
||||
|
||||
Projects maintained by the unified collective are compatible with maintained
|
||||
versions of Node.js.
|
||||
|
||||
When we cut a new major release, we drop support for unmaintained versions of
|
||||
Node.
|
||||
This means we try to keep the current release line,
|
||||
`micromark-extension-gfm-footnote@^2`, compatible with Node.js 16.
|
||||
|
||||
This package works with `micromark` version `3` and later.
|
||||
|
||||
## Security
|
||||
|
||||
This package is safe.
|
||||
Setting `clobberPrefix = ''` is dangerous, it opens you up to DOM clobbering.
|
||||
The `labelTagName` and `labelAttributes` options are unsafe when used with user
|
||||
content, they allow defining arbitrary HTML.
|
||||
|
||||
## Related
|
||||
|
||||
* [`micromark-extension-gfm`][micromark-extension-gfm]
|
||||
— support all of GFM
|
||||
* [`mdast-util-gfm-footnote`][mdast-util-gfm-footnote]
|
||||
— support all of GFM in mdast
|
||||
* [`mdast-util-gfm`][mdast-util-gfm]
|
||||
— support all of GFM in mdast
|
||||
* [`remark-gfm`][remark-gfm]
|
||||
— support all of GFM in remark
|
||||
|
||||
## Contribute
|
||||
|
||||
See [`contributing.md` in `micromark/.github`][contributing] for ways to get
|
||||
started.
|
||||
See [`support.md`][support] for ways to get help.
|
||||
|
||||
This project has a [code of conduct][coc].
|
||||
By interacting with this repository, organization, or community you agree to
|
||||
abide by its terms.
|
||||
|
||||
## License
|
||||
|
||||
[MIT][license] © [Titus Wormer][author]
|
||||
|
||||
<!-- Definitions -->
|
||||
|
||||
[build-badge]: https://github.com/micromark/micromark-extension-gfm-footnote/workflows/main/badge.svg
|
||||
|
||||
[build]: https://github.com/micromark/micromark-extension-gfm-footnote/actions
|
||||
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/micromark/micromark-extension-gfm-footnote.svg
|
||||
|
||||
[coverage]: https://codecov.io/github/micromark/micromark-extension-gfm-footnote
|
||||
|
||||
[downloads-badge]: https://img.shields.io/npm/dm/micromark-extension-gfm-footnote.svg
|
||||
|
||||
[downloads]: https://www.npmjs.com/package/micromark-extension-gfm-footnote
|
||||
|
||||
[size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size&query=$.size.compressedSize&url=https://deno.bundlejs.com/?q=micromark-extension-gfm-footnote
|
||||
|
||||
[size]: https://bundlejs.com/?q=micromark-extension-gfm-footnote
|
||||
|
||||
[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
|
||||
|
||||
[backers-badge]: https://opencollective.com/unified/backers/badge.svg
|
||||
|
||||
[collective]: https://opencollective.com/unified
|
||||
|
||||
[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
|
||||
|
||||
[chat]: https://github.com/micromark/micromark/discussions
|
||||
|
||||
[npm]: https://docs.npmjs.com/cli/install
|
||||
|
||||
[esmsh]: https://esm.sh
|
||||
|
||||
[license]: license
|
||||
|
||||
[author]: https://wooorm.com
|
||||
|
||||
[contributing]: https://github.com/micromark/.github/blob/main/contributing.md
|
||||
|
||||
[support]: https://github.com/micromark/.github/blob/main/support.md
|
||||
|
||||
[coc]: https://github.com/micromark/.github/blob/main/code-of-conduct.md
|
||||
|
||||
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
|
||||
|
||||
[typescript]: https://www.typescriptlang.org
|
||||
|
||||
[development]: https://nodejs.org/api/packages.html#packages_resolving_user_conditions
|
||||
|
||||
[micromark]: https://github.com/micromark/micromark
|
||||
|
||||
[micromark-content-types]: https://github.com/micromark/micromark#content-types
|
||||
|
||||
[micromark-extension]: https://github.com/micromark/micromark#syntaxextension
|
||||
|
||||
[micromark-html-extension]: https://github.com/micromark/micromark#htmlextension
|
||||
|
||||
[micromark-normalize-identifier]: https://github.com/micromark/micromark/tree/main/packages/micromark-util-normalize-identifier
|
||||
|
||||
[micromark-extension-gfm]: https://github.com/micromark/micromark-extension-gfm
|
||||
|
||||
[mdast-util-gfm-footnote]: https://github.com/syntax-tree/mdast-util-gfm-footnote
|
||||
|
||||
[mdast-util-gfm]: https://github.com/syntax-tree/mdast-util-gfm
|
||||
|
||||
[remark-gfm]: https://github.com/remarkjs/remark-gfm
|
||||
|
||||
[post]: https://github.blog/changelog/2021-09-30-footnotes-now-supported-in-markdown-fields/
|
||||
|
||||
[cmark-gfm]: https://github.com/github/cmark-gfm
|
||||
|
||||
[commonmark-block]: https://spec.commonmark.org/0.30/#phase-1-block-structure
|
||||
|
||||
[html-a]: https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element
|
||||
|
||||
[html-data]: https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
|
||||
|
||||
[html-h]: https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
|
||||
|
||||
[html-li]: https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element
|
||||
|
||||
[html-ol]: https://html.spec.whatwg.org/multipage/grouping-content.html#the-ol-element
|
||||
|
||||
[html-p]: https://html.spec.whatwg.org/multipage/grouping-content.html#the-p-element
|
||||
|
||||
[html-section]: https://html.spec.whatwg.org/multipage/sections.html#the-section-element
|
||||
|
||||
[html-sup]: https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-sub-and-sup-elements
|
||||
|
||||
[aria-describedby]: https://w3c.github.io/aria/#aria-describedby
|
||||
|
||||
[aria-label]: https://w3c.github.io/aria/#aria-label
|
||||
|
||||
[api-gfm-footnote]: #gfmfootnote
|
||||
|
||||
[api-gfm-footnote-html]: #gfmfootnotehtmloptions
|
||||
|
||||
[api-html-options]: #htmloptions
|
||||
|
||||
[api-default-back-label]: #defaultbacklabelreferenceindex-rereferenceindex
|
||||
|
||||
[api-back-label-template]: #backlabeltemplate
|
Reference in New Issue
Block a user