From 91f56ec0d2fbbe674bf7eb377dc4f8df8dc8d8e1 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 14:58:02 +0100 Subject: [PATCH 1/7] Update .travis.yml We test against node 4, 6 and 7 now. --- .travis.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 From 3499242a17e8e49fd13a169c9c369f12b2890f84 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 15:00:08 +0100 Subject: [PATCH 2/7] Add engines node ^4.0.0 --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 44c13dc..7e759bf 100644 --- a/package.json +++ b/package.json @@ -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", From 35b8c331e327ac999ff74095b7e0eb758f9761db Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 15:13:45 +0100 Subject: [PATCH 3/7] Replace var with let and const --- .eslintrc.json | 4 +- index.js | 120 +++++++++++++++++++++++++------------------------ package.json | 1 + test/index.js | 60 +++++++++++++------------ 4 files changed, 96 insertions(+), 89 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index be5f440..18ce3c9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -47,6 +47,8 @@ } }], "no-console": "off", - "valid-jsdoc": "error" + "valid-jsdoc": "error", + "no-var": "error", + "prefer-const": "error" } } \ No newline at end of file diff --git a/index.js b/index.js index d394f25..d863183 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,19 @@ -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) { +"use strict"; + +const JSON5 = require("json5"); +const path = require("path"); +const util = require("util"); +const os = require("os"); +const assign = require("object-assign"); +const emojiRegex = /[\uD800-\uDFFF]./; +const 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 = /^\.\.?[/\\]/; +const matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute() +const matchAbsoluteWin32Path = /^[A-Z]:[/\\]|^\\\\/i; +const matchRelativePath = /^\.\.?[/\\]/; -var baseEncodeTables = { +const baseEncodeTables = { 26: "abcdefghijklmnopqrstuvwxyz", 32: "123456789abcdefghjkmnpqrstuvwxyz", // no 0lio 36: "0123456789abcdefghijklmnopqrstuvwxyz", @@ -21,8 +23,8 @@ var baseEncodeTables = { 62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 64: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" }; -var emojiCache = {}; -var parseQueryDeprecationWarning = util.deprecate(function() {}, +const emojiCache = {}; +const parseQueryDeprecationWarning = util.deprecate(function() {}, "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." @@ -31,31 +33,31 @@ var parseQueryDeprecationWarning = util.deprecate(function() {}, 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); @@ -68,7 +70,7 @@ function encodeBufferToBase(buffer, base) { } exports.parseQuery = function parseQuery(query) { - var specialValues = { + const specialValues = { "null": null, "true": true, "false": false @@ -84,13 +86,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 = {}; + const queryArgs = query.split(/[,\&]/g); + const result = {}; queryArgs.forEach(function(arg) { - var idx = arg.indexOf("="); + 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]; } @@ -117,10 +119,10 @@ exports.parseQuery = function parseQuery(query) { }; exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { - var query = exports.parseQuery(loaderContext.query); - var configKey = query.config || defaultConfigKey; + const query = exports.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); } @@ -129,13 +131,13 @@ exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { }; exports.stringifyRequest = function(loaderContext, request) { - var splitted = request.split("!"); - var context = loaderContext.context || (loaderContext.options && loaderContext.options.context); + const splitted = request.split("!"); + const context = loaderContext.context || (loaderContext.options && loaderContext.options.context); return JSON.stringify(splitted.map(function(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] : ""; + const splittedPart = part.match(/^(.*?)(\?.*)/); + let singlePath = splittedPart ? splittedPart[1] : part; + const query = splittedPart ? splittedPart[2] : ""; if(matchAbsolutePath.test(singlePath) && context) { singlePath = path.relative(context, singlePath); if(matchAbsolutePath.test(singlePath)) { @@ -160,14 +162,14 @@ function dotRequest(obj) { exports.getRemainingRequest = function(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) { 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("!"); }; @@ -183,8 +185,8 @@ exports.isUrlRequest = function(url, root) { }; exports.urlToRequest = function(url, root) { - var moduleRequestRegex = /^[^?]*~/; - var request; + const moduleRequestRegex = /^[^?]*~/; + let request; if(matchAbsoluteWin32Path.test(url)) { // absolute windows path, keep it @@ -243,7 +245,7 @@ exports.parseString = function parseString(str) { exports.getHashDigest = 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" || @@ -255,25 +257,25 @@ exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, max }; exports.interpolateName = function interpolateName(loaderContext, name, options) { - var filename; + 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); @@ -294,7 +296,7 @@ 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() { @@ -313,11 +315,11 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) return folder; }); if(regExp && loaderContext.resourcePath) { - var re = new RegExp(regExp); - var match = loaderContext.resourcePath.match(re); + const re = new RegExp(regExp); + const match = loaderContext.resourcePath.match(re); if(match) { - for(i = 0; i < match.length; i++) { - re = new RegExp("\\[" + i + "\\]", "ig"); + for(let i = 0; i < match.length; i++) { + const re = new RegExp("\\[" + i + "\\]", "ig"); url = url.replace(re, match[i]); } } diff --git a/package.json b/package.json index 7e759bf..2159952 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "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", diff --git a/test/index.js b/test/index.js index 918c505..53b0430 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; @@ -41,9 +43,9 @@ describe("loader-utils", function() { [["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]; + 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) { @@ -68,7 +70,7 @@ describe("loader-utils", function() { }, "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] }); + const interpolatedName = loaderUtils.interpolateName({ resourcePath: test[0] }, test[1], { content: test[2] }); assert.equal(interpolatedName, test[3]); }); }); @@ -83,7 +85,7 @@ describe("loader-utils", function() { ["\'inconsistent start and end\"", "\'inconsistent start and end\""] ].forEach(function(test) { it("should parse " + test[0], function() { - var parsed = loaderUtils.parseString(test[0]); + const parsed = loaderUtils.parseString(test[0]); assert.equal(parsed, test[1]); }); }); @@ -117,7 +119,7 @@ describe("loader-utils", function() { ] ].forEach(function(test) { it("should parse " + test[0], function() { - var parsed = loaderUtils.parseQuery(test[0]); + const parsed = loaderUtils.parseQuery(test[0]); assert.deepEqual(parsed, test[1]); }); }); @@ -125,19 +127,19 @@ describe("loader-utils", function() { 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"); + 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"); + 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"); + 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: {} }); + const config = loaderUtils.getLoaderConfig({ query: "?slices=8",options: {} }); assert.deepEqual(config, { slices: 8 }); }); }); @@ -153,7 +155,7 @@ describe("loader-utils", function() { ["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]); + const hashDigest = loaderUtils.getHashDigest(test[0], test[1], test[2], test[3]); assert.equal(hashDigest, test[4]); }); }); @@ -162,11 +164,11 @@ describe("loader-utils", function() { describe("#interpolateName", function() { function run(tests) { tests.forEach(function(test) { - var args = test[0]; - var expected = test[1]; - var message = test[2]; + const args = test[0]; + const expected = test[1]; + const message = test[2]; it(message, function() { - var result = loaderUtils.interpolateName.apply(loaderUtils, args); + 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" }], @@ -201,14 +203,14 @@ describe("loader-utils", function() { ], ]); 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); + 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 = {}; + const loaderContext = {}; run([ [[loaderContext, "[ext]", {}], "bin", "should interpolate [ext] token"], [[loaderContext, "[name]", {}], "file", "should interpolate [name] token"], @@ -218,7 +220,7 @@ describe("loader-utils", function() { }); context("with loader context", function() { - var loaderContext = { resourcePath: "/path/to/file.exe" }; + const loaderContext = { resourcePath: "/path/to/file.exe" }; run([ [[loaderContext, "[ext]", {}], "exe", "should interpolate [ext] token"], [[loaderContext, "[name]", {}], "file", "should interpolate [name] token"], @@ -244,14 +246,14 @@ describe("loader-utils", function() { describe("#stringifyRequest", function() { // 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 === "/"; + const win = path.sep === "\\"; + const posix = path.sep === "/"; [ { request: "./a.js", expected: s("./a.js") }, { request: ".\\a.js", expected: s("./a.js") }, @@ -328,7 +330,7 @@ describe("loader-utils", function() { return; } it("should stringify request " + testCase.request + " to " + testCase.expected + " inside context " + testCase.context, function() { - var actual = loaderUtils.stringifyRequest({ context: testCase.context }, testCase.request); + const actual = loaderUtils.stringifyRequest({ context: testCase.context }, testCase.request); assert.equal(actual, testCase.expected); }); }); From 4130dff5d46376b1f7b46162893e0a6e7547b27a Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 15:36:47 +0100 Subject: [PATCH 4/7] Prefer arrow callback --- .eslintrc.json | 3 ++- index.js | 64 ++++++++++++++++++++++++-------------------------- test/index.js | 60 +++++++++++++++++++++++----------------------- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 18ce3c9..65b1ec4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -49,6 +49,7 @@ "no-console": "off", "valid-jsdoc": "error", "no-var": "error", - "prefer-const": "error" + "prefer-const": "error", + "prefer-arrow-callback": "error" } } \ No newline at end of file diff --git a/index.js b/index.js index d863183..bec3df9 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,7 @@ const util = require("util"); const os = require("os"); const assign = require("object-assign"); const emojiRegex = /[\uD800-\uDFFF]./; -const emojiList = require("emojis-list").filter(function(emoji) { - return emojiRegex.test(emoji); -}); +const emojiList = require("emojis-list").filter(emoji => emojiRegex.test(emoji)); const matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute() const matchAbsoluteWin32Path = /^[A-Z]:[/\\]|^\\\\/i; const matchRelativePath = /^\.\.?[/\\]/; @@ -24,7 +22,7 @@ const baseEncodeTables = { 64: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" }; const emojiCache = {}; -const parseQueryDeprecationWarning = util.deprecate(function() {}, +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." @@ -88,7 +86,7 @@ exports.parseQuery = function parseQuery(query) { } const queryArgs = query.split(/[,\&]/g); const result = {}; - queryArgs.forEach(function(arg) { + queryArgs.forEach(arg => { const idx = arg.indexOf("="); if(idx >= 0) { let name = arg.substr(0, idx); @@ -133,7 +131,7 @@ exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { exports.stringifyRequest = function(loaderContext, request) { const splitted = request.split("!"); const context = loaderContext.context || (loaderContext.options && loaderContext.options.context); - return JSON.stringify(splitted.map(function(part) { + return JSON.stringify(splitted.map(part => { // First, separate singlePath from query, because the query might contain paths again const splittedPart = part.match(/^(.*?)(\?.*)/); let singlePath = splittedPart ? splittedPart[1] : part; @@ -231,10 +229,11 @@ exports.parseString = 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) { @@ -287,7 +286,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) { @@ -299,30 +298,29 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) 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) => exports.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) { - const re = new RegExp(regExp); - const match = loaderContext.resourcePath.match(re); - if(match) { - for(let i = 0; i < match.length; i++) { - const 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); diff --git a/test/index.js b/test/index.js index 53b0430..16d6bc0 100644 --- a/test/index.js +++ b/test/index.js @@ -13,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"], @@ -41,8 +41,8 @@ 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() { + ].forEach((test) => { + it(test[2], () => { const expected = test[1]; try { const request = loaderUtils.urlToRequest.apply(loaderUtils, test[0]); @@ -58,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"], @@ -68,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() { + ].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() { + ].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", @@ -117,34 +117,34 @@ describe("loader-utils", function() { { obj: "test" }, { obj: "test" } ] - ].forEach(function(test) { - it("should parse " + test[0], function() { + ].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() { + 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() { + 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() { + 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() { + 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"], @@ -153,21 +153,21 @@ 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() { + ].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) { + tests.forEach((test) => { const args = test[0]; const expected = test[1]; const message = test[2]; - it(message, function() { + it(message, () => { const result = loaderUtils.interpolateName.apply(loaderUtils, args); if(typeof expected === "function") { expected(result); @@ -202,14 +202,14 @@ describe("loader-utils", function() { "should interpolate [emoji:3]" ], ]); - it("should return the same emoji for the same string", function() { + 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() { + context("no loader context", () => { const loaderContext = {}; run([ [[loaderContext, "[ext]", {}], "bin", "should interpolate [ext] token"], @@ -219,7 +219,7 @@ describe("loader-utils", function() { ]); }); - context("with loader context", function() { + context("with loader context", () => { const loaderContext = { resourcePath: "/path/to/file.exe" }; run([ [[loaderContext, "[ext]", {}], "exe", "should interpolate [ext] token"], @@ -243,7 +243,7 @@ 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 const paramQueryString = "?questionMark?posix=path/to/thing&win=path\\to\\thing"; @@ -322,14 +322,14 @@ describe("loader-utils", function() { ["../../a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "./e/f.js"].join("!") ) } - ].forEach(function(testCase, i) { + ].forEach((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() { + it("should stringify request " + testCase.request + " to " + testCase.expected + " inside context " + testCase.context, () => { const actual = loaderUtils.stringifyRequest({ context: testCase.context }, testCase.request); assert.equal(actual, testCase.expected); }); From ae1d865dbc6b15ccfb4ee8fa6bf7e4fd6c05ea20 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 15:39:09 +0100 Subject: [PATCH 5/7] Replace "object-assign" with native Object.assign --- index.js | 3 +-- package.json | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index bec3df9..3c45837 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,6 @@ const JSON5 = require("json5"); const path = require("path"); const util = require("util"); const os = require("os"); -const assign = require("object-assign"); const emojiRegex = /[\uD800-\uDFFF]./; const emojiList = require("emojis-list").filter(emoji => emojiRegex.test(emoji)); const matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute() @@ -122,7 +121,7 @@ exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { if(configKey) { const config = loaderContext.options[configKey] || {}; delete query.config; - return assign({}, config, query); + return Object.assign({}, config, query); } return query; diff --git a/package.json b/package.json index 2159952..5941e71 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "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", From 87cc614eed1df37921ebfbb43ecf832ad15db162 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 15:48:10 +0100 Subject: [PATCH 6/7] Replace exports with function declarations --- index.js | 55 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index 3c45837..cbc6d5f 100644 --- a/index.js +++ b/index.js @@ -66,7 +66,7 @@ function encodeBufferToBase(buffer, base) { return output; } -exports.parseQuery = function parseQuery(query) { +function parseQuery(query) { const specialValues = { "null": null, "true": true, @@ -113,10 +113,10 @@ exports.parseQuery = function parseQuery(query) { } }); return result; -}; +} -exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { - const query = exports.parseQuery(loaderContext.query); +function getLoaderConfig(loaderContext, defaultConfigKey) { + const query = parseQuery(loaderContext.query); const configKey = query.config || defaultConfigKey; if(configKey) { const config = loaderContext.options[configKey] || {}; @@ -125,9 +125,9 @@ exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { } return query; -}; +} -exports.stringifyRequest = function(loaderContext, request) { +function stringifyRequest(loaderContext, request) { const splitted = request.split("!"); const context = loaderContext.context || (loaderContext.options && loaderContext.options.context); return JSON.stringify(splitted.map(part => { @@ -150,27 +150,27 @@ exports.stringifyRequest = function(loaderContext, request) { } 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; 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; 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 @@ -179,9 +179,9 @@ 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) { +function urlToRequest(url, root) { const moduleRequestRegex = /^[^?]*~/; let request; @@ -222,9 +222,9 @@ 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) === "'") { @@ -238,9 +238,9 @@ exports.parseString = function parseString(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; const hash = require("crypto").createHash(hashType); @@ -252,9 +252,9 @@ exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, max } else { return hash.digest(digestType || "hex").substr(0, maxLength); } -}; +} -exports.interpolateName = function interpolateName(loaderContext, name, options) { +function interpolateName(loaderContext, name, options) { let filename; if(typeof name === "function") { filename = name(loaderContext.resourcePath); @@ -300,7 +300,7 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) url = url .replace( /\[(?:(\w+):)?hash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, - (all, hashType, digestType, maxLength) => exports.getHashDigest(content, hashType, digestType, parseInt(maxLength, 10)) + (all, hashType, digestType, maxLength) => getHashDigest(content, hashType, digestType, parseInt(maxLength, 10)) ) .replace( /\[emoji(?::(\d+))?\]/ig, @@ -325,4 +325,15 @@ exports.interpolateName = function interpolateName(loaderContext, name, options) 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; From 57452fdf5a1a8dc1ba24a52b9f41efa66748eb77 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Fri, 17 Feb 2017 16:42:18 +0100 Subject: [PATCH 7/7] Use node's path module where possible Additionally, all stringifyRequest tests are now executed on every OS. --- index.js | 20 +++++++++----- test/index.js | 73 +++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index cbc6d5f..bd7936f 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,7 @@ const util = require("util"); const os = require("os"); const emojiRegex = /[\uD800-\uDFFF]./; const emojiList = require("emojis-list").filter(emoji => emojiRegex.test(emoji)); -const matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute() -const matchAbsoluteWin32Path = /^[A-Z]:[/\\]|^\\\\/i; +const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i; const matchRelativePath = /^\.\.?[/\\]/; const baseEncodeTables = { @@ -27,6 +26,14 @@ const parseQueryDeprecationWarning = util.deprecate(() => {}, "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; @@ -135,15 +142,15 @@ function stringifyRequest(loaderContext, request) { const splittedPart = part.match(/^(.*?)(\?.*)/); let singlePath = splittedPart ? splittedPart[1] : part; const query = splittedPart ? splittedPart[2] : ""; - if(matchAbsolutePath.test(singlePath) && context) { + 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; } @@ -185,7 +192,8 @@ 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)) { diff --git a/test/index.js b/test/index.js index 16d6bc0..8caa631 100644 --- a/test/index.js +++ b/test/index.js @@ -252,40 +252,43 @@ describe("loader-utils", () => { posix: "path/to/thing", win: "path\\to\\file" }); - const win = path.sep === "\\"; - const 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( @@ -293,6 +296,7 @@ describe("loader-utils", () => { ) }, { + test: 22, request: ["a/b.js", "c/d.js", "e/f.js", "g"].join("!"), expected: s( @@ -300,13 +304,16 @@ describe("loader-utils", () => { ) }, { + 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("!"), @@ -314,7 +321,9 @@ describe("loader-utils", () => { ["../../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("!"), @@ -322,16 +331,16 @@ describe("loader-utils", () => { ["../../a/b.js" + paramQueryString, "c/d.js" + jsonQueryString, "./e/f.js"].join("!") ) } - ].forEach((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, () => { + ].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; }); }); });