diff --git a/index.d.ts b/index.d.ts index 0739b57c..916901b5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,16 +16,104 @@ import {Node} from 'unist' import {VFile, VFileCompatible} from 'vfile' +/* eslint-disable @typescript-eslint/ban-types */ + +type VFileWithOutput = Result extends Uint8Array // Buffer. + ? VFile + : Result extends object // Custom result type + ? VFile & {result: Result} + : VFile + +// Get the right most non-void thing. +type Specific = Right extends void ? Left : Right + +// Create a processor based on the input/output of a plugin. +type UsePlugin< + ParseTree extends Node | void = void, + CurrentTree extends Node | void = void, + CompileTree extends Node | void = void, + CompileResult = void, + Input = void, + Output = void +> = Output extends Node + ? Input extends string + ? // If `Input` is `string` and `Output` is `Node`, then this plugin + // defines a parser, so set `ParseTree`. + Processor< + Output, + Specific, + Specific, + CompileResult + > + : Input extends Node + ? // If `Input` is `Node` and `Output` is `Node`, then this plugin defines a + // transformer, its output defines the input of the next, so set + // `CurrentTree`. + Processor< + Specific, + Output, + Specific, + CompileResult + > + : // Else, `Input` is something else and `Output` is `Node`: + never + : Input extends Node + ? // If `Input` is `Node` and `Output` is not a `Node`, then this plugin + // defines a compiler, so set `CompileTree` and `CompileResult` + Processor< + Specific, + Specific, + Input, + Output + > + : // Else, `Input` is not a `Node` and `Output` is not a `Node`. + // Maybe it’s untyped, or the plugin throws an error (`never`), so lets + // just keep it as it was. + Processor + +/* eslint-enable @typescript-eslint/ban-types */ + /** * Processor allows plugins to be chained together to transform content. * The chain of plugins defines how content flows through it. + * + * @typeParam ParseTree + * The node that the parser yields (and `run` receives). + * @typeParam CurrentTree + * The node that the last attached plugin yields. + * @typeParam CompileTree + * The node that the compiler receives (and `run` yields). + * @typeParam CompileResult + * The thing that the compiler yields. */ -export interface Processor extends FrozenProcessor { +export interface Processor< + ParseTree extends Node | void = void, + CurrentTree extends Node | void = void, + CompileTree extends Node | void = void, + CompileResult = void +> extends FrozenProcessor { /** * Configure the processor to use a plugin. * * @typeParam PluginParameters * Plugin settings. + * @typeParam Input + * Value that is accepted by the plugin. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer expects. + * * If the plugin sets a parser, then this should be `string`. + * * If the plugin sets a compiler, then this should be the node type that + * the compiler expects. + * @typeParam Output + * Value that the plugin yields. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer yields, and defaults to `Input`. + * * If the plugin sets a parser, then this should be the node type that + * the parser yields. + * * If the plugin sets a compiler, then this should be the result that + * the compiler yields (`string`, `Buffer`, or something else). * @param plugin * Plugin (function) to use. * Plugins are deduped based on identity: passing a function in twice will @@ -39,16 +127,44 @@ export interface Processor extends FrozenProcessor { * @returns * Current processor. */ - use( - plugin: Plugin, + use< + PluginParameters extends any[] = any[], + Input = Specific, + Output = Input + >( + plugin: Plugin, ...settings: PluginParameters | [boolean] - ): Processor + ): UsePlugin< + ParseTree, + CurrentTree, + CompileTree, + CompileResult, + Input, + Output + > /** * Configure the processor with a tuple of a plugin and setting(s). * * @typeParam PluginParameters * Plugin settings. + * @typeParam Input + * Value that is accepted by the plugin. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer expects. + * * If the plugin sets a parser, then this should be `string`. + * * If the plugin sets a compiler, then this should be the node type that + * the compiler expects. + * @typeParam Output + * Value that the plugin yields. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer yields, and defaults to `Input`. + * * If the plugin sets a parser, then this should be the node type that + * the parser yields. + * * If the plugin sets a compiler, then this should be the result that + * the compiler yields (`string`, `Buffer`, or something else). * @param tuple * A tuple where the first item is a plugin (function) to use and other * items are options. @@ -59,9 +175,22 @@ export interface Processor extends FrozenProcessor { * @returns * Current processor. */ - use( - tuple: PluginTuple | [Plugin, boolean] - ): Processor + use< + PluginParameters extends any[] = any[], + Input = Specific, + Output = Input + >( + tuple: + | PluginTuple + | [Plugin, boolean] + ): UsePlugin< + ParseTree, + CurrentTree, + CompileTree, + CompileResult, + Input, + Output + > /** * Configure the processor with a preset or list of plugins and presets. @@ -73,7 +202,9 @@ export interface Processor extends FrozenProcessor { * @returns * Current processor. */ - use(presetOrList: Preset | PluggableList): Processor + use( + presetOrList: Preset | PluggableList + ): Processor } /** @@ -82,7 +213,12 @@ export interface Processor extends FrozenProcessor { * A frozen processor can be created by calling `.freeze()` on a processor. * An unfrozen processor can be created by calling a processor. */ -export interface FrozenProcessor { +export interface FrozenProcessor< + ParseTree extends Node | void = void, + CurrentTree extends Node | void = void, + CompileTree extends Node | void = void, + CompileResult = void +> { /** * Clone current processor * @@ -92,7 +228,7 @@ export interface FrozenProcessor { * But when the descendant processor is configured it does not affect the * ancestral processor. */ - (): Processor + (): Processor /** * Internal list of configured plugins. @@ -101,8 +237,10 @@ export interface FrozenProcessor { */ attachers: Array<[Plugin, ...unknown[]]> - Parser?: Parser | undefined - Compiler?: Compiler | undefined + Parser?: Parser> | undefined + Compiler?: + | Compiler, Specific> + | undefined /** * Parse a file. @@ -113,7 +251,7 @@ export interface FrozenProcessor { * @returns * Resulting tree. */ - parse(file?: VFileCompatible | undefined): Node + parse(file?: VFileCompatible | undefined): Specific /** * Compile a file. @@ -127,7 +265,10 @@ export interface FrozenProcessor { * This depends on which plugins you use: typically text, but could for * example be a React node. */ - stringify(node: Node, file?: VFileCompatible | undefined): unknown + stringify( + node: Specific, + file?: VFileCompatible | undefined + ): CompileTree extends Node ? CompileResult : unknown /** * Run transforms on the given tree. @@ -139,7 +280,10 @@ export interface FrozenProcessor { * @returns * Nothing. */ - run(node: Node, callback: RunCallback): void + run( + node: Specific, + callback: RunCallback> + ): void /** * Run transforms on the given node. @@ -155,9 +299,9 @@ export interface FrozenProcessor { * Nothing. */ run( - node: Node, + node: Specific, file: VFileCompatible | undefined, - callback: RunCallback + callback: RunCallback> ): void /** @@ -171,10 +315,13 @@ export interface FrozenProcessor { * @returns * Promise that resolves to the resulting tree. */ - run(node: Node, file?: VFileCompatible | undefined): Promise + run( + node: Specific, + file?: VFileCompatible | undefined + ): Promise> /** - * Run transforms on the given node, synchroneously. + * Run transforms on the given node, synchronously. * Throws when asynchronous transforms are configured. * * @param node @@ -185,7 +332,10 @@ export interface FrozenProcessor { * @returns * Resulting tree. */ - runSync(node: Node, file?: VFileCompatible | undefined): Node + runSync( + node: Specific, + file?: VFileCompatible | undefined + ): Specific /** * Process a file. @@ -210,7 +360,10 @@ export interface FrozenProcessor { * @returns * Nothing. */ - process(file: VFileCompatible | undefined, callback: ProcessCallback): void + process( + file: VFileCompatible | undefined, + callback: ProcessCallback> + ): void /** * Process a file. @@ -233,10 +386,10 @@ export interface FrozenProcessor { * @returns * Promise that resolves to the resulting `VFile`. */ - process(file: VFileCompatible): Promise + process(file: VFileCompatible): Promise> /** - * Process a file, synchroneously. + * Process a file, synchronously. * Throws when asynchronous transforms are configured. * * This performs all phases of the processor: @@ -257,7 +410,9 @@ export interface FrozenProcessor { * @returns * Resulting file. */ - processSync(file?: VFileCompatible | undefined): VFile + processSync( + file?: VFileCompatible | undefined + ): VFileWithOutput /** * Get an in-memory key-value store accessible to all phases of the process. @@ -275,7 +430,9 @@ export interface FrozenProcessor { * @returns * Current processor. */ - data(data: Record): Processor + data( + data: Record + ): Processor /** * Get an in-memory value by key. @@ -297,7 +454,10 @@ export interface FrozenProcessor { * @returns * Current processor. */ - data(key: string, value: unknown): Processor + data( + key: string, + value: unknown + ): Processor /** * Freeze a processor. @@ -315,7 +475,7 @@ export interface FrozenProcessor { * @returns * Frozen processor. */ - freeze(): FrozenProcessor + freeze(): FrozenProcessor } /** @@ -327,6 +487,23 @@ export interface FrozenProcessor { * * @typeParam PluginParameters * Plugin settings. + * @typeParam Input + * Value that is accepted by the plugin. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer expects. + * * If the plugin sets a parser, then this should be `string`. + * * If the plugin sets a compiler, then this should be the node type that + * the compiler expects. + * @typeParam Output + * Value that the plugin yields. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer yields, and defaults to `Input`. + * * If the plugin sets a parser, then this should be the node type that + * the parser yields. + * * If the plugin sets a compiler, then this should be the result that + * the compiler yields (`string`, `Buffer`, or something else). * @this * The current processor. * Plugins can configure the processor by interacting with `this.Parser` or @@ -347,10 +524,29 @@ export interface FrozenProcessor { * Plugins can return a `Transformer` to specify how the syntax tree is * handled. */ -export type Plugin = ( - this: Processor, +export type Plugin< + PluginParameters extends any[] = any[], + Input = Node, + Output = Input +> = ( + this: Input extends Node + ? Output extends Node + ? // This is a transform, so define `Input` as the current tree. + Processor + : // Compiler. + Processor + : Output extends Node + ? // Parser. + Processor + : // No clue. + Processor, ...settings: PluginParameters -) => Transformer | void +) => // If both `Input` and `Output` are `Node`, expect an optional `Transformer`. +Input extends Node + ? Output extends Node + ? Transformer | void + : void + : void /** * Presets provide a sharable way to configure processors with multiple plugins @@ -369,11 +565,29 @@ export interface Preset { * * @typeParam PluginParameters * Plugin settings. + * @typeParam Input + * Value that is accepted by the plugin. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer expects. + * * If the plugin sets a parser, then this should be `string`. + * * If the plugin sets a compiler, then this should be the node type that + * the compiler expects. + * @typeParam Output + * Value that the plugin yields. + * + * * If the plugin returns a transformer, then this should be the node + * type that the transformer yields, and defaults to `Input`. + * * If the plugin sets a parser, then this should be the node type that + * the parser yields. + * * If the plugin sets a compiler, then this should be the result that + * the compiler yields (`string`, `Buffer`, or something else). */ -export type PluginTuple = [ - Plugin, - ...PluginParameters -] +export type PluginTuple< + PluginParameters extends any[] = any[], + Input = Node, + Output = Input +> = [Plugin, ...PluginParameters] /** * A union of the different ways to add plugins and settings. @@ -382,8 +596,8 @@ export type PluginTuple = [ * Plugin settings. */ export type Pluggable = - | PluginTuple - | Plugin + | PluginTuple + | Plugin | Preset /** @@ -395,8 +609,11 @@ export type PluggableList = Pluggable[] * @deprecated * Please use `Plugin`. */ -export type Attacher = - Plugin +export type Attacher< + PluginParameters extends any[] = any[], + Input = Node, + Output = Input +> = Plugin /** * Transformers modify the syntax tree or metadata of a file. @@ -405,6 +622,10 @@ export type Attacher = * If an error occurs (either because it’s thrown, returned, rejected, or passed * to `next`), the process stops. * + * @typeParam Input + * Node type that the transformer expects. + * @typeParam Output + * Node type that the transformer yields. * @param node * Tree to be transformed. * @param file @@ -428,15 +649,20 @@ export type Attacher = * * If you accept a `next` callback, nothing should be returned. */ -type Transformer = ( - node: Node, +export type Transformer< + Input extends Node = Node, + Output extends Node = Input +> = ( + node: Input, file: VFile, - next: TransformCallback -) => Promise | Node | Error | undefined | void + next: TransformCallback +) => Promise | Output | Error | undefined | void /** * Callback you must call when a transformer is done. * + * @typeParam Tree + * Node that the plugin yields. * @param error * Pass an error to stop the process. * @param node @@ -446,9 +672,9 @@ type Transformer = ( * @returns * Nothing. */ -export type TransformCallback = ( +export type TransformCallback = ( error?: Error | null | undefined, - node?: Node | undefined, + node?: Tree | undefined, file?: VFile | undefined ) => void @@ -464,13 +690,21 @@ export type TransformCallback = ( * `prototype`), in which case it’s called with `new`. * Instances must have a parse method that is called without arguments and * must return a `Node`. + * + * @typeParam Tree + * The node that the parser yields (and `run` receives). */ -export type Parser = ParserClass | ParserFunction +export type Parser = + | ParserClass + | ParserFunction /** * A class to parse files. + * + * @typeParam Tree + * The node that the parser yields. */ -export class ParserClass { +export class ParserClass { prototype: { /** * Parse a file. @@ -478,7 +712,7 @@ export class ParserClass { * @returns * Parsed tree. */ - parse(): Node + parse(): Tree } /** @@ -497,6 +731,8 @@ export class ParserClass { /** * Normal function to parse a file. * + * @typeParam Tree + * The node that the parser yields. * @param document * Document to parse. * @param file @@ -504,7 +740,10 @@ export class ParserClass { * @returns * Node representing the given file. */ -export type ParserFunction = (document: string, file: VFile) => Node +export type ParserFunction = ( + document: string, + file: VFile +) => Tree /** * Function handling the compilation of syntax tree to a text. @@ -518,13 +757,25 @@ export type ParserFunction = (document: string, file: VFile) => Node * `prototype`), in which case it’s called with `new`. * Instances must have a `compile` method that is called without arguments * and must return a `string`. + * + * @typeParam Tree + * The node that the compiler receives. + * @typeParam Result + * The thing that the compiler yields. */ -export type Compiler = CompilerClass | CompilerFunction +export type Compiler = + | CompilerClass + | CompilerFunction /** * A class to compile trees. + * + * @typeParam Tree + * The node that the compiler receives. + * @typeParam Result + * The thing that the compiler yields. */ -export class CompilerClass { +export class CompilerClass { prototype: { /** * Compile a tree. @@ -533,7 +784,7 @@ export class CompilerClass { * New content: compiled text (`string` or `Buffer`, for `file.value`) or * something else (for `file.result`). */ - compile(): unknown + compile(): Result } /** @@ -546,12 +797,16 @@ export class CompilerClass { * @returns * Instance. */ - constructor(tree: Node, file: VFile) + constructor(tree: Tree, file: VFile) } /** * Normal function to compile a tree. * + * @typeParam Tree + * The node that the compiler receives. + * @typeParam Result + * The thing that the compiler yields. * @param tree * Tree to compile. * @param file @@ -560,13 +815,18 @@ export class CompilerClass { * New content: compiled text (`string` or `Buffer`, for `file.value`) or * something else (for `file.result`). */ -export type CompilerFunction = (tree: Node, file: VFile) => unknown +export type CompilerFunction = ( + tree: Tree, + file: VFile +) => Result /** * Callback called when a done running. * + * @typeParam Tree + * The tree that the callback receives. * @param error - * Error passed when unsuccesful. + * Error passed when unsuccessful. * @param node * Tree to transform. * @param file @@ -574,25 +834,27 @@ export type CompilerFunction = (tree: Node, file: VFile) => unknown * @returns * Nothing. */ -export type RunCallback = ( +export type RunCallback = ( error?: Error | null | undefined, - node?: Node | undefined, + node?: Tree | undefined, file?: VFile | undefined ) => void /** * Callback called when a done processing. * + * @typeParam File + * The file that the callback receives. * @param error - * Error passed when unsuccesful. + * Error passed when unsuccessful. * @param file * File passed when successful. * @returns * Nothing. */ -export type ProcessCallback = ( +export type ProcessCallback = ( error?: Error | null | undefined, - file?: VFile | undefined + file?: File | undefined ) => void /** diff --git a/index.test-d.ts b/index.test-d.ts index 1572d157..5ff91e09 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,7 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ + import {expectType, expectError} from 'tsd' -import {Node} from 'unist' +import {Node, Parent, Literal} from 'unist' import {VFile} from 'vfile' import { unified, @@ -155,6 +157,318 @@ unified() .use(() => () => { /* Empty */ }) - .use(() => () => { - throw new Error('x') + .use(() => (x) => { + if (x) { + throw new Error('x') + } }) + +// Plugins bound to a certain node. + +// A small subset of mdast. +interface MdastRoot extends Parent { + type: 'root' + children: MdastFlow[] +} + +type MdastFlow = MdastParagraph + +interface MdastParagraph extends Parent { + type: 'paragraph' + children: MdastPhrasing[] +} + +type MdastPhrasing = MdastText + +interface MdastText extends Literal { + type: 'text' + value: string +} + +// A small subset of hast. +interface HastRoot extends Parent { + type: 'root' + children: HastChild[] +} + +type HastChild = HastElement | HastText + +interface HastElement extends Parent { + type: 'element' + tagName: string + properties: Record + children: HastChild[] +} + +interface HastText extends Literal { + type: 'text' + value: string +} + +const explicitPluginWithInputTree: Plugin = + () => (tree, file) => { + expectType(tree) + expectType(file) + } + +const explicitPluginWithTrees: Plugin = + () => (tree, file) => { + expectType(tree) + expectType(file) + return { + type: 'root', + children: [ + { + type: 'element', + tagName: 'a', + properties: {}, + children: [{type: 'text', value: 'a'}] + } + ] + } + } + +unified().use(explicitPluginWithInputTree) +unified().use([explicitPluginWithInputTree]) +unified().use({plugins: [explicitPluginWithInputTree], settings: {}}) +unified().use(() => (tree: MdastRoot) => { + expectType(tree) +}) +unified().use([ + () => (tree: MdastRoot) => { + expectType(tree) + } +]) +unified().use({ + plugins: [ + () => (tree: MdastRoot) => { + expectType(tree) + } + ], + settings: {} +}) + +unified().use(explicitPluginWithTrees) +unified().use([explicitPluginWithTrees]) +unified().use({plugins: [explicitPluginWithTrees], settings: {}}) +unified().use(() => (_: MdastRoot) => ({ + type: 'root', + children: [{type: 'text', value: 'a'}] +})) +unified().use([ + () => (_: MdastRoot) => ({ + type: 'root', + children: [{type: 'text', value: 'a'}] + }) +]) +unified().use({ + plugins: [ + () => (_: MdastRoot) => ({ + type: 'root', + children: [{type: 'text', value: 'a'}] + }) + ], + settings: {} +}) + +// Input and output types. +interface ReactNode { + kind: string +} + +const someMdast: MdastRoot = { + type: 'root', + children: [{type: 'paragraph', children: [{type: 'text', value: 'a'}]}] +} + +const someHast: HastRoot = { + type: 'root', + children: [ + { + type: 'element', + tagName: 'a', + properties: {}, + children: [{type: 'text', value: 'a'}] + } + ] +} + +const remarkParse: Plugin = () => { + /* Empty */ +} + +const remarkStringify: Plugin = () => { + /* Empty */ +} + +const rehypeParse: Plugin = () => { + /* Empty */ +} + +const rehypeStringify: Plugin = () => { + /* Empty */ +} + +const rehypeStringifyBuffer: Plugin = () => { + /* Empty */ +} + +const explicitRemarkPlugin: Plugin = () => { + /* Empty */ +} + +const implicitPlugin: Plugin = () => { + /* Empty */ +} + +const remarkRehype: Plugin = () => { + /* Empty */ +} + +const explicitRehypePlugin: Plugin = () => { + /* Empty */ +} + +const rehypeReact: Plugin = () => { + /* Empty */ +} + +// If a plugin is defined with string as input and a node as output, it +// configures a parser. +expectType(unified().use(remarkParse).parse('')) +expectType(unified().use(rehypeParse).parse('')) +expectType(unified().parse('')) // No parser. + +// If a plugin is defined with a node as input and a non-node as output, it +// configures a compiler. +expectType(unified().use(remarkStringify).stringify(someMdast)) +expectType(unified().use(rehypeStringify).stringify(someHast)) +expectType(unified().use(rehypeStringifyBuffer).stringify(someHast)) +expectType(unified().stringify(someHast)) // No compiler. +expectType(unified().use(rehypeReact).stringify(someHast)) +expectError(unified().use(remarkStringify).stringify(someHast)) +expectError(unified().use(rehypeStringify).stringify(someMdast)) + +// Compilers configure the output of `process`, too. +expectType(unified().use(remarkStringify).processSync('')) +expectType(unified().use(rehypeStringify).processSync('')) +expectType(unified().use(rehypeStringifyBuffer).processSync('')) +expectType(unified().processSync('')) +expectType( + unified().use(rehypeReact).processSync('') +) + +// A parser plugin defines the input of `.run`: +expectType(unified().use(remarkParse).runSync(someMdast)) +expectError(unified().use(remarkParse).runSync(someHast)) + +// A compiler plugin defines the input/output of `.run`: +expectError(unified().use(rehypeStringify).runSync(someMdast)) +// As a parser and a compiler are set, it can be assumed that the input of `run` +// is the result of the parser, and the output is the input of the compiler. +expectType( + unified().use(remarkParse).use(rehypeStringify).runSync(someMdast) +) +// Probably hast expected. +expectError(unified().use(rehypeStringify).runSync(someMdast)) + +unified() + .use(rehypeStringify) + .run(someHast) + .then((thing) => { + expectType(thing) + }) + +unified() + .use(rehypeStringify) + .run(someHast, (error, thing) => { + expectType(error) + expectType(thing) + }) + +// A compiler plugin defines the output of `.process`: +expectType( + unified().use(rehypeReact).processSync('') +) +expectType( + unified().use(remarkParse).use(rehypeReact).processSync('') +) + +unified() + .use(rehypeReact) + .process('') + .then((file) => { + expectType(file) + }) + +unified() + .use(rehypeReact) + .process('', (error, thing) => { + expectType(error) + expectType<(VFile & {result: ReactNode}) | undefined>(thing) + }) + +// Plugins work! +unified() + .use(remarkParse) + .use(explicitRemarkPlugin) + .use(implicitPlugin) + .use(remarkRehype) + .use(implicitPlugin) + .use(rehypeStringify) + .freeze() + +// Parsers define the input of transformers. +unified().use(() => (node) => { + expectType(node) +}) +unified() + .use(remarkParse) + .use(() => (node) => { + expectType(node) + }) +unified() + .use(rehypeParse) + .use(() => (node) => { + expectType(node) + }) + +unified() + // Using a parser plugin also defines the current tree (see next). + .use(remarkParse) + // A plugin following a typed parser receives the defined AST. + // If it doesn’t resolve anything, that AST remains for the next plugin. + .use(() => (node) => { + expectType(node) + }) + // A plugin that returns a certain AST, defines it for the next plugin. + .use(() => (node) => { + expectType(node) + return someHast + }) + .use(() => (node) => { + expectType(node) + }) + .use(rehypeStringify) + +// Using two parsers or compilers is fine. The last one sticks. +const p1 = unified().use(remarkParse).use(rehypeParse) +expectType(p1.parse('')) +const p2 = unified().use(remarkStringify).use(rehypeStringify) +expectError(p2.stringify(someMdast)) + +// Using mismatched explicit plugins is fine (for now). +unified() + .use(explicitRemarkPlugin) + .use(explicitRehypePlugin) + .use(explicitRemarkPlugin) + +expectType( + unified() + .use(explicitRemarkPlugin) + .use(remarkRehype) + .use(explicitRehypePlugin) + .runSync(someMdast) +) + +/* eslint-enable @typescript-eslint/no-floating-promises */ diff --git a/test/async-function.js b/test/async-function.js index cc6d2060..7f876e28 100644 --- a/test/async-function.js +++ b/test/async-function.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('unist').Node} Node + */ + import test from 'tape' import {VFile} from 'vfile' import {unified} from '../index.js' @@ -12,19 +16,18 @@ test('async function transformer () {}', (t) => { unified() .use(() => async function () {}) .use( + // Note: TS JS doesn’t understand the `Promise` w/o explicit type. + /** @type {import('../index.js').Plugin<[]>} */ () => async function () { return undefined } ) - .use( - () => - async function (tree, file) { - t.equal(tree, givenNode, 'passes correct tree to an async function') - t.equal(file, givenFile, 'passes correct file to an async function') - return modifiedNode - } - ) + .use(() => async (tree, file) => { + t.equal(tree, givenNode, 'passes correct tree to an async function') + t.equal(file, givenFile, 'passes correct file to an async function') + return modifiedNode + }) .run(givenNode, givenFile, (error, tree, file) => { t.error(error, 'should’t fail') t.equal(tree, modifiedNode, 'passes given tree to `done`') diff --git a/test/run.js b/test/run.js index 7ca0e300..c843b24b 100644 --- a/test/run.js +++ b/test/run.js @@ -118,6 +118,8 @@ test('run(node[, file], done)', (t) => { unified() .use( + // Note: TS JS doesn’t understand the promise w/o explicit type. + /** @type {import('../index.js').Plugin<[]>} */ () => function () { return new Promise((resolve) => { @@ -371,6 +373,8 @@ test('run(node[, file])', (t) => { unified() .use( + // Note: TS JS doesn’t understand the promise w/o explicit type. + /** @type {import('../index.js').Plugin<[]>} */ () => function () { return new Promise((resolve) => { @@ -579,6 +583,8 @@ test('runSync(node[, file])', (t) => { () => { unified() .use( + // Note: TS JS doesn’t understand the promise w/o explicit type. + /** @type {import('../index.js').Plugin<[]>} */ () => function () { return new Promise((resolve) => { diff --git a/test/use.js b/test/use.js index 43911c02..1a3eb423 100644 --- a/test/use.js +++ b/test/use.js @@ -262,19 +262,19 @@ test('use(plugin[, options])', (t) => { t.test('should attach transformers', (t) => { const processor = unified() const givenNode = {type: 'test'} + const condition = true t.plan(3) processor - .use( - () => - function (node, file) { - t.equal(node, givenNode, 'should attach a transformer (#1)') - t.ok('message' in file, 'should attach a transformer (#2)') + .use(() => (node, file) => { + t.equal(node, givenNode, 'should attach a transformer (#1)') + t.ok('message' in file, 'should attach a transformer (#2)') - throw new Error('Alpha bravo charlie') - } - ) + if (condition) { + throw new Error('Alpha bravo charlie') + } + }) .freeze() t.throws(