Repository: webpack-contrib/css-loader
Pull request #1176 (2020-08-24 15:52:40)

refactor: improve sources handling in source maps
<!--
  HOLY CRAP a Pull Request. We ❤️ those!

  If you remove or skip this template, you'll make the 🐼 sad and the mighty god
  of Github will appear and pile-drive the close button from a great height
  while making animal noises.

  Please place an x (no spaces!) in all [ ] that apply
-->

This PR contains a:

- [x] **bugfix**
- [x] new **feature**
- [x] **code refactor**
- [x] **test update** <!-- if bug or feature is checked, this should be too -->
- [ ] **typo fix**
- [ ] **metadata update**

### Motivation / Use-Case

improve handling

### Breaking Changes

No

### Additional Info

No

codecov[bot]: # [Codecov](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=h1) Report
> Merging [#1176](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=desc) into [master](https://codecov.io/gh/webpack-contrib/css-loader/commit/4ce556abdbab11cb3bce5d746e38254aa6d3b795?el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `95.65%`.

[![Impacted file tree graph](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176/graphs/tree.svg?width=650&height=150&src=pr&token=DlN3OQ0JnO)](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1176      +/-   ##
==========================================
+ Coverage   98.78%   98.79%   +0.01%     
==========================================
  Files          10       10              
  Lines         656      665       +9     
  Branches      210      211       +1     
==========================================
+ Hits          648      657       +9     
  Misses          8        8              
```


| [Impacted Files](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/utils.js](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176/diff?src=pr&el=tree#diff-c3JjL3V0aWxzLmpz) | `97.34% <95.65%> (+0.08%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=footer). Last update [4ce556a...f6c2dd8](https://codecov.io/gh/webpack-contrib/css-loader/pull/1176?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/utils.js b/src/utils.js
index 49f37d2..92c2361 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -298,6 +298,25 @@ function getModulesPlugins(options, loaderContext) {
   return plugins;
 }
 
+const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
+const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i;
+
+function getURLType(source) {
+  if (source[0] === '/') {
+    if (source[1] === '/') {
+      return 'scheme-relative';
+    }
+
+    return 'path-absolute';
+  }
+
+  if (IS_NATIVE_WIN32_PATH.test(source)) {
+    return 'path-absolute';
+  }
+
+  return ABSOLUTE_SCHEME.test(source) ? 'absolute' : 'path-relative';
+}
+
 function normalizeSourceMap(map, resourcePath) {
   let newMap = map;
 
@@ -307,36 +326,34 @@ function normalizeSourceMap(map, resourcePath) {
     newMap = JSON.parse(newMap);
   }
 
-  // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
-  // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
-
-  if (newMap.file) {
-    delete newMap.file;
-  }
+  delete newMap.file;
 
   const { sourceRoot } = newMap;
 
-  if (newMap.sourceRoot) {
-    delete newMap.sourceRoot;
-  }
+  delete newMap.sourceRoot;
 
   if (newMap.sources) {
+    // Source maps should use forward slash because it is URLs (https://github.com/mozilla/source-map/issues/91)
+    // We should normalize path because previous loaders like `sass-loader` using backslash when generate source map
     newMap.sources = newMap.sources.map((source) => {
+      // Non-standard syntax from `postcss`
       if (source.indexOf('<') === 0) {
         return source;
       }
 
-      if (/^\w+:\/\//.test(source)) {
-        return source;
-      }
+      const sourceType = getURLType(source);
 
-      const absoluteSource = !sourceRoot
-        ? source
-        : path.resolve(sourceRoot, source);
+      // Do no touch `scheme-relative` and `absolute` URLs
+      if (sourceType === 'path-relative' || sourceType === 'path-absolute') {
+        const absoluteSource =
+          sourceType === 'path-relative' && sourceRoot
+            ? path.resolve(sourceRoot, normalizePath(source))
+            : normalizePath(source);
 
-      const resourceDirname = path.dirname(resourcePath);
+        return path.relative(path.dirname(resourcePath), absoluteSource);
+      }
 
-      return normalizePath(path.relative(resourceDirname, absoluteSource));
+      return source;
     });
   }
 
@@ -395,16 +412,19 @@ function normalizeSourceMapForRuntime(map, loaderContext) {
   const resultMap = map ? map.toJSON() : null;
 
   if (resultMap) {
-    if (typeof resultMap.file !== 'undefined') {
-      delete resultMap.file;
-    }
+    delete resultMap.file;
+
+    resultMap.sourceRoot = '';
 
     resultMap.sources = resultMap.sources.map((source) => {
+      // Non-standard syntax from `postcss`
       if (source.indexOf('<') === 0) {
         return source;
       }
 
-      if (/^\w+:\/\//.test(source)) {
+      const sourceType = getURLType(source);
+
+      if (sourceType !== 'path-relative') {
         return source;
       }
 


Test patch:
diff --git a/test/__snapshots__/sourceMap-option.test.js.snap b/test/__snapshots__/sourceMap-option.test.js.snap
index 5b46664..a664b23 100644
--- a/test/__snapshots__/sourceMap-option.test.js.snap
+++ b/test/__snapshots__/sourceMap-option.test.js.snap
@@ -159,7 +159,7 @@ exports[`"sourceMap" option true should generate source maps #2: module 1`] = `
 import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".foo {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/with-query.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\".foo {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".foo {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/with-query.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\".foo {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -177,6 +177,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/with-query.css",
       ],
@@ -203,7 +204,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://test/fixtures/source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://test/fixtures/source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -221,6 +222,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://test/fixtures/source-map/nested/nested.css",
       ],
@@ -243,6 +245,7 @@ Array [
     Object {
       "mappings": "AAEA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://test/fixtures/source-map/basic.css",
       ],
@@ -357,7 +360,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -375,6 +378,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/nested/nested.css",
       ],
@@ -397,6 +401,7 @@ Array [
     Object {
       "mappings": "AAEA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/basic.css",
       ],
@@ -416,6 +421,76 @@ Array [
 
 exports[`"sourceMap" option true should generate source maps when previous loader does not generate source maps: warnings 1`] = `Array []`;
 
+exports[`"sourceMap" option true should generate source maps when previous loader generates different source in source maps: errors 1`] = `Array []`;
+
+exports[`"sourceMap" option true should generate source maps when previous loader generates different source in source maps: module 1`] = `
+"// Imports
+import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
+import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident]!./nested/nested.css\\";
+var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
+___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
+// Module
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic-1.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
+// Exports
+export default ___CSS_LOADER_EXPORT___;
+"
+`;
+
+exports[`"sourceMap" option true should generate source maps when previous loader generates different source in source maps: result 1`] = `
+Array [
+  Array [
+    "../../src/index.js?[ident]!./source-map/nested/nested.css",
+    ".nested {
+  color: blue;
+}
+",
+    "",
+    Object {
+      "mappings": "AAAA;EACE,WAAW;AACb",
+      "names": Array [],
+      "sourceRoot": "",
+      "sources": Array [
+        "webpack://source-map/nested/nested.css",
+      ],
+      "sourcesContent": Array [
+        ".nested {
+  color: blue;
+}
+",
+      ],
+      "version": 3,
+    },
+  ],
+  Array [
+    "./source-map/basic.css",
+    ".class {
+  color: red;
+}
+",
+    "",
+    Object {
+      "mappings": "AAEA;EACE,UAAU;AACZ",
+      "names": Array [],
+      "sourceRoot": "",
+      "sources": Array [
+        "webpack://source-map/basic-1.css",
+      ],
+      "sourcesContent": Array [
+        "@import \\"./nested/nested.css\\";
+
+.class {
+  color: red;
+}
+",
+      ],
+      "version": 3,
+    },
+  ],
+]
+`;
+
+exports[`"sourceMap" option true should generate source maps when previous loader generates different source in source maps: warnings 1`] = `Array []`;
+
 exports[`"sourceMap" option true should generate source maps when previous loader generates source maps ("less-loader"): errors 1`] = `Array []`;
 
 exports[`"sourceMap" option true should generate source maps when previous loader generates source maps ("less-loader"): module 1`] = `
@@ -423,7 +498,7 @@ exports[`"sourceMap" option true should generate source maps when previous loade
 import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\"body {\\\\n  font: 100% Helvetica, sans-serif;\\\\n  color: #333;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/base.less\\"],\\"names\\":[],\\"mappings\\":\\"AAGA;EACE,gCAAA;EACA,WAAA;AAFF\\",\\"sourcesContent\\":[\\"@font-stack:    Helvetica, sans-serif;\\\\n@primary-color: #333;\\\\n\\\\nbody {\\\\n  font: 100% @font-stack;\\\\n  color: @primary-color;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\"body {\\\\n  font: 100% Helvetica, sans-serif;\\\\n  color: #333;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/base.less\\"],\\"names\\":[],\\"mappings\\":\\"AAGA;EACE,gCAAA;EACA,WAAA;AAFF\\",\\"sourcesContent\\":[\\"@font-stack:    Helvetica, sans-serif;\\\\n@primary-color: #333;\\\\n\\\\nbody {\\\\n  font: 100% @font-stack;\\\\n  color: @primary-color;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -442,6 +517,7 @@ Array [
     Object {
       "mappings": "AAGA;EACE,gCAAA;EACA,WAAA;AAFF",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/base.less",
       ],
@@ -472,7 +548,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\":root {\\\\n  --fontSize: 1rem;\\\\n  --mainColor: rgba(18,52,86,0.47059);\\\\n  --secondaryColor: rgba(102, 51, 153, 0.9);\\\\n}\\\\n\\\\nhtml {\\\\n  overflow-x: hidden;\\\\n  overflow-y: auto;\\\\n  overflow: hidden auto;\\\\n}\\\\n\\\\n@media (max-width: 50rem) {\\\\n  body {\\\\n    color: rgba(18,52,86,0.47059);\\\\n    color: var(--mainColor);\\\\n    font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\\\\n    font-size: 1rem;\\\\n    font-size: var(--fontSize);\\\\n    line-height: calc(1rem * 1.5);\\\\n    line-height: calc(var(--fontSize) * 1.5);\\\\n    word-wrap: break-word;\\\\n    padding-left: calc(1rem / 2 + 1px);\\\\n    padding-right: calc(1rem / 2 + 1px);\\\\n    padding-left: calc(var(--fontSize) / 2 + 1px);\\\\n    padding-right: calc(var(--fontSize) / 2 + 1px);\\\\n  }\\\\n}\\\\n\\\\nh1,h2,h3,h4,h5,h6 {\\\\n  margin-top: 0;\\\\n  margin-bottom: 0;\\\\n}\\\\n\\\\na {\\\\n  color: rgba(0, 0, 255, 0.9)\\\\n}\\\\n\\\\na:hover {\\\\n   color: #639;\\\\n }\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.postcss.css\\"],\\"names\\":[],\\"mappings\\":\\"AAKA;EACE,gBAAgB;EAChB,mCAAsB;EACtB,yCAA4C;AAC9C;;AAEA;EACE,kBAAqB;EAArB,gBAAqB;EAArB,qBAAqB;AACvB;;AAEA;EACE;IACE,6BAAuB;IAAvB,uBAAuB;IACvB,iGAAsB;IACtB,eAA0B;IAA1B,0BAA0B;IAC1B,6BAAwC;IAAxC,wCAAwC;IACxC,qBAAyB;IACzB,kCAA+C;IAA/C,mCAA+C;IAA/C,6CAA+C;IAA/C,8CAA+C;EACjD;AACF;;AAEA;EACE,aAAe;EAAf,gBAAe;AACjB;;AAEA;EACE;AAKF;;AAHA;GACG,WAAoB;CACtB\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.postcss.css\\\\\\";\\\\n\\\\n@custom-media --viewport-medium (width <= 50rem);\\\\n@custom-selector :--heading h1, h2, h3, h4, h5, h6;\\\\n\\\\n:root {\\\\n  --fontSize: 1rem;\\\\n  --mainColor: #12345678;\\\\n  --secondaryColor: lab(32.5 38.5 -47.6 / 90%);\\\\n}\\\\n\\\\nhtml {\\\\n  overflow: hidden auto;\\\\n}\\\\n\\\\n@media (--viewport-medium) {\\\\n  body {\\\\n    color: var(--mainColor);\\\\n    font-family: system-ui;\\\\n    font-size: var(--fontSize);\\\\n    line-height: calc(var(--fontSize) * 1.5);\\\\n    overflow-wrap: break-word;\\\\n    padding-inline: calc(var(--fontSize) / 2 + 1px);\\\\n  }\\\\n}\\\\n\\\\n:--heading {\\\\n  margin-block: 0;\\\\n}\\\\n\\\\na {\\\\n  color: rgb(0 0 100% / 90%);\\\\n\\\\n&:hover {\\\\n   color: rebeccapurple;\\\\n }\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\":root {\\\\n  --fontSize: 1rem;\\\\n  --mainColor: rgba(18,52,86,0.47059);\\\\n  --secondaryColor: rgba(102, 51, 153, 0.9);\\\\n}\\\\n\\\\nhtml {\\\\n  overflow-x: hidden;\\\\n  overflow-y: auto;\\\\n  overflow: hidden auto;\\\\n}\\\\n\\\\n@media (max-width: 50rem) {\\\\n  body {\\\\n    color: rgba(18,52,86,0.47059);\\\\n    color: var(--mainColor);\\\\n    font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\\\\n    font-size: 1rem;\\\\n    font-size: var(--fontSize);\\\\n    line-height: calc(1rem * 1.5);\\\\n    line-height: calc(var(--fontSize) * 1.5);\\\\n    word-wrap: break-word;\\\\n    padding-left: calc(1rem / 2 + 1px);\\\\n    padding-right: calc(1rem / 2 + 1px);\\\\n    padding-left: calc(var(--fontSize) / 2 + 1px);\\\\n    padding-right: calc(var(--fontSize) / 2 + 1px);\\\\n  }\\\\n}\\\\n\\\\nh1,h2,h3,h4,h5,h6 {\\\\n  margin-top: 0;\\\\n  margin-bottom: 0;\\\\n}\\\\n\\\\na {\\\\n  color: rgba(0, 0, 255, 0.9)\\\\n}\\\\n\\\\na:hover {\\\\n   color: #639;\\\\n }\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.postcss.css\\"],\\"names\\":[],\\"mappings\\":\\"AAKA;EACE,gBAAgB;EAChB,mCAAsB;EACtB,yCAA4C;AAC9C;;AAEA;EACE,kBAAqB;EAArB,gBAAqB;EAArB,qBAAqB;AACvB;;AAEA;EACE;IACE,6BAAuB;IAAvB,uBAAuB;IACvB,iGAAsB;IACtB,eAA0B;IAA1B,0BAA0B;IAC1B,6BAAwC;IAAxC,wCAAwC;IACxC,qBAAyB;IACzB,kCAA+C;IAA/C,mCAA+C;IAA/C,6CAA+C;IAA/C,8CAA+C;EACjD;AACF;;AAEA;EACE,aAAe;EAAf,gBAAe;AACjB;;AAEA;EACE;AAKF;;AAHA;GACG,WAAoB;CACtB\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.postcss.css\\\\\\";\\\\n\\\\n@custom-media --viewport-medium (width <= 50rem);\\\\n@custom-selector :--heading h1, h2, h3, h4, h5, h6;\\\\n\\\\n:root {\\\\n  --fontSize: 1rem;\\\\n  --mainColor: #12345678;\\\\n  --secondaryColor: lab(32.5 38.5 -47.6 / 90%);\\\\n}\\\\n\\\\nhtml {\\\\n  overflow: hidden auto;\\\\n}\\\\n\\\\n@media (--viewport-medium) {\\\\n  body {\\\\n    color: var(--mainColor);\\\\n    font-family: system-ui;\\\\n    font-size: var(--fontSize);\\\\n    line-height: calc(var(--fontSize) * 1.5);\\\\n    overflow-wrap: break-word;\\\\n    padding-inline: calc(var(--fontSize) / 2 + 1px);\\\\n  }\\\\n}\\\\n\\\\n:--heading {\\\\n  margin-block: 0;\\\\n}\\\\n\\\\na {\\\\n  color: rgb(0 0 100% / 90%);\\\\n\\\\n&:hover {\\\\n   color: rebeccapurple;\\\\n }\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -490,6 +566,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/nested/nested.postcss.css",
       ],
@@ -550,6 +627,7 @@ a:hover {
     Object {
       "mappings": "AAKA;EACE,gBAAgB;EAChB,mCAAsB;EACtB,yCAA4C;AAC9C;;AAEA;EACE,kBAAqB;EAArB,gBAAqB;EAArB,qBAAqB;AACvB;;AAEA;EACE;IACE,6BAAuB;IAAvB,uBAAuB;IACvB,iGAAsB;IACtB,eAA0B;IAA1B,0BAA0B;IAC1B,6BAAwC;IAAxC,wCAAwC;IACxC,qBAAyB;IACzB,kCAA+C;IAA/C,mCAA+C;IAA/C,6CAA+C;IAA/C,8CAA+C;EACjD;AACF;;AAEA;EACE,aAAe;EAAf,gBAAe;AACjB;;AAEA;EACE;AAKF;;AAHA;GACG,WAAoB;CACtB",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/basic.postcss.css",
       ],
@@ -608,7 +686,7 @@ exports[`"sourceMap" option true should generate source maps when previous loade
 import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\"body {\\\\n  font: 14px/1.5 Helvetica, arial, sans-serif;\\\\n}\\\\nbody #logo {\\\\n  border-radius: 5px;\\\\n}\\\\n/*# sourceMappingURL=test/fixtures/source-map/base.css.map */\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/base.styl\\"],\\"names\\":[],\\"mappings\\":\\"AAAA;EACE,2CAAwB;AAC1B;AAAE;EACE,kBAAe;AAEnB;AACA,4DAA4D\\",\\"sourcesContent\\":[\\"body {\\\\n  font: 14px/1.5 Helvetica, arial, sans-serif;\\\\n  #logo {\\\\n    border-radius: 5px;\\\\n  }\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\"body {\\\\n  font: 14px/1.5 Helvetica, arial, sans-serif;\\\\n}\\\\nbody #logo {\\\\n  border-radius: 5px;\\\\n}\\\\n/*# sourceMappingURL=test/fixtures/source-map/base.css.map */\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/base.styl\\"],\\"names\\":[],\\"mappings\\":\\"AAAA;EACE,2CAAwB;AAC1B;AAAE;EACE,kBAAe;AAEnB;AACA,4DAA4D\\",\\"sourcesContent\\":[\\"body {\\\\n  font: 14px/1.5 Helvetica, arial, sans-serif;\\\\n  #logo {\\\\n    border-radius: 5px;\\\\n  }\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -629,6 +707,7 @@ body #logo {
     Object {
       "mappings": "AAAA;EACE,2CAAwB;AAC1B;AAAE;EACE,kBAAe;AAEnB;AACA,4DAA4D",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/base.styl",
       ],
@@ -658,7 +737,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -676,6 +755,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/nested/nested.css",
       ],
@@ -698,6 +778,7 @@ Array [
     Object {
       "mappings": "AAEA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/basic.css",
       ],
@@ -726,7 +807,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -744,6 +825,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/nested/nested.css",
       ],
@@ -766,6 +848,7 @@ Array [
     Object {
       "mappings": "AAEA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/basic.css",
       ],
@@ -794,7 +877,7 @@ import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident
 var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);
 ___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___);
 // Module
-___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"]}]);
+___CSS_LOADER_EXPORT___.push([module.id, \\".class {\\\\n  color: red;\\\\n}\\\\n\\", \\"\\",{\\"version\\":3,\\"sources\\":[\\"webpack://source-map/basic.css\\"],\\"names\\":[],\\"mappings\\":\\"AAEA;EACE,UAAU;AACZ\\",\\"sourcesContent\\":[\\"@import \\\\\\"./nested/nested.css\\\\\\";\\\\n\\\\n.class {\\\\n  color: red;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}]);
 // Exports
 export default ___CSS_LOADER_EXPORT___;
 "
@@ -812,6 +895,7 @@ Array [
     Object {
       "mappings": "AAAA;EACE,WAAW;AACb",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/nested/nested.css",
       ],
@@ -834,6 +918,7 @@ Array [
     Object {
       "mappings": "AAEA;EACE,UAAU;AACZ",
       "names": Array [],
+      "sourceRoot": "",
       "sources": Array [
         "webpack://source-map/basic.css",
       ],
diff --git a/test/sourceMap-option.test.js b/test/sourceMap-option.test.js
index d484534..247ff51 100644
--- a/test/sourceMap-option.test.js
+++ b/test/sourceMap-option.test.js
@@ -164,6 +164,83 @@ describe('"sourceMap" option', () => {
       expect(getErrors(stats)).toMatchSnapshot('errors');
     });
 
+    it('should generate source maps when previous loader generates different source in source maps', async () => {
+      const absoluteSourceRoot = path.resolve(
+        __dirname,
+        'fixtures',
+        'source-map'
+      );
+      const absolutePath = path.resolve(absoluteSourceRoot, 'basic-1.css');
+      const relativePath = path.relative(
+        absoluteSourceRoot,
+        path.resolve(__dirname, 'fixtures', 'source-map', 'basic-2.css')
+      );
+
+      const compiler = getCompiler(
+        './source-map/basic.js',
+        {},
+        {
+          module: {
+            rules: [
+              {
+                test: /\.css$/i,
+                use: [
+                  {
+                    loader: path.resolve(__dirname, '../src'),
+                    options: { sourceMap: true },
+                  },
+                  {
+                    loader: path.resolve(
+                      __dirname,
+                      './fixtures/source-map-loader.js'
+                    ),
+                    options: {
+                      sourceMap: JSON.stringify({
+                        version: 3,
+                        sourceRoot: absoluteSourceRoot,
+                        sources: [
+                          // Absolute path
+                          absolutePath,
+                          // Relative path
+                          relativePath,
+                          // Absolute URL
+                          'https://example.com/foo.css',
+                          // Scheme-relative URL,
+                          '//example.com/foo.css',
+                          // Non-standard postcss syntax
+                          `</foo/bar/baz.css`,
+                        ],
+                        names: [],
+                        mappings: 'AAAA,6BAA6B;;AAE7B;EACE,UAAU;AACZ',
+                        file: absolutePath,
+                        sourcesContent: [
+                          '@import "./nested/nested.css";\n\n.class {\n  color: red;\n}\n',
+                          'a { color: red; }',
+                          'a { color: green; }',
+                          'a { color: blue; }',
+                          'a { color: azure; }',
+                        ],
+                      }),
+                    },
+                  },
+                ],
+              },
+            ],
+          },
+        }
+      );
+      const stats = await compile(compiler);
+
+      expect(getModuleSource('./source-map/basic.css', stats)).toMatchSnapshot(
+        'module'
+      );
+      expect(
+        getExecutedCode('main.bundle.js', compiler, stats)
+      ).toMatchSnapshot('result');
+      expect(getWarnings(stats)).toMatchSnapshot('warnings');
+      expect(getErrors(stats)).toMatchSnapshot('errors');
+    });
+
     it('should generate source maps when previous loader generates source maps with "sourceRoot"', async () => {
       const absoluteSourceRoot = path.resolve(
         __dirname,
@@ -422,8 +499,8 @@ describe('"sourceMap" option', () => {
 
       expect(chunkName).toBe(
         webpack.version[0] === '5'
-          ? 'main.b58b73eca7517a2128fd.bundle.js'
-          : 'main.1e45307f085c8aadaf4c.bundle.js'
+          ? 'main.f8a941345941e8190135.bundle.js'
+          : 'main.bad4e3fa71e9f3e4c073.bundle.js'
       );
       expect(
         getModuleSource('fixtures/source-map/basic.css', stats)
@@ -530,7 +607,7 @@ describe('"sourceMap" option', () => {
 
       expect(chunkName).toBe(
         // TODO still buggy on webpack@4
-        webpack.version[0] === '5' ? 'main.8189c1c4f956dd69079a.css' : chunkName
+        webpack.version[0] === '5' ? 'main.208dafd9be3442f7a927.css' : chunkName
       );
 
       expect(