Conversation
`genString(input, { singleQuotes: true })` ran `escapeString` over the
result of `JSON.stringify`, so every escape the JSON pass had already
produced was escaped a second time. The generated literal no longer
evaluates back to the input:
genString("a\nb", { singleQuotes: true })
// 'a\\nb' -> evaluates to "a\nb" (literal backslash + n)
The same applies to `\\`, `\t`, `\u0000` and any other escape. It is
worse than a wrong string: `escapeString` also escapes characters that
carry no meaning behind a backslash, so a plain space becomes `\ `,
which evaluates to nothing at all and silently drops the character.
The two passes cannot be composed, because after `JSON.stringify` a
backslash is no longer a literal backslash — it is the start of an
escape sequence. Instead of escaping twice, convert the JSON body to a
single-quoted body: `\"` no longer needs escaping, `'` now does, and
every other escape sequence is already valid and is copied verbatim.
While here, escape U+2028/U+2029 on both paths. `JSON.stringify` leaves
them raw, so the double-quoted output was not safe to embed in JSON;
the single-quoted path escaped them already.
Existing expectations in `test/string.test.ts` encoded the doubled
output (`'foo\\\\nbar'`) and are corrected. Added cases for backslash,
tab, a backslash followed by a quote, and the line separators, plus a
round-trip test asserting each generated literal evaluates back to the
input it was generated from — the property that was broken.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthrough
ChangesString literal escaping
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to This localized change corrects generated string escaping and adds round-trip coverage; no actionable merge-blocking risk remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Problem
genString(input, { singleQuotes: true })escapes its input twice:The generated literal no longer evaluates back to the input it was generated from:
singleQuotes: trueoutputa\nb(newline)'a\\nb'a\nb— literal\+na\\b(backslash)'a\\\\b'a\\b— two backslashesa\tb(tab)'a\\tb'a\tb— literal\+ta b(space)'a\ b'ab— the space is goneThe last row is the worst case.
escapeStringescapes characters that carry no meaning behind a backslash, and in a JS string literal\<char>for an unknown<char>is just<char>— so\collapses to a space in some positions and the character disappears in others. A codegen helper silently emitting a different string than it was asked to is a hard bug to trace back to its source.Why the two passes cannot be composed
After
JSON.stringify, a backslash in the buffer is no longer a literal backslash — it is the opening character of an escape sequence.escapeStringcannot tell the two apart, so it re-escapes payloads that were already correct.Instead of escaping twice, the fix converts the JSON body into a single-quoted body. Only two things differ between the two literal forms:
\"no longer needs escaping → emitted as"'now needs escaping → emitted as\'Everything else (
\\,\n,\uXXXX, …) is already a valid escape sequence in a single-quoted literal and is copied verbatim. The scan steps over escape sequences so their payload is never re-examined.escapeStringitself is unchanged and remains exported.Also fixed: U+2028 / U+2029
JSON.stringifyleaves U+2028 and U+2029 raw. They are legal in ES2019+ string literals but not in JSON, so the double-quoted output was not safe to embed in a JSON document. The old single-quoted path escaped them viaescapeString; both paths now do.Tests
The existing fixtures in
test/string.test.tsencoded the doubled output ([`foo\nbar`, `"foo\\nbar"`, `'foo\\\\nbar'`]) and are corrected to the values that actually round-trip. Added fixtures for a lone backslash, a tab, a backslash followed by a quote, and both line separators.Added a third suite asserting the property the bug violated — that every generated literal evaluates back to the string it was generated from — across both quote styles.
I also fuzzed the change locally (not committed) over 400k random strings drawn from
a ' " \ \n \r \t \u2028 \u2029 \0 \b \u0001 é 😀, evaluating each literal and comparing to the input:Validation
Summary by CodeRabbit
Bug Fixes
Tests