Add JSDoc based types
This commit is contained in:
parent
472c259375
commit
a0f521746f
14 changed files with 291 additions and 19 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
.DS_Store
|
||||
*.d.ts
|
||||
*.log
|
||||
coverage/
|
||||
node_modules/
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
/**
|
||||
* @typedef {import('./lib/html.js').Handle} Handle
|
||||
* @typedef {import('./lib/html.js').Options} Options
|
||||
*/
|
||||
|
||||
export {directive} from './lib/syntax.js'
|
||||
export {directiveHtml} from './lib/html.js'
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Construct} Construct
|
||||
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
* @typedef {import('micromark-util-types').Token} Token
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {factorySpace} from 'micromark-factory-space'
|
||||
import {markdownLineEnding} from 'micromark-util-character'
|
||||
|
@ -8,6 +15,7 @@ import {factoryAttributes} from './factory-attributes.js'
|
|||
import {factoryLabel} from './factory-label.js'
|
||||
import {factoryName} from './factory-name.js'
|
||||
|
||||
/** @type {Construct} */
|
||||
export const directiveContainer = {
|
||||
tokenize: tokenizeDirectiveContainer,
|
||||
concrete: true
|
||||
|
@ -16,6 +24,7 @@ export const directiveContainer = {
|
|||
const label = {tokenize: tokenizeLabel, partial: true}
|
||||
const attributes = {tokenize: tokenizeAttributes, partial: true}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeDirectiveContainer(effects, ok, nok) {
|
||||
const self = this
|
||||
const tail = self.events[self.events.length - 1]
|
||||
|
@ -24,10 +33,12 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
? tail[2].sliceSerialize(tail[1], true).length
|
||||
: 0
|
||||
let sizeOpen = 0
|
||||
/** @type {Token} */
|
||||
let previous
|
||||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
assert(code === codes.colon, 'expected `:`')
|
||||
effects.enter('directiveContainer')
|
||||
|
@ -36,6 +47,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return sequenceOpen(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function sequenceOpen(code) {
|
||||
if (code === codes.colon) {
|
||||
effects.consume(code)
|
||||
|
@ -57,22 +69,26 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
)(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterName(code) {
|
||||
return code === codes.leftSquareBracket
|
||||
? effects.attempt(label, afterLabel, afterLabel)(code)
|
||||
: afterLabel(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterLabel(code) {
|
||||
return code === codes.leftCurlyBrace
|
||||
? effects.attempt(attributes, afterAttributes, afterAttributes)(code)
|
||||
: afterAttributes(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterAttributes(code) {
|
||||
return factorySpace(effects, openAfter, types.whitespace)(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function openAfter(code) {
|
||||
effects.exit('directiveContainerFence')
|
||||
|
||||
|
@ -91,6 +107,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return nok(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function contentStart(code) {
|
||||
if (code === codes.eof) {
|
||||
effects.exit('directiveContainer')
|
||||
|
@ -101,6 +118,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return lineStart(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function lineStart(code) {
|
||||
if (code === codes.eof) {
|
||||
return after(code)
|
||||
|
@ -115,6 +133,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
)(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function chunkStart(code) {
|
||||
if (code === codes.eof) {
|
||||
return after(code)
|
||||
|
@ -129,6 +148,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return contentContinue(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function contentContinue(code) {
|
||||
if (code === codes.eof) {
|
||||
effects.exit('chunkDocument')
|
||||
|
@ -145,12 +165,14 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return contentContinue
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function after(code) {
|
||||
effects.exit('directiveContainerContent')
|
||||
effects.exit('directiveContainer')
|
||||
return ok(code)
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeClosingFence(effects, ok, nok) {
|
||||
let size = 0
|
||||
|
||||
|
@ -161,12 +183,14 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
constants.tabSize
|
||||
)
|
||||
|
||||
/** @type {State} */
|
||||
function closingPrefixAfter(code) {
|
||||
effects.enter('directiveContainerFence')
|
||||
effects.enter('directiveContainerSequence')
|
||||
return closingSequence(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function closingSequence(code) {
|
||||
if (code === codes.colon) {
|
||||
effects.consume(code)
|
||||
|
@ -179,6 +203,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
return factorySpace(effects, closingSequenceEnd, types.whitespace)(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function closingSequenceEnd(code) {
|
||||
if (code === codes.eof || markdownLineEnding(code)) {
|
||||
effects.exit('directiveContainerFence')
|
||||
|
@ -190,6 +215,7 @@ function tokenizeDirectiveContainer(effects, ok, nok) {
|
|||
}
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeLabel(effects, ok, nok) {
|
||||
// Always a `[`
|
||||
return factoryLabel(
|
||||
|
@ -203,6 +229,7 @@ function tokenizeLabel(effects, ok, nok) {
|
|||
)
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeAttributes(effects, ok, nok) {
|
||||
// Always a `{`
|
||||
return factoryAttributes(
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Construct} Construct
|
||||
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {factorySpace} from 'micromark-factory-space'
|
||||
import {markdownLineEnding} from 'micromark-util-character'
|
||||
|
@ -7,16 +13,19 @@ import {factoryAttributes} from './factory-attributes.js'
|
|||
import {factoryLabel} from './factory-label.js'
|
||||
import {factoryName} from './factory-name.js'
|
||||
|
||||
/** @type {Construct} */
|
||||
export const directiveLeaf = {tokenize: tokenizeDirectiveLeaf}
|
||||
|
||||
const label = {tokenize: tokenizeLabel, partial: true}
|
||||
const attributes = {tokenize: tokenizeAttributes, partial: true}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeDirectiveLeaf(effects, ok, nok) {
|
||||
const self = this
|
||||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
assert(code === codes.colon, 'expected `:`')
|
||||
effects.enter('directiveLeaf')
|
||||
|
@ -25,6 +34,7 @@ function tokenizeDirectiveLeaf(effects, ok, nok) {
|
|||
return inStart
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function inStart(code) {
|
||||
if (code === codes.colon) {
|
||||
effects.consume(code)
|
||||
|
@ -41,22 +51,26 @@ function tokenizeDirectiveLeaf(effects, ok, nok) {
|
|||
return nok(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterName(code) {
|
||||
return code === codes.leftSquareBracket
|
||||
? effects.attempt(label, afterLabel, afterLabel)(code)
|
||||
: afterLabel(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterLabel(code) {
|
||||
return code === codes.leftCurlyBrace
|
||||
? effects.attempt(attributes, afterAttributes, afterAttributes)(code)
|
||||
: afterAttributes(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterAttributes(code) {
|
||||
return factorySpace(effects, end, types.whitespace)(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function end(code) {
|
||||
if (code === codes.eof || markdownLineEnding(code)) {
|
||||
effects.exit('directiveLeaf')
|
||||
|
@ -67,6 +81,7 @@ function tokenizeDirectiveLeaf(effects, ok, nok) {
|
|||
}
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeLabel(effects, ok, nok) {
|
||||
// Always a `[`
|
||||
return factoryLabel(
|
||||
|
@ -80,6 +95,7 @@ function tokenizeLabel(effects, ok, nok) {
|
|||
)
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeAttributes(effects, ok, nok) {
|
||||
// Always a `{`
|
||||
return factoryAttributes(
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Construct} Construct
|
||||
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
|
||||
* @typedef {import('micromark-util-types').Previous} Previous
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {codes} from 'micromark-util-symbol/codes.js'
|
||||
import {types} from 'micromark-util-symbol/types.js'
|
||||
|
@ -5,6 +12,7 @@ import {factoryAttributes} from './factory-attributes.js'
|
|||
import {factoryLabel} from './factory-label.js'
|
||||
import {factoryName} from './factory-name.js'
|
||||
|
||||
/** @type {Construct} */
|
||||
export const directiveText = {
|
||||
tokenize: tokenizeDirectiveText,
|
||||
previous
|
||||
|
@ -13,6 +21,7 @@ export const directiveText = {
|
|||
const label = {tokenize: tokenizeLabel, partial: true}
|
||||
const attributes = {tokenize: tokenizeAttributes, partial: true}
|
||||
|
||||
/** @type {Previous} */
|
||||
function previous(code) {
|
||||
// If there is a previous code, there will always be a tail.
|
||||
return (
|
||||
|
@ -21,11 +30,13 @@ function previous(code) {
|
|||
)
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeDirectiveText(effects, ok, nok) {
|
||||
const self = this
|
||||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
assert(code === codes.colon, 'expected `:`')
|
||||
assert(previous.call(self, self.previous), 'expected correct previous')
|
||||
|
@ -36,6 +47,7 @@ function tokenizeDirectiveText(effects, ok, nok) {
|
|||
return factoryName.call(self, effects, afterName, nok, 'directiveTextName')
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterName(code) {
|
||||
return code === codes.colon
|
||||
? nok(code)
|
||||
|
@ -44,18 +56,21 @@ function tokenizeDirectiveText(effects, ok, nok) {
|
|||
: afterLabel(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterLabel(code) {
|
||||
return code === codes.leftCurlyBrace
|
||||
? effects.attempt(attributes, afterAttributes, afterAttributes)(code)
|
||||
: afterAttributes(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterAttributes(code) {
|
||||
effects.exit('directiveText')
|
||||
return ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeLabel(effects, ok, nok) {
|
||||
// Always a `[`
|
||||
return factoryLabel(
|
||||
|
@ -68,6 +83,7 @@ function tokenizeLabel(effects, ok, nok) {
|
|||
)
|
||||
}
|
||||
|
||||
/** @type {Tokenizer} */
|
||||
function tokenizeAttributes(effects, ok, nok) {
|
||||
// Always a `{`
|
||||
return factoryAttributes(
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Effects} Effects
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
* @typedef {import('micromark-util-types').Code} Code
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {factorySpace} from 'micromark-factory-space'
|
||||
import {factoryWhitespace} from 'micromark-factory-whitespace'
|
||||
|
@ -11,6 +17,23 @@ import {
|
|||
import {codes} from 'micromark-util-symbol/codes.js'
|
||||
import {types} from 'micromark-util-symbol/types.js'
|
||||
|
||||
/**
|
||||
* @param {Effects} effects
|
||||
* @param {State} ok
|
||||
* @param {State} nok
|
||||
* @param {string} attributesType
|
||||
* @param {string} attributesMarkerType
|
||||
* @param {string} attributeType
|
||||
* @param {string} attributeIdType
|
||||
* @param {string} attributeClassType
|
||||
* @param {string} attributeNameType
|
||||
* @param {string} attributeInitializerType
|
||||
* @param {string} attributeValueLiteralType
|
||||
* @param {string} attributeValueType
|
||||
* @param {string} attributeValueMarker
|
||||
* @param {string} attributeValueData
|
||||
* @param {boolean} [disallowEol=false]
|
||||
*/
|
||||
/* eslint-disable-next-line max-params */
|
||||
export function factoryAttributes(
|
||||
effects,
|
||||
|
@ -29,11 +52,14 @@ export function factoryAttributes(
|
|||
attributeValueData,
|
||||
disallowEol
|
||||
) {
|
||||
/** @type {string} */
|
||||
let type
|
||||
/** @type {Code|undefined} */
|
||||
let marker
|
||||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
assert(code === codes.leftCurlyBrace, 'expected `{`')
|
||||
effects.enter(attributesType)
|
||||
|
@ -43,6 +69,7 @@ export function factoryAttributes(
|
|||
return between
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function between(code) {
|
||||
if (code === codes.numberSign) {
|
||||
type = attributeIdType
|
||||
|
@ -72,6 +99,7 @@ export function factoryAttributes(
|
|||
return end(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function shortcutStart(code) {
|
||||
effects.enter(attributeType)
|
||||
effects.enter(type)
|
||||
|
@ -81,6 +109,7 @@ export function factoryAttributes(
|
|||
return shortcutStartAfter
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function shortcutStartAfter(code) {
|
||||
if (
|
||||
code === codes.eof ||
|
||||
|
@ -103,6 +132,7 @@ export function factoryAttributes(
|
|||
return shortcut
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function shortcut(code) {
|
||||
if (
|
||||
code === codes.eof ||
|
||||
|
@ -132,6 +162,7 @@ export function factoryAttributes(
|
|||
return shortcut
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function name(code) {
|
||||
if (
|
||||
code === codes.dash ||
|
||||
|
@ -157,6 +188,7 @@ export function factoryAttributes(
|
|||
return nameAfter(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function nameAfter(code) {
|
||||
if (code === codes.equalsTo) {
|
||||
effects.enter(attributeInitializerType)
|
||||
|
@ -170,6 +202,7 @@ export function factoryAttributes(
|
|||
return between(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function valueBefore(code) {
|
||||
if (
|
||||
code === codes.eof ||
|
||||
|
@ -207,6 +240,7 @@ export function factoryAttributes(
|
|||
return valueUnquoted
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function valueUnquoted(code) {
|
||||
if (
|
||||
code === codes.eof ||
|
||||
|
@ -231,6 +265,7 @@ export function factoryAttributes(
|
|||
return valueUnquoted
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function valueQuotedStart(code) {
|
||||
if (code === marker) {
|
||||
effects.enter(attributeValueMarker)
|
||||
|
@ -245,6 +280,7 @@ export function factoryAttributes(
|
|||
return valueQuotedBetween(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function valueQuotedBetween(code) {
|
||||
if (code === marker) {
|
||||
effects.exit(attributeValueType)
|
||||
|
@ -267,6 +303,7 @@ export function factoryAttributes(
|
|||
return valueQuoted
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function valueQuoted(code) {
|
||||
if (code === marker || code === codes.eof || markdownLineEnding(code)) {
|
||||
effects.exit(attributeValueData)
|
||||
|
@ -277,12 +314,14 @@ export function factoryAttributes(
|
|||
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)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Effects} Effects
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {markdownLineEnding} from 'micromark-util-character'
|
||||
import {codes} from 'micromark-util-symbol/codes.js'
|
||||
|
@ -9,6 +14,15 @@ import {types} from 'micromark-util-symbol/types.js'
|
|||
// 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 {string} type
|
||||
* @param {string} markerType
|
||||
* @param {string} stringType
|
||||
* @param {boolean} [disallowEol=false]
|
||||
*/
|
||||
// eslint-disable-next-line max-params
|
||||
export function factoryLabel(
|
||||
effects,
|
||||
|
@ -24,6 +38,7 @@ export function factoryLabel(
|
|||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
assert(code === codes.leftSquareBracket, 'expected `[`')
|
||||
effects.enter(type)
|
||||
|
@ -33,6 +48,7 @@ export function factoryLabel(
|
|||
return afterStart
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function afterStart(code) {
|
||||
if (code === codes.rightSquareBracket) {
|
||||
effects.enter(markerType)
|
||||
|
@ -46,6 +62,7 @@ export function factoryLabel(
|
|||
return atBreak(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function atBreak(code) {
|
||||
if (code === codes.eof || size > constants.linkReferenceSizeMax) {
|
||||
return nok(code)
|
||||
|
@ -70,6 +87,7 @@ export function factoryLabel(
|
|||
return label(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function label(code) {
|
||||
if (
|
||||
code === codes.eof ||
|
||||
|
@ -96,6 +114,7 @@ export function factoryLabel(
|
|||
return code === codes.backslash ? labelEscape : label
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function atClosingBrace(code) {
|
||||
effects.exit(stringType)
|
||||
effects.enter(markerType)
|
||||
|
@ -105,6 +124,7 @@ export function factoryLabel(
|
|||
return ok
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function labelEscape(code) {
|
||||
if (
|
||||
code === codes.leftSquareBracket ||
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
|
||||
* @typedef {import('micromark-util-types').Effects} Effects
|
||||
* @typedef {import('micromark-util-types').State} State
|
||||
*/
|
||||
|
||||
import {asciiAlpha, asciiAlphanumeric} from 'micromark-util-character'
|
||||
import {codes} from 'micromark-util-symbol/codes.js'
|
||||
|
||||
export function factoryName(effects, ok, nok, nameType) {
|
||||
/**
|
||||
* @this {TokenizeContext}
|
||||
* @param {Effects} effects
|
||||
* @param {State} ok
|
||||
* @param {State} nok
|
||||
* @param {string} type
|
||||
*/
|
||||
export function factoryName(effects, ok, nok, type) {
|
||||
const self = this
|
||||
|
||||
return start
|
||||
|
||||
/** @type {State} */
|
||||
function start(code) {
|
||||
if (asciiAlpha(code)) {
|
||||
effects.enter(nameType)
|
||||
effects.enter(type)
|
||||
effects.consume(code)
|
||||
return name
|
||||
}
|
||||
|
@ -16,6 +30,7 @@ export function factoryName(effects, ok, nok, nameType) {
|
|||
return nok(code)
|
||||
}
|
||||
|
||||
/** @type {State} */
|
||||
function name(code) {
|
||||
if (
|
||||
code === codes.dash ||
|
||||
|
@ -26,7 +41,7 @@ export function factoryName(effects, ok, nok, nameType) {
|
|||
return name
|
||||
}
|
||||
|
||||
effects.exit(nameType)
|
||||
effects.exit(type)
|
||||
return self.previous === codes.dash || self.previous === codes.underscore
|
||||
? nok(code)
|
||||
: ok(code)
|
||||
|
|
110
dev/lib/html.js
110
dev/lib/html.js
|
@ -1,7 +1,35 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').HtmlExtension} HtmlExtension
|
||||
* @typedef {import('micromark-util-types').Handle} _Handle
|
||||
* @typedef {import('micromark-util-types').CompileContext} CompileContext
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {[string, string]} Attribute
|
||||
* @typedef {'containerDirective'|'leafDirective'|'textDirective'} DirectiveType
|
||||
*
|
||||
* @typedef Directive
|
||||
* @property {DirectiveType} type
|
||||
* @property {string} name
|
||||
* @property {string} [label]
|
||||
* @property {Record<string, string>} [attributes]
|
||||
* @property {string} [content]
|
||||
* @property {number} [_fenceCount]
|
||||
*
|
||||
* @typedef {(this: CompileContext, directive: Directive) => boolean|void} Handle
|
||||
*
|
||||
* @typedef {Record<string, Handle>} Options
|
||||
*/
|
||||
|
||||
import assert from 'assert'
|
||||
import {decodeEntity} from 'parse-entities/decode-entity.js'
|
||||
|
||||
const own = {}.hasOwnProperty
|
||||
|
||||
/**
|
||||
* @param {Options} [options]
|
||||
* @returns {HtmlExtension}
|
||||
*/
|
||||
export function directiveHtml(options = {}) {
|
||||
return {
|
||||
enter: {
|
||||
|
@ -58,65 +86,97 @@ export function directiveHtml(options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {CompileContext}
|
||||
* @param {DirectiveType} type
|
||||
*/
|
||||
function enter(type) {
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
let stack = this.getData('directiveStack')
|
||||
if (!stack) this.setData('directiveStack', (stack = []))
|
||||
stack.push({type})
|
||||
stack.push({type, name: ''})
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitName(token) {
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
const stack = this.getData('directiveStack')
|
||||
stack[stack.length - 1].name = this.sliceSerialize(token)
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function enterLabel() {
|
||||
this.buffer()
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitLabel() {
|
||||
const data = this.resume()
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
const stack = this.getData('directiveStack')
|
||||
stack[stack.length - 1].label = data
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function enterAttributes() {
|
||||
this.buffer()
|
||||
this.setData('directiveAttributes', [])
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitAttributeIdValue(token) {
|
||||
this.getData('directiveAttributes').push([
|
||||
'id',
|
||||
decodeLight(this.sliceSerialize(token))
|
||||
])
|
||||
/** @type {Attribute[]} */
|
||||
// @ts-expect-error
|
||||
const attributes = this.getData('directiveAttributes')
|
||||
attributes.push(['id', decodeLight(this.sliceSerialize(token))])
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitAttributeClassValue(token) {
|
||||
this.getData('directiveAttributes').push([
|
||||
'class',
|
||||
decodeLight(this.sliceSerialize(token))
|
||||
])
|
||||
/** @type {Attribute[]} */
|
||||
// @ts-expect-error
|
||||
const attributes = this.getData('directiveAttributes')
|
||||
|
||||
attributes.push(['class', decodeLight(this.sliceSerialize(token))])
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitAttributeName(token) {
|
||||
// Attribute names in CommonMark are significantly limited, so character
|
||||
// references can’t exist.
|
||||
this.getData('directiveAttributes').push([this.sliceSerialize(token), ''])
|
||||
/** @type {Attribute[]} */
|
||||
// @ts-expect-error
|
||||
const attributes = this.getData('directiveAttributes')
|
||||
|
||||
attributes.push([this.sliceSerialize(token), ''])
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitAttributeValue(token) {
|
||||
/** @type {Attribute[]} */
|
||||
// @ts-expect-error
|
||||
const attributes = this.getData('directiveAttributes')
|
||||
attributes[attributes.length - 1][1] = decodeLight(
|
||||
this.sliceSerialize(token)
|
||||
)
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitAttributes() {
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
const stack = this.getData('directiveStack')
|
||||
/** @type {Attribute[]} */
|
||||
// @ts-expect-error
|
||||
const attributes = this.getData('directiveAttributes')
|
||||
/** @type {Directive['attributes']} */
|
||||
const cleaned = {}
|
||||
let index = -1
|
||||
/** @type {Attribute} */
|
||||
let attribute
|
||||
let index = -1
|
||||
|
||||
while (++index < attributes.length) {
|
||||
attribute = attributes[index]
|
||||
|
@ -133,25 +193,38 @@ export function directiveHtml(options = {}) {
|
|||
stack[stack.length - 1].attributes = cleaned
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitContainerContent() {
|
||||
const data = this.resume()
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
const stack = this.getData('directiveStack')
|
||||
stack[stack.length - 1].content = data
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exitContainerFence() {
|
||||
/** @type {Directive[]} */
|
||||
// @ts-expect-error
|
||||
const stack = this.getData('directiveStack')
|
||||
const directive = stack[stack.length - 1]
|
||||
if (!directive.fenceCount) directive.fenceCount = 0
|
||||
directive.fenceCount++
|
||||
if (directive.fenceCount === 1) this.setData('slurpOneLineEnding', true)
|
||||
if (!directive._fenceCount) directive._fenceCount = 0
|
||||
directive._fenceCount++
|
||||
if (directive._fenceCount === 1) this.setData('slurpOneLineEnding', true)
|
||||
}
|
||||
|
||||
/** @type {_Handle} */
|
||||
function exit() {
|
||||
/** @type {Directive} */
|
||||
// @ts-expect-error
|
||||
const directive = this.getData('directiveStack').pop()
|
||||
/** @type {boolean|undefined} */
|
||||
let found
|
||||
/** @type {boolean|void} */
|
||||
let result
|
||||
|
||||
assert(directive.name, 'expected `name`')
|
||||
|
||||
if (own.call(options, directive.name)) {
|
||||
result = options[directive.name].call(this, directive)
|
||||
found = result !== false
|
||||
|
@ -168,6 +241,10 @@ export function directiveHtml(options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function decodeLight(value) {
|
||||
return value.replace(
|
||||
/&(#(\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi,
|
||||
|
@ -175,6 +252,11 @@ function decodeLight(value) {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} $0
|
||||
* @param {string} $1
|
||||
* @returns {string}
|
||||
*/
|
||||
function decodeIfPossible($0, $1) {
|
||||
return decodeEntity($1) || $0
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
/**
|
||||
* @typedef {import('micromark-util-types').Extension} Extension
|
||||
*/
|
||||
|
||||
import {codes} from 'micromark-util-symbol/codes.js'
|
||||
import {directiveContainer} from './directive-container.js'
|
||||
import {directiveLeaf} from './directive-leaf.js'
|
||||
import {directiveText} from './directive-text.js'
|
||||
|
||||
/**
|
||||
* @returns {Extension}
|
||||
*/
|
||||
export function directive() {
|
||||
return {
|
||||
text: {[codes.colon]: directiveText},
|
||||
|
|
15
package.json
15
package.json
|
@ -26,9 +26,11 @@
|
|||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"files": [
|
||||
"dev/",
|
||||
"lib/",
|
||||
"index.d.ts",
|
||||
"index.js"
|
||||
],
|
||||
"exports": {
|
||||
|
@ -40,9 +42,11 @@
|
|||
"micromark-factory-whitespace": "^1.0.0-alpha.2",
|
||||
"micromark-util-character": "^1.0.0-alpha.2",
|
||||
"micromark-util-symbol": "^1.0.0-alpha.2",
|
||||
"micromark-util-types": "^1.0.0-alpha.2",
|
||||
"parse-entities": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/tape": "^4.0.0",
|
||||
"c8": "^7.0.0",
|
||||
"html-void-elements": "^2.0.0",
|
||||
"micromark": "^3.0.0-alpha.2",
|
||||
|
@ -50,11 +54,14 @@
|
|||
"prettier": "^2.0.0",
|
||||
"remark-cli": "^9.0.0",
|
||||
"remark-preset-wooorm": "^8.0.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"tape": "^5.0.0",
|
||||
"type-coverage": "^2.0.0",
|
||||
"typescript": "^4.0.0",
|
||||
"xo": "^0.39.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "micromark-build",
|
||||
"build": "rimraf \"dev/**/*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage && micromark-build",
|
||||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
|
||||
"test-api": "node --conditions development test/index.js",
|
||||
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --conditions development test/index.js",
|
||||
|
@ -78,5 +85,11 @@
|
|||
"plugins": [
|
||||
"preset-wooorm"
|
||||
]
|
||||
},
|
||||
"typeCoverage": {
|
||||
"atLeast": 100,
|
||||
"detail": true,
|
||||
"strict": true,
|
||||
"ignoreCatch": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,8 @@ None yet, but might be added in the future.
|
|||
|
||||
###### `htmlOptions`
|
||||
|
||||
An object mapping names of directives to handlers ([`Object.<Handle>`][handle]).
|
||||
An object mapping names of directives to handlers
|
||||
([`Record<string, Handle>`][handle]).
|
||||
The special name `'*'` is the fallback to handle all unhandled directives.
|
||||
|
||||
### `function handle(directive)`
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* @typedef {import('../dev/index.js').Options} Options
|
||||
* @typedef {import('../dev/index.js').Handle} Handle
|
||||
*/
|
||||
|
||||
import test from 'tape'
|
||||
import {micromark} from 'micromark'
|
||||
import {htmlVoidElements} from 'html-void-elements'
|
||||
|
@ -1369,6 +1374,7 @@ test('content', (t) => {
|
|||
t.end()
|
||||
})
|
||||
|
||||
/** @type {Handle} */
|
||||
function abbr(d) {
|
||||
if (d.type !== 'textDirective') return false
|
||||
|
||||
|
@ -1383,9 +1389,11 @@ function abbr(d) {
|
|||
this.tag('</abbr>')
|
||||
}
|
||||
|
||||
/** @type {Handle} */
|
||||
function youtube(d) {
|
||||
const attrs = d.attributes || {}
|
||||
const v = attrs.v
|
||||
/** @type {string} */
|
||||
let prop
|
||||
|
||||
if (!v) return false
|
||||
|
@ -1416,10 +1424,13 @@ function youtube(d) {
|
|||
this.tag('</iframe>')
|
||||
}
|
||||
|
||||
/** @type {Handle} */
|
||||
function h(d) {
|
||||
const content = d.content || d.label
|
||||
const attrs = d.attributes || {}
|
||||
/** @type {Array.<string>} */
|
||||
const list = []
|
||||
/** @type {string} */
|
||||
let prop
|
||||
|
||||
for (prop in attrs) {
|
||||
|
@ -1441,6 +1452,9 @@ function h(d) {
|
|||
if (!htmlVoidElements.includes(d.name)) this.tag('</' + d.name + '>')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Options} [options]
|
||||
*/
|
||||
function options(options) {
|
||||
return {
|
||||
allowDangerousHtml: true,
|
||||
|
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"include": ["dev/**/*.js", "test/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020"],
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue