From 7821d527deefee40bc11db994dafecc7fe98e07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 10 Jun 2026 12:37:32 +0200 Subject: [PATCH 1/2] feat: add unstable_css_imports option Allows a 'css' import attribute type to be treated as an asset import (like text/bytes), recorded as an external module in the graph. Used by Deno's CSS module scripts prototype behind --unstable-raw-imports. --- src/analysis.rs | 2 +- src/graph.rs | 8 +- tests/ecosystem_test.rs | 1 + tests/helpers/mod.rs | 9 +++ tests/specs/graph/bytes_and_text/css.txt | 77 +++++++++++++++++++ .../graph/bytes_and_text/not_enabled_css.txt | 45 +++++++++++ tests/specs_test.rs | 4 + 7 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 tests/specs/graph/bytes_and_text/css.txt create mode 100644 tests/specs/graph/bytes_and_text/not_enabled_css.txt diff --git a/src/analysis.rs b/src/analysis.rs index 1602b7e1c..9e722a369 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -87,7 +87,7 @@ impl ImportAttributes { let Some(value) = self.get("type") else { return false; }; - matches!(value, "text" | "bytes") + matches!(value, "text" | "bytes" | "css") } } diff --git a/src/graph.rs b/src/graph.rs index fc9de59b6..f444d9956 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1694,6 +1694,8 @@ pub struct BuildOptions<'a> { pub unstable_bytes_imports: bool, /// Support unstable text imports. pub unstable_text_imports: bool, + /// Support unstable css imports. + pub unstable_css_imports: bool, pub executor: &'a dyn Executor, pub locker: Option<&'a mut dyn Locker>, pub file_system: &'a FileSystem, @@ -1718,6 +1720,7 @@ impl Default for BuildOptions<'_> { skip_dynamic_deps: false, unstable_bytes_imports: false, unstable_text_imports: false, + unstable_css_imports: false, executor: Default::default(), locker: None, file_system: &NullFileSystem, @@ -2681,7 +2684,7 @@ impl ModuleGraph { } /// List of all the url in the graph that are statically known to be - /// imported with type "text" or "bytes". + /// imported with type "text", "bytes" or "css". pub fn asset_module_urls(&self) -> IndexSet<&Url> { let mut result = IndexSet::with_capacity(self.module_slots.len()); for module in self.module_slots.values() { @@ -4558,6 +4561,7 @@ struct Builder<'a, 'graph> { was_dynamic_root: bool, unstable_bytes_imports: bool, unstable_text_imports: bool, + unstable_css_imports: bool, file_system: &'a FileSystem, jsr_url_provider: &'a dyn JsrUrlProvider, jsr_version_resolver: Cow<'a, JsrVersionResolver>, @@ -4592,6 +4596,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { was_dynamic_root: options.is_dynamic, unstable_bytes_imports: options.unstable_bytes_imports, unstable_text_imports: options.unstable_text_imports, + unstable_css_imports: options.unstable_css_imports, file_system: options.file_system, jsr_url_provider: options.jsr_url_provider, jsr_version_resolver: options.jsr_version_resolver, @@ -5351,6 +5356,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { let is_allowed = match attribute.kind.as_str() { "bytes" => self.unstable_bytes_imports, "text" => self.unstable_text_imports, + "css" => self.unstable_css_imports, _ => false, }; if !is_allowed { diff --git a/tests/ecosystem_test.rs b/tests/ecosystem_test.rs index b3471278b..5afe43d04 100644 --- a/tests/ecosystem_test.rs +++ b/tests/ecosystem_test.rs @@ -298,6 +298,7 @@ async fn test_version( skip_dynamic_deps: false, unstable_bytes_imports: false, unstable_text_imports: false, + unstable_css_imports: false, module_analyzer: &module_analyzer, module_info_cacher: Default::default(), file_system: &NullFileSystem, diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 9c4d638c7..eaed92c42 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -160,6 +160,7 @@ pub struct TestBuilder { workspace_fast_check: bool, unstable_bytes_imports: bool, unstable_text_imports: bool, + unstable_css_imports: bool, } impl TestBuilder { @@ -179,6 +180,7 @@ impl TestBuilder { workspace_fast_check: false, unstable_bytes_imports: false, unstable_text_imports: false, + unstable_css_imports: false, } } @@ -268,6 +270,12 @@ impl TestBuilder { self } + #[allow(unused)] + pub fn unstable_css_imports(&mut self, value: bool) -> &mut Self { + self.unstable_css_imports = value; + self + } + #[allow(unused)] pub fn ensure_locker(&mut self) -> &mut Self { self.locker.get_or_insert_with(Default::default); @@ -330,6 +338,7 @@ impl TestBuilder { skip_dynamic_deps: self.skip_dynamic_deps, unstable_bytes_imports: self.unstable_bytes_imports, unstable_text_imports: self.unstable_text_imports, + unstable_css_imports: self.unstable_css_imports, jsr_version_resolver: Cow::Owned(JsrVersionResolver { newest_dependency_date_options: self.newest_dependency_date.clone(), }), diff --git a/tests/specs/graph/bytes_and_text/css.txt b/tests/specs/graph/bytes_and_text/css.txt new file mode 100644 index 000000000..6d07f3e0c --- /dev/null +++ b/tests/specs/graph/bytes_and_text/css.txt @@ -0,0 +1,77 @@ +~~ {"unstableCssImports":true} ~~ +# mod.ts +import sheet from "./a.css" with { type: "css" }; +const dynamic = await import("./b.css", { + with: { + "type": "css", + } +}); + +# a.css +body { color: red; } + +# b.css +.a { color: green; } + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "kind": "external", + "specifier": "file:///a.css" + }, + { + "kind": "external", + "specifier": "file:///b.css" + }, + { + "kind": "esm", + "dependencies": [ + { + "specifier": "./a.css", + "code": { + "specifier": "file:///a.css", + "resolutionMode": "import", + "span": { + "start": { + "line": 0, + "character": 18 + }, + "end": { + "line": 0, + "character": 27 + } + } + }, + "assertionType": "css" + }, + { + "specifier": "./b.css", + "code": { + "specifier": "file:///b.css", + "resolutionMode": "import", + "span": { + "start": { + "line": 1, + "character": 29 + }, + "end": { + "line": 1, + "character": 38 + } + } + }, + "isDynamic": true, + "assertionType": "css" + } + ], + "size": 129, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + } + ], + "redirects": {} +} diff --git a/tests/specs/graph/bytes_and_text/not_enabled_css.txt b/tests/specs/graph/bytes_and_text/not_enabled_css.txt new file mode 100644 index 000000000..c87ff1251 --- /dev/null +++ b/tests/specs/graph/bytes_and_text/not_enabled_css.txt @@ -0,0 +1,45 @@ +# mod.ts +import sheet from "./a.css" with { type: "css" }; + +# a.css +body { color: red; } + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "specifier": "file:///a.css", + "error": "The import attribute type of \"css\" is unsupported.\n Specifier: file:///a.css" + }, + { + "kind": "esm", + "dependencies": [ + { + "specifier": "./a.css", + "code": { + "specifier": "file:///a.css", + "resolutionMode": "import", + "span": { + "start": { + "line": 0, + "character": 18 + }, + "end": { + "line": 0, + "character": 27 + } + } + }, + "assertionType": "css" + } + ], + "size": 50, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + } + ], + "redirects": {} +} diff --git a/tests/specs_test.rs b/tests/specs_test.rs index f883b50d3..c9d4e1875 100644 --- a/tests/specs_test.rs +++ b/tests/specs_test.rs @@ -107,6 +107,7 @@ fn run_graph_test(test: &CollectedTest) { builder.skip_dynamic_deps(options.skip_dynamic_deps); builder.unstable_bytes_imports(options.unstable_bytes_imports); builder.unstable_text_imports(options.unstable_text_imports); + builder.unstable_css_imports(options.unstable_css_imports); builder.workspace_fast_check(options.workspace_fast_check); builder.fast_check_cache(options.fast_check_cache); if let Some(checksums) = options.remote_checksums.as_ref() { @@ -359,6 +360,9 @@ pub struct SpecOptions { #[serde(default)] #[serde(skip_serializing_if = "is_false")] pub unstable_text_imports: bool, + #[serde(default)] + #[serde(skip_serializing_if = "is_false")] + pub unstable_css_imports: bool, } fn is_false(v: &bool) -> bool { From 796ef7bdbdb13b95311d9ba6df5c5296498e9ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 10 Jun 2026 12:57:02 +0200 Subject: [PATCH 2/2] fix: add unstable_css_imports to wasm BuildOptions initializer --- lib/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/lib.rs b/lib/lib.rs index dbc14fd0b..c4337a681 100644 --- a/lib/lib.rs +++ b/lib/lib.rs @@ -313,6 +313,7 @@ pub async fn js_create_graph( skip_dynamic_deps: false, unstable_bytes_imports: true, unstable_text_imports: true, + unstable_css_imports: true, resolver: maybe_resolver.as_ref().map(|r| r as &dyn Resolver), // todo(dsherret): actually implement this for Wasm users // and don't just use a RealSys here as it would be better