Skip to content

fix: preserve blank line after inline block comment in stringify#65

Open
spokodev wants to merge 1 commit into
kaelzhang:masterfrom
spokodev:fix/blank-line-after-inline-block-comment
Open

fix: preserve blank line after inline block comment in stringify#65
spokodev wants to merge 1 commit into
kaelzhang:masterfrom
spokodev:fix/blank-line-after-inline-block-comment

Conversation

@spokodev

Copy link
Copy Markdown

The bug

A blank line that follows an inline block comment (/* ... */) placed after a comma is silently dropped by stringify. The identical construct with a line comment (// ...) round-trips correctly.

const {parse, stringify} = require('comment-json')

// inline BLOCK comment after the comma, then a blank line:
const input = '{\n  "a": 1, /* x */\n\n  "b": 2\n}'
stringify(parse(input), null, 2) === input
// false -- the blank line before "b" is lost

// CONTROL: identical shape but a LINE comment round-trips fine:
const ctrl = '{\n  "a": 1, // x\n\n  "b": 2\n}'
stringify(parse(ctrl), null, 2) === ctrl
// true

It also reproduces in arrays ([\n 1, /* x */\n\n 2\n]) and collapses multiple blank lines to zero.

Why it is a stringify defect

For both the line- and block-comment inputs, parse produces the same structure. The only difference is the comment type; the BlankLine token in before:b is captured identically in both. Yet it renders only in the line-comment case, so the loss happens purely in stringify.

Root cause

In join(), the one && two branch did:

one + two.trim() + LF + gap

Here two is the rendered before:<key> slot and carries the blank-line count as leading line breaks. two.trim() discards them, and the branch hardcodes a single LF + gap. The line-comment case survives only because process_comments appends a trailing LF after a LineComment, which lands the missing break in one by coincidence; the block-comment case has no such trailing LF, so the blank line is lost.

The fix

Emit the blank lines that two carries beyond those already supplied by the trailing LF + gap and the trailing line breaks of one:

one
  + repeat_line_breaks(
    Math.max(
      count_leading_line_breaks(two)
        - count_trailing_line_breaks(one) - 1,
      0
    ),
    gap
  )
  + two.trim() + LF + gap

count_leading_line_breaks mirrors the existing count_trailing_line_breaks helper. When two carries no extra blank lines the clamp yields 0, so the output is byte-identical to before for every existing case.

Tests

Added three round-trip tests (block after object property, line-comment control, block in array). The block cases fail on master and pass with the fix; the line-comment control passes both ways. The full suite stays green (403 tests, 100% branch coverage on stringify.js).

A blank line after an inline block comment (/* ... */) that follows a
comma was silently dropped by stringify, while the identical case with a
line comment (// ...) round-tripped correctly.

The parse trees for both cases are identical (both carry the BlankLine
token), so the defect is in stringify. In join(), the one && two branch
did one + two.trim() + LF + gap, and two.trim() discarded the blank-line
count carried by two. The line-comment case survived only because
process_comments appends a trailing LF after a LineComment, which landed
the missing break in one by coincidence.

Emit the blank lines that two carries beyond those already supplied by
the trailing LF + gap and the trailing line breaks of one.
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