diff --git a/.eslintrc.json b/.eslintrc.json index be5f440..65b1ec4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -47,6 +47,9 @@ } }], "no-console": "off", - "valid-jsdoc": "error" + "valid-jsdoc": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-arrow-callback": "error" } } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 92ff8a4..b453997 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,9 @@ sudo: false language: node_js node_js: - - "0.10" - - "0.12" - - "iojs" + - "4" + - "6" + - "7" script: npm run travis -before_install: - - '[ "${TRAVIS_NODE_VERSION}" != "0.10" ] || npm install -g npm' - after_success: cat ./coverage/lcov.info | node_modules/.bin/coveralls --verbose && rm -rf ./coverage diff --git a/index.js b/index.js index d394f25..bd7936f 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,15 @@ -var JSON5 = require("json5"); -var path = require("path"); -var util = require("util"); -var os = require("os"); -var assign = require("object-assign"); -var emojiRegex = /[\uD800-\uDFFF]./; -var emojiList = require("emojis-list").filter(function(emoji) { - return emojiRegex.test(emoji); -}); -var matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute() -var matchAbsoluteWin32Path = /^[A-Z]:[/\\]|^\\\\/i; -var matchRelativePath = /^\.\.?[/\\]/; +"use strict"; -var baseEncodeTables = { +const JSON5 = require("json5"); +const path = require("path"); +const util = require("util"); +const os = require("os"); +const emojiRegex = /[\uD800-\uDFFF]./; +const emojiList = require("emojis-list").filter(emoji => emojiRegex.test(emoji)); +const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i; +const matchRelativePath = /^\.\.?[/\\]/; + +const baseEncodeTables = { 26: "abcdefghijklmnopqrstuvwxyz", 32: "123456789abcdefghjkmnpqrstuvwxyz", // no 0lio 36: "0123456789abcdefghijklmnopqrstuvwxyz", @@ -21,41 +19,49 @@ var baseEncodeTables = { 62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 64: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" }; -var emojiCache = {}; -var parseQueryDeprecationWarning = util.deprecate(function() {}, +const emojiCache = {}; +const parseQueryDeprecationWarning = util.deprecate(() => {}, "loaderUtils.parseQuery() received a non-string value which can be problematic, " + "see https://github.com/webpack/loader-utils/issues/56" + os.EOL + "parseQuery() will be replaced with getOptions() in the next major version of loader-utils." ); +function isAbsolutePath(str) { + return path.posix.isAbsolute(str) || path.win32.isAbsolute(str); +} + +function isRelativePath(str) { + return matchRelativePath.test(str); +} + function encodeStringToEmoji(content, length) { if(emojiCache[content]) return emojiCache[content]; length = length || 1; - var emojis = []; + const emojis = []; do { - var index = Math.floor(Math.random() * emojiList.length); + const index = Math.floor(Math.random() * emojiList.length); emojis.push(emojiList[index]); emojiList.splice(index, 1); } while(--length > 0); - var emojiEncoding = emojis.join(""); + const emojiEncoding = emojis.join(""); emojiCache[content] = emojiEncoding; return emojiEncoding; } function encodeBufferToBase(buffer, base) { - var encodeTable = baseEncodeTables[base]; + const encodeTable = baseEncodeTables[base]; if(!encodeTable) throw new Error("Unknown encoding base" + base); - var readLength = buffer.length; + const readLength = buffer.length; - var Big = require("big.js"); + const Big = require("big.js"); Big.RM = Big.DP = 0; - var b = new Big(0); - for(var i = readLength - 1; i >= 0; i--) { + let b = new Big(0); + for(let i = readLength - 1; i >= 0; i--) { b = b.times(256).plus(buffer[i]); } - var output = ""; + let output = ""; while(b.gt(0)) { output = encodeTable[b.mod(base)] + output; b = b.div(base); @@ -67,8 +73,8 @@ function encodeBufferToBase(buffer, base) { return output; } -exports.parseQuery = function parseQuery(query) { - var specialValues = { +function parseQuery(query) { + const specialValues = { "null": null, "true": true, "false": false @@ -84,13 +90,13 @@ exports.parseQuery = function parseQuery(query) { if(query.substr(0, 1) === "{" && query.substr(-1) === "}") { return JSON5.parse(query); } - var queryArgs = query.split(/[,\&]/g); - var result = {}; - queryArgs.forEach(function(arg) { - var idx = arg.indexOf("="); + const queryArgs = query.split(/[,\&]/g); + const result = {}; + queryArgs.forEach(arg => { + const idx = arg.indexOf("="); if(idx >= 0) { - var name = arg.substr(0, idx); - var value = decodeURIComponent(arg.substr(idx + 1)); + let name = arg.substr(0, idx); + let value = decodeURIComponent(arg.substr(idx + 1)); if(specialValues.hasOwnProperty(value)) { value = specialValues[value]; } @@ -114,64 +120,64 @@ exports.parseQuery = function parseQuery(query) { } }); return result; -}; +} -exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { - var query = exports.parseQuery(loaderContext.query); - var configKey = query.config || defaultConfigKey; +function getLoaderConfig(loaderContext, defaultConfigKey) { + const query = parseQuery(loaderContext.query); + const configKey = query.config || defaultConfigKey; if(configKey) { - var config = loaderContext.options[configKey] || {}; + const config = loaderContext.options[configKey] || {}; delete query.config; - return assign({}, config, query); + return Object.assign({}, config, query); } return query; -}; +} -exports.stringifyRequest = function(loaderContext, request) { - var splitted = request.split("!"); - var context = loaderContext.context || (loaderContext.options && loaderContext.options.context); - return JSON.stringify(splitted.map(function(part) { +function stringifyRequest(loaderContext, request) { + const splitted = request.split("!"); + const context = loaderContext.context || (loaderContext.options && loaderContext.options.context); + return JSON.stringify(splitted.map(part => { // First, separate singlePath from query, because the query might contain paths again - var splittedPart = part.match(/^(.*?)(\?.*)/); - var singlePath = splittedPart ? splittedPart[1] : part; - var query = splittedPart ? splittedPart[2] : ""; - if(matchAbsolutePath.test(singlePath) && context) { + const splittedPart = part.match(/^(.*?)(\?.*)/); + let singlePath = splittedPart ? splittedPart[1] : part; + const query = splittedPart ? splittedPart[2] : ""; + if(isAbsolutePath(singlePath) && context) { singlePath = path.relative(context, singlePath); - if(matchAbsolutePath.test(singlePath)) { + if(isAbsolutePath(singlePath)) { // If singlePath still matches an absolute path, singlePath was on a different drive than context. // In this case, we leave the path platform-specific without replacing any separators. // @see https://github.com/webpack/loader-utils/pull/14 return singlePath + query; } - if(matchRelativePath.test(singlePath) === false) { + if(isRelativePath(singlePath) === false) { // Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules). singlePath = "./" + singlePath; } } return singlePath.replace(/\\/g, "/") + query; }).join("!")); -}; +} function dotRequest(obj) { return obj.request; } -exports.getRemainingRequest = function(loaderContext) { +function getRemainingRequest(loaderContext) { if(loaderContext.remainingRequest) return loaderContext.remainingRequest; - var request = loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(dotRequest).concat([loaderContext.resource]); + const request = loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(dotRequest).concat([loaderContext.resource]); return request.join("!"); -}; +} -exports.getCurrentRequest = function(loaderContext) { +function getCurrentRequest(loaderContext) { if(loaderContext.currentRequest) return loaderContext.currentRequest; - var request = loaderContext.loaders.slice(loaderContext.loaderIndex).map(dotRequest).concat([loaderContext.resource]); + const request = loaderContext.loaders.slice(loaderContext.loaderIndex).map(dotRequest).concat([loaderContext.resource]); return request.join("!"); -}; +} -exports.isUrlRequest = function(url, root) { +function isUrlRequest(url, root) { // An URL is not an request if // 1. it's a Data Url // 2. it's an absolute url or and protocol-relative @@ -180,13 +186,14 @@ exports.isUrlRequest = function(url, root) { // 4. It's also not an request if root isn't set and it's a root-relative url if((root === undefined || root === false) && /^\//.test(url)) return false; return true; -}; +} -exports.urlToRequest = function(url, root) { - var moduleRequestRegex = /^[^?]*~/; - var request; +function urlToRequest(url, root) { + const moduleRequestRegex = /^[^?]*~/; + let request; - if(matchAbsoluteWin32Path.test(url)) { + // we can't use path.win32.isAbsolute because it also matches paths starting with a forward slash + if(matchNativeWin32Path.test(url)) { // absolute windows path, keep it request = url; } else if(root !== undefined && root !== false && /^\//.test(url)) { @@ -223,27 +230,28 @@ exports.urlToRequest = function(url, root) { } return request; -}; +} -exports.parseString = function parseString(str) { +function parseString(str) { try { if(str[0] === "\"") return JSON.parse(str); if(str[0] === "'" && str.substr(str.length - 1) === "'") { - return parseString(str.replace(/\\.|"/g, function(x) { - if(x === "\"") return "\\\""; - return x; - }).replace(/^'|'$/g, "\"")); + return parseString( + str + .replace(/\\.|"/g, x => x === "\"" ? "\\\"" : x) + .replace(/^'|'$/g, "\"") + ); } return JSON.parse("\"" + str + "\""); } catch(e) { return str; } -}; +} -exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, maxLength) { +function getHashDigest(buffer, hashType, digestType, maxLength) { hashType = hashType || "md5"; maxLength = maxLength || 9999; - var hash = require("crypto").createHash(hashType); + const hash = require("crypto").createHash(hashType); hash.update(buffer); if(digestType === "base26" || digestType === "base32" || digestType === "base36" || digestType === "base49" || digestType === "base52" || digestType === "base58" || @@ -252,28 +260,28 @@ exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, max } else { return hash.digest(digestType || "hex").substr(0, maxLength); } -}; +} -exports.interpolateName = function interpolateName(loaderContext, name, options) { - var filename; +function interpolateName(loaderContext, name, options) { + let filename; if(typeof name === "function") { filename = name(loaderContext.resourcePath); } else { filename = name || "[hash].[ext]"; } - var context = options.context; - var content = options.content; - var regExp = options.regExp; - var ext = "bin"; - var basename = "file"; - var directory = ""; - var folder = ""; + const context = options.context; + const content = options.content; + const regExp = options.regExp; + let ext = "bin"; + let basename = "file"; + let directory = ""; + let folder = ""; if(loaderContext.resourcePath) { - var resourcePath = loaderContext.resourcePath; - var idx = resourcePath.lastIndexOf("."); - var i = resourcePath.lastIndexOf("\\"); - var j = resourcePath.lastIndexOf("/"); - var p = i < 0 ? j : j < 0 ? i : i < j ? i : j; + let resourcePath = loaderContext.resourcePath; + const idx = resourcePath.lastIndexOf("."); + const i = resourcePath.lastIndexOf("\\"); + const j = resourcePath.lastIndexOf("/"); + const p = i < 0 ? j : j < 0 ? i : i < j ? i : j; if(idx >= 0) { ext = resourcePath.substr(idx + 1); resourcePath = resourcePath.substr(0, idx); @@ -285,7 +293,7 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) if(typeof context !== "undefined") { directory = path.relative(context, resourcePath + "_").replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1"); directory = directory.substr(0, directory.length - 1); - } else { + } else { directory = resourcePath.replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1"); } if(directory.length === 1) { @@ -294,36 +302,46 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) folder = path.basename(directory); } } - var url = filename; + let url = filename; if(content) { // Match hash template - url = url.replace(/\[(?:(\w+):)?hash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function() { - return exports.getHashDigest(content, arguments[1], arguments[2], parseInt(arguments[3], 10)); - }).replace(/\[emoji(?::(\d+))?\]/ig, function() { - return encodeStringToEmoji(content, arguments[1]); - }); + url = url + .replace( + /\[(?:(\w+):)?hash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, + (all, hashType, digestType, maxLength) => getHashDigest(content, hashType, digestType, parseInt(maxLength, 10)) + ) + .replace( + /\[emoji(?::(\d+))?\]/ig, + (all, length) => encodeStringToEmoji(content, length) + ); } - url = url.replace(/\[ext\]/ig, function() { - return ext; - }).replace(/\[name\]/ig, function() { - return basename; - }).replace(/\[path\]/ig, function() { - return directory; - }).replace(/\[folder\]/ig, function() { - return folder; - }); + url = url + .replace(/\[ext\]/ig, () => ext) + .replace(/\[name\]/ig, () => basename) + .replace(/\[path\]/ig, () => directory) + .replace(/\[folder\]/ig, () => folder); if(regExp && loaderContext.resourcePath) { - var re = new RegExp(regExp); - var match = loaderContext.resourcePath.match(re); - if(match) { - for(i = 0; i < match.length; i++) { - re = new RegExp("\\[" + i + "\\]", "ig"); - url = url.replace(re, match[i]); - } - } + const match = loaderContext.resourcePath.match(new RegExp(regExp)); + match && match.forEach((matched, i) => { + url = url.replace( + new RegExp("\\[" + i + "\\]", "ig"), + matched + ); + }); } if(typeof loaderContext.options === "object" && typeof loaderContext.options.customInterpolateName === "function") { url = loaderContext.options.customInterpolateName.call(loaderContext, url, name, options); } return url; -}; +} + +exports.parseQuery = parseQuery; +exports.getLoaderConfig = getLoaderConfig; +exports.stringifyRequest = stringifyRequest; +exports.getRemainingRequest = getRemainingRequest; +exports.getCurrentRequest = getCurrentRequest; +exports.isUrlRequest = isUrlRequest; +exports.urlToRequest = urlToRequest; +exports.parseString = parseString; +exports.getHashDigest = getHashDigest; +exports.interpolateName = interpolateName; diff --git a/package.json b/package.json index 44c13dc..5941e71 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "dependencies": { "big.js": "^3.1.3", "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "json5": "^0.5.0" }, "scripts": { "test": "mocha", + "posttest": "npm run lint", "lint": "eslint --fix *.js test", "travis": "npm run cover -- --report lcovonly", "cover": "istanbul cover -x *.runtime.js node_modules/mocha/bin/_mocha", @@ -21,6 +21,9 @@ "type": "git", "url": "https://github.com/webpack/loader-utils.git" }, + "engines": { + "node": "^4.0.0" + }, "devDependencies": { "coveralls": "^2.11.2", "eslint": "^3.15.0", diff --git a/test/index.js b/test/index.js index 918c505..8caa631 100644 --- a/test/index.js +++ b/test/index.js @@ -1,8 +1,10 @@ -var assert = require("assert"), - path = require("path"), - loaderUtils = require("../"); +"use strict"; -var s = JSON.stringify; +const assert = require("assert"); +const path = require("path"); +const loaderUtils = require("../"); + +const s = JSON.stringify; function ExpectedError(regex) { this.regex = regex; @@ -11,9 +13,9 @@ ExpectedError.prototype.matches = function(err) { return this.regex.test(err.message); }; -describe("loader-utils", function() { +describe("loader-utils", () => { - describe("#urlToRequest()", function() { + describe("#urlToRequest()", () => { [ // without root [["path/to/thing"], "./path/to/thing", "should handle implicit relative urls"], @@ -39,11 +41,11 @@ describe("loader-utils", function() { [["/path/to/thing", 1], new ExpectedError(/unexpected parameters/i), "should throw an error on invalid root"], // difficult cases [["a:b-not-\\window-path"], "./a:b-not-\\window-path", "should not incorrectly detect windows paths"] - ].forEach(function(test) { - it(test[2], function() { - var expected = test[1]; + ].forEach((test) => { + it(test[2], () => { + const expected = test[1]; try { - var request = loaderUtils.urlToRequest.apply(loaderUtils, test[0]); + const request = loaderUtils.urlToRequest.apply(loaderUtils, test[0]); assert.equal(request, expected); } catch(e) { if(expected instanceof ExpectedError) { @@ -56,7 +58,7 @@ describe("loader-utils", function() { }); }); - describe("#interpolateName", function() { + describe("#interpolateName", () => { [ ["/app/js/javascript.js", "js/[hash].script.[ext]", "test content", "js/9473fdd0d880a43c21b7778d34872157.script.js"], ["/app/page.html", "html-[hash:6].html", "test content", "html-9473fd.html"], @@ -66,30 +68,30 @@ describe("loader-utils", function() { ["/vendor/test/images/loading.gif", function(path) { return path.replace(/\/?vendor\/?/, ""); }, "test content", "test/images/loading.gif"] - ].forEach(function(test) { - it("should interpolate " + test[0] + " " + test[1], function() { - var interpolatedName = loaderUtils.interpolateName({ resourcePath: test[0] }, test[1], { content: test[2] }); + ].forEach((test) => { + it("should interpolate " + test[0] + " " + test[1], () => { + const interpolatedName = loaderUtils.interpolateName({ resourcePath: test[0] }, test[1], { content: test[2] }); assert.equal(interpolatedName, test[3]); }); }); }); - describe("#parseString", function() { + describe("#parseString", () => { [ ["test string", "test string"], [s("!\"§$%&/()=?'*#+,.-;öäü:_test"), "!\"§$%&/()=?'*#+,.-;öäü:_test"], ["'escaped with single \"'", "escaped with single \""], ["invalid \"' string", "invalid \"' string"], ["\'inconsistent start and end\"", "\'inconsistent start and end\""] - ].forEach(function(test) { - it("should parse " + test[0], function() { - var parsed = loaderUtils.parseString(test[0]); + ].forEach((test) => { + it("should parse " + test[0], () => { + const parsed = loaderUtils.parseString(test[0]); assert.equal(parsed, test[1]); }); }); }); - describe("#parseQuery", function() { + describe("#parseQuery", () => { [ [ "?sweet=true&name=cheesecake&slices=8&delicious&warm=false", @@ -115,34 +117,34 @@ describe("loader-utils", function() { { obj: "test" }, { obj: "test" } ] - ].forEach(function(test) { - it("should parse " + test[0], function() { - var parsed = loaderUtils.parseQuery(test[0]); + ].forEach((test) => { + it("should parse " + test[0], () => { + const parsed = loaderUtils.parseQuery(test[0]); assert.deepEqual(parsed, test[1]); }); }); }); - describe("#getLoaderConfig", function() { - it("should merge loaderContext.query and loaderContext.options.testLoader", function() { - var config = loaderUtils.getLoaderConfig({ query: "?name=cheesecake",options: { testLoader: { slices: 8 } } }, "testLoader"); + describe("#getLoaderConfig", () => { + it("should merge loaderContext.query and loaderContext.options.testLoader", () => { + const config = loaderUtils.getLoaderConfig({ query: "?name=cheesecake",options: { testLoader: { slices: 8 } } }, "testLoader"); assert.deepEqual(config, { name: "cheesecake",slices: 8 }); }); - it("should allow to specify a config property name via loaderContext.query.config", function() { - var config = loaderUtils.getLoaderConfig({ query: "?name=cheesecake&config=otherConfig",options: { otherConfig: { slices: 8 } } }, "testLoader"); + it("should allow to specify a config property name via loaderContext.query.config", () => { + const config = loaderUtils.getLoaderConfig({ query: "?name=cheesecake&config=otherConfig",options: { otherConfig: { slices: 8 } } }, "testLoader"); assert.deepEqual(config, { name: "cheesecake",slices: 8 }); }); - it("should prefer loaderContext.query.slices over loaderContext.options.slices", function() { - var config = loaderUtils.getLoaderConfig({ query: "?slices=8",options: { testLoader: { slices: 4 } } }, "testLoader"); + it("should prefer loaderContext.query.slices over loaderContext.options.slices", () => { + const config = loaderUtils.getLoaderConfig({ query: "?slices=8",options: { testLoader: { slices: 4 } } }, "testLoader"); assert.deepEqual(config, { slices: 8 }); }); - it("should allow no default key", function() { - var config = loaderUtils.getLoaderConfig({ query: "?slices=8",options: {} }); + it("should allow no default key", () => { + const config = loaderUtils.getLoaderConfig({ query: "?slices=8",options: {} }); assert.deepEqual(config, { slices: 8 }); }); }); - describe("#getHashDigest", function() { + describe("#getHashDigest", () => { [ ["test string", "md5", "hex", undefined, "6f8db599de986fab7a21625b7916589c"], ["test string", "md5", "hex", 4, "6f8d"], @@ -151,22 +153,22 @@ describe("loader-utils", function() { ["test string", "md5", "base26", 6, "bhtsgu"], ["test string", "sha512", "base64", undefined, "2IS-kbfIPnVflXb9CzgoNESGCkvkb0urMmucPD9z8q6HuYz8RShY1-tzSUpm5-Ivx_u4H1MEzPgAhyhaZ7RKog"], ["test_string", "md5", "hex", undefined, "3474851a3410906697ec77337df7aae4"] - ].forEach(function(test) { - it("should getHashDigest " + test[0] + " " + test[1] + " " + test[2] + " " + test[3], function() { - var hashDigest = loaderUtils.getHashDigest(test[0], test[1], test[2], test[3]); + ].forEach((test) => { + it("should getHashDigest " + test[0] + " " + test[1] + " " + test[2] + " " + test[3], () => { + const hashDigest = loaderUtils.getHashDigest(test[0], test[1], test[2], test[3]); assert.equal(hashDigest, test[4]); }); }); }); - describe("#interpolateName", function() { + describe("#interpolateName", () => { function run(tests) { - tests.forEach(function(test) { - var args = test[0]; - var expected = test[1]; - var message = test[2]; - it(message, function() { - var result = loaderUtils.interpolateName.apply(loaderUtils, args); + tests.forEach((test) => { + const args = test[0]; + const expected = test[1]; + const message = test[2]; + it(message, () => { + const result = loaderUtils.interpolateName.apply(loaderUtils, args); if(typeof expected === "function") { expected(result); } else { @@ -182,7 +184,7 @@ describe("loader-utils", function() { [[{}, "[unrecognized]", { content: "test string" }], "[unrecognized]", "should not interpolate unrecognized token"], ]); - var emojiRegex = /[\uD800-\uDFFF]./; + const emojiRegex = /[\uD800-\uDFFF]./; run([ [ [{}, "[emoji]", { content: "test" }], @@ -200,15 +202,15 @@ describe("loader-utils", function() { "should interpolate [emoji:3]" ], ]); - it("should return the same emoji for the same string", function() { - var args = [{}, "[emoji:5]", { content: "same_emoji" }]; - var result1 = loaderUtils.interpolateName.apply(loaderUtils, args); - var result2 = loaderUtils.interpolateName.apply(loaderUtils, args); + it("should return the same emoji for the same string", () => { + const args = [{}, "[emoji:5]", { content: "same_emoji" }]; + const result1 = loaderUtils.interpolateName.apply(loaderUtils, args); + const result2 = loaderUtils.interpolateName.apply(loaderUtils, args); assert.equal(result1, result2); }); - context("no loader context", function() { - var loaderContext = {}; + context("no loader context", () => { + const loaderContext = {}; run([ [[loaderContext, "[ext]", {}], "bin", "should interpolate [ext] token"], [[loaderContext, "[name]", {}], "file", "should interpolate [name] token"], @@ -217,8 +219,8 @@ describe("loader-utils", function() { ]); }); - context("with loader context", function() { - var loaderContext = { resourcePath: "/path/to/file.exe" }; + context("with loader context", () => { + const loaderContext = { resourcePath: "/path/to/file.exe" }; run([ [[loaderContext, "[ext]", {}], "exe", "should interpolate [ext] token"], [[loaderContext, "[name]", {}], "file", "should interpolate [name] token"], @@ -241,49 +243,52 @@ describe("loader-utils", function() { ]); }); - describe("#stringifyRequest", function() { + describe("#stringifyRequest", () => { // We know that query strings that contain paths and question marks can be problematic. // We must ensure that stringifyRequest is not messing with them - var paramQueryString = "?questionMark?posix=path/to/thing&win=path\\to\\thing"; - var jsonQueryString = "?" + s({ + const paramQueryString = "?questionMark?posix=path/to/thing&win=path\\to\\thing"; + const jsonQueryString = "?" + s({ questionMark: "?", posix: "path/to/thing", win: "path\\to\\file" }); - var win = path.sep === "\\"; - var posix = path.sep === "/"; [ - { request: "./a.js", expected: s("./a.js") }, - { request: ".\\a.js", expected: s("./a.js") }, - { request: "./a/b.js", expected: s("./a/b.js") }, - { request: ".\\a\\b.js", expected: s("./a/b.js") }, - { request: "module", expected: s("module") }, // without ./ is a request into the modules directory - { request: "module/a.js", expected: s("module/a.js") }, - { request: "module\\a.js", expected: s("module/a.js") }, - { request: "./a.js" + paramQueryString, expected: s("./a.js" + paramQueryString) }, - { request: "./a.js" + jsonQueryString, expected: s("./a.js" + jsonQueryString) }, - { request: "module" + paramQueryString, expected: s("module" + paramQueryString) }, - { request: "module" + jsonQueryString, expected: s("module" + jsonQueryString) }, - posix && { context: "/path/to", request: "/path/to/module/a.js", expected: s("./module/a.js") }, - win && { context: "C:\\path\\to\\", request: "C:\\path\\to\\module\\a.js", expected: s("./module/a.js") }, - posix && { context: "/path/to/thing", request: "/path/to/module/a.js", expected: s("../module/a.js") }, - win && { context: "C:\\path\\to\\thing", request: "C:\\path\\to\\module\\a.js", expected: s("../module/a.js") }, - win && { context: "\\\\A\\path\\to\\thing", request: "\\\\A\\path\\to\\module\\a.js", expected: s("../module/a.js") }, + { test: 1, request: "./a.js", expected: s("./a.js") }, + { test: 2, request: ".\\a.js", expected: s("./a.js") }, + { test: 3, request: "./a/b.js", expected: s("./a/b.js") }, + { test: 4, request: ".\\a\\b.js", expected: s("./a/b.js") }, + { test: 5, request: "module", expected: s("module") }, // without ./ is a request into the modules directory + { test: 6, request: "module/a.js", expected: s("module/a.js") }, + { test: 7, request: "module\\a.js", expected: s("module/a.js") }, + { test: 8, request: "./a.js" + paramQueryString, expected: s("./a.js" + paramQueryString) }, + { test: 9, request: "./a.js" + jsonQueryString, expected: s("./a.js" + jsonQueryString) }, + { test: 10, request: "module" + paramQueryString, expected: s("module" + paramQueryString) }, + { test: 11, request: "module" + jsonQueryString, expected: s("module" + jsonQueryString) }, + { test: 12, os: "posix", context: "/path/to", request: "/path/to/module/a.js", expected: s("./module/a.js") }, + { test: 13, os: "win32", context: "C:\\path\\to\\", request: "C:\\path\\to\\module\\a.js", expected: s("./module/a.js") }, + { test: 14, os: "posix", context: "/path/to/thing", request: "/path/to/module/a.js", expected: s("../module/a.js") }, + { test: 15, os: "win32", context: "C:\\path\\to\\thing", request: "C:\\path\\to\\module\\a.js", expected: s("../module/a.js") }, + { test: 16, os: "win32", context: "\\\\A\\path\\to\\thing", request: "\\\\A\\path\\to\\module\\a.js", expected: s("../module/a.js") }, // If context and request are on different drives, the path should not be relative // @see https://github.com/webpack/loader-utils/pull/14 - win && { context: "D:\\path\\to\\thing", request: "C:\\path\\to\\module\\a.js", expected: s("C:\\path\\to\\module\\a.js") }, - win && { context: "\\\\A\\path\\to\\thing", request: "\\\\B\\path\\to\\module\\a.js", expected: s("\\\\B\\path\\to\\module\\a.js") }, - posix && { + { test: 17, os: "win32", context: "D:\\path\\to\\thing", request: "C:\\path\\to\\module\\a.js", expected: s("C:\\path\\to\\module\\a.js") }, + { test: 18, os: "win32", context: "\\\\A\\path\\to\\thing", request: "\\\\B\\path\\to\\module\\a.js", expected: s("\\\\B\\path\\to\\module\\a.js") }, + { + test: 19, + os: "posix", context: "/path/to", request: "/path/to/module/a.js" + paramQueryString, expected: s("./module/a.js" + paramQueryString) }, - win && { + { + test: 20, + os: "win32", context: "C:\\path\\to\\", request: "C:\\path\\to\\module\\a.js" + paramQueryString, expected: s("./module/a.js" + paramQueryString) }, { + test: 21, request: ["./a.js", "./b.js", "./c.js"].join("!"), expected: s( @@ -291,6 +296,7 @@ describe("loader-utils", function() { ) }, { + test: 22, request: ["a/b.js", "c/d.js", "e/f.js", "g"].join("!"), expected: s( @@ -298,13 +304,16 @@ describe("loader-utils", function() { ) }, { + test: 23, request: ["a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "e/f.js"].join("!"), expected: s( ["a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "e/f.js"].join("!") ) }, - posix && { + { + test: 24, + os: "posix", context: "/path/to", request: ["/a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "/path/to/e/f.js"].join("!"), @@ -312,7 +321,9 @@ describe("loader-utils", function() { ["../../a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "./e/f.js"].join("!") ) }, - win && { + { + test: 25, + os: "win32", context: "C:\\path\\to\\", request: ["C:\\a\\b.js" + paramQueryString, "c\\d.js" + jsonQueryString, "C:\\path\\to\\e\\f.js"].join("!"), @@ -320,16 +331,16 @@ describe("loader-utils", function() { ["../../a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "./e/f.js"].join("!") ) } - ].forEach(function(testCase, i) { - if(!testCase) { - // If testCase is not defined, this test case should not be executed on this OS. - // This is because node's path module is OS agnostic which means that path.relative won't produce - // a relative path with absolute windows paths on posix systems. - return; - } - it("should stringify request " + testCase.request + " to " + testCase.expected + " inside context " + testCase.context, function() { - var actual = loaderUtils.stringifyRequest({ context: testCase.context }, testCase.request); + ].forEach(testCase => { + it(`${ testCase.test }. should stringify request ${ testCase.request } to ${ testCase.expected } inside context ${ testCase.context }`, () => { + const relative = path.relative; + if(testCase.os) { + // monkey patch path.relative in order to make this test work in every OS + path.relative = path[testCase.os].relative; + } + const actual = loaderUtils.stringifyRequest({ context: testCase.context }, testCase.request); assert.equal(actual, testCase.expected); + path.relative = relative; }); }); });