140 lines
3.2 KiB
JavaScript
140 lines
3.2 KiB
JavaScript
/**
|
|
* @typedef {import('micromark-util-types').Effects} Effects
|
|
* @typedef {import('micromark-util-types').State} State
|
|
* @typedef {import('micromark-util-types').Token} Token
|
|
* @typedef {import('micromark-util-types').TokenType} TokenType
|
|
*/
|
|
|
|
import {ok as assert} from 'uvu/assert'
|
|
import {markdownLineEnding} from 'micromark-util-character'
|
|
import {codes} from 'micromark-util-symbol/codes.js'
|
|
import {constants} from 'micromark-util-symbol/constants.js'
|
|
import {types} from 'micromark-util-symbol/types.js'
|
|
|
|
// This is a fork of:
|
|
// <https://github.com/micromark/micromark/tree/main/packages/micromark-factory-label>
|
|
// to allow empty labels, balanced brackets (such as for nested directives),
|
|
// text instead of strings, and optionally disallows EOLs.
|
|
|
|
/**
|
|
* @param {Effects} effects
|
|
* @param {State} ok
|
|
* @param {State} nok
|
|
* @param {TokenType} type
|
|
* @param {TokenType} markerType
|
|
* @param {TokenType} stringType
|
|
* @param {boolean} [disallowEol=false]
|
|
*/
|
|
// eslint-disable-next-line max-params
|
|
export function factoryLabel(
|
|
effects,
|
|
ok,
|
|
nok,
|
|
type,
|
|
markerType,
|
|
stringType,
|
|
disallowEol
|
|
) {
|
|
let size = 0
|
|
let balance = 0
|
|
/** @type {Token|undefined} */
|
|
let previous
|
|
|
|
return start
|
|
|
|
/** @type {State} */
|
|
function start(code) {
|
|
assert(code === codes.leftSquareBracket, 'expected `[`')
|
|
effects.enter(type)
|
|
effects.enter(markerType)
|
|
effects.consume(code)
|
|
effects.exit(markerType)
|
|
return afterStart
|
|
}
|
|
|
|
/** @type {State} */
|
|
function afterStart(code) {
|
|
if (code === codes.rightSquareBracket) {
|
|
effects.enter(markerType)
|
|
effects.consume(code)
|
|
effects.exit(markerType)
|
|
effects.exit(type)
|
|
return ok
|
|
}
|
|
|
|
effects.enter(stringType)
|
|
return lineStart(code)
|
|
}
|
|
|
|
/** @type {State} */
|
|
function lineStart(code) {
|
|
if (code === codes.rightSquareBracket && !balance) {
|
|
return atClosingBrace(code)
|
|
}
|
|
|
|
const token = effects.enter(types.chunkText, {
|
|
contentType: constants.contentTypeText,
|
|
previous
|
|
})
|
|
if (previous) previous.next = token
|
|
previous = token
|
|
return data(code)
|
|
}
|
|
|
|
/** @type {State} */
|
|
function data(code) {
|
|
if (code === codes.eof || size > constants.linkReferenceSizeMax) {
|
|
return nok(code)
|
|
}
|
|
|
|
if (
|
|
code === codes.leftSquareBracket &&
|
|
++balance > constants.linkResourceDestinationBalanceMax
|
|
) {
|
|
return nok(code)
|
|
}
|
|
|
|
if (code === codes.rightSquareBracket && !balance--) {
|
|
effects.exit(types.chunkText)
|
|
return atClosingBrace(code)
|
|
}
|
|
|
|
if (markdownLineEnding(code)) {
|
|
if (disallowEol) {
|
|
return nok(code)
|
|
}
|
|
|
|
effects.consume(code)
|
|
effects.exit(types.chunkText)
|
|
return lineStart
|
|
}
|
|
|
|
effects.consume(code)
|
|
return code === codes.backslash ? dataEscape : data
|
|
}
|
|
|
|
/** @type {State} */
|
|
function dataEscape(code) {
|
|
if (
|
|
code === codes.leftSquareBracket ||
|
|
code === codes.backslash ||
|
|
code === codes.rightSquareBracket
|
|
) {
|
|
effects.consume(code)
|
|
size++
|
|
return data
|
|
}
|
|
|
|
return data(code)
|
|
}
|
|
|
|
/** @type {State} */
|
|
function atClosingBrace(code) {
|
|
effects.exit(stringType)
|
|
effects.enter(markerType)
|
|
effects.consume(code)
|
|
effects.exit(markerType)
|
|
effects.exit(type)
|
|
return ok
|
|
}
|
|
}
|