Repository: parcel-bundler/parcel (js)

Issue:
sourcemaps always forced to be relative even for absolute paths and http/https/file locations
    "parcel-bundler": "^1.12.4",

In parcel-bundler/src/utils/loadSourceMap.js The code always assumes that source maps are relative to the asset it is describing, which is not always the case.

For example, if the source map sources item starts with "/" it should probably not use the relative scheme. Source maps that start with http/https should be passed through and those with file: could be passed through or resolved (parcel could choose or perhaps a switch).

For example, this fixes the absolute path issue:

```js
     if(source.startsWith("file:///")) source = source.slice(7) // triple slash is also absolute
    try {
              let sourceFile = path.isAbsolute(source) ? // CHECK FOR ABSOLUTE PATH HERE
                source:
                path.join(
                path.dirname(filename),
                sourceMap.sourceRoot || '',
                source
              );
```

Is this something that makes sense to fix?

Pull request description:
support absolute paths and file:// extension
<!---
Thanks for filing a pull request 😄 ! Before you submit, please read the following:

Search open/closed issues before submitting since someone might have pushed the same thing before!
-->

# ↪️ Pull Request

<!---
Provide a general summary of the pull request here
Please look for any issues that this PR resolves and tag them in the PR.
-->

This PR implements the removal of `file://` and support for absolute paths in sourcemap comments.

This does seem to be a very niche edge case but apprarently it happens so this PR implements it.

Fixes #4316

## ✔️ PR Todo

- [x] Added/updated unit tests for this change
- [x] Filled out test instructions (In case there aren't any unit tests)
- [x] Included links to related issues/PRs


Patch:
diff --git a/packages/core/utils/src/path.js b/packages/core/utils/src/path.js
index 2464c1158..66474824f 100644
--- a/packages/core/utils/src/path.js
+++ b/packages/core/utils/src/path.js
@@ -2,8 +2,13 @@
 import type {FilePath} from '@parcel/types';
 import path from 'path';
 
+const ABSOLUTE_PATH_REGEX = /^([a-zA-Z]:){0,1}[\\/]+/;
 const SEPARATOR_REGEX = /[\\]+/g;
 
+export function isAbsolute(filepath: string): boolean {
+  return ABSOLUTE_PATH_REGEX.test(filepath);
+}
+
 export function normalizeSeparators(filePath: FilePath): FilePath {
   return filePath.replace(SEPARATOR_REGEX, '/');
 }
diff --git a/packages/core/utils/src/sourcemap.js b/packages/core/utils/src/sourcemap.js
index 672a1bbe7..4ff6822a4 100644
--- a/packages/core/utils/src/sourcemap.js
+++ b/packages/core/utils/src/sourcemap.js
@@ -2,7 +2,7 @@
 import type {FileSystem} from '@parcel/fs';
 import SourceMap from '@parcel/source-map';
 import path from 'path';
-import {normalizeSeparators} from './path';
+import {normalizeSeparators, isAbsolute} from './path';
 
 export const SOURCEMAP_RE: RegExp = /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
 const DATA_URL_RE = /^data:[^;]+(?:;charset=[^;]+)?;base64,(.*)/;
@@ -30,15 +30,24 @@ export async function loadSourceMapUrl(
   if (match) {
     let url = match[1].trim();
     let dataURLMatch = url.match(DATA_URL_RE);
-    filename = dataURLMatch ? filename : path.join(path.dirname(filename), url);
+
+    let mapFilePath;
+    if (dataURLMatch) {
+      mapFilePath = filename;
+    } else {
+      mapFilePath = url.replace(/^file:\/\//, '');
+      mapFilePath = isAbsolute(mapFilePath)
+        ? mapFilePath
+        : path.join(path.dirname(filename), mapFilePath);
+    }
 
     return {
       url,
-      filename,
+      filename: mapFilePath,
       map: JSON.parse(
         dataURLMatch
           ? Buffer.from(dataURLMatch[1], 'base64').toString()
-          : await fs.readFile(filename, 'utf8'),
+          : await fs.readFile(mapFilePath, 'utf8'),
       ),
     };
   }


Test patch:
diff --git a/packages/core/utils/test/input/sourcemap/referenced-min.js b/packages/core/utils/test/input/sourcemap/referenced-min.js
index 210b0cd28..1deab33cd 100644
--- a/packages/core/utils/test/input/sourcemap/referenced-min.js
+++ b/packages/core/utils/test/input/sourcemap/referenced-min.js
@@ -1,2 +1,2 @@
 function hello(){var l="Hello",o="world";console.log(l+" "+o+"!")}hello();
-//# sourceMappingURL=referenced-min.js.map
+//# sourceMappingURL=file://referenced-min.js.map
diff --git a/packages/core/utils/test/sourcemap.test.js b/packages/core/utils/test/sourcemap.test.js
index 37c35efd9..55528fe0f 100644
--- a/packages/core/utils/test/sourcemap.test.js
+++ b/packages/core/utils/test/sourcemap.test.js
@@ -46,10 +46,10 @@ describe('loadSourceMap', () => {
     let contents = fs.readFileSync(filename, 'utf-8');
 
     let foundMap = await loadSourceMapUrl(fs, filename, contents);
-    assert.equal(foundMap.url, 'referenced-min.js.map');
+    assert.equal(foundMap.url, 'file://referenced-min.js.map');
     assert.equal(
       foundMap.filename,
-      path.join(path.dirname(filename), foundMap.url),
+      path.join(__dirname, 'input/sourcemap/referenced-min.js.map'),
     );
     assert.deepEqual(foundMap.map, {
       version: 3,