-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
143 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
}) | ||
}) |