diff --git a/README.md b/README.md index 3057748..b261596 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,17 @@ If the loader options have been passed as loader query string (`loader?some¶ Converts some resource URL to a webpack module request. +> i Before call `urlToRequest` you need call `isUrlRequest` to ensure it is requestable url + ```javascript -const request = loaderUtils.urlToRequest(url); +const url = "path/to/module.js"; + +if (loaderUtils.isUrlRequest(url)) { + // Logic for requestable url + const request = loaderUtils.urlToRequest(url); +} else { + // Logic for not requestable url +} ``` Simple example: diff --git a/lib/index.js b/lib/index.js index 86722dc..d24f3d5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,6 @@ "use strict"; +const isUrlRequest = require("./isUrlRequest"); const urlToRequest = require("./urlToRequest"); const getHashDigest = require("./getHashDigest"); const interpolateName = require("./interpolateName"); @@ -7,3 +8,4 @@ const interpolateName = require("./interpolateName"); exports.urlToRequest = urlToRequest; exports.getHashDigest = getHashDigest; exports.interpolateName = interpolateName; +exports.isUrlRequest = isUrlRequest; diff --git a/lib/isUrlRequest.js b/lib/isUrlRequest.js new file mode 100644 index 0000000..0114c41 --- /dev/null +++ b/lib/isUrlRequest.js @@ -0,0 +1,31 @@ +"use strict"; + +const path = require("path"); + +function isUrlRequest(url) { + // An URL is not an request if + + // 1. Allow `data URI` + if (/^data:/i.test(url)) { + return true; + } + + // 2. 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; + } + + // 3. It's a protocol-relative + if (/^\/\//.test(url)) { + return false; + } + + // 4. It's some kind of url for a template + if (/^#/.test(url)) { + return false; + } + + return true; +} + +module.exports = isUrlRequest; diff --git a/test/isUrlRequest.test.js b/test/isUrlRequest.test.js new file mode 100644 index 0000000..56b8622 --- /dev/null +++ b/test/isUrlRequest.test.js @@ -0,0 +1,181 @@ +"use strict"; + +const loaderUtils = require("../"); + +function ExpectedError(regex) { + this.regex = regex; +} +ExpectedError.prototype.matches = function (err) { + return this.regex.test(err.message); +}; + +describe("isUrlRequest()", () => { + [ + // without root + [["//google.com"], false, "should be negative for scheme-agnostic urls"], + [["http://google.com"], false, "should be negative for http urls"], + [["HTTP://google.com"], false, "should be negative for http urls"], + [["https://google.com"], false, "should be negative for https urls"], + [["HTTPS://google.com"], false, "should be negative for https urls"], + + [["chrome-extension://"], false, "should be negative for nonstandard urls"], + [["moz-extension://"], false, "should be negative for nonstandard urls"], + [ + ["ms-browser-extension://"], + false, + "should be negative for nonstandard urls", + ], + [["custom-extension://"], false, "should be negative for nonstandard urls"], + + [["path/to/thing"], true, "should be positive for implicit relative urls"], + [["./img.png"], true, "should be positive for implicit relative urls"], + [["../img.png"], true, "should be positive for implicit relative urls"], + [ + ["./img.png?foo=bar#hash"], + true, + "should be positive for implicit relative urls", + ], + [ + ["./path/to/thing"], + true, + "should be positive for explicit relative urls", + ], + [["~path/to/thing"], true, "should be positive for module urls (with ~)"], + [ + ["some/other/stuff/and/then~path/to/thing"], + true, + "should be positive for module urls with path prefix", + ], + [ + ["./some/other/stuff/and/then~path/to/thing"], + true, + "should be positive for module urls with relative path prefix", + ], + [["C:/thing"], true, "should be positive for linux path with driver"], + [["C:\\thing"], true, "should be positive for windows path with driver"], + [ + ["directory/things"], + true, + "should be positive for relative path (linux)", + ], + [ + ["directory\\things"], + true, + "should be positive for relative path (windows)", + ], + + // with root (normal path) + [ + ["path/to/thing", "root/dir"], + true, + "should be positive with root if implicit relative url", + ], + [ + ["./path/to/thing", "root/dir"], + true, + "should be positive with root if explicit relative url", + ], + + // with root (boolean) on Windows + [ + ["C:\\path\\to\\thing"], + true, + "should be positive for Windows absolute paths with drive letter", + ], + [ + ["\\\\?\\UNC\\ComputerName\\path\\to\\thing"], + true, + "should be positive for Windows absolute UNC paths", + ], + + // with root (module) + [ + ["/path/to/thing", "~"], + true, + "should be positive for module url if root = ~", + ], + + // with root (module path) + [ + ["/path/to/thing", "~module"], + true, + "should be positive for module prefixes when root starts with ~", + ], + [ + ["/path/to/thing", "~module/"], + true, + "should be positive for module prefixes (with trailing slash) when root starts with ~", + ], + + // error cases + [ + ["/path/to/thing", 1], + new ExpectedError(/unexpected parameters/i), + "should throw an error on invalid root", + ], + + // empty url + [[""], true, "should be positive if url is empty"], + + // about url + [["about:blank"], false, "should be negative for about:blank"], + + // hash + [["#gradient"], false, "should be negative for hash url"], + + // url + [["//sindresorhus.com"], false, "should ignore noscheme url"], + [ + ["//at.alicdn.com/t/font_515771_emcns5054x3whfr.eot"], + false, + "should ignore noscheme url with path", + ], + [ + ["https://example.com/././foo"], + false, + "should ignore absolute url with relative", + ], + + // non standard protocols + [ + ["file://sindresorhus.com"], + false, + "should ignore non standard protocols (file)", + ], + [ + ["mailto:someone@example.com"], + false, + "should ignore non standard protocols (mailto)", + ], + [ + ["data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D"], + true, + "should work with non standard protocols (data)", + ], + [ + ["DATA:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D"], + true, + "should work with non standard protocols (data)", + ], + + // root-relative url + [["/"], true, "should work with root-relative url 1"], + [["//"], false, "ignore root-relative url 1"], + ].forEach((test) => { + it(test[2], () => { + const expected = test[1]; + + try { + const request = loaderUtils.isUrlRequest.apply(loaderUtils, test[0]); + + expect(request).toBe(expected); + } catch (e) { + if (expected instanceof ExpectedError) { + expect(expected.matches(e)).toBe(true); + } else { + throw new Error("should not have thrown an error: " + e.message); + } + } + }); + }); +});