This commit is contained in:
2024-03-22 03:47:51 +05:30
parent 8bcf3d211e
commit 89819f6fe2
28440 changed files with 3211033 additions and 2 deletions

View File

@@ -0,0 +1,341 @@
/**
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Effects} Effects
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').TokenType} TokenType
*/
import {ok as assert} from 'devlop'
import {factorySpace} from 'micromark-factory-space'
import {factoryWhitespace} from 'micromark-factory-whitespace'
import {
asciiAlpha,
asciiAlphanumeric,
markdownLineEnding,
markdownLineEndingOrSpace,
markdownSpace
} from 'micromark-util-character'
import {codes, types} from 'micromark-util-symbol'
/**
* @param {Effects} effects
* @param {State} ok
* @param {State} nok
* @param {TokenType} attributesType
* @param {TokenType} attributesMarkerType
* @param {TokenType} attributeType
* @param {TokenType} attributeIdType
* @param {TokenType} attributeClassType
* @param {TokenType} attributeNameType
* @param {TokenType} attributeInitializerType
* @param {TokenType} attributeValueLiteralType
* @param {TokenType} attributeValueType
* @param {TokenType} attributeValueMarker
* @param {TokenType} attributeValueData
* @param {boolean | undefined} [disallowEol=false]
*/
export function factoryAttributes(
effects,
ok,
nok,
attributesType,
attributesMarkerType,
attributeType,
attributeIdType,
attributeClassType,
attributeNameType,
attributeInitializerType,
attributeValueLiteralType,
attributeValueType,
attributeValueMarker,
attributeValueData,
disallowEol
) {
/** @type {TokenType} */
let type
/** @type {Code | undefined} */
let marker
return start
/** @type {State} */
function start(code) {
assert(code === codes.leftCurlyBrace, 'expected `{`')
effects.enter(attributesType)
effects.enter(attributesMarkerType)
effects.consume(code)
effects.exit(attributesMarkerType)
return between
}
/** @type {State} */
function between(code) {
if (code === codes.numberSign) {
type = attributeIdType
return shortcutStart(code)
}
if (code === codes.dot) {
type = attributeClassType
return shortcutStart(code)
}
if (code === codes.colon || code === codes.underscore || asciiAlpha(code)) {
effects.enter(attributeType)
effects.enter(attributeNameType)
effects.consume(code)
return name
}
if (disallowEol && markdownSpace(code)) {
return factorySpace(effects, between, types.whitespace)(code)
}
if (!disallowEol && markdownLineEndingOrSpace(code)) {
return factoryWhitespace(effects, between)(code)
}
return end(code)
}
/** @type {State} */
function shortcutStart(code) {
// Assume its registered.
const markerType = /** @type {TokenType} */ (type + 'Marker')
effects.enter(attributeType)
effects.enter(type)
effects.enter(markerType)
effects.consume(code)
effects.exit(markerType)
return shortcutStartAfter
}
/** @type {State} */
function shortcutStartAfter(code) {
if (
code === codes.eof ||
code === codes.quotationMark ||
code === codes.numberSign ||
code === codes.apostrophe ||
code === codes.dot ||
code === codes.lessThan ||
code === codes.equalsTo ||
code === codes.greaterThan ||
code === codes.graveAccent ||
code === codes.rightCurlyBrace ||
markdownLineEndingOrSpace(code)
) {
return nok(code)
}
// Assume its registered.
const valueType = /** @type {TokenType} */ (type + 'Value')
effects.enter(valueType)
effects.consume(code)
return shortcut
}
/** @type {State} */
function shortcut(code) {
if (
code === codes.eof ||
code === codes.quotationMark ||
code === codes.apostrophe ||
code === codes.lessThan ||
code === codes.equalsTo ||
code === codes.greaterThan ||
code === codes.graveAccent
) {
return nok(code)
}
if (
code === codes.numberSign ||
code === codes.dot ||
code === codes.rightCurlyBrace ||
markdownLineEndingOrSpace(code)
) {
// Assume its registered.
const valueType = /** @type {TokenType} */ (type + 'Value')
effects.exit(valueType)
effects.exit(type)
effects.exit(attributeType)
return between(code)
}
effects.consume(code)
return shortcut
}
/** @type {State} */
function name(code) {
if (
code === codes.dash ||
code === codes.dot ||
code === codes.colon ||
code === codes.underscore ||
asciiAlphanumeric(code)
) {
effects.consume(code)
return name
}
effects.exit(attributeNameType)
if (disallowEol && markdownSpace(code)) {
return factorySpace(effects, nameAfter, types.whitespace)(code)
}
if (!disallowEol && markdownLineEndingOrSpace(code)) {
return factoryWhitespace(effects, nameAfter)(code)
}
return nameAfter(code)
}
/** @type {State} */
function nameAfter(code) {
if (code === codes.equalsTo) {
effects.enter(attributeInitializerType)
effects.consume(code)
effects.exit(attributeInitializerType)
return valueBefore
}
// Attribute w/o value.
effects.exit(attributeType)
return between(code)
}
/** @type {State} */
function valueBefore(code) {
if (
code === codes.eof ||
code === codes.lessThan ||
code === codes.equalsTo ||
code === codes.greaterThan ||
code === codes.graveAccent ||
code === codes.rightCurlyBrace ||
(disallowEol && markdownLineEnding(code))
) {
return nok(code)
}
if (code === codes.quotationMark || code === codes.apostrophe) {
effects.enter(attributeValueLiteralType)
effects.enter(attributeValueMarker)
effects.consume(code)
effects.exit(attributeValueMarker)
marker = code
return valueQuotedStart
}
if (disallowEol && markdownSpace(code)) {
return factorySpace(effects, valueBefore, types.whitespace)(code)
}
if (!disallowEol && markdownLineEndingOrSpace(code)) {
return factoryWhitespace(effects, valueBefore)(code)
}
effects.enter(attributeValueType)
effects.enter(attributeValueData)
effects.consume(code)
marker = undefined
return valueUnquoted
}
/** @type {State} */
function valueUnquoted(code) {
if (
code === codes.eof ||
code === codes.quotationMark ||
code === codes.apostrophe ||
code === codes.lessThan ||
code === codes.equalsTo ||
code === codes.greaterThan ||
code === codes.graveAccent
) {
return nok(code)
}
if (code === codes.rightCurlyBrace || markdownLineEndingOrSpace(code)) {
effects.exit(attributeValueData)
effects.exit(attributeValueType)
effects.exit(attributeType)
return between(code)
}
effects.consume(code)
return valueUnquoted
}
/** @type {State} */
function valueQuotedStart(code) {
if (code === marker) {
effects.enter(attributeValueMarker)
effects.consume(code)
effects.exit(attributeValueMarker)
effects.exit(attributeValueLiteralType)
effects.exit(attributeType)
return valueQuotedAfter
}
effects.enter(attributeValueType)
return valueQuotedBetween(code)
}
/** @type {State} */
function valueQuotedBetween(code) {
if (code === marker) {
effects.exit(attributeValueType)
return valueQuotedStart(code)
}
if (code === codes.eof) {
return nok(code)
}
// Note: blank lines cant exist in content.
if (markdownLineEnding(code)) {
return disallowEol
? nok(code)
: factoryWhitespace(effects, valueQuotedBetween)(code)
}
effects.enter(attributeValueData)
effects.consume(code)
return valueQuoted
}
/** @type {State} */
function valueQuoted(code) {
if (code === marker || code === codes.eof || markdownLineEnding(code)) {
effects.exit(attributeValueData)
return valueQuotedBetween(code)
}
effects.consume(code)
return valueQuoted
}
/** @type {State} */
function valueQuotedAfter(code) {
return code === codes.rightCurlyBrace || markdownLineEndingOrSpace(code)
? between(code)
: end(code)
}
/** @type {State} */
function end(code) {
if (code === codes.rightCurlyBrace) {
effects.enter(attributesMarkerType)
effects.consume(code)
effects.exit(attributesMarkerType)
effects.exit(attributesType)
return ok
}
return nok(code)
}
}