Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Preserve the BOM when stringifying - #2119

Merged
ai merged 1 commit into
postcss:mainfrom
hdimer:preserve-bom-on-stringify
Jul 28, 2026
Merged

Preserve the BOM when stringifying#2119
ai merged 1 commit into
postcss:mainfrom
hdimer:preserve-bom-on-stringify

Conversation

@hdimer

@hdimer hdimer commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

PostCSS aims for byte-to-byte equal output, but the stringifier drops a leading byte order mark:

postcss.parse('a{color:red}').toString() // => 'a{color:red}', BOM lost

The BOM is already detected (Input#hasBOM), but it was never re-emitted, so the round-trip isn't lossless. The bom.css parser-test fixture was excluded from the stringify round-trip test for this reason (if (name === 'bom.css') return).

This emits the BOM in Stringifier#root when the source had one, and removes that skip so the bom.css round-trip is now covered. A programmatic root with no source emits no BOM, and non-BOM input is unchanged.

Disclosure: written with AI assistance (Claude Code); I reviewed it and verified against the full suite (unit, eslint, check-dts, size-limit all pass, incl. the newly re-enabled test).

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.
@ai
ai merged commit 9a114f6 into postcss:main Jul 28, 2026
10 checks passed
@ai

ai commented Jul 28, 2026

Copy link
Copy Markdown
Member

Thanks. Nice idea.

@ai

ai commented Jul 28, 2026

Copy link
Copy Markdown
Member

Released in 8.5.24.

@UncleJart

UncleJart commented Aug 5, 2026

Copy link
Copy Markdown

Interesting why BOM was stripped before? Because now this change breaks application production builds where SASS is in chain. Sass by default has charset: true option enabled and in case SCSS file has non-ASCII it adds at the top of the file @charset UTF-8; string which is been replaced with BOM when source is been compressed in production mode. Our app injects styles inline into page markdown dynamically when they are used via isomorphic-style-loader. Previously BOM was stripped and everything worked OK, but after this change BOM is been preserved and all major browsers just ignore the first rule of the compiled CSS causing styling breakages. So the question is what is the right behavior then, should it be treated as lib issue or not? Maybe BOM was stripped for this reason? Maybe it is better to introduce an option in configuration for BOM to be stripped/kept?
image

We had to downgrade to v 8.5.23 for now to fix this.

@ai What are your thoughts?

@ai

ai commented Aug 5, 2026

Copy link
Copy Markdown
Member

Interesting why BOM was stripped before?

It was a mistake (we forgot about it).

BOM is a well expected symbol. It is a big issue of the parser if it can't process it.

@UncleJart

Copy link
Copy Markdown

So you want to say that this is browsers (Chrome, Firefox, ...) bug that they do not parse BOM properly in inline styles?

@ai

ai commented Aug 5, 2026

Copy link
Copy Markdown
Member

No, BOM should be only in the begging of the file.

It is bug on inliner tool to not remove BOM in this case.

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.

3 participants