Repository: webpack-contrib/source-map-loader (js)

Issue:
"File URL path must be absolute" on Windows
<!--
  Issues are so 🔥

  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.

  👉🏽 Need support, advice, or help? Don't open an issue!
  Head to StackOverflow or https://gitter.im/webpack/webpack.
-->

- Operating System: Windows
- Node Version: 13.*
- NPM Version: yarn 1.21.1 (does not matter)
- webpack Version: 4.*
- source-map-loader Version: 1.0.0

### Problem description (actual behavior)

On Windows, cryptic warnings are emitted, saying:

> WARNING in d:/my/library/dist/index.js
> Module Warning (from ../node_modules/source-map-loader/dist/cjs.js):
> File URL path must be absolute
>  @ ./myProgram.js 8:19-42

### Expected Behavior

No warning. Does not happen on Mac.

### How to reproduce?

No way to easily produce a minimal example of this problem, since the given warning is too cryptic.

### First Analysis

#### Step 1

I found out that the error message indicates one of two possible problems:
1. Incorrect/unrecognized file/path for path/file conversion (as seen [here](https://stackoverflow.com/questions/60800239/is-there-a-bug-in-node-js-pathtofileurl-fileurltopath))
2. An incorrect file URL on Windows, as explained in node documentation ([here](https://nodejs.org/api/fs.html#fs_url_object_support)): 
> On Windows, file: URLs with a host name convert to UNC paths, while file: URLs with drive letters convert to local absolute paths. file: URLs without a host name nor a drive letter will result in a throw


#### Step 2: Debugging source-map-loader

I went into `dist/index.js` and supplemented the two calls to `this.emitWarning(error)` with `console.error(error)`. After snooping around a bit more, I found that this makes the problem entirely visible:

I added `console.error('fetchFromUrl failed at', `>>${source}<<`, ':\n', error);` to [the `catch` block around line 93 of the original source](https://github.com/webpack-contrib/source-map-loader/blob/4d043f0e0ce270f8979f3f1efd5b7ce426793d64/src/index.js#L93) (NOTE: the `source` variable apparently is the url to the sourcemapped file); yielding error messages like this:

> fetchFromUrl failed at >>file:///webpack/universalModuleDefinition<< :
> TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must be absolute
>     at getPathFromURLWin32 (internal/url.js:1310:13)
>     at Object.fileURLToPath (internal/url.js:1342:22)
>     at fetchFromURL (d:\my\project\node_modules\source-map-loader\dist\utils.js:151:40)
>     at d:\my\project\node_modules\source-map-loader\dist\index.js:89:41
>     at Array.map (<anonymous>)
>     at Object.loader (d:\my\project\node_modules\source-map-loader\dist\index.js:77:57)


## Analysis Results

It does not seem to handle all kinds of "special" webpack urls. More invalid url examples include:

> file:///webpack/universalModuleDefinition
> file:///webpack/bootstrap
> file:///external "colors/safe"
> file:///external "core-js/modules/es.array.concat"
> file:///external "lodash/isObject"
> file:///external "core-js/modules/web.timers"

and many more...

Also, possibly related to the fact that the imported library is `umd`?

## Solutions?

Not sure what to do here. But getting rid of those annoying warnings would be a great start (as I am generally not too concerned about source maps to these sorts of files to begin with).


Pull request:
fix: webpack protocol
<!--
  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**
- [ ] new **feature**
- [ ] **code refactor**
- [x] **test update** <!-- if bug or feature is checked, this should be too -->
- [ ] **typo fix**
- [ ] **metadata update**

### Motivation / Use-Case

Fixes #116 

### Breaking Changes

No

### Additional Info

No


Patch:
diff --git a/src/index.js b/src/index.js
index 77c6da1..136228a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -82,6 +82,9 @@ export default async function loader(input, inputMap) {
           : null;
       const skipReading = originalSourceContent !== null;
 
+      // We do not skipReading here, because we need absolute paths in sources.
+      // This is necessary so that for sourceMaps with the same file structure in sources, name collisions do not occur.
+      // https://github.com/webpack-contrib/source-map-loader/issues/51
       try {
         ({ sourceURL, sourceContent } = await fetchFromURL(
           this,
diff --git a/src/utils.js b/src/utils.js
index f24a4f0..736460d 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -160,16 +160,14 @@ async function fetchFromURL(
       return { sourceContent };
     }
 
+    if (skipReading) {
+      return { sourceURL: url };
+    }
+
     if (protocol === 'file:') {
       const pathFromURL = urlUtils.fileURLToPath(url);
-
       const sourceURL = path.normalize(pathFromURL);
-
-      let sourceContent;
-
-      if (!skipReading) {
-        sourceContent = await fetchFromFilesystem(loaderContext, sourceURL);
-      }
+      const sourceContent = await fetchFromFilesystem(loaderContext, sourceURL);
 
       return { sourceURL, sourceContent };
     }


Test patch:
diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap
index d41f4d9..faa14bd 100644
--- a/test/__snapshots__/loader.test.js.snap
+++ b/test/__snapshots__/loader.test.js.snap
@@ -128,12 +128,7 @@ anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\
 }
 `;
 
-exports[`source-map-loader should process css sourceMap: warnings 1`] = `
-Array [
-  "ModuleWarning: Module Warning (from \`replaced original path\`):
-Failed to parse source map: \\"webpack:///./src/app/app.scss\\" URL is not supported",
-]
-`;
+exports[`source-map-loader should process css sourceMap: warnings 1`] = `Array []`;
 
 exports[`source-map-loader should process css sourceMap: warnings 2`] = `
 Array [
@@ -310,6 +305,28 @@ Failed to parse source map: \\"ftp://exampleurl.com\\" URL is not supported",
 ]
 `;
 
+exports[`source-map-loader should skip file protocol path if sourcesContent is set: css 1`] = `
+"// Some content
+ "
+`;
+
+exports[`source-map-loader should skip file protocol path if sourcesContent is set: errors 1`] = `Array []`;
+
+exports[`source-map-loader should skip file protocol path if sourcesContent is set: map 1`] = `
+Object {
+  "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+  "sources": Array [
+    "file:///external normal-file2.js",
+  ],
+  "sourcesContent": Array [
+    "export default 3;",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should skip file protocol path if sourcesContent is set: warnings 1`] = `Array []`;
+
 exports[`source-map-loader should skip invalid base64 SourceMap: css 1`] = `
 "without SourceMap
 // @sourceMappingURL=data:application/source-map;base64,\\"something invalid\\"
@@ -325,6 +342,34 @@ Failed to parse source map from 'undefined': SyntaxError: Unexpected end of JSON
 ]
 `;
 
+exports[`source-map-loader should skip webpack protocol path if sourcesContent is set: css 1`] = `
+"!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=0)}([function(e,t,r){\\"use strict\\";r.r(t),t.default=3}]);
+"
+`;
+
+exports[`source-map-loader should skip webpack protocol path if sourcesContent is set: errors 1`] = `Array []`;
+
+exports[`source-map-loader should skip webpack protocol path if sourcesContent is set: map 1`] = `
+Object {
+  "file": "main.js",
+  "mappings": "aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,+BClFrD,OAAe",
+  "names": Array [],
+  "sources": Array [
+    "webpack:///webpack/bootstrap",
+    "webpack:///./src/index.js",
+    "webpack:///external \\"$\\"",
+  ],
+  "sourcesContent": Array [
+    " //webpack bootstrap;",
+    "export default 3;",
+    "module.exports = $;",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should skip webpack protocol path if sourcesContent is set: warnings 1`] = `Array []`;
+
 exports[`source-map-loader should support absolute paths to sourcemaps: css 1`] = `
 "// Some content 
  "
diff --git a/test/fixtures/file-broken-protocol-path.js b/test/fixtures/file-broken-protocol-path.js
new file mode 100644
index 0000000..8157f16
--- /dev/null
+++ b/test/fixtures/file-broken-protocol-path.js
@@ -0,0 +1,2 @@
+// Some content
+ // # sourceMappingURL=file-broken-protocol-path.js.map
diff --git a/test/fixtures/file-broken-protocol-path.js.map b/test/fixtures/file-broken-protocol-path.js.map
new file mode 100644
index 0000000..f519c01
--- /dev/null
+++ b/test/fixtures/file-broken-protocol-path.js.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["file:///external normal-file2.js"],"sourcesContent":["export default 3;"],"mappings":"CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA"}
diff --git a/test/fixtures/webpack/main.js b/test/fixtures/webpack/main.js
new file mode 100644
index 0000000..0ef79a0
--- /dev/null
+++ b/test/fixtures/webpack/main.js
@@ -0,0 +1,2 @@
+!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t),t.default=3}]);
+//# sourceMappingURL=main.js.map
diff --git a/test/fixtures/webpack/main.js.map b/test/fixtures/webpack/main.js.map
new file mode 100644
index 0000000..9009e4d
--- /dev/null
+++ b/test/fixtures/webpack/main.js.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.js","webpack:///external \"$\""],"names":[],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,+BClFrD,OAAe","file":"main.js","sourcesContent":[" //webpack bootstrap;","export default 3;","module.exports = $;"],"sourceRoot":""}
diff --git a/test/loader.test.js b/test/loader.test.js
index 6f74f06..fb69023 100644
--- a/test/loader.test.js
+++ b/test/loader.test.js
@@ -154,6 +154,32 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
+  it('should skip file protocol path if sourcesContent is set', async () => {
+    const testId = 'file-broken-protocol-path.js';
+    const compiler = getCompiler(testId);
+    const stats = await compile(compiler);
+    const codeFromBundle = getCodeFromBundle(stats, compiler);
+
+    expect(codeFromBundle.map).toBeDefined();
+    expect(normalizeMap(codeFromBundle.map)).toMatchSnapshot('map');
+    expect(codeFromBundle.css).toMatchSnapshot('css');
+    expect(getWarnings(stats)).toMatchSnapshot('warnings');
+    expect(getErrors(stats)).toMatchSnapshot('errors');
+  });
+
+  it('should skip webpack protocol path if sourcesContent is set', async () => {
+    const testId = 'webpack/main.js';
+    const compiler = getCompiler(testId);
+    const stats = await compile(compiler);
+    const codeFromBundle = getCodeFromBundle(stats, compiler);
+
+    expect(codeFromBundle.map).toBeDefined();
+    expect(normalizeMap(codeFromBundle.map)).toMatchSnapshot('map');
+    expect(codeFromBundle.css).toMatchSnapshot('css');
+    expect(getWarnings(stats)).toMatchSnapshot('warnings');
+    expect(getErrors(stats)).toMatchSnapshot('errors');
+  });
+
   it('should use last SourceMap directive', async () => {
     const testId = 'multi-source-map.js';
     const compiler = getCompiler(testId);