Repository: webpack/webpack (JavaScript)

Issue:
Support default rule for internal types
<!-- Please don't delete this template or we'll close your issue -->

## Feature request

<!-- Issues that contain questions or support requests will be closed. -->
<!-- Before creating an issue please make sure you are using the latest version of webpack. -->
<!-- Check if this feature needs to be implemented in a plugin or loader instead -->
<!-- If yes: file the issue on the plugin/loader repo -->
<!-- Features related to the development server should be filed on this repo instead -->


Currently, for those who use `experiments.css` would encounter an issue for custom loader pipelines. I don't know if it is the design by webpack:

less file
```less
@import "variable.css";
```

The rule is configured as follows:

```js
{
  test: /\.less$/,
  use: ['less-loader'],
  type: 'css'
}
```

The transformation will result in a failure as `preferRelative` for this rule is not configured. This leads to an misalignment with the builtin rule for `css` files. In order to make it work, developers should configure like this:

```js
{
  test: /\.less$/,
  use: ['less-loader'],
  type: 'css',
  resolve: { preferRelative: true  }  // There might be other default rules to be applied.
}
```

To avoid the logic difference, we'd better to introduce a default rule for the internal type.

See more details here: https://github.com/web-infra-dev/rspack/issues/3913

@alexander-akait Any bias on this one?

**Are you willing to work on this yourself?**
yes



Hints:
<!-- identifier: inactive-warning -->

**This issue had no activity for at least three months.**

It's subject to automatic issue closing if there is no activity in the next 15 days.
<!-- identifier: inactive-close -->

Issue was closed because of inactivity.

If you think this is still a valid issue, please file a new issue with additional information.
@h-a-n-a Sorry for delay, is it still valid?
Yes, still valid.

I think it's caused by didn't add `resolve.preferRelative = true` for `resolve.byDependency["css-import"]`

https://github.com/webpack/webpack/blob/45c39892e105ce5d486b2aa07796b3c12fc019af/lib/config/defaults.js#L713-L717

https://github.com/webpack/webpack/blob/45c39892e105ce5d486b2aa07796b3c12fc019af/lib/config/defaults.js#L1472-L1480

Patch:
diff --git a/lib/config/defaults.js b/lib/config/defaults.js
--- a/lib/config/defaults.js
+++ b/lib/config/defaults.js
@@ -1515,7 +1515,8 @@ const getResolveDefaults = ({
 			mainFiles: [],
 			mainFields: ["style", "..."],
 			conditionNames: styleConditions,
-			extensions: [".css"]
+			extensions: [".css"],
+			preferRelative: true
 		};
 	}
 


Test patch:
diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js
--- a/test/Defaults.unittest.js
+++ b/test/Defaults.unittest.js
@@ -2197,123 +2197,125 @@ describe("snapshots", () => {
 		},
 		e =>
 			e.toMatchInlineSnapshot(`
-			- Expected
-			+ Received
+- Expected
++ Received
 
-			@@ ... @@
-			-     "asyncWebAssembly": false,
-			-     "backCompat": true,
-			+     "asyncWebAssembly": true,
-			+     "backCompat": false,
-			@@ ... @@
-			-     "cacheUnaffected": false,
-			-     "css": undefined,
-			-     "futureDefaults": false,
-			+     "cacheUnaffected": true,
-			+     "css": true,
-			+     "futureDefaults": true,
-			@@ ... @@
-			+       },
-			+       Object {
-			+         "rules": Array [
-			+           Object {
-			+             "descriptionData": Object {
-			+               "type": "module",
-			+             },
-			+             "resolve": Object {
-			+               "fullySpecified": true,
-			+             },
-			+           },
-			+         ],
-			+         "test": /\\.wasm$/i,
-			+         "type": "webassembly/async",
-			+       },
-			+       Object {
-			+         "mimetype": "application/wasm",
-			+         "rules": Array [
-			+           Object {
-			+             "descriptionData": Object {
-			+               "type": "module",
-			+             },
-			+             "resolve": Object {
-			+               "fullySpecified": true,
-			+             },
-			+           },
-			+         ],
-			+         "type": "webassembly/async",
-			+       },
-			+       Object {
-			+         "resolve": Object {
-			+           "fullySpecified": true,
-			+           "preferRelative": true,
-			+         },
-			+         "test": /\\.css$/i,
-			+         "type": "css/auto",
-			+       },
-			+       Object {
-			+         "mimetype": "text/css+module",
-			+         "resolve": Object {
-			+           "fullySpecified": true,
-			+           "preferRelative": true,
-			+         },
-			+         "type": "css/module",
-			+       },
-			+       Object {
-			+         "mimetype": "text/css",
-			+         "resolve": Object {
-			+           "fullySpecified": true,
-			+           "preferRelative": true,
-			@@ ... @@
-			+         "type": "css",
-			+       },
-			@@ ... @@
-			-     "generator": Object {},
-			+     "generator": Object {
-			+       "css": Object {
-			+         "exportsOnly": false,
-			+       },
-			+     },
-			@@ ... @@
-			+       "css": Object {
-			+         "namedExports": true,
-			+       },
-			@@ ... @@
-			+         "exportsPresence": "error",
-			@@ ... @@
-			-     "__dirname": "mock",
-			-     "__filename": "mock",
-			-     "global": true,
-			+     "__dirname": "warn-mock",
-			+     "__filename": "warn-mock",
-			+     "global": "warn",
-			@@ ... @@
-			+         "css",
-			@@ ... @@
-			-     "hashDigestLength": 20,
-			-     "hashFunction": "md4",
-			+     "hashDigestLength": 16,
-			+     "hashFunction": "xxhash64",
-			@@ ... @@
-			+           "...",
-			+         ],
-			+       },
-			+       "css-import": Object {
-			+         "conditionNames": Array [
-			+           "webpack",
-			+           "production",
-			+           "style",
-			+         ],
-			+         "extensions": Array [
-			+           ".css",
-			+         ],
-			+         "mainFields": Array [
-			+           "style",
-			@@ ... @@
-			+         "mainFiles": Array [],
-			@@ ... @@
-			-       "<cwd>/node_modules/",
-			+       /^(.+?[\\\\/]node_modules[\\\\/])/,
-		`)
+@@ ... @@
+-     "asyncWebAssembly": false,
+-     "backCompat": true,
++     "asyncWebAssembly": true,
++     "backCompat": false,
+@@ ... @@
+-     "cacheUnaffected": false,
+-     "css": undefined,
+-     "futureDefaults": false,
++     "cacheUnaffected": true,
++     "css": true,
++     "futureDefaults": true,
+@@ ... @@
++       },
++       Object {
++         "rules": Array [
++           Object {
++             "descriptionData": Object {
++               "type": "module",
++             },
++             "resolve": Object {
++               "fullySpecified": true,
++             },
++           },
++         ],
++         "test": /\\.wasm$/i,
++         "type": "webassembly/async",
++       },
++       Object {
++         "mimetype": "application/wasm",
++         "rules": Array [
++           Object {
++             "descriptionData": Object {
++               "type": "module",
++             },
++             "resolve": Object {
++               "fullySpecified": true,
++             },
++           },
++         ],
++         "type": "webassembly/async",
++       },
++       Object {
++         "resolve": Object {
++           "fullySpecified": true,
++           "preferRelative": true,
+@@ ... @@
++         "test": /\\.css$/i,
++         "type": "css/auto",
++       },
+@@ ... @@
++         "mimetype": "text/css+module",
++         "resolve": Object {
++           "fullySpecified": true,
++           "preferRelative": true,
++         },
++         "type": "css/module",
++       },
++       Object {
++         "mimetype": "text/css",
++         "resolve": Object {
++           "fullySpecified": true,
++           "preferRelative": true,
++         },
++         "type": "css",
++       },
++       Object {
+@@ ... @@
+-     "generator": Object {},
++     "generator": Object {
++       "css": Object {
++         "exportsOnly": false,
++       },
++     },
+@@ ... @@
++       },
++       "css": Object {
++         "namedExports": true,
+@@ ... @@
++         "exportsPresence": "error",
+@@ ... @@
+-     "__dirname": "mock",
+-     "__filename": "mock",
+-     "global": true,
++     "__dirname": "warn-mock",
++     "__filename": "warn-mock",
++     "global": "warn",
+@@ ... @@
++         "css",
+@@ ... @@
+-     "hashDigestLength": 20,
+-     "hashFunction": "md4",
++     "hashDigestLength": 16,
++     "hashFunction": "xxhash64",
+@@ ... @@
++           "...",
++         ],
++       },
++       "css-import": Object {
++         "conditionNames": Array [
++           "webpack",
++           "production",
++           "style",
++         ],
++         "extensions": Array [
++           ".css",
++         ],
++         "mainFields": Array [
++           "style",
+@@ ... @@
++         "mainFiles": Array [],
++         "preferRelative": true,
+@@ ... @@
+-       "<cwd>/node_modules/",
++       /^(.+?[\\\\/]node_modules[\\\\/])/,
+`)
 	);
 
 	test(
diff --git a/test/configCases/css/prefer-relative-css-import/bar.modules.css b/test/configCases/css/prefer-relative-css-import/bar.modules.css
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/bar.modules.css
@@ -0,0 +1,7 @@
+body {
+	color: red;
+}
+
+.bar {
+	color: red;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/foo.css b/test/configCases/css/prefer-relative-css-import/foo.css
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/foo.css
@@ -0,0 +1,7 @@
+body {
+	background: red;
+}
+
+.foo {
+	color: red;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/index.js b/test/configCases/css/prefer-relative-css-import/index.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/index.js
@@ -0,0 +1,14 @@
+import * as styles1 from "./style.less";
+import * as styles2 from "./style.modules.less";
+
+it("should prefer relative", () => {
+	expect(styles1).toEqual(nsObj({}));
+	expect(styles2).toEqual(nsObj({
+		"style-module": "./style.modules.less-style-module",
+	}));
+
+	const style = getComputedStyle(document.body);
+
+	expect(style.getPropertyValue("background")).toBe(" red");
+	expect(style.getPropertyValue("color")).toBe(" red");
+});
diff --git a/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/package.json b/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/package.json
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "bar.modules.css",
+  "main": "style.css"
+}
diff --git a/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/style.css b/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/style.css
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/node_modules/bar.modules.css/style.css
@@ -0,0 +1,3 @@
+body {
+	color: blue;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/package.json b/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/package.json
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "foo.css",
+  "main": "style.css"
+}
diff --git a/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/style.css b/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/style.css
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/node_modules/foo.css/style.css
@@ -0,0 +1,3 @@
+body {
+	background: blue;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/style.less b/test/configCases/css/prefer-relative-css-import/style.less
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/style.less
@@ -0,0 +1,5 @@
+@import 'foo.css';
+
+.style {
+	color: red;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/style.modules.less b/test/configCases/css/prefer-relative-css-import/style.modules.less
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/style.modules.less
@@ -0,0 +1,5 @@
+@import 'bar.modules.css';
+
+.style-module {
+	color: red;
+}
diff --git a/test/configCases/css/prefer-relative-css-import/test.config.js b/test/configCases/css/prefer-relative-css-import/test.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/test.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+	moduleScope(scope) {
+		const link = scope.window.document.createElement("link");
+		link.rel = "stylesheet";
+		link.href = "bundle0.css";
+		scope.window.document.head.appendChild(link);
+	}
+};
diff --git a/test/configCases/css/prefer-relative-css-import/webpack.config.js b/test/configCases/css/prefer-relative-css-import/webpack.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/css/prefer-relative-css-import/webpack.config.js
@@ -0,0 +1,17 @@
+/** @type {import("../../../../").Configuration} */
+module.exports = {
+	target: "web",
+	mode: "development",
+	module: {
+		rules: [
+			{
+				test: /\.less$/,
+				use: "less-loader",
+				type: "css/auto"
+			}
+		]
+	},
+	experiments: {
+		css: true
+	}
+};