You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
5.0 KiB
136 lines
5.0 KiB
/* |
|
* MIT License http://opensource.org/licenses/MIT |
|
* Author: Ben Holloway @bholloway |
|
*/ |
|
'use strict'; |
|
|
|
var path = require('path'), |
|
loaderUtils = require('loader-utils'); |
|
|
|
/** |
|
* Create a value processing function for a given file path. |
|
* |
|
* @param {function(Object):string} join The inner join function |
|
* @param {string} root The loader options.root value where given |
|
* @param {string} directory The directory of the file webpack is currently processing |
|
* @return {function} value processing function |
|
*/ |
|
function valueProcessor({ join, root, directory }) { |
|
var URL_STATEMENT_REGEX = /(url\s*\(\s*)(?:(['"])((?:(?!\2).)*)(\2)|([^'"](?:(?!\)).)*[^'"]))(\s*\))/g, |
|
QUERY_REGEX = /([?#])/g; |
|
|
|
/** |
|
* Process the given CSS declaration value. |
|
* |
|
* @param {string} value A declaration value that may or may not contain a url() statement |
|
* @param {function(number):Object} getPathsAtChar Given an offset in the declaration value get a |
|
* list of possible absolute path strings |
|
*/ |
|
return function transformValue(value, getPathsAtChar) { |
|
|
|
// allow multiple url() values in the declaration |
|
// split by url statements and process the content |
|
// additional capture groups are needed to match quotations correctly |
|
// escaped quotations are not considered |
|
return value |
|
.split(URL_STATEMENT_REGEX) |
|
.map(initialise) |
|
.map(eachSplitOrGroup) |
|
.join(''); |
|
|
|
/** |
|
* Ensure all capture group tokens are a valid string. |
|
* |
|
* @param {string|void} token A capture group or uncaptured token |
|
* @returns {string} |
|
*/ |
|
function initialise(token) { |
|
return typeof token === 'string' ? token : ''; |
|
} |
|
|
|
/** |
|
* An Array reduce function that accumulates string length. |
|
*/ |
|
function accumulateLength(accumulator, element) { |
|
return accumulator + element.length; |
|
} |
|
|
|
/** |
|
* Encode the content portion of <code>url()</code> statements. |
|
* There are 6 capture groups in the split making every 7th unmatched. |
|
* |
|
* @param {string} element A single split item |
|
* @param {number} i The index of the item in the split |
|
* @param {Array} arr The array of split values |
|
* @returns {string} Every 3 or 5 items is an encoded url everything else is as is |
|
*/ |
|
function eachSplitOrGroup(element, i, arr) { |
|
|
|
// the content of the url() statement is either in group 3 or group 5 |
|
var mod = i % 7; |
|
|
|
// only one of the capture groups 3 or 5 will match the other will be falsey |
|
if (element && ((mod === 3) || (mod === 5))) { |
|
|
|
// calculate the offset of the match from the front of the string |
|
var position = arr.slice(0, i - mod + 1).reduce(accumulateLength, 0); |
|
|
|
// detect quoted url and unescape backslashes |
|
var before = arr[i - 1], |
|
after = arr[i + 1], |
|
isQuoted = (before === after) && ((before === '\'') || (before === '"')), |
|
unescaped = isQuoted ? element.replace(/\\{2}/g, '\\') : element; |
|
|
|
// split into uri and query/hash and then determine if the uri is some type of file |
|
var split = unescaped.split(QUERY_REGEX), |
|
uri = split[0], |
|
query = split.slice(1).join(''), |
|
isRelative = testIsRelative(uri), |
|
isAbsolute = testIsAbsolute(uri); |
|
|
|
// file like URIs are processed but not all URIs are files |
|
if (isRelative || isAbsolute) { |
|
var bases = getPathsAtChar(position), // construct iterator as late as possible in case sourcemap invalid |
|
absolute = join({ uri, query, isAbsolute, bases }); |
|
|
|
if (typeof absolute === 'string') { |
|
var relative = path.relative(directory, absolute) |
|
.replace(/\\/g, '/'); // #6 - backslashes are not legal in URI |
|
|
|
return loaderUtils.urlToRequest(relative + query); |
|
} |
|
} |
|
} |
|
|
|
// everything else, including parentheses and quotation (where present) and media statements |
|
return element; |
|
} |
|
}; |
|
|
|
/** |
|
* The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that |
|
* dogma so we add path.isAbsolute() check to allow them. |
|
* |
|
* We also eliminate module relative (~) paths. |
|
* |
|
* @param {string|undefined} uri A uri string possibly empty or undefined |
|
* @return {boolean} True for relative uri |
|
*/ |
|
function testIsRelative(uri) { |
|
return !!uri && loaderUtils.isUrlRequest(uri, false) && !path.isAbsolute(uri) && (uri.indexOf('~') !== 0); |
|
} |
|
|
|
/** |
|
* The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that |
|
* dogma so we add path.isAbsolute() check to allow them. |
|
* |
|
* @param {string|undefined} uri A uri string possibly empty or undefined |
|
* @return {boolean} True for absolute uri |
|
*/ |
|
function testIsAbsolute(uri) { |
|
return !!uri && (typeof root === 'string') && loaderUtils.isUrlRequest(uri, root) && |
|
(/^\//.test(uri) || path.isAbsolute(uri)); |
|
} |
|
} |
|
|
|
module.exports = valueProcessor;
|
|
|