| 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 = /^\.\.?[/\\]/; |
| |
| var baseEncodeTables = { |
| 26: "abcdefghijklmnopqrstuvwxyz", |
| 32: "123456789abcdefghjkmnpqrstuvwxyz", // no 0lio |
| 36: "0123456789abcdefghijklmnopqrstuvwxyz", |
| 49: "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", // no lIO |
| 52: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", |
| 58: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", // no 0lIO |
| 62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", |
| 64: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" |
| }; |
| var emojiCache = {}; |
| var 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." |
| ); |
| |
| function encodeStringToEmoji(content, length) { |
| if (emojiCache[content]) return emojiCache[content]; |
| length = length || 1; |
| var emojis = []; |
| do { |
| var index = Math.floor(Math.random() * emojiList.length); |
| emojis.push(emojiList[index]); |
| emojiList.splice(index, 1); |
| } while (--length > 0); |
| var emojiEncoding = emojis.join(''); |
| emojiCache[content] = emojiEncoding; |
| return emojiEncoding; |
| } |
| |
| function encodeBufferToBase(buffer, base) { |
| var encodeTable = baseEncodeTables[base]; |
| if (!encodeTable) throw new Error("Unknown encoding base" + base); |
| |
| var readLength = buffer.length; |
| |
| var Big = require('big.js'); |
| Big.RM = Big.DP = 0; |
| var b = new Big(0); |
| for (var i = readLength - 1; i >= 0; i--) { |
| b = b.times(256).plus(buffer[i]); |
| } |
| |
| var output = ""; |
| while (b.gt(0)) { |
| output = encodeTable[b.mod(base)] + output; |
| b = b.div(base); |
| } |
| |
| Big.DP = 20; |
| Big.RM = 1; |
| |
| return output; |
| } |
| |
| exports.parseQuery = function parseQuery(query) { |
| var specialValues = { |
| 'null': null, |
| 'true': true, |
| 'false': false |
| }; |
| if(!query) return {}; |
| if(typeof query !== "string") { |
| parseQueryDeprecationWarning(); |
| return query; |
| } |
| if(query.substr(0, 1) !== "?") |
| throw new Error("a valid query string passed to parseQuery should begin with '?'"); |
| query = query.substr(1); |
| var queryLength = query.length; |
| 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("="); |
| if(idx >= 0) { |
| var name = arg.substr(0, idx); |
| var value = decodeURIComponent(arg.substr(idx+1)); |
| if (specialValues.hasOwnProperty(value)) { |
| value = specialValues[value]; |
| } |
| if(name.substr(-2) === "[]") { |
| name = decodeURIComponent(name.substr(0, name.length-2)); |
| if(!Array.isArray(result[name])) |
| result[name] = []; |
| result[name].push(value); |
| } else { |
| name = decodeURIComponent(name); |
| result[name] = value; |
| } |
| } else { |
| if(arg.substr(0, 1) === "-") { |
| result[decodeURIComponent(arg.substr(1))] = false; |
| } else if(arg.substr(0, 1) === "+") { |
| result[decodeURIComponent(arg.substr(1))] = true; |
| } else { |
| result[decodeURIComponent(arg)] = true; |
| } |
| } |
| }); |
| return result; |
| }; |
| |
| exports.getLoaderConfig = function(loaderContext, defaultConfigKey) { |
| var query = exports.parseQuery(loaderContext.query); |
| var configKey = query.config || defaultConfigKey; |
| if (configKey) { |
| var config = loaderContext.options[configKey] || {}; |
| delete query.config; |
| return 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) { |
| // 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) { |
| singlePath = path.relative(context, singlePath); |
| if(matchAbsolutePath.test(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) { |
| // 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) { |
| if(loaderContext.remainingRequest) |
| return loaderContext.remainingRequest; |
| var 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]); |
| return request.join("!"); |
| }; |
| |
| exports.isUrlRequest = function(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 |
| // 3. it's some kind of url for a template |
| if(/^data:|^chrome-extension:|^(https?:)?\/\/|^[\{\}\[\]#*;,'§\$%&\(=?`´\^°<>]/.test(url)) return false; |
| // 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; |
| |
| if(matchAbsoluteWin32Path.test(url)) { |
| // absolute windows path, keep it |
| request = url; |
| } else if(root !== undefined && root !== false && /^\//.test(url)) { |
| // if root is set and the url is root-relative |
| switch(typeof root) { |
| // 1. root is a string: root is prefixed to the url |
| case "string": |
| // special case: `~` roots convert to module request |
| if (moduleRequestRegex.test(root)) { |
| request = root.replace(/([^~\/])$/, "$1/") + url.slice(1); |
| } else { |
| request = root + url; |
| } |
| break; |
| // 2. root is `true`: absolute paths are allowed |
| // *nix only, windows-style absolute paths are always allowed as they doesn't start with a `/` |
| case "boolean": |
| request = url; |
| break; |
| default: |
| throw new Error("Unexpected parameters to loader-utils 'urlToRequest': url = " + url + ", root = " + root + "."); |
| } |
| } else if(/^\.\.?\//.test(url)) { |
| // A relative url stays |
| request = url; |
| } else { |
| // every other url is threaded like a relative url |
| request = "./" + url; |
| } |
| |
| // A `~` makes the url an module |
| if (moduleRequestRegex.test(request)) { |
| request = request.replace(moduleRequestRegex, ""); |
| } |
| |
| return request; |
| }; |
| |
| 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 JSON.parse('"' + str + '"'); |
| } catch(e) { |
| return str; |
| } |
| }; |
| |
| exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, maxLength) { |
| hashType = hashType || "md5"; |
| maxLength = maxLength || 9999; |
| var hash = require("crypto").createHash(hashType); |
| hash.update(buffer); |
| if (digestType === "base26" || digestType === "base32" || digestType === "base36" || |
| digestType === "base49" || digestType === "base52" || digestType === "base58" || |
| digestType === "base62" || digestType === "base64") { |
| return encodeBufferToBase(hash.digest(), digestType.substr(4)).substr(0, maxLength); |
| } else { |
| return hash.digest(digestType || "hex").substr(0, maxLength); |
| } |
| }; |
| |
| exports.interpolateName = function interpolateName(loaderContext, name, options) { |
| var 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 = ""; |
| 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; |
| if(idx >= 0) { |
| ext = resourcePath.substr(idx+1); |
| resourcePath = resourcePath.substr(0, idx); |
| } |
| if(p >= 0) { |
| basename = resourcePath.substr(p+1); |
| resourcePath = resourcePath.substr(0, p+1); |
| } |
| if (typeof context !== 'undefined') { |
| directory = path.relative(context, resourcePath + "_").replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1"); |
| directory = directory.substr(0, directory.length-1); |
| } |
| else { |
| directory = resourcePath.replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1"); |
| } |
| if (directory.length === 1) { |
| directory = ""; |
| } else if (directory.length > 1) { |
| folder = path.basename(directory); |
| } |
| } |
| var 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(/\[ext\]/ig, function() { |
| return ext; |
| }).replace(/\[name\]/ig, function() { |
| return basename; |
| }).replace(/\[path\]/ig, function() { |
| return directory; |
| }).replace(/\[folder\]/ig, function() { |
| return folder; |
| }); |
| if(regExp && loaderContext.resourcePath) { |
| var re = new RegExp(regExp); |
| var match = loaderContext.resourcePath.match(re); |
| if(match) { |
| for (var i = 0; i < match.length; i++) { |
| var re = new RegExp("\\[" + i + "\\]", "ig"); |
| url = url.replace(re, match[i]); |
| } |
| } |
| } |
| if(typeof loaderContext.options === "object" && typeof loaderContext.options.customInterpolateName === "function") { |
| url = loaderContext.options.customInterpolateName.call(loaderContext, url, name, options); |
| } |
| return url; |
| }; |