PostCSS aims for byte-to-byte equal output, but the stringifier dropped a
leading byte order mark: `parse('a{}').toString()` returned `a{}`. The
BOM is already detected (`Input#hasBOM`) but was never re-emitted, so the
`bom.css` round-trip test had to be skipped.
Emit the BOM in `Stringifier#root` when the source had one, and re-enable the
previously skipped test.
A custom property declaration that is the last non-comment child but is
followed by comment siblings was stringified without its terminating
semicolon. Unlike a normal declaration, a custom property keeps everything
up to the next `;` or `}` as its value, so on re-parse the trailing
comments were folded into the value and the comment nodes disappeared.
Building such a tree with append()/insertAfter()/after() therefore
silently dropped the comments. Emit the semicolon when a custom property
still has following siblings so the output round-trips, mirroring the
existing handling for childless at-rules.
A childless (statement) at-rule that is the last non-comment child but is
followed by comment siblings was stringified without its terminating
semicolon. On re-parse the trailing comments were folded into the
at-rule prelude and dropped, so building such a tree with append() or
insertBefore() silently lost the comment nodes.
Emit the semicolon when a childless at-rule still has following siblings
so the output round-trips.
When the requested end position has no mapping in the source map,
originalPositionFor() returns an object with line/column set to null
rather than omitting them. endLine picked that null up correctly, but
endColumn was computed as `to && to.column + 1`, which due to operator
precedence is `to && (to.column + 1)`, turning null into 1. So callers
could get back { endColumn: 1, endLine: null }, which doesn't match
how every other unmapped case in this function behaves (undefined for
both). Looks like the +1 fix in #2036 covered the mapped case but not
this one.
Guard `to` the same way `from` already is, so an unmapped end position
just stays undefined.
The container constructor cloned every real Node passed in `nodes`, so a
freshly created (parent-less) node was adopted as a copy and the caller's
original reference never had its `parent` set — later operating on it threw
`Cannot read properties of undefined`. The clone was added only to keep the
source tree intact when moving nodes between roots, so it's skipped when the
node has no parent. Nodes that already belong to another tree are still
cloned, preserving the existing behavior. Closes#1987.
fromJSON() attached child nodes through the container constructor, which
runs them back through append()/normalize(). For root-level children that
re-applies insertion spacing normalization and overwrites each node's own
raws.before with the previous sibling's, so toJSON() -> fromJSON() was not
lossless: stylesheets with non-uniform blank lines between top-level rules
came back with their spacing flattened.
Rehydrate children separately and attach them directly, keeping the raws
exactly as serialized.
When calling Node#rangeBy() (and therefore Node#error() and
Node#warn()) with index 0, the resulting range covered the whole node
instead of a single character at offset 0.
The end-of-range branch used a truthy check on opts.index, so an index
of 0 fell through and left the end position at the node's end. This is
the same off-by-zero issue that was previously fixed for endIndex; the
index branches were missed. Switch both index checks to a numeric type
check, matching the existing endIndex handling.
index 0 now produces a single-character range, consistent with every
other index value.