Skip to content

fix(migrate): ctx codemod no longer rewrites inside strings and comments - #511

Open
JonasJesus42 wants to merge 1 commit into
mainfrom
fix/ctx-compat-string-guard
Open

fix(migrate): ctx codemod no longer rewrites inside strings and comments#511
JonasJesus42 wants to merge 1 commit into
mainfrom
fix/ctx-compat-string-guard

Conversation

@JonasJesus42

@JonasJesus42 JonasJesus42 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Context

The transformCtxCompat codemod (Fresh → TanStack migration) turns ctx reads into optional chains, so that
an app not configured on the target site yields undefined instead of throwing — without it,
withSectionLoader swallows the exception and the section renders blank.

It walks the file character by character, with no notion of string or comment boundaries. The result is
that a ctx. appearing as text was rewritten just like one appearing as code:

console.log('ctx.device:', ctx.device);
// becomes
console.log('ctx?.device:', ctx?.device);

The call site is patched correctly and the message next to it is silently corrupted. And it compiles, so
nothing catches it — not the build, not typecheck, not tests. Same for double-quoted strings, line and block
comments, and template literal text.

No test covered this.

What changes

A literal/comment guard in the scan loop. On //, /* */, '…' or "…", the span is copied verbatim
instead of scanned.

Template literals are not skipped wholesale, because their interpolations are real code. They are tracked
with a small context stack, so:

`${ctx.vtex.account}`      `${ctx?.vtex?.account}`   // code: rewritten
`ctx.device`               `ctx.device`              // text: preserved

This also covers nested templates (`a${`b${ctx.device}`}c`) and escaped quotes inside strings.

Regex literals are deliberately left out, and the reason is in a code comment: telling /re/ from a
division requires real parsing, and guessing wrong would make the scanner consume live code. A ctx. inside a
regex in a loader is vanishingly rare next to that risk.

Why not ts-morph

blocks-cli already depends on ts-morph, and rewriting via AST would eliminate the entire bug class rather
than the cases we happened to think of. I didn't do it here because the scanner carries subtle, already-tested
logic (isAssignTarget, already-optional chains, balanced computed access), and the rewrite would carry
regression risk out of proportion to the bug. Noting it as a known path if the scanner needs to grow again.

Validation

Five regression tests. Four of them fail against the previous implementation — verified by reverting the
source and re-running, so the tests aren't decorative:

× does not rewrite ctx inside string literals
× does not rewrite ctx inside double-quoted strings or escaped quotes
× does not rewrite ctx inside comments
× does not rewrite template text but DOES rewrite ${} interpolation

13 tests in the file (8 existing + 5 new), all passing. Full suite at 2589 passing; the 4 draft preview
failures are pre-existing on main. Lint and typecheck clean.

Provenance

Found by a /code-review that announced itself as being for #510. It isn't: #510 had 16 files and none of
them was ctx-compat.ts (checked via the GitHub API). The review most likely ran against another workspace's
local diff. The bug is real and independent of any PR, hence this going separately.

🤖 Generated with Claude Code

…comments

`transformCtxCompat` walks the file character by character with no notion of
string or comment boundaries, so a `ctx.` occurring as *text* was rewritten the
same as one occurring as code:

    console.log('ctx.device:', ctx.device);
 -> console.log('ctx?.device:', ctx?.device);

The call site is patched correctly and the message next to it is silently
corrupted — it still compiles, so nothing catches it. Same for double-quoted
strings, line and block comments, and template literal text.

Adds a literal/comment guard to the scan loop. Template literals are tracked
with a small context stack rather than skipped wholesale, because their `${}`
interpolations ARE code: `` `${ctx.vtex.account}` `` must still be rewritten
while `` `ctx.device` `` must not.

Regex literals are deliberately left untracked, and the reason is in a comment:
telling `/re/` from division requires real parsing, and guessing wrong would
consume live code. A `ctx.` inside a regex in a loader is vanishingly rare next
to that risk.

Five regression tests, four of which fail against the previous implementation
(verified by reverting the source and re-running).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JonasJesus42
JonasJesus42 requested a review from a team August 27, 2026 21:42
@JonasJesus42 JonasJesus42 changed the title fix(migrate): codemod ctx não reescreve mais dentro de strings e comentários fix(migrate): ctx codemod no longer rewrites inside strings and comments Aug 28, 2026
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