Repository: webpack-contrib/source-map-loader
Pull request #109 (2020-05-25 17:04:25)

Refactor
<!--
  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:

- [ ] **bugfix**
- [ ] 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

Refactor code + tests

### Breaking Changes

No

### Additional Info

No

codecov[bot]: # [Codecov](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109?src=pr&el=h1) Report
> Merging [#109](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109?src=pr&el=desc) into [master](https://codecov.io/gh/webpack-contrib/source-map-loader/commit/2ceba27270ac53751adb5a71352dc298fcecb106&el=desc) will **increase** coverage by `0.41%`.
> The diff coverage is `95.00%`.

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

```diff
@@            Coverage Diff             @@
##           master     #109      +/-   ##
==========================================
+ Coverage   95.62%   96.03%   +0.41%     
==========================================
  Files           3        3              
  Lines         137      126      -11     
  Branches       29       33       +4     
==========================================
- Hits          131      121      -10     
+ Misses          6        4       -2     
- Partials        0        1       +1     
```


| [Impacted Files](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/utils.js](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109/diff?src=pr&el=tree#diff-c3JjL3V0aWxzLmpz) | `93.15% <91.37%> (+1.48%)` | :arrow_up: |
| [src/index.js](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109/diff?src=pr&el=tree#diff-c3JjL2luZGV4Lmpz) | `100.00% <100.00%> (+2.32%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109?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/source-map-loader/pull/109?src=pr&el=footer). Last update [2ceba27...1defda3](https://codecov.io/gh/webpack-contrib/source-map-loader/pull/109?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/index.js b/src/index.js
index 64a5211..8c7f703 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,27 +1,14 @@
 /*
-	MIT License http://www.opensource.org/licenses/mit-license.php
-	Author Tobias Koppers @sokra
+  MIT License http://www.opensource.org/licenses/mit-license.php
+  Author Tobias Koppers @sokra
 */
 import path from 'path';
 
-import { promisify } from 'util';
-
 import validateOptions from 'schema-utils';
-import parseDataURL from 'data-urls';
-
-import { SourceMapConsumer } from 'source-map';
-
-import { labelToName, decode } from 'whatwg-encoding';
-import { getOptions, urlToRequest } from 'loader-utils';
+import { getOptions } from 'loader-utils';
 
 import schema from './options.json';
-import {
-  flattenSourceMap,
-  readFile,
-  getContentFromSourcesContent,
-  getSourceMappingUrl,
-  getRequestedUrl,
-} from './utils';
+import { getSourceMappingURL, fetchFromURL, flattenSourceMap } from './utils';
 
 export default async function loader(input, inputMap) {
   const options = getOptions(this);
@@ -31,179 +18,117 @@ export default async function loader(input, inputMap) {
     baseDataPath: 'options',
   });
 
-  let { url } = getSourceMappingUrl(input);
-  const { replacementString } = getSourceMappingUrl(input);
+  const { sourceMappingURL, replacementString } = getSourceMappingURL(input);
   const callback = this.async();
 
-  if (!url) {
+  if (!sourceMappingURL) {
     callback(null, input, inputMap);
 
     return;
   }
 
-  const { fs, context, resolve, addDependency, emitWarning } = this;
-  const resolver = promisify(resolve);
-  const reader = promisify(fs.readFile).bind(fs);
+  let sourceURL;
+  let sourceContent;
 
-  if (url.toLowerCase().startsWith('data:')) {
-    const dataURL = parseDataURL(url);
-
-    if (dataURL) {
-      let map;
-
-      try {
-        dataURL.encodingName =
-          labelToName(dataURL.mimeType.parameters.get('charset')) || 'UTF-8';
-
-        map = decode(dataURL.body, dataURL.encodingName);
-        map = JSON.parse(map.replace(/^\)\]\}'/, ''));
-      } catch (error) {
-        emitWarning(
-          `Cannot parse inline SourceMap with Charset ${dataURL.encodingName}: ${error}`
-        );
+  try {
+    ({ sourceURL, sourceContent } = await fetchFromURL(
+      this,
+      this.context,
+      sourceMappingURL
+    ));
+  } catch (error) {
+    this.emitWarning(error);
 
-        callback(null, input, inputMap);
+    callback(null, input, inputMap);
 
-        return;
-      }
+    return;
+  }
 
-      processMap(map, context, callback);
+  if (sourceURL) {
+    this.addDependency(sourceURL);
+  }
 
-      return;
-    }
+  let map;
 
-    emitWarning(`Cannot parse inline SourceMap: ${url}`);
+  try {
+    map = JSON.parse(sourceContent.replace(/^\)\]\}'/, ''));
+  } catch (parseError) {
+    this.emitWarning(
+      new Error(`Cannot parse source map from '${sourceURL}': ${parseError}`)
+    );
 
     callback(null, input, inputMap);
 
     return;
   }
 
-  try {
-    url = getRequestedUrl(url);
-  } catch (error) {
-    emitWarning(error.message);
+  const context = sourceURL ? path.dirname(sourceURL) : this.context;
 
-    callback(null, input, inputMap);
-
-    return;
+  if (map.sections) {
+    // eslint-disable-next-line no-param-reassign
+    map = await flattenSourceMap(map);
   }
 
-  let urlResolved;
+  const resolvedSources = await Promise.all(
+    map.sources.map(async (source, i) => {
+      // eslint-disable-next-line no-shadow
+      let sourceURL;
+      // eslint-disable-next-line no-shadow
+      let sourceContent;
 
-  try {
-    urlResolved = await resolver(context, urlToRequest(url, true));
-  } catch (resolveError) {
-    emitWarning(`Cannot find SourceMap '${url}': ${resolveError}`);
+      const originalSourceContent =
+        map.sourcesContent && map.sourcesContent[i]
+          ? map.sourcesContent[i]
+          : null;
+      const skipReading = originalSourceContent !== null;
 
-    callback(null, input, inputMap);
+      try {
+        ({ sourceURL, sourceContent } = await fetchFromURL(
+          this,
+          context,
+          source,
+          map.sourceRoot,
+          skipReading
+        ));
+      } catch (error) {
+        this.emitWarning(error);
 
-    return;
-  }
+        sourceURL = source;
+      }
 
-  urlResolved = urlResolved.toString();
+      if (originalSourceContent) {
+        sourceContent = originalSourceContent;
+      }
 
-  addDependency(urlResolved);
+      if (sourceURL) {
+        this.addDependency(sourceURL);
+      }
 
-  const content = await reader(urlResolved);
-  let map;
+      return { sourceURL, sourceContent };
+    })
+  );
 
-  try {
-    map = JSON.parse(content.toString());
-  } catch (parseError) {
-    emitWarning(`Cannot parse SourceMap '${url}': ${parseError}`);
+  const newMap = { ...map };
 
-    callback(null, input, inputMap);
+  newMap.sources = [];
+  newMap.sourcesContent = [];
 
-    return;
-  }
+  delete newMap.sourceRoot;
 
-  processMap(map, path.dirname(urlResolved), callback);
-
-  // eslint-disable-next-line no-shadow
-  async function processMap(map, context, callback) {
-    if (map.sections) {
-      // eslint-disable-next-line no-param-reassign
-      map = await flattenSourceMap(map);
-    }
-
-    const mapConsumer = await new SourceMapConsumer(map);
-
-    let resolvedSources;
-
-    try {
-      resolvedSources = await Promise.all(
-        map.sources.map(async (source) => {
-          const fullPath = map.sourceRoot
-            ? `${map.sourceRoot}${path.sep}${source}`
-            : source;
-
-          const originalData = getContentFromSourcesContent(
-            mapConsumer,
-            source
-          );
-
-          if (path.isAbsolute(fullPath)) {
-            return originalData
-              ? { source: fullPath, content: originalData }
-              : readFile(fullPath, emitWarning, reader);
-          }
-
-          let fullPathResolved;
-
-          try {
-            fullPathResolved = await resolver(
-              context,
-              urlToRequest(fullPath, true)
-            );
-          } catch (resolveError) {
-            emitWarning(`Cannot find source file '${source}': ${resolveError}`);
-
-            return originalData
-              ? {
-                  source: fullPath,
-                  content: originalData,
-                }
-              : { source: fullPath, content: null };
-          }
-
-          return originalData
-            ? {
-                source: fullPathResolved,
-                content: originalData,
-              }
-            : readFile(fullPathResolved, emitWarning, reader);
-        })
-      );
-    } catch (error) {
-      emitWarning(error);
-
-      callback(null, input, inputMap);
-    }
-
-    const resultMap = { ...map };
-    resultMap.sources = [];
-    resultMap.sourcesContent = [];
-
-    delete resultMap.sourceRoot;
-
-    resolvedSources.forEach((res) => {
-      // eslint-disable-next-line no-param-reassign
-      resultMap.sources.push(path.normalize(res.source));
-      resultMap.sourcesContent.push(res.content);
-
-      if (res.source) {
-        addDependency(res.source);
-      }
-    });
+  resolvedSources.forEach((source) => {
+    // eslint-disable-next-line no-shadow
+    const { sourceURL, sourceContent } = source;
 
-    const sourcesContentIsEmpty =
-      resultMap.sourcesContent.filter((entry) => !!entry).length === 0;
+    newMap.sources.push(sourceURL || '');
+    newMap.sourcesContent.push(sourceContent || '');
+  });
 
-    if (sourcesContentIsEmpty) {
-      delete resultMap.sourcesContent;
-    }
+  const sourcesContentIsEmpty =
+    newMap.sourcesContent.filter((entry) => Boolean(entry)).length === 0;
 
-    callback(null, input.replace(replacementString, ''), resultMap);
+  if (sourcesContentIsEmpty) {
+    delete newMap.sourcesContent;
   }
+
+  callback(null, input.replace(replacementString, ''), newMap);
 }
diff --git a/src/utils.js b/src/utils.js
index 283ef62..fe105d7 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,8 +1,11 @@
 import path from 'path';
-
 import urlUtils from 'url';
 
 import sourceMap from 'source-map';
+import parseDataURL from 'data-urls';
+import { labelToName, decode } from 'whatwg-encoding';
+
+import { urlToRequest } from 'loader-utils';
 
 // Matches only the last occurrence of sourceMappingURL
 const innerRegex = /\s*[#@]\s*sourceMappingURL\s*=\s*([^\s'"]*)\s*/;
@@ -28,15 +31,11 @@ const sourceMappingURLRegex = RegExp(
 
 async function flattenSourceMap(map) {
   const consumer = await new sourceMap.SourceMapConsumer(map);
-  let generatedMap;
-
-  if (map.file) {
-    generatedMap = new sourceMap.SourceMapGenerator({
-      file: map.file,
-    });
-  } else {
-    generatedMap = new sourceMap.SourceMapGenerator();
-  }
+  const generatedMap = map.file
+    ? new sourceMap.SourceMapGenerator({
+        file: map.file,
+      })
+    : new sourceMap.SourceMapGenerator();
 
   consumer.sources.forEach((sourceFile) => {
     const sourceContent = consumer.sourceContentFor(sourceFile, true);
@@ -69,80 +68,131 @@ async function flattenSourceMap(map) {
   return generatedMap.toJSON();
 }
 
-async function readFile(fullPath, emitWarning, reader) {
-  let content;
+function getSourceMappingURL(code) {
+  const lines = code.split(/^/m);
+  let match;
 
-  try {
-    content = await reader(fullPath);
-  } catch (readFileError) {
-    emitWarning(`Cannot open source file '${fullPath}': ${readFileError}`);
+  for (let i = lines.length - 1; i >= 0; i--) {
+    match = lines[i].match(sourceMappingURLRegex);
+    if (match) {
+      break;
+    }
+  }
+
+  return {
+    sourceMappingURL: match ? match[1] || match[2] || '' : null,
+    replacementString: match ? match[0] : null,
+  };
+}
 
-    return { source: null, content: null };
+function getAbsolutePath(context, url, sourceRoot) {
+  const request = urlToRequest(url, true);
+
+  if (sourceRoot) {
+    if (path.isAbsolute(sourceRoot)) {
+      return path.join(sourceRoot, request);
+    }
+
+    return path.join(context, urlToRequest(sourceRoot, true), request);
   }
 
-  return { source: fullPath, content: content.toString() };
+  return path.join(context, request);
 }
 
-function getContentFromSourcesContent(consumer, source) {
-  return consumer.sourceContentFor(source, true);
+function fetchFromDataURL(loaderContext, sourceURL) {
+  const dataURL = parseDataURL(sourceURL);
+
+  if (dataURL) {
+    dataURL.encodingName =
+      labelToName(dataURL.mimeType.parameters.get('charset')) || 'UTF-8';
+
+    return decode(dataURL.body, dataURL.encodingName);
+  }
+
+  throw new Error(`Can not parse inline source map: ${sourceURL}`);
 }
 
-function isUrlRequest(url) {
-  // An URL is not an request if
+async function fetchFromFilesystem(loaderContext, sourceURL) {
+  let buffer;
 
+  try {
+    buffer = await new Promise((resolve, reject) => {
+      loaderContext.fs.readFile(sourceURL, (error, data) => {
+        if (error) {
+          return reject(error);
+        }
+
+        return resolve(data);
+      });
+    });
+  } catch (error) {
+    throw new Error(`Cannot read '${sourceURL}' file: ${error}`);
+  }
+
+  return buffer.toString();
+}
+
+async function fetchFromURL(
+  loaderContext,
+  context,
+  url,
+  sourceRoot,
+  skipReading = false
+) {
   // 1. It's an absolute url and it is not `windows` path like `C:\dir\file`
   if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !path.win32.isAbsolute(url)) {
-    return false;
+    const { protocol } = urlUtils.parse(url);
+
+    if (protocol === 'data:') {
+      const sourceContent = fetchFromDataURL(loaderContext, url);
+
+      return { sourceContent };
+    }
+
+    if (protocol === 'file:') {
+      const pathFromURL = urlUtils.fileURLToPath(url);
+      const sourceURL = getAbsolutePath(context, pathFromURL, sourceRoot);
+
+      let sourceContent;
+
+      if (!skipReading) {
+        sourceContent = await fetchFromFilesystem(loaderContext, sourceURL);
+      }
+
+      return { sourceURL, sourceContent };
+    }
+
+    throw new Error(`Absolute '${url}' URL is not supported`);
   }
 
-  // 2. It's a protocol-relative
+  // 2. It's a scheme-relative
   if (/^\/\//.test(url)) {
-    return false;
+    throw new Error(`Scheme-relative '${url}' URL is not supported`);
   }
 
-  return true;
-}
+  // 3. Absolute path
+  if (path.isAbsolute(url)) {
+    const sourceURL = path.normalize(url);
 
-function getSourceMappingUrl(code) {
-  const lines = code.split(/^/m);
-  let match;
+    let sourceContent;
 
-  for (let i = lines.length - 1; i >= 0; i--) {
-    match = lines[i].match(sourceMappingURLRegex);
-    if (match) {
-      break;
+    if (!skipReading) {
+      sourceContent = await fetchFromFilesystem(loaderContext, sourceURL);
     }
-  }
-
-  return {
-    url: match ? match[1] || match[2] || '' : null,
-    replacementString: match ? match[0] : null,
-  };
-}
 
-function getRequestedUrl(url) {
-  if (isUrlRequest(url)) {
-    return url;
+    return { sourceURL, sourceContent };
   }
 
-  const { protocol } = urlUtils.parse(url);
+  // 4. Relative path
+  const sourceURL = getAbsolutePath(context, url, sourceRoot);
 
-  if (protocol !== 'file:') {
-    throw new Error(`URL scheme not supported: ${protocol}`);
-  }
+  let sourceContent;
 
-  try {
-    return urlUtils.fileURLToPath(url);
-  } catch (error) {
-    throw new Error(error);
+  if (!skipReading) {
+    sourceContent = await fetchFromFilesystem(loaderContext, sourceURL);
   }
+
+  return { sourceURL, sourceContent };
 }
 
-export {
-  flattenSourceMap,
-  readFile,
-  getContentFromSourcesContent,
-  isUrlRequest,
-  getSourceMappingUrl,
-  getRequestedUrl,
-};
+export { getSourceMappingURL, fetchFromURL, flattenSourceMap };


Test patch:
diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap
index e17faf2..345920e 100644
--- a/test/__snapshots__/loader.test.js.snap
+++ b/test/__snapshots__/loader.test.js.snap
@@ -17,6 +17,116 @@ exports[`source-map-loader should leave normal files with fake source-map untouc
 
 exports[`source-map-loader should leave normal files with fake source-map untouched: warnings 1`] = `Array []`;
 
+exports[`source-map-loader should process css sourceMap: css 1`] = `
+"* {
+  box-sizing: border-box; }
+
+.row {
+  display: flex;
+  margin-right: -15px;
+  margin-left: -15px; }
+
+.col-inner {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #fff;
+  height: 50px;
+  background: coral; }
+
+.col-s3 {
+  flex: 0 1 25%;
+  padding: 0 15px; }
+
+
+"
+`;
+
+exports[`source-map-loader should process css sourceMap: css 2`] = `
+"// Skip SourcesContent in SourceMap
+// comment
+"
+`;
+
+exports[`source-map-loader should process css sourceMap: errors 1`] = `Array []`;
+
+exports[`source-map-loader should process css sourceMap: errors 2`] = `Array []`;
+
+exports[`source-map-loader should process css sourceMap: map 1`] = `
+Object {
+  "file": "app.css",
+  "mappings": "AAAA;EACE,sBAAsB;;AAGxB;EACE,aAAa;EACb,mBAAmB;EACnB,kBAAkB;;AAIlB;EACE,aAAa;EACb,mBAAmB;EACnB,uBAAuB;EACvB,WAAW;EACX,YAAY;EACZ,iBAAiB;;AAInB;EACE,aAAa;EACb,eAAe",
+  "names": Array [],
+  "sources": Array [
+    "webpack:///./src/app/app.scss",
+  ],
+  "sourcesContent": Array [
+    "* {
+  box-sizing: border-box;
+}
+
+.row {
+  display: flex;
+  margin-right: -15px;
+  margin-left: -15px;
+}
+
+.col {
+  &-inner {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    height: 50px;
+    background: coral;
+    //background: red;
+  }
+
+  &-s3 {
+    flex: 0 1 25%;
+    padding: 0 15px;
+  }
+}
+",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should process css sourceMap: map 2`] = `
+Object {
+  "mappings": "AAAA",
+  "sources": Array [
+    "/test/fixtures/normal-file.js - (normalized for test)",
+    "unresolved-file.js",
+    "/test/fixtures/normal-file2.js - (normalized for test)",
+  ],
+  "sourcesContent": Array [
+    "without SourceMap",
+    "",
+    "// without SourceMap
+anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\\" */\\";
+// comment
+",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should process css sourceMap: warnings 1`] = `
+Array [
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'webpack:///./src/app/app.scss' URL is not supported",
+]
+`;
+
+exports[`source-map-loader should process css sourceMap: warnings 2`] = `
+Array [
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Cannot read '/test/fixtures/unresolved-file.js' file: Error: ENOENT: no such file or directory, open '/test/fixtures/unresolved-file.js'",
+]
+`;
+
 exports[`source-map-loader should process external SourceMaps (external sources): css 1`] = `
 "with SourceMap
 // comment"
@@ -52,7 +162,7 @@ Object {
   "file": "external-source-map.js",
   "mappings": "AAAA",
   "sources": Array [
-    "external-source-map.txt",
+    "/test/fixtures/external-source-map.txt - (normalized for test)",
   ],
   "sourcesContent": Array [
     "with SourceMap",
@@ -61,12 +171,7 @@ Object {
 }
 `;
 
-exports[`source-map-loader should process external SourceMaps: warnings 1`] = `
-Array [
-  "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot find source file 'external-source-map.txt': Error: Can't resolve './external-source-map.txt' in '/test/fixtures'",
-]
-`;
+exports[`source-map-loader should process external SourceMaps: warnings 1`] = `Array []`;
 
 exports[`source-map-loader should process inlined SourceMaps with charset: css 1`] = `
 "with SourceMap
@@ -103,7 +208,7 @@ Object {
   "file": "inline-source-map.js",
   "mappings": "AAAA",
   "sources": Array [
-    "inline-source-map.txt",
+    "/test/fixtures/inline-source-map.txt - (normalized for test)",
   ],
   "sourcesContent": Array [
     "with SourceMap",
@@ -112,10 +217,45 @@ Object {
 }
 `;
 
-exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `
+exports[`source-map-loader should process inlined SourceMaps: warnings 1`] = `Array []`;
+
+exports[`source-map-loader should process inlined sources: css 1`] = `
+"// Inline Sources in SourceMap
+// comment
+"
+`;
+
+exports[`source-map-loader should process inlined sources: errors 1`] = `Array []`;
+
+exports[`source-map-loader should process inlined sources: map 1`] = `
+Object {
+  "file": "inline-sources.js",
+  "mappings": "AAAA",
+  "sources": Array [
+    "",
+  ],
+  "sourcesContent": Array [
+    "some kind content",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should process inlined sources: warnings 1`] = `Array []`;
+
+exports[`source-map-loader should process protocol-relative-url-path: css 1`] = `
+"// Some content
+// # sourceMappingURL=//sampledomain.com/external-source-map2.map
+// comment
+"
+`;
+
+exports[`source-map-loader should process protocol-relative-url-path: errors 1`] = `Array []`;
+
+exports[`source-map-loader should process protocol-relative-url-path: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot find source file 'inline-source-map.txt': Error: Can't resolve './inline-source-map.txt' in '/test/fixtures'",
+Scheme-relative '//sampledomain.com/external-source-map2.map' URL is not supported",
 ]
 `;
 
@@ -131,7 +271,7 @@ exports[`source-map-loader should reject http SourceMaps: errors 1`] = `Array []
 exports[`source-map-loader should reject http SourceMaps: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) URL scheme not supported: http:",
+Absolute 'http://sampledomain.com/external-source-map2.map' URL is not supported",
 ]
 `;
 
@@ -139,6 +279,22 @@ exports[`source-map-loader should reject not exist file: SourceMaps: errors 1`]
 
 exports[`source-map-loader should reject not exist file: SourceMaps: warnings 1`] = `"TypeError [ERR_INVALID_FILE"`;
 
+exports[`source-map-loader should reject not support url: css 1`] = `
+"// with SourceMap
+//#sourceMappingURL=ftp://exampleurl.com
+// comment
+"
+`;
+
+exports[`source-map-loader should reject not support url: errors 1`] = `Array []`;
+
+exports[`source-map-loader should reject not support url: warnings 1`] = `
+Array [
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'ftp://exampleurl.com' URL is not supported",
+]
+`;
+
 exports[`source-map-loader should skip invalid base64 SourceMap: css 1`] = `
 "without SourceMap
 // @sourceMappingURL=data:application/source-map;base64,\\"something invalid\\"
@@ -150,10 +306,37 @@ exports[`source-map-loader should skip invalid base64 SourceMap: errors 1`] = `A
 exports[`source-map-loader should skip invalid base64 SourceMap: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot parse inline SourceMap with Charset UTF-8: SyntaxError: Unexpected end of JSON input",
+Cannot parse source map from 'undefined': SyntaxError: Unexpected end of JSON input",
 ]
 `;
 
+exports[`source-map-loader should support absolute paths to sourcemaps: css 1`] = `
+"// Some content 
+ "
+`;
+
+exports[`source-map-loader should support absolute paths to sourcemaps: errors 1`] = `Array []`;
+
+exports[`source-map-loader should support absolute paths to sourcemaps: map 1`] = `
+Object {
+  "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+  "sources": Array [
+    "/test/fixtures/normal-file.js - (normalized for test)",
+    "/test/fixtures/normal-file2.js - (normalized for test)",
+  ],
+  "sourcesContent": Array [
+    "without SourceMap",
+    "// without SourceMap
+anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\\" */\\";
+// comment
+",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should support absolute paths to sourcemaps: warnings 1`] = `Array []`;
+
 exports[`source-map-loader should support absolute sourceRoot paths in sourcemaps: css 1`] = `
 "with SourceMap
 // comment"
@@ -210,6 +393,82 @@ Object {
 
 exports[`source-map-loader should support indexed sourcemaps: warnings 1`] = `Array []`;
 
+exports[`source-map-loader should support mixed paths in sources with sourceRoot: css 1`] = `
+"// Some content 
+ "
+`;
+
+exports[`source-map-loader should support mixed paths in sources with sourceRoot: errors 1`] = `Array []`;
+
+exports[`source-map-loader should support mixed paths in sources with sourceRoot: map 1`] = `
+Object {
+  "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+  "sources": Array [
+    "/test/fixtures/normal-file.js - (normalized for test)",
+    "/test/fixtures/normal-file2.js - (normalized for test)",
+    "http://path-to-map.com",
+    "ftp://path-to-map.com",
+  ],
+  "sourcesContent": Array [
+    "without SourceMap",
+    "// without SourceMap
+anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\\" */\\";
+// comment
+",
+    "",
+    "",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should support mixed paths in sources with sourceRoot: warnings 1`] = `
+Array [
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'ftp://path-to-map.com' URL is not supported",
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'http://path-to-map.com' URL is not supported",
+]
+`;
+
+exports[`source-map-loader should support mixed paths in sources without sourceRoot: css 1`] = `
+"// Some content 
+ "
+`;
+
+exports[`source-map-loader should support mixed paths in sources without sourceRoot: errors 1`] = `Array []`;
+
+exports[`source-map-loader should support mixed paths in sources without sourceRoot: map 1`] = `
+Object {
+  "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
+  "sources": Array [
+    "/test/fixtures/normal-file.js - (normalized for test)",
+    "/test/fixtures/normal-file2.js - (normalized for test)",
+    "http://path-to-map.com",
+    "ftp://path-to-map.com",
+  ],
+  "sourcesContent": Array [
+    "without SourceMap",
+    "// without SourceMap
+anInvalidDirective = \\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\\" */\\";
+// comment
+",
+    "",
+    "",
+  ],
+  "version": 3,
+}
+`;
+
+exports[`source-map-loader should support mixed paths in sources without sourceRoot: warnings 1`] = `
+Array [
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'ftp://path-to-map.com' URL is not supported",
+  "ModuleWarning: Module Warning (from \`replaced original path\`):
+Absolute 'http://path-to-map.com' URL is not supported",
+]
+`;
+
 exports[`source-map-loader should support relative sourceRoot paths in sourcemaps: css 1`] = `
 "with SourceMap
 // comment"
@@ -247,7 +506,7 @@ Object {
   "file": "inline-source-map.js",
   "mappings": "AAAA",
   "sources": Array [
-    "inline-source-map.txt",
+    "/test/fixtures/inline-source-map.txt - (normalized for test)",
   ],
   "sourcesContent": Array [
     "with SourceMap",
@@ -256,12 +515,7 @@ Object {
 }
 `;
 
-exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `
-Array [
-  "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot find source file 'inline-source-map.txt': Error: Can't resolve './inline-source-map.txt' in '/test/fixtures'",
-]
-`;
+exports[`source-map-loader should use last SourceMap directive: warnings 1`] = `Array []`;
 
 exports[`source-map-loader should warn on invalid SourceMap: css 1`] = `
 "with SourceMap
@@ -274,7 +528,7 @@ exports[`source-map-loader should warn on invalid SourceMap: errors 1`] = `Array
 exports[`source-map-loader should warn on invalid SourceMap: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot parse SourceMap 'invalid-source-map.map': SyntaxError: Unexpected string in JSON at position 102",
+Cannot parse source map from '/test/fixtures/invalid-source-map.map': SyntaxError: Unexpected string in JSON at position 102",
 ]
 `;
 
@@ -289,7 +543,7 @@ exports[`source-map-loader should warn on invalid base64 SourceMap: errors 1`] =
 exports[`source-map-loader should warn on invalid base64 SourceMap: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot parse inline SourceMap: data:application/source-map;base64,invalid/base64=",
+Can not parse inline source map: data:application/source-map;base64,invalid/base64=",
 ]
 `;
 
@@ -304,7 +558,7 @@ exports[`source-map-loader should warn on missing SourceMap: errors 1`] = `Array
 exports[`source-map-loader should warn on missing SourceMap: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot find SourceMap 'missing-source-map.map': Error: Can't resolve './missing-source-map.map' in '/test/fixtures'",
+Cannot read '/test/fixtures/missing-source-map.map' file: Error: ENOENT: no such file or directory, open '/test/fixtures/missing-source-map.map'",
 ]
 `;
 
@@ -329,6 +583,6 @@ Object {
 exports[`source-map-loader should warn on missing source file: warnings 1`] = `
 Array [
   "ModuleWarning: Module Warning (from \`replaced original path\`):
-(Emitted value instead of an instance of Error) Cannot find source file 'missing-source-map2.txt': Error: Can't resolve './missing-source-map2.txt' in '/test/fixtures'",
+Cannot read '/test/fixtures/missing-source-map2.txt' file: Error: ENOENT: no such file or directory, open '/test/fixtures/missing-source-map2.txt'",
 ]
 `;
diff --git a/test/__snapshots__/sourceMapperRegexp.test.js.snap b/test/__snapshots__/sourceMapperRegexp.test.js.snap
index 1bade89..5865447 100644
--- a/test/__snapshots__/sourceMapperRegexp.test.js.snap
+++ b/test/__snapshots__/sourceMapperRegexp.test.js.snap
@@ -1,29 +1,41 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`source-map-loader should be null: result 1`] = `null`;
+exports[`source-map-loader should work with "
+    with SourceMap
 
-exports[`source-map-loader should be null: result 2`] = `null`;
+    // #sourceMappingURL = /sample-source-map.map
+    // comment
+    " url: result 1`] = `"/sample-source-map.map"`;
 
-exports[`source-map-loader should find last map: result 1`] = `"/sample-source-map-last.map"`;
+exports[`source-map-loader should work with "
+    with SourceMap
+    // #sourceMappingURL = /sample-source-map-1.map
+    // #sourceMappingURL = /sample-source-map-2.map
+    // #sourceMappingURL = /sample-source-map-last.map
+    // comment
+    " url: result 1`] = `"/sample-source-map-last.map"`;
 
-exports[`source-map-loader should not include quotes: result 1`] = `"data:application/json;base64,"`;
+exports[`source-map-loader should work with " //  #  sourceMappingURL  =   absolute-sourceRoot-source-map.map  " url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
 
-exports[`source-map-loader should work: result 1`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with " //  #sourceMappingURL=absolute-sourceRoot-source-map.map" url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
 
-exports[`source-map-loader should work: result 2`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with ""
+    /*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";" url: result 1`] = `null`;
 
-exports[`source-map-loader should work: result 3`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with "/*  #sourceMappingURL=absolute-sourceRoot-source-map.map  */" url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
 
-exports[`source-map-loader should work: result 4`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with "/*#sourceMappingURL=absolute-sourceRoot-source-map.map*/" url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
 
-exports[`source-map-loader should work: result 5`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with "// # sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+"" url: result 1`] = `"data:application/json;base64,"`;
 
-exports[`source-map-loader should work: result 6`] = `"absolute-sourceRoot-source-map.map"`;
+exports[`source-map-loader should work with "// #sourceMappingURL = //sampledomain.com/external-source-map2.map" url: result 1`] = `"//sampledomain.com/external-source-map2.map"`;
 
-exports[`source-map-loader should work: result 7`] = `"http://sampledomain.com/external-source-map2.map"`;
+exports[`source-map-loader should work with "// #sourceMappingURL = http://sampledomain.com/external-source-map2.map" url: result 1`] = `"http://sampledomain.com/external-source-map2.map"`;
 
-exports[`source-map-loader should work: result 8`] = `"//sampledomain.com/external-source-map2.map"`;
+exports[`source-map-loader should work with "// @sourceMappingURL=data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ==" url: result 1`] = `"data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ=="`;
 
-exports[`source-map-loader should work: result 9`] = `"data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ=="`;
+exports[`source-map-loader should work with "//#sourceMappingURL=absolute-sourceRoot-source-map.map" url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
 
-exports[`source-map-loader should work: result 10`] = `"/sample-source-map.map"`;
+exports[`source-map-loader should work with "//@sourceMappingURL=absolute-sourceRoot-source-map.map" url: result 1`] = `"absolute-sourceRoot-source-map.map"`;
+
+exports[`source-map-loader should work with "anInvalidDirective = "\\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";" url: result 1`] = `null`;
diff --git a/test/fixtures/.gitignore b/test/fixtures/.gitignore
index 46b6e08..0b8906c 100644
--- a/test/fixtures/.gitignore
+++ b/test/fixtures/.gitignore
@@ -1,1 +1,4 @@
+absolute-path.js
 absolute-sourceRoot-source-map.map
+map-with-sourceroot.js.map
+normal-map.js.map
diff --git a/test/loader.test.js b/test/loader.test.js
index 9837a39..2daf5f8 100644
--- a/test/loader.test.js
+++ b/test/loader.test.js
@@ -81,6 +81,7 @@ describe('source-map-loader', () => {
 
     const dependencies = [
       path.resolve(__dirname, 'fixtures', 'data', 'external-source-map2.map'),
+      path.resolve(__dirname, 'fixtures', 'external-source-map2.js'),
       path.resolve(__dirname, 'fixtures', 'external-source-map2.txt'),
     ];
 
@@ -305,7 +306,7 @@ describe('source-map-loader', () => {
       path.join(currentDirPath, 'file.js'),
       path.join(currentDirPath, 'file.js.map'),
       path.join(currentDirPath, 'nested1.js'),
-      `/different/root/nested2.js`,
+      path.normalize(`/different/root/nested2.js`),
     ];
 
     dependencies.forEach((fixture) => {
@@ -342,7 +343,7 @@ describe('source-map-loader', () => {
     });
   });
 
-  it.skip('should process protocol-relative-url-path', async () => {
+  it('should process protocol-relative-url-path', async () => {
     const testId = 'protocol-relative-url-path.js';
     const compiler = getCompiler(testId);
     const stats = await compile(compiler);
@@ -354,7 +355,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should support mixed paths in sources without sourceRoot', async () => {
+  it('should support mixed paths in sources without sourceRoot', async () => {
     const sourceRoot = path.resolve(__dirname, 'fixtures');
     const javaScriptFilename = 'absolute-path.js';
     const entryFileAbsolutePath = path.join(sourceRoot, javaScriptFilename);
@@ -388,7 +389,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should support mixed paths in sources with sourceRoot', async () => {
+  it('should support mixed paths in sources with sourceRoot', async () => {
     const sourceRoot = path.resolve(__dirname, 'fixtures');
     const javaScriptFilename = 'absolute-path.js';
     const entryFileAbsolutePath = path.join(sourceRoot, javaScriptFilename);
@@ -423,7 +424,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should support absolute paths to sourcemaps', async () => {
+  it('should support absolute paths to sourcemaps', async () => {
     const sourceRoot = path.resolve(__dirname, 'fixtures');
     const javaScriptFilename = 'absolute-path.js';
     const entryFileAbsolutePath = path.join(sourceRoot, javaScriptFilename);
@@ -456,7 +457,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should reject not support url', async () => {
+  it('should reject not support url', async () => {
     const testId = 'unSupport-file-source-map.js';
     const compiler = getCompiler(testId);
     const stats = await compile(compiler);
@@ -467,7 +468,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should process inlined sources', async () => {
+  it('should process inlined sources', async () => {
     const testId = 'inline-sources.js';
     const compiler = getCompiler(testId);
     const stats = await compile(compiler);
@@ -480,7 +481,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should process css sourceMap', async () => {
+  it('should process css sourceMap', async () => {
     const testId = 'app.css';
     const compiler = getCompiler(testId);
     const stats = await compile(compiler);
@@ -493,7 +494,7 @@ describe('source-map-loader', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it.skip('should process css sourceMap', async () => {
+  it('should process css sourceMap', async () => {
     const testId = 'skip-sourcesContent.js';
     const compiler = getCompiler(testId);
     const stats = await compile(compiler);
diff --git a/test/sourceMapperRegexp.test.js b/test/sourceMapperRegexp.test.js
index 3db379e..8f1d98a 100644
--- a/test/sourceMapperRegexp.test.js
+++ b/test/sourceMapperRegexp.test.js
@@ -1,113 +1,40 @@
-import { getSourceMappingUrl } from '../src/utils';
+import { getSourceMappingURL } from '../src/utils';
 
 describe('source-map-loader', () => {
-  it('should work', async () => {
-    const code = `/*#sourceMappingURL=absolute-sourceRoot-source-map.map*/`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `/*  #sourceMappingURL=absolute-sourceRoot-source-map.map  */`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `//#sourceMappingURL=absolute-sourceRoot-source-map.map`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `//@sourceMappingURL=absolute-sourceRoot-source-map.map`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = ` //  #sourceMappingURL=absolute-sourceRoot-source-map.map`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = ` //  #  sourceMappingURL  =   absolute-sourceRoot-source-map.map  `;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `// #sourceMappingURL = http://sampledomain.com/external-source-map2.map`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `// #sourceMappingURL = //sampledomain.com/external-source-map2.map`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `// @sourceMappingURL=data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ==`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should work', async () => {
-    const code = `
+  const cases = [
+    '/*#sourceMappingURL=absolute-sourceRoot-source-map.map*/',
+    '/*  #sourceMappingURL=absolute-sourceRoot-source-map.map  */',
+    '//#sourceMappingURL=absolute-sourceRoot-source-map.map',
+    '//@sourceMappingURL=absolute-sourceRoot-source-map.map',
+    ' //  #sourceMappingURL=absolute-sourceRoot-source-map.map',
+    ' //  #  sourceMappingURL  =   absolute-sourceRoot-source-map.map  ',
+    '// #sourceMappingURL = http://sampledomain.com/external-source-map2.map',
+    '// #sourceMappingURL = //sampledomain.com/external-source-map2.map',
+    '// @sourceMappingURL=data:application/source-map;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5saW5lLXNvdXJjZS1tYXAuanMiLCJzb3VyY2VzIjpbImlubGluZS1zb3VyY2UtbWFwLnR4dCJdLCJzb3VyY2VzQ29udGVudCI6WyJ3aXRoIFNvdXJjZU1hcCJdLCJtYXBwaW5ncyI6IkFBQUEifQ==',
+    `
     with SourceMap
-    
+
     // #sourceMappingURL = /sample-source-map.map
     // comment
-    `;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should find last map', async () => {
-    const code = `
+    `,
+    `
     with SourceMap
     // #sourceMappingURL = /sample-source-map-1.map
     // #sourceMappingURL = /sample-source-map-2.map
     // #sourceMappingURL = /sample-source-map-last.map
     // comment
-    `;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should be null', async () => {
-    const code = `"
-    /*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should be null', async () => {
-    const code = `anInvalidDirective = "\\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
-  });
-
-  it('should not include quotes', async () => {
-    const code = `// # sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+"`;
-    const { url } = getSourceMappingUrl(code);
-
-    expect(url).toMatchSnapshot('result');
+    `,
+    `"
+    /*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`,
+    `anInvalidDirective = "\\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+" */";`,
+    `// # sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+"`,
+  ];
+
+  cases.forEach((item) => {
+    it(`should work with "${item}" url`, async () => {
+      const { sourceMappingURL } = getSourceMappingURL(item);
+
+      expect(sourceMappingURL).toMatchSnapshot('result');
+    });
   });
 });