Found while evaluating 0.24.0-rc.3 in an adopter project.
Summary
repin_npm() writes with JSON.stringify(p, null, 2). For any manifest not already 2-space indented, this reformats every line — turning a one-line version bump into a whole-file diff.
Observed
- A tab-indented
package.json: 100 changed lines (50− / 50+) for a single dependency pin.
- A second manifest had a
\u2192 escape inside its description string unescaped to a literal →. Semantically identical JSON, but another spurious diff line.
Why it matters
The function already recognises this hazard. Its own comment reads:
Only WRITE when something changed. JSON.stringify normalizes indentation and line endings, so an unconditional write reformats a file it had no reason to touch — a whole-file diff (LF over CRLF) on a manifest with no vendor deps.
The guard built from that reasoning is if (n) plus CRLF/LF preservation. But the same normalization applies to indentation and \uXXXX escapes inside a file that legitimately does change, and neither is preserved.
The practical cost lands on the rule the tooling is trying to enforce. link warns "never commit what this writes to a tracked file" — but a reviewer cannot spot the one pin among 50 reformatted lines, so the reformat actively works against the review that is supposed to catch an accidental commit. It also churns any repo whose formatter disagrees with 2-space JSON.
Suggested fix
Skip the parse/serialize round-trip on write. A targeted textual substitution over the raw source preserves the file byte-for-byte apart from the version literals:
- find
"<scope>/<pkg>": "<version>" within the dependency blocks, replace only the version literal.
If a structural edit is preferred, detect the file's existing indentation (tabs vs n spaces, from the first indented line) and reuse it, and re-escape non-ASCII to match the input.
Found while evaluating
0.24.0-rc.3in an adopter project.Summary
repin_npm()writes withJSON.stringify(p, null, 2). For any manifest not already 2-space indented, this reformats every line — turning a one-line version bump into a whole-file diff.Observed
package.json: 100 changed lines (50− / 50+) for a single dependency pin.\u2192escape inside itsdescriptionstring unescaped to a literal→. Semantically identical JSON, but another spurious diff line.Why it matters
The function already recognises this hazard. Its own comment reads:
The guard built from that reasoning is
if (n)plus CRLF/LF preservation. But the same normalization applies to indentation and\uXXXXescapes inside a file that legitimately does change, and neither is preserved.The practical cost lands on the rule the tooling is trying to enforce.
linkwarns "never commit what this writes to a tracked file" — but a reviewer cannot spot the one pin among 50 reformatted lines, so the reformat actively works against the review that is supposed to catch an accidental commit. It also churns any repo whose formatter disagrees with 2-space JSON.Suggested fix
Skip the parse/serialize round-trip on write. A targeted textual substitution over the raw source preserves the file byte-for-byte apart from the version literals:
"<scope>/<pkg>": "<version>"within the dependency blocks, replace only the version literal.If a structural edit is preferred, detect the file's existing indentation (tabs vs n spaces, from the first indented line) and reuse it, and re-escape non-ASCII to match the input.