Skip to content

Stop Oxc::Node from shadowing the ESTree fields it reads - #5

Merged
marcoroth merged 1 commit into
mainfrom
node-field-access
Aug 26, 2026
Merged

Stop Oxc::Node from shadowing the ESTree fields it reads#5
marcoroth merged 1 commit into
mainfrom
node-field-access

Conversation

@marcoroth

@marcoroth marcoroth commented Aug 26, 2026

Copy link
Copy Markdown
Owner

This pull request fixes three fields that Oxc::Node was hiding behind methods of its own, and teaches a field to answer to its snake_case name.

A node reads a field through method_missing, so every method it defines shadows a field called the same thing. Three of them did, and each answered something plausible instead of raising.

Oxc.parse("let count = 0").root.every("Identifier").first.name
#=> "identifier"

Identifier#name is the one that matters, since it is the most common read in any codemod. JSXElement#children answered the opening and closing elements instead of what the element wraps, and ImportDeclaration#attributes answered the wrapper's own hash instead of the import's with clause. Nothing raised in any of the three, so the wrong answer just flowed onward.

What makes a name safe

ESTree spells its fields in camelCase or as a single lowercase word, so a method name carrying an underscore can never collide with one.

node.underscored_type   # was `name`
node.child_nodes        # was `children`
node.to_h               # `attributes` is now protected

Checking a candidate name against @oxc-project/types overreports. It declares parent on all 121 interfaces and the serializer emits it on none of them, so the only reliable check is what a parse actually returns.

Reading a field the Ruby way

Since ESTree is camelCase throughout, reading an AST used to mean writing JavaScript casing in Ruby. A field now answers to either spelling.

node.type_annotation.type_annotation.type_name.name
#=> "Thing"

root.source_type
#=> "module"

declaration.super_class.name
#=> "B"

respond_to? answers for both names. An unknown field still raises NoMethodError, and a method the walker defines still wins, since method_missing never sees it.

@marcoroth
marcoroth marked this pull request as ready for review August 26, 2026 04:53
@marcoroth
marcoroth merged commit 5f4aaca into main Aug 26, 2026
11 checks passed
@marcoroth
marcoroth deleted the node-field-access branch August 26, 2026 04:57
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.

1 participant