-
-
Notifications
You must be signed in to change notification settings - Fork 7
Support for async visitors #8
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Can you go a level higher? What do you practically want to do? |
I'm doing a live preview. I find |
I was thinking about it just a little bit more and a workaround could look like this: async function transform( tree ) {
const matches = [];
visit( tree, 'code', visitor )
function visitor( node, index, parent ) {
matches.push({ node, index, parent })
}
const promises = matches.map( async match => {
const filename = await someAsyncStuff( match.node.value )
// ... modify tree ...
})
await Promise.all( promises );
return tree;
} But I'm not sure, how exactly does |
I think your new example is indeed (almost) the way to go: otherwise, you would do a waterfall. You’d only be able to process serially, but with that new approach, you can send out all requests immediately! The point about the indexes is true! But you can work around that: you need the parent, and the node. And then you can do |
Of course one can do import visit from 'unist-util-visit'
export async function visitAsync( tree, matcher, asyncVisitor )
{
const matches = [];
visit( tree, matcher, (...args) => { matches.push( args ); return tree } )
const promises = matches.map( match => asyncVisitor( ...match ) )
await Promise.all( promises );
return tree;
} Example usage: await visitAsync( tree, 'code', visitor )
async function visitor( node, index, parent )
{
// ! Note: Index is not guaranteed, in case of adding/removing nodes
// ... async stuff here ...
} Thanks for your assistance! |
If you’re making this into a utility, then it would make sense to correct I think this is the solution to go with, no need to change this project. Thanks! |
You cannot really correct the I'm not going to make it into utility, but please feel free to do it yourself. |
Oh, right, I was thinking this would somehow run the promises serially instead of all at once, in which case If you can’t make visit( tree, matcher, (node, _, parent) => { matches.push([node, parent]); } ) Btw |
You are right about |
The last part is weird! The return value of a node isn’t handled: https://github.com/syntax-tree/unist-util-visit-parents#returns 🤔 Maybe your arrow function was without braces, with push, and thus return an index ( |
That is exactly what happened. I wasn't aware of that behavior. Thank you! |
any updates about async visitor? it is a big need! |
No, it is not needed. It is an anti pattern and it is very slow. |
UPDATE: Came across the The key is to call I'll try and write something up and make a contribution to improve the docs. It's not clear to me how one transforms a doc using async code. I need to process markdown docs in order to translate code blocks. Given there isn't a plugin to do this I'm trying to write one. In the transformer plugin I need to do something like How do I solve for this? I'm probably missing something about how unified works. const myvisitor = (node: any) => {
if (node.lang && node.lang == 'X') {
// run async code to convert from X to Y
// set node.value to Y
} else if (node.lang == null) {
node.lang = 'shell-session'
}
//console.log(node);
//console.log("====");
}
export const mycodeconverterPlugin = (_options: any, _set: any) => {
return (tree: any, _file: any) => {
visit(tree, 'code', myvisitor);
};
}
const buffer = fs.readFileSync('something-file.markdown');
const s = await remark()
.use([mycodeconverterPlugin])
.process(buffer); |
@janaka You don’t need As for how to do async work, split the work up in two stages: a) find things, b) transform things. Such as like this: /**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Code} Code
*/
import fs from 'node:fs/promises'
import {remark} from 'remark'
import {visit} from 'unist-util-visit'
const file = await remark()
.use(remarkConvertMyCode)
.process(await fs.readFile('something-file.markdown'))
console.log(String(file))
/**
* @type {import('unified').Plugin<[], Root>}
*/
function remarkConvertMyCode() {
return async function (tree) {
/** @type {Array<Code>} */
const codes = []
visit(tree, (node) => {
if (node.type === 'code') {
codes.push(node)
}
})
/** @type {Array<Promise<void>>} */
const promises = []
for (const code of codes) {
if (code.lang && code.lang === 'X') {
// Run async code to convert from X to Y
// set node.value to Y
promises.push(somePromise)
} else if (code.lang === null) {
code.lang = 'shell-session'
}
}
await Promise.all(promises)
}
} |
@wooorm ah, I see, cool. I was close but can clean up my code. Thanks for the example. |
Thank you @wooorm. This is a much needed example, it should probably figure on the main README somewhere |
I’m not sure a longer readme will improve this, what I believe to be, a more general “how to do async programming”, question (see my comment “split the work up in two stages: a) find things, b) transform things”) You can also recommend recipes to https://unifiedjs.com/learn/. |
In my opinion, this should be working:
Another (more abstract) example:
Background
Visitor might do:
However, currently, only sync operations are allowed. That's why it cannot be used for async transformers, which should be supported by unified.
The text was updated successfully, but these errors were encountered: