Skip to content

Commit

Permalink
allow plugins to declare an interface for their settings (#58)
Browse files Browse the repository at this point in the history
* allow plugins to declare an interface for their settings

* Adjust pluggable list to support different setting interfaces

* add example of implicitly typed plugin

* support a second generic settings parameter for plugins
  • Loading branch information
ChristianMurphy authored Jul 10, 2019
1 parent 38d2c82 commit a3a20e0
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 15 deletions.
39 changes: 30 additions & 9 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,33 @@ declare namespace unified {
* Configure the processor to use a plugin and optionally configure that plugin with options.
*
* @param plugin unified plugin
* @param options Configuration for plugin]
* @param options Configuration for plugin
* @param extraOptions Additional configuration for plugin
* @returns The processor on which use is invoked
*/
use(plugin: Plugin, options?: unknown): Processor
use<T = Settings, S = undefined>(
plugin: Plugin<T, S>,
options?: T,
extraOptions?: S
): Processor

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

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

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

/**
* @param list List of plugins, presets, and pairs
*/
Expand Down Expand Up @@ -145,7 +160,7 @@ declare namespace unified {
freeze(): Processor
}

type Plugin = Attacher
type Plugin<T = Settings, S = undefined> = Attacher<T, S>
type Settings = {
[key: string]: unknown
}
Expand All @@ -157,9 +172,14 @@ declare namespace unified {
plugins?: PluggableList
settings?: Settings
}
type PluginTuple = [Plugin, Settings]
type Pluggable = Plugin | Preset | PluginTuple
type PluggableList = Pluggable[]
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>
| Preset
| PluginTuple<T>
| PluginTriple<T, S>
type PluggableList = Array<Pluggable<any, any>>

/**
* An attacher is the thing passed to `use`.
Expand All @@ -169,10 +189,11 @@ declare namespace unified {
*
* @this Processor context object is set to the invoked on processor.
* @param options Configuration
* @param extraOptions Secondary configuration
* @returns Optional.
*/
interface Attacher {
(this: Processor, options?: unknown): Transformer | void
interface Attacher<T = Settings, S = undefined> {
(this: Processor, options?: T, extraOptions?: S): Transformer | void
}

/**
Expand Down
3 changes: 2 additions & 1 deletion types/tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"semicolon": false,
"unified-signatures": false,
"whitespace": false,
"interface-over-type-literal": false
"interface-over-type-literal": false,
"no-unnecessary-generics": false
}
}
89 changes: 84 additions & 5 deletions types/unified-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,97 @@ const settings = {
random: 'option'
}

interface ExamplePluginSettings {
example: string
}
const typedPlugin: Plugin<ExamplePluginSettings> = function() {}
const typedSetting = {example: 'example'}

const implicitlyTypedPlugin = (settings?: ExamplePluginSettings) => {}

const pluginWithTwoSettings = (
processor?: Processor,
settings?: ExamplePluginSettings
) => {}

processor.use(plugin)
processor.use(plugin).use(plugin)
processor.use(plugin, settings)
processor.use([plugin, plugin])
processor.use([plugin])
processor.use([plugin, settings])
processor.use([[plugin, settings], [plugin, settings]])

processor.use(typedPlugin)
processor.use(typedPlugin).use(typedPlugin)
processor.use(typedPlugin, typedSetting)
processor.use([typedPlugin, typedSetting])
processor.use([[typedPlugin, typedSetting], [typedPlugin, typedSetting]])
processor.use([[plugin, settings], [typedPlugin, typedSetting]])
processor.use([typedPlugin])

processor.use(implicitlyTypedPlugin)
processor.use(implicitlyTypedPlugin).use(implicitlyTypedPlugin)
processor.use(implicitlyTypedPlugin, typedSetting)
processor.use([implicitlyTypedPlugin, typedSetting])
processor.use([
[implicitlyTypedPlugin, typedSetting],
[implicitlyTypedPlugin, typedSetting]
])
processor.use([[plugin, settings], [implicitlyTypedPlugin, typedSetting]])
processor.use([implicitlyTypedPlugin])

// NOTE: settings overrides the generic undefined
// settings value will be unused but TypeScript will not warn
processor.use(implicitlyTypedPlugin, typedSetting, settings)
processor.use([implicitlyTypedPlugin, typedSetting, settings])

processor.use(pluginWithTwoSettings)
processor.use(pluginWithTwoSettings).use(pluginWithTwoSettings)
processor.use(pluginWithTwoSettings, processor, typedSetting)
processor.use(pluginWithTwoSettings, processor)
processor.use([pluginWithTwoSettings, processor, typedSetting])
processor.use([pluginWithTwoSettings, processor])
processor.use([
[pluginWithTwoSettings, processor, typedSetting],
[pluginWithTwoSettings, processor, typedSetting]
])
processor.use([
[plugin, settings],
[pluginWithTwoSettings, processor, typedSetting]
])
processor.use([pluginWithTwoSettings])

// $ExpectError
processor.use(typedPlugin, settings)
// $ExpectError
processor.use([typedPlugin, settings])
// $ExpectError
processor.use(typedPlugin, typedSetting, settings)
// $ExpectError
processor.use([typedPlugin, typedSetting, settings])

// $ExpectError
processor.use(implicitlyTypedPlugin, settings)
// $ExpectError
processor.use([implicitlyTypedPlugin, settings])

// $ExpectError
processor.use(pluginWithTwoSettings, typedSetting)
// $ExpectError
processor.use(pluginWithTwoSettings, typedSetting)

// $ExpectError
processor.use(pluginWithTwoSettings, processor, settings)
// $ExpectError
processor.use([pluginWithTwoSettings, processor, settings])

// $ExpectError
processor.use(false)
// $ExpectError
processor.use(true)
// $ExpectError
processor.use('alfred')
processor.use(plugin, settings)
processor.use([plugin, plugin])
processor.use([plugin])
processor.use([plugin, settings])
processor.use([[plugin, settings], [plugin, settings]])
// $ExpectError
processor.use([false])
// $ExpectError
Expand All @@ -54,6 +132,7 @@ processor.use({
// $ExpectError
plugins: {foo: true}
})

processor.use({})
processor.use({
plugins: []
Expand Down

0 comments on commit a3a20e0

Please sign in to comment.