From a0f521746f8286b5834eafbfd52ccba7034b2de4 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 8 Jun 2021 17:11:30 +0200 Subject: [PATCH] Add JSDoc based types --- .gitignore | 1 + dev/index.js | 5 ++ dev/lib/directive-container.js | 27 ++++++++ dev/lib/directive-leaf.js | 16 +++++ dev/lib/directive-text.js | 16 +++++ dev/lib/factory-attributes.js | 39 ++++++++++++ dev/lib/factory-label.js | 20 ++++++ dev/lib/factory-name.js | 21 ++++++- dev/lib/html.js | 110 ++++++++++++++++++++++++++++----- dev/lib/syntax.js | 7 +++ package.json | 15 ++++- readme.md | 3 +- test/index.js | 14 +++++ tsconfig.json | 16 +++++ 14 files changed, 291 insertions(+), 19 deletions(-) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index b657905..b957bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +*.d.ts *.log coverage/ node_modules/ diff --git a/dev/index.js b/dev/index.js index c34d8a3..4e4844b 100644 --- a/dev/index.js +++ b/dev/index.js @@ -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' diff --git a/dev/lib/directive-container.js b/dev/lib/directive-container.js index 98a4eaa..afa0fb3 100644 --- a/dev/lib/directive-container.js +++ b/dev/lib/directive-container.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( diff --git a/dev/lib/directive-leaf.js b/dev/lib/directive-leaf.js index 44ddfa0..283560c 100644 --- a/dev/lib/directive-leaf.js +++ b/dev/lib/directive-leaf.js @@ -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( diff --git a/dev/lib/directive-text.js b/dev/lib/directive-text.js index 2f03d3b..67973ac 100644 --- a/dev/lib/directive-text.js +++ b/dev/lib/directive-text.js @@ -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( diff --git a/dev/lib/factory-attributes.js b/dev/lib/factory-attributes.js index 503d52a..6c88a65 100644 --- a/dev/lib/factory-attributes.js +++ b/dev/lib/factory-attributes.js @@ -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) diff --git a/dev/lib/factory-label.js b/dev/lib/factory-label.js index 4d85edd..b8a73a6 100644 --- a/dev/lib/factory-label.js +++ b/dev/lib/factory-label.js @@ -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 || diff --git a/dev/lib/factory-name.js b/dev/lib/factory-name.js index 7d3d26d..aa66d86 100644 --- a/dev/lib/factory-name.js +++ b/dev/lib/factory-name.js @@ -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) diff --git a/dev/lib/html.js b/dev/lib/html.js index 53fc3b6..b4fd231 100644 --- a/dev/lib/html.js +++ b/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} [attributes] + * @property {string} [content] + * @property {number} [_fenceCount] + * + * @typedef {(this: CompileContext, directive: Directive) => boolean|void} Handle + * + * @typedef {Record} 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 } diff --git a/dev/lib/syntax.js b/dev/lib/syntax.js index d272765..e84b03f 100644 --- a/dev/lib/syntax.js +++ b/dev/lib/syntax.js @@ -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}, diff --git a/package.json b/package.json index b12d974..b353423 100644 --- a/package.json +++ b/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 } } diff --git a/readme.md b/readme.md index a055e8e..d8797db 100644 --- a/readme.md +++ b/readme.md @@ -103,7 +103,8 @@ None yet, but might be added in the future. ###### `htmlOptions` -An object mapping names of directives to handlers ([`Object.`][handle]). +An object mapping names of directives to handlers +([`Record`][handle]). The special name `'*'` is the fallback to handle all unhandled directives. ### `function handle(directive)` diff --git a/test/index.js b/test/index.js index 765f02b..120cfd7 100644 --- a/test/index.js +++ b/test/index.js @@ -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('') } +/** @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('') } +/** @type {Handle} */ function h(d) { const content = d.content || d.label const attrs = d.attributes || {} + /** @type {Array.} */ const list = [] + /** @type {string} */ let prop for (prop in attrs) { @@ -1441,6 +1452,9 @@ function h(d) { if (!htmlVoidElements.includes(d.name)) this.tag('') } +/** + * @param {Options} [options] + */ function options(options) { return { allowDangerousHtml: true, diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..34dd6a4 --- /dev/null +++ b/tsconfig.json @@ -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 + } +}