Skip to content
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

Only traverse replaced node after calling .replace() #25

Merged
merged 2 commits into from
May 30, 2024

Conversation

davepagurek
Copy link
Contributor

Resolves #24

I was looking into this a bit and I think the problem was that, when evaluating a conditional, although it would correctly call .replace() with the block that should run, the visitor would continue to visit all children, so the else blocks would step over any #defines in the if block.

I think what we want to have happen here is that if we replace the current node with something else, rather than continuing to visit the old node, the visitor should traverse the reversed node. This would definitely be a behaviour change, but I was thinking that if you wanted to replace but keep traversing the original tree, using exit instead of enter might be a better fit for that already. Let me know if I'm wrong in this assumption though, this changed behaviour could also easily just be an option in visit() instead!

@AndrewRayCode AndrewRayCode self-requested a review May 29, 2024 05:56
@AndrewRayCode
Copy link
Collaborator

AndrewRayCode commented May 30, 2024

I really appreciate the PR! This solves the problem and all the tests pass.

I'm trying to figure out what Babel does with visitors when a node is replaced with path.replaceWith(), but I don't see it explicitly called out in the documentation.

I agree it's a bug that the visitor traverses into the node that was replaced, since that node should no longer be considered part of the AST. It also feels weird that the visitor would visit the replaced node + children. I think that might surprise an end user of the visit() API. I generally try to follow whatever Babel does, I've asked in their Slack what Babel's behavior is here.

I'm merging this PR regardless, but I won't release it yet. Depending on what Babel does, I might open a PR to refactor this to not visit replaced children, and instead manually recursively visit node.ifPart.body, etc in preprocessor.ts with the same visitors, to preprocess those.

A case where I could see a user wants to visit replaced children: Doing something like collecting variable names, and wanting to inject their own variable calls with replaceWith() and still have the visitor visit them to pick them up.

A case where it might be surprising to a user: The visitor does some AST manipulation, deleting nodes, etc, that are then applied to the hand-crafted replacer nodes, which would surprisingly(?) mutate the replacer nodes.

@AndrewRayCode AndrewRayCode merged commit 9c1c4a9 into ShaderFrog:main May 30, 2024
1 check passed
AndrewRayCode added a commit that referenced this pull request May 30, 2024
Refactor proposal of PR #25 and addressing issue #24

The semi-breaking API change here is that if a node is replaced, it
won't be traversed by the existing visitor. Copied from the Github
comment:

I'm trying to figure out what Babel does with visitors when a node is
replaced with path.replaceWith(), but I don't see it explicitly called
out in the documentation.

It feels weird that the visitor would visit the replaced node
+ children. I think that might surprise an end user of the visit() API.
I generally try to follow whatever Babel does, I've asked in their Slack
what Babel's behavior is here.

A case where I could see a user wants to visit replaced children: Doing
something like collecting variable names, and wanting to inject their
own variable calls with replaceWith() and still have the visitor visit
them to pick them up.

A case where it might be surprising to a user: The visitor does some AST
manipulation, deleting nodes, etc, that are then applied to the
hand-crafted replacer nodes, which would surprisingly(?) mutate the
replacer nodes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Preprocessor incorrectly handles #defines in if/else
2 participants