Skip to content

Commit

Permalink
Add support for processor to declare their options
Browse files Browse the repository at this point in the history
Related to remarkjs/remark#383.
Closes GH-59.

Reviewed-by: Junyoung Choi <fluke8259@gmail.com>
Reviewed-by: Titus Wormer <tituswormer@gmail.com>
  • Loading branch information
ChristianMurphy authored and wooorm committed Jul 12, 2019
1 parent 55c4a38 commit 4cc7a7c
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 37 deletions.
89 changes: 53 additions & 36 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,52 @@ import {VFile, VFileContents, VFileOptions} from 'vfile'
import vfile = require('vfile')

declare namespace unified {
interface Processor {
interface Processor<P = Settings> {
/**
* @returns New unfrozen processor which is configured to function the same as its ancestor. But when the descendant processor is configured in the future it does not affect the ancestral processor.
*/
(): Processor
(): Processor<P>

/**
* Configure the processor to use a plugin and optionally configure that plugin with options.
*
* @param plugin unified plugin
* @param options Configuration for plugin
* @param extraOptions Additional configuration for plugin
* @param settings Configuration for plugin
* @param extraSettings Additional configuration for plugin
* @returns The processor on which use is invoked
*/
use<T = Settings, S = undefined>(
plugin: Plugin<T, S>,
options?: T,
extraOptions?: S
): Processor
use<S = Settings, S2 = undefined>(
plugin: Plugin<S, S2, P>,
settings?: S,
extraSettings?: S2
): Processor<P>

/**
* @param preset `Object` with an optional plugins (set to list), and/or an optional settings object
* @param preset `Object` with an plugins (set to list), and/or an optional settings object
*/
use(preset: Preset): Processor
use(preset: Preset<P>): Processor<P>

/**
* @param pluginTuple pairs, plugin and options in an array
* @param pluginTuple pairs, plugin and settings in an array
*/
use<T = Settings>(pluginTuple: PluginTuple<T>): Processor
use<S = Settings>(pluginTuple: PluginTuple<S, P>): Processor<P>

/**
* @param pluginTriple plugin, options, and extraOptions in an array
* @param pluginTriple plugin, setting, and extraSettings in an array
*/
use<T = Settings, S = undefined>(
pluginTriple: PluginTriple<T, S>
): Processor
use<S = Settings, S2 = undefined>(
pluginTriple: PluginTriple<S, S2, P>
): Processor<P>

/**
* @param list List of plugins, presets, and pairs
*/
use(list: PluggableList): Processor
use(list: PluggableList<P>): Processor<P>

/**
* @param processorSettings Settings passed to processor
*/
use(processorSettings: ProcessorSettings<P>): Processor<P>

/**
* Parse text to a syntax tree.
Expand Down Expand Up @@ -146,7 +151,7 @@ declare namespace unified {
* @param value Value to set. Omit if getting key
* @returns If setting, the processor on which data is invoked
*/
data(key: string, value: any): Processor
data(key: string, value: any): Processor<P>

/**
* Freeze a processor. Frozen processors are meant to be extended and not to be configured or processed directly.
Expand All @@ -157,29 +162,41 @@ declare namespace unified {
*
* @returns The processor on which freeze is invoked.
*/
freeze(): Processor
freeze(): Processor<P>
}

type Plugin<T = Settings, S = undefined> = Attacher<T, S>
type Plugin<S = Settings, S2 = undefined, P = Settings> = Attacher<S, S2, P>
type Settings = {
[key: string]: unknown
}
/**
* Presets provide a potentially sharable way to configure processors.
* They can contain multiple plugins and optionally settings as well.
*/
interface Preset {
plugins?: PluggableList
settings?: Settings
interface Preset<S = Settings, P = Settings> {
plugins: PluggableList<P>
settings?: S
}

/**
* Settings can be passed directly to the processor
*/
interface ProcessorSettings<S = Settings> {
settings: S
}
type PluginTuple<T = Settings> = [Plugin<T>, T]
type PluginTriple<T = Settings, S = undefined> = [Plugin<T, S>, T, S]
type Pluggable<T = Settings, S = undefined> =
| Plugin<T>

type PluginTuple<S = Settings, P = Settings> = [Plugin<S, undefined, P>, S]
type PluginTriple<S = Settings, S2 = undefined, P = Settings> = [
Plugin<S, S2, P>,
S,
S2
]
type Pluggable<S = Settings, S2 = undefined, P = Settings> =
| Plugin<S, S2, P>
| Preset
| PluginTuple<T>
| PluginTriple<T, S>
type PluggableList = Array<Pluggable<any, any>>
| PluginTuple<S, P>
| PluginTriple<S, S2, P>
type PluggableList<P = Settings> = Array<Pluggable<any, any, P>>

/**
* An attacher is the thing passed to `use`.
Expand All @@ -188,12 +205,12 @@ declare namespace unified {
* Attachers can configure processors, such as by interacting with parsers and compilers, linking them to other processors, or by specifying how the syntax tree is handled.
*
* @this Processor context object is set to the invoked on processor.
* @param options Configuration
* @param extraOptions Secondary configuration
* @param settings Configuration
* @param extraSettings Secondary configuration
* @returns Optional.
*/
interface Attacher<T = Settings, S = undefined> {
(this: Processor, options?: T, extraOptions?: S): Transformer | void
interface Attacher<S = Settings, S2 = undefined, P = Settings> {
(this: Processor<P>, settings?: S, extraSettings?: S2): Transformer | void
}

/**
Expand Down Expand Up @@ -239,5 +256,5 @@ declare namespace unified {
/**
* Object describing how to process text.
*/
declare function unified(): unified.Processor
declare function unified<P = unified.Settings>(): unified.Processor<P>
export = unified
33 changes: 32 additions & 1 deletion types/unified-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ processor.use(pluginWithTwoSettings, processor, settings)
// $ExpectError
processor.use([pluginWithTwoSettings, processor, settings])

// $ExpectError
processor.use({})
// $ExpectError
processor.use(false)
// $ExpectError
Expand All @@ -133,7 +135,6 @@ processor.use({
plugins: {foo: true}
})

processor.use({})
processor.use({
plugins: []
})
Expand Down Expand Up @@ -249,3 +250,33 @@ unknownValue = processor.data().randomKey
* processor.freeze
*/
processor = processor.freeze()

/**
* Language specific processors
*/
interface RemarkSettings {
gfm: boolean
}

const remark = unified<RemarkSettings>()
.use(() => {})
.freeze()
remark
.use({settings: {gfm: true}})
// $ExpectError
.use({settings: {dne: true}})
remark
// $ExpectError
.use({settings: {dne: true}})
.use({settings: {gfm: true}})
remark.use(function() {
this
// $ExpectError
.use({settings: {dne: true}})
.use({settings: {gfm: true}})
this.use({settings: {gfm: true}})
// $ExpectError
.use({settings: {dne: true}})
})
// $ExpectError
remark.use({})

0 comments on commit 4cc7a7c

Please sign in to comment.