Skip to content

refactor(plugins): share small AST helper utilities#1754

Merged
james-elicx merged 3 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/ast-utils
Jun 5, 2026
Merged

refactor(plugins): share small AST helper utilities#1754
james-elicx merged 3 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/ast-utils

Conversation

@NathanDrake2406

Copy link
Copy Markdown
Contributor

Extracts small AST syntax helpers shared by AST-heavy plugin transforms.

PR #1736 has duplicated logic for loose AST node handling, child traversal, binding-pattern extraction, and Identifier/Literal name extraction. This PR lands the small shared utility first so #1736 can rebase onto it without carrying those local copies.

Validation:

  • vp test run tests/import-meta-url.test.ts tests/optimize-imports.test.ts tests/remove-console.test.ts
  • vp check

@pkg-pr-new

pkg-pr-new Bot commented Jun 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1754

commit: b608047

AST-heavy plugin transforms currently duplicate loose node guards, child traversal, binding-pattern extraction, and Identifier or Literal name extraction. That makes PR 1736 larger than it needs to be and increases the chance that each transform grows its own slightly different syntax helpers.

Extract the small parser-shape helpers into a local plugin utility module and migrate import-meta-url plus optimize-imports to use it. Scope-aware transform logic remains local to each plugin, so this preserves runtime behaviour while creating a narrow reuse point for the dynamic-request transform.
@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, low-risk extraction. I verified the validation steps locally: vp test run tests/import-meta-url.test.ts tests/optimize-imports.test.ts tests/remove-console.test.ts (117 passed) and vp check on the three files (format/lint/types clean).

I traced the subtle semantic changes between the old local helpers and the new shared ones, and they are behaviorally equivalent (or slightly more correct) for the real AST shapes produced by Vite's parseAst:

  • isNodeLikeisAstRecord: stricter — old returned true for any non-null, non-array object; new requires a string type. All call sites pass real AST nodes (which have type), and the recursive walkers (visit, recordReads) bail on non-nodes anyway, so the effective result is unchanged.
  • forEachAstChild adds "parent" to the skip set: a behavior change vs. the old inline loops, but defensive and harmless since parseAst (oxc) ASTs don't carry parent backrefs. Nice guard against accidental cycles.
  • findDirectivePrologueEnd hashbang: good catch keeping the raw typeof ... === "object" + Reflect.get check here rather than isAstRecord(hashbang). A hashbang node may not have a string type, so isAstRecord would have regressed this. Worth a one-line comment so a future cleanup doesn't "simplify" it back into isAstRecord.
  • astNamegetAstName: stricter (now requires type === "Identifier" for the name path) but equivalent for ESTree Identifier/Literal/StringLiteral shapes the callers actually receive.

No leftover references to the removed NodeLike/isNodeLike/astName helpers anywhere in the source. Approving in spirit — only optional nits below.

end: number;
};

const SKIP_CHILD_KEYS = new Set(["type", "parent", "loc", "start", "end"]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: the old inline walkers in import-meta-url.ts skipped only type/start/end/loc and did not skip parent. Adding parent here is a safe, defensive change (avoids cycles if a parent backref ever exists), but it is a behavioral difference worth a short comment so the intent is clear, e.g. // skip backrefs + positional metadata.

return isAstRecord(value) && value.type === "Identifier" && value.name === name;
}

export function getAstName(value: unknown): string | null {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAstName is intentionally stricter than the old astName (which returned node.name whenever it was defined, regardless of node type). For the real Identifier/Literal/StringLiteral shapes the callers pass, this is equivalent. Just flagging the change so it's a conscious decision rather than an accidental tightening.

@ask-bonk

ask-bonk Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review posted successfully. Here's my summary of the review for PR #1754:

Review Summary

Verdict: Approve in spirit — clean, low-risk refactor.

The PR extracts duplicated AST helper utilities into a shared ast-utils.ts module so PR #1736 can rebase onto it. I verified locally:

  • All 117 validation tests pass (import-meta-url, optimize-imports, remove-console)
  • vp check clean (format/lint/types) on all three files
  • No leftover references to the removed NodeLike/isNodeLike/astName helpers

I traced each subtle semantic change between the old local helpers and the new shared ones, confirming behavioral equivalence (or slight improvement) for the real AST shapes from Vite's parseAst:

  1. isNodeLikeisAstRecord — stricter (requires string type), but all call sites pass real AST nodes and the walkers bail on non-nodes regardless. Effectively unchanged.
  2. forEachAstChild now skips parent — defensive guard against cycles; harmless since oxc ASTs lack parent backrefs.
  3. findDirectivePrologueEnd hashbang — the author correctly kept a raw typeof/Reflect.get check instead of isAstRecord(hashbang), which would have regressed (hashbang nodes may lack a string type).
  4. astNamegetAstName — stricter but equivalent for the Identifier/Literal shapes callers pass.

Posted 2 optional nits (suggesting clarifying comments on the parent skip and the getAstName tightening) — no blockers.

github run

@NathanDrake2406

Copy link
Copy Markdown
Contributor Author

@james-elicx time for a 4th shard?

@james-elicx

Copy link
Copy Markdown
Member

@james-elicx time for a 4th shard?

yeah i was thinking about that as well, lets do it

@james-elicx james-elicx merged commit 5ce51a1 into cloudflare:main Jun 5, 2026
30 checks passed
@NathanDrake2406

Copy link
Copy Markdown
Contributor Author

@james-elicx time for a 4th shard?

yeah i was thinking about that as well, lets do it

on it

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.

2 participants