Repository: vercel/next.js
Pull request: Turbopack: support for CSS Modules in Data URLs

<!-- Thanks for opening a PR! Your contribution is much appreciated. -->

## For Contributors

### Adding a feature
- Implements turbopack support for CSS Modules in Data URLs
- Adds test cases to verify functionality

## What?
This PR adds support for CSS Modules in Data URLs by adding the `text/css+module` content type to the module rules that handle CSS modules

## Why?
This enables new use cases for CSS Modules without requiring physical files, which is particularly useful for tooling that needs to generate CSS Modules programmatically

## How?
The implementation extends existing rule conditions in the Turbopack module options to recognize the "text/css+module" content type, following the same pattern used for regular CSS in data URLs. Test cases have been added to verify both regular and base64-encoded CSS module data URLs work correctly

Changed files:
filename: turbopack/crates/turbopack-tests/tests/execution/turbopack/data-url/basic/input/dark.svg
patch: @@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="114px" height="100px" viewBox="0 0 114 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
+    <title>Logotype - Black</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <linearGradient x1="100.929941%" y1="181.283245%" x2="41.7687834%" y2="100%" id="linearGradient-1">
+            <stop stop-color="#FFFFFF" offset="0%"></stop>
+            <stop stop-color="#000000" offset="100%"></stop>
+        </linearGradient>
+    </defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Black-Triangle" transform="translate(-293.000000, -150.000000)" fill="url(#linearGradient-1)">
+            <polygon id="Logotype---Black" points="350 150 407 250 293 250"></polygon>
+        </g>
+    </g>
+</svg>

filename: turbopack/crates/turbopack-tests/tests/execution/turbopack/data-url/basic/input/index.js
patch: @@ -3,10 +3,15 @@ import data from 'data:application/json,{ "foo": 1234 }';
 import dataURLEncoded from 'data:application/json,%7B%20%22foo%22%3A%201234%20%7D';
 import dataBase64Encoded from 'data:application/json;base64,eyAiZm9vIjogMTIzNCB9';
 import "data:text/css,body { color: red }";
+import "data:text/css,body { background: url('./dark.svg') }";
+import * as style from "data:text/css+module,.bar { background: url('./dark.svg') }";
+import * as style64 from "data:text/css+module;base64,LmJhciB7IGJhY2tncm91bmQ6IHVybCgnLi9kYXJrLnN2ZycpIH0=";
 
 it("support data URL imports", () => {
   expect(bar).toEqual(1234);
   expect(data).toEqual({ foo: 1234 });
   expect(dataURLEncoded).toEqual({ foo: 1234 });
   expect(dataBase64Encoded).toEqual({ foo: 1234 });
+  expect(style.bar).toMatch(/bar/);
+  expect(style64.bar).toMatch(/bar/);
 });

filename: turbopack/crates/turbopack/src/module_options/mod.rs
patch: @@ -450,7 +450,10 @@ impl ModuleOptions {
                     })],
                 ),
                 ModuleRule::new(
-                    RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                    RuleCondition::any(vec![
+                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                        RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
+                    ]),
                     vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
                         ty: CssModuleAssetType::Module,
                     })],
@@ -509,7 +512,10 @@ impl ModuleOptions {
                 ),
                 ModuleRule::new(
                     RuleCondition::all(vec![
-                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                        RuleCondition::Any(vec![
+                            RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                            RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
+                        ]),
                         // Only create a module CSS asset if not `@import`ed from CSS already.
                         // NOTE: `composes` references should not be treated as `@import`s and
                         // should also create a module CSS asset.
@@ -533,7 +539,10 @@ impl ModuleOptions {
                 ),
                 // Ecmascript CSS Modules referencing the actual CSS module to include it
                 ModuleRule::new_internal(
-                    RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                    RuleCondition::Any(vec![
+                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                        RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
+                    ]),
                     vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
                         ty: CssModuleAssetType::Module,
                     })],
@@ -544,7 +553,10 @@ impl ModuleOptions {
                         RuleCondition::ReferenceType(ReferenceType::Css(
                             CssReferenceSubType::Analyze,
                         )),
-                        RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                        RuleCondition::Any(vec![
+                            RuleCondition::ResourcePathEndsWith(".module.css".to_string()),
+                            RuleCondition::ContentTypeStartsWith("text/css+module".to_string()),
+                        ]),
                     ]),
                     vec![ModuleRuleEffect::ModuleType(ModuleType::Css {
                         ty: CssModuleAssetType::Module,