Skip to content

fix(bundle): emit CSSStyleSheet for CSS raw imports - #35598

Merged
bartlomieju merged 3 commits into
mainfrom
fix/bundle-css-raw-import
Jun 29, 2026
Merged

bartlomieju merged 3 commits into
mainfrom
fix/bundle-css-raw-import

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

Fixes #35596.

A CSS raw import (import sheet from "./a.css" with { type: "css" },
gated behind --unstable-raw-imports) evaluates to a constructable
CSSStyleSheet at runtime via the custom module evaluation callback in
runtime/worker.rs. The bundler did not mirror this: it routed the CSS
source through esbuild's builtin CSS loader, which has no notion of a
CSSStyleSheet default export. The result was that the default import
came back as an empty object (var style_default = {}), and when no
output path was configured the build failed outright with "Cannot
import into a JavaScript file without an output path configured".

esbuild has no builtin loader that produces a CSSStyleSheet, so this
synthesizes an equivalent JS module for with { type: "css" } imports
instead of handing the source to the CSS loader:

const sheet = new CSSStyleSheet();
sheet.replaceSync(<css text>);
export default sheet;

The CSS text is emitted as a JSON-encoded string literal so it is safely
escaped. With this change, bundling the reproduction from the issue
produces a real CSSStyleSheet, and running the bundle logs the
expected stylesheet rather than {}.

A spec test under tests/specs/bundle/raw_imports_css bundles a CSS
import, deletes the source .css file to prove the contents are inlined
into the bundle, then runs the output and asserts it is a CSSStyleSheet
with the expected rule.

A `with { type: "css" }` import evaluates to a constructable
`CSSStyleSheet` at runtime, but `deno bundle` routed the CSS source
through esbuild's CSS loader, leaving the default import as an empty
object (or failing when no output path was configured). Synthesize an
equivalent JS module that constructs the `CSSStyleSheet` instead.

Closes #35596
The bundled output uses `new CSSStyleSheet()`, but the `CSSStyleSheet`
global is only exposed when `--unstable-raw-imports` is enabled, so
running the bundle without the flag failed with "CSSStyleSheet is not
defined".
@bartlomieju
bartlomieju enabled auto-merge (squash) June 29, 2026 10:39
The expected output had the CSS rule on a single line (h1 { margin: 0; }),
but CSSStyleSheet.cssRules[0].cssText serializes multi-line in Deno, both
for a direct runtime import and for the bundled output. Match the
expectation to the canonical serialization so the spec test passes.
@bartlomieju
bartlomieju merged commit 3fe48f6 into main Jun 29, 2026
268 of 270 checks passed
@bartlomieju
bartlomieju deleted the fix/bundle-css-raw-import branch June 29, 2026 12:32
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.

deno bundle produces incorrect output when using CSS module scripts

1 participant