Skip to content

Commit

Permalink
Update @types/mdast, utilities, etc
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Sep 23, 2023
1 parent 30a4981 commit d98329e
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 98 deletions.
164 changes: 108 additions & 56 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,132 @@
/**
* @typedef {import('unist').Node} Node
* @typedef {import('mdast').Root} MdastRoot
* @typedef {import('mdast-util-to-nlcst').ParserInstance} ParserInstance
* @typedef {import('mdast-util-to-nlcst').ParserConstructor} ParserConstructor
* @typedef {import('mdast-util-to-nlcst').Options} Options
* @typedef {import('unified').Processor<any, any, any, any>} Processor
* @typedef {import('unified').Parser<any>} Parser
* @typedef {import('mdast-util-to-nlcst').ParserConstructor} ParserConstructor
* @typedef {import('mdast-util-to-nlcst').ParserInstance} ParserInstance
* @typedef {import('nlcst').Root} NlcstRoot
* @typedef {import('unified').Processor<NlcstRoot>} Processor
* @typedef {import('vfile').VFile} VFile
*/

/**
* @typedef {ParserConstructor | ParserInstance} Parser
*
* @callback TransformBridge
* Bridge-mode.
*
* Runs the destination with the new nlcst tree.
* Discards result.
* @param {MdastRoot} tree
* Tree.
* @param {VFile} file
* File.
* @returns {Promise<undefined>}
* Nothing.
*
* @callback TransformMutate
* Mutate-mode.
*
* Further transformers run on the nlcst tree.
*
* @param {MdastRoot} tree
* Tree.
* @param {VFile} file
* File.
* @returns {NlcstRoot}
* Tree (nlcst).
*/

import {toNlcst} from 'mdast-util-to-nlcst'
import {ParseLatin} from 'parse-latin'

/**
* Plugin to support retext.
* Bridge or mutate to retext.
*
* ###### Notes
*
* * If a destination processor is given, runs the plugins attached to it with
* the new nlcst tree (bridge-mode).
* This given processor must have a parser attached (this can be done by
* using the plugin `retext-english` or similar) and should use other retext
* plugins.
* * If a parser is given, runs further plugins attached to the same processor
* with the new tree (mutate-mode).
* Such parsers are exported by packages like `retext-english` as `Parser`.
* You should use other retext plugins after `remark-retext`.
* * if a processor is given, uses its parser to create a new nlcst tree,
* then runs the plugins attached to with that ([*bridge mode*][bridge]);
* you can add a parser to processor for example with `retext-english`; other
* plugins used on the processor should be retext plugins
* * if a parser is given, uses it to create a new nlcst tree, and returns
* it (*mutate mode*); you can get a parser by importing `Parser` from
* `retext-english` for example; other plugins used after `remarkRetext`
* should be retext plugins
*
* @param destination
* Either a processor (`unified().use(retextEnglish)…`) or a parser.
* @param options
* Configuration passed to `mdast-util-to-nlcst`.
* @overload
* @param {Processor} processor
* @param {Options | null | undefined} [options]
* @returns {TransformBridge}
*
* @overload
* @param {Parser} parser
* @param {Options | null | undefined} [options]
* @returns {TransformMutate}
*
* @param {Parser | Processor} parserOrProcessor
* Parser or processor (required).
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {TransformBridge | TransformMutate}
* Transform.
*/
const remarkRetext =
/**
* @type {(import('unified').Plugin<[Processor, Options?]|[Processor], MdastRoot, MdastRoot> & import('unified').Plugin<[Parser, Options?]|[Parser], MdastRoot, Node>)}
*/
(
export default function remarkRetext(parserOrProcessor, options) {
if (!parserOrProcessor) {
throw new Error(
'Expected `parser` (such as from `parse-english`) or `processor` (a unified pipeline) as `parserOrProcessor`'
)
}

if ('run' in parserOrProcessor) {
const processor = parserOrProcessor.freeze()

/**
* @param {Processor|Parser} destination
* @param {Options|undefined} options
* @type {TransformBridge}
*/
function (destination, options) {
return destination && 'run' in destination
? // @ts-expect-error: to do.
bridge(destination, options)
: // @ts-expect-error: to do.
mutate(destination, options)
return async function (tree, file) {
const nlcstTree = toNlcst(
tree,
file,
parserFromRetextParse(processor),
options
)
await processor.run(nlcstTree, file)
}
)
}

export default remarkRetext
const parser = parserOrProcessor

/**
* Mutate-mode.
* Further transformers run on the nlcst tree.
*
* @type {import('unified').Plugin<[Parser, Options?], MdastRoot, Node>}
*/
function mutate(parser, options) {
// Assume the parser is a retext parser.
const Parser = /** @type {ParserInstance|ParserConstructor} */ (parser)
return (node, file) => toNlcst(node, file, Parser, options)
/**
* @type {TransformMutate}
*/
return function (tree, file) {
return toNlcst(tree, file, parser, options)
}
}

/**
* Bridge-mode.
* Runs the destination with the new nlcst tree.
*
* @type {import('unified').Plugin<[Processor, Options?], MdastRoot>}
* @param {Processor} processor
* @returns {ParseLatin}
*/
function bridge(destination, options) {
return (node, file, next) => {
// Assume the parser is a retext parser.
const Parser = /** @type {ParserConstructor|ParserInstance} */ (
destination.freeze().Parser
)
function parserFromRetextParse(processor) {
const parser = new ParseLatin()
add(
parser.tokenizeParagraphPlugins,
processor.data('nlcstParagraphExtensions')
)
add(parser.tokenizeRootPlugins, processor.data('nlcstRootExtensions'))
add(parser.tokenizeSentencePlugins, processor.data('nlcstSentenceExtensions'))

return parser

destination.run(toNlcst(node, file, Parser, options), file, (error) => {
next(error)
})
/**
* @template T
* @param {Array<T>} list
* @param {Array<T> | undefined} values
*/
function add(list, values) {
/* c8 ignore next -- plugins like `retext-emoji`. */
if (values) list.unshift(...values)
}
}
34 changes: 11 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,24 @@
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"@types/unist": "^2.0.0",
"mdast-util-to-nlcst": "^5.0.0",
"unified": "^10.0.0"
"@types/mdast": "^4.0.0",
"@types/nlcst": "^2.0.0",
"mdast-util-to-nlcst": "^7.0.0",
"parse-latin": "^7.0.0",
"unified": "^11.0.0",
"vfile": "^6.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"c8": "^8.0.0",
"parse-english": "^7.0.0",
"prettier": "^3.0.0",
"remark-cli": "^11.0.0",
"remark-parse": "^10.0.0",
"remark-parse": "^11.0.0",
"remark-preset-wooorm": "^9.0.0",
"remark-stringify": "^10.0.0",
"retext-english": "^4.0.0",
"retext-stringify": "^3.0.0",
"remark-stringify": "^11.0.0",
"retext-english": "^5.0.0",
"retext-stringify": "^4.0.0",
"type-coverage": "^2.0.0",
"typescript": "^5.0.0",
"xo": "^0.56.0"
Expand Down Expand Up @@ -79,24 +82,9 @@
"atLeast": 100,
"detail": true,
"ignoreCatch": true,
"#": "needed `any`s",
"ignoreFiles": [
"lib/index.d.ts",
"lib/index.js"
],
"strict": true
},
"xo": {
"overrides": [
{
"files": [
"test/**/*.js"
],
"rules": {
"no-await-in-loop": "off"
}
}
],
"prettier": true
}
}
43 changes: 24 additions & 19 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {unified} from 'unified'
import {ParseEnglish} from 'parse-english'
import remarkParse from 'remark-parse'
import retextEnglish, {Parser as RetextEnglish} from 'retext-english'
import retextEnglish from 'retext-english'
import remarkStringify from 'remark-stringify'
import retextStringify from 'retext-stringify'
import remarkRetext from './index.js'

test('remarkRetext', async function (t) {
await t.test('should throw when w/o parser or processor', async function () {
assert.throws(function () {
// @ts-expect-error: check how missing options is handled.
unified().use(remarkRetext).freeze()
}, /Expected `parser` \(such as from `parse-english`\) or `processor` \(a unified pipeline\) as `parserOrProcessor`/)
})

await t.test('should mutate', async function () {
assert.equal(
unified()
.use(remarkParse)
.use(remarkRetext, RetextEnglish)
.use(retextStringify)
.processSync('## Hello, world! ##')
.toString(),
'Hello, world!'
)
const file = await unified()
.use(remarkParse)
.use(remarkRetext, ParseEnglish)
.use(retextStringify)
.process('## Hello, world! ##')

assert.equal(String(file), 'Hello, world!')
})

await t.test('should bridge', async function () {
assert.equal(
unified()
.use(remarkParse)
.use(remarkRetext, unified().use(retextEnglish))
.use(remarkStringify)
.processSync('## Hello, world! ##')
.toString(),
'## Hello, world!\n'
)
const file = await unified()
.use(remarkParse)
// @ts-expect-error: to do.
.use(remarkRetext, unified().use(retextEnglish))
.use(remarkStringify)
.process('## Hello, world! ##')

assert.equal(String(file), '## Hello, world!\n')
})
})

0 comments on commit d98329e

Please sign in to comment.