-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add plugin tree type parameter #156
Conversation
This allows plugins to define what type of tree they can process in their transformer. It does not prevent misuse of those plugins (such as a rehype plugin and then a remark plugin). Nor does it handle parser or compiler plugins.
This allows plugins to define what type of tree they yield in their transformer. It does not prevent misuse of those plugins (such as a rehype plugin and then a remark plugin). Nor does it handle parser or compiler plugins.
/cc @remcohaszing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 I like the idea of checking input and output AST types. 👍
In an ideal world, unified could do chained type checking, ensuring the output of a transform aligns with the input of the next transform.
parser and compiler plugins will always be a bit funky since their order is independent of transformers, but checking just the transformers could add value. 👍
This seems to be a bit different from what is described above though.
It allows Plugins
to set a generic for their input and output, but this doesn't appear to be checked by unified to ensure the unified.use
chain is valid?
Is this intended? (from your description it appear so?) If so, could you expand a bit on the value of adding the generics if they are not checked by unified.use
?
Correct.
So that plugins can start defining what input/output they receive/yield.
A future idea I have:
Perhaps this could be includes in this PR. The last step is checking whether the
Though, most people will probably chain? |
Follow up question on this added at syntax-tree/unist-util-visit-parents#10 (comment)
A fair point.
That's what I was thinking as well
Each of these |
Working on it! |
This comment has been minimized.
This comment has been minimized.
I’ve implemented parsers, compilers, etc for plugins. Now they:
I have not implemented errors of some sort when misconfiguring plugins.
I’m quite okay with not throwing on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is fine to only define transform plugins on a processor
I'm not sure I follow, there is a CurrentTree
which I assume is the current tree, as output the most recent plugin?
We can assert that it should match the Input
of the next Plugin?
several parser plugins / several compiler plugins could be used, overwriting what came before
This impacts parsers and compilers, but transformers should still be fine?
code is already used a lot in the wild
It is, but these errors would be things which never worked
the complexity of it all
Most of the complexity seems to come from conditionals to infer, would matching the generics add a lot more complexity?
index.d.ts
Outdated
type UsePlugin< | ||
ParseTree extends Node, | ||
CurrentTree extends Node, | ||
CompileTree extends Node, | ||
CompileResult, | ||
Input, | ||
Output | ||
> = Output extends Node | ||
? Input extends string | ||
? // If `Input` is `string` and `Output` is `Node`, then this plugin | ||
// defines a parser, so set `ParseTree`. | ||
// This also assumes that this is the first plugin (so no current tree is | ||
// or compile tree is defined yet), so set those too. | ||
Processor<Output, Output, Output, 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<ParseTree, Output, CompileTree, 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<ParseTree, CurrentTree, 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<ParseTree, CurrentTree, CompileTree, CompileResult> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting use of inference to extract generics.
Is this approach used to overcome quirks in JSDoc generics?
Are there other benefits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How else could it be done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would Plugin<Input, Output = Input>
work?
Letting TS infer when a transformer is provided, otherwise pass through the input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that’s done in:
export type Plugin<
PluginParameters extends any[] = any[],
Input = Node,
Output = Input
> = (
And the use
overloads:
use<
PluginParameters extends any[] = any[],
Input = Specific<Node, CurrentTree>,
Output = Input
>
…but this conditional makes a processor, based on how one is currently configured, and applying those Input
/ Output
from a plugin
export type Pluggable<PluginParameters extends any[] = any[]> = | ||
| PluginTuple<PluginParameters> | ||
| Plugin<PluginParameters> | ||
| PluginTuple<PluginParameters, any, any> | ||
| Plugin<PluginParameters, any, any> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense/add value to carry the generics through this type as well, rather then any
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I’ll try!
We could, but existing plugins can be untyped, not typed with input/output, or implicit. I think it’ll cause way to many errors. I’ll check again but what I tried yesterday resulted in 100s of errors in this code base
Wrong code never worked (rehype plugin before remark plugin without rehype-remark), but I’m not worried about that. |
* Clean `VFileWithOutput` * Add generics to `Parser`, `Compiler` * Smarter defaults for `ParserTree`, `CurrentTree`, `CompilerTree` * Be smarter about parse/compile trees if only parse/compile plugins are used * Add smarter type of `this` in parse/transform/compile plugins. * Add docs for type parameters
Made the types smarter. |
Co-authored-by: Christian Murphy <[email protected]>
Initial checklist
Description of changes
This allows plugins to define what type of tree they receive and yield in their transformer.
It does not prevent misuse of those plugins (such as a rehype plugin and then a remark plugin). Nor does it handle parser or compiler plugins.