[6a3a178] | 1 | /*
|
---|
| 2 | * MIT License http://opensource.org/licenses/MIT
|
---|
| 3 | * Author: Ben Holloway @bholloway
|
---|
| 4 | */
|
---|
| 5 | 'use strict';
|
---|
| 6 |
|
---|
| 7 | var path = require('path'),
|
---|
| 8 | loaderUtils = require('loader-utils');
|
---|
| 9 |
|
---|
| 10 | /**
|
---|
| 11 | * Create a value processing function for a given file path.
|
---|
| 12 | *
|
---|
| 13 | * @param {function(Object):string} join The inner join function
|
---|
| 14 | * @param {string} root The loader options.root value where given
|
---|
| 15 | * @param {string} directory The directory of the file webpack is currently processing
|
---|
| 16 | * @return {function} value processing function
|
---|
| 17 | */
|
---|
| 18 | function valueProcessor({ join, root, directory }) {
|
---|
| 19 | var URL_STATEMENT_REGEX = /(url\s*\(\s*)(?:(['"])((?:(?!\2).)*)(\2)|([^'"](?:(?!\)).)*[^'"]))(\s*\))/g,
|
---|
| 20 | QUERY_REGEX = /([?#])/g;
|
---|
| 21 |
|
---|
| 22 | /**
|
---|
| 23 | * Process the given CSS declaration value.
|
---|
| 24 | *
|
---|
| 25 | * @param {string} value A declaration value that may or may not contain a url() statement
|
---|
| 26 | * @param {function(number):Object} getPathsAtChar Given an offset in the declaration value get a
|
---|
| 27 | * list of possible absolute path strings
|
---|
| 28 | */
|
---|
| 29 | return function transformValue(value, getPathsAtChar) {
|
---|
| 30 |
|
---|
| 31 | // allow multiple url() values in the declaration
|
---|
| 32 | // split by url statements and process the content
|
---|
| 33 | // additional capture groups are needed to match quotations correctly
|
---|
| 34 | // escaped quotations are not considered
|
---|
| 35 | return value
|
---|
| 36 | .split(URL_STATEMENT_REGEX)
|
---|
| 37 | .map(initialise)
|
---|
| 38 | .map(eachSplitOrGroup)
|
---|
| 39 | .join('');
|
---|
| 40 |
|
---|
| 41 | /**
|
---|
| 42 | * Ensure all capture group tokens are a valid string.
|
---|
| 43 | *
|
---|
| 44 | * @param {string|void} token A capture group or uncaptured token
|
---|
| 45 | * @returns {string}
|
---|
| 46 | */
|
---|
| 47 | function initialise(token) {
|
---|
| 48 | return typeof token === 'string' ? token : '';
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | /**
|
---|
| 52 | * An Array reduce function that accumulates string length.
|
---|
| 53 | */
|
---|
| 54 | function accumulateLength(accumulator, element) {
|
---|
| 55 | return accumulator + element.length;
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | /**
|
---|
| 59 | * Encode the content portion of <code>url()</code> statements.
|
---|
| 60 | * There are 6 capture groups in the split making every 7th unmatched.
|
---|
| 61 | *
|
---|
| 62 | * @param {string} element A single split item
|
---|
| 63 | * @param {number} i The index of the item in the split
|
---|
| 64 | * @param {Array} arr The array of split values
|
---|
| 65 | * @returns {string} Every 3 or 5 items is an encoded url everything else is as is
|
---|
| 66 | */
|
---|
| 67 | function eachSplitOrGroup(element, i, arr) {
|
---|
| 68 |
|
---|
| 69 | // the content of the url() statement is either in group 3 or group 5
|
---|
| 70 | var mod = i % 7;
|
---|
| 71 |
|
---|
| 72 | // only one of the capture groups 3 or 5 will match the other will be falsey
|
---|
| 73 | if (element && ((mod === 3) || (mod === 5))) {
|
---|
| 74 |
|
---|
| 75 | // calculate the offset of the match from the front of the string
|
---|
| 76 | var position = arr.slice(0, i - mod + 1).reduce(accumulateLength, 0);
|
---|
| 77 |
|
---|
| 78 | // detect quoted url and unescape backslashes
|
---|
| 79 | var before = arr[i - 1],
|
---|
| 80 | after = arr[i + 1],
|
---|
| 81 | isQuoted = (before === after) && ((before === '\'') || (before === '"')),
|
---|
| 82 | unescaped = isQuoted ? element.replace(/\\{2}/g, '\\') : element;
|
---|
| 83 |
|
---|
| 84 | // split into uri and query/hash and then determine if the uri is some type of file
|
---|
| 85 | var split = unescaped.split(QUERY_REGEX),
|
---|
| 86 | uri = split[0],
|
---|
| 87 | query = split.slice(1).join(''),
|
---|
| 88 | isRelative = testIsRelative(uri),
|
---|
| 89 | isAbsolute = testIsAbsolute(uri);
|
---|
| 90 |
|
---|
| 91 | // file like URIs are processed but not all URIs are files
|
---|
| 92 | if (isRelative || isAbsolute) {
|
---|
| 93 | var bases = getPathsAtChar(position), // construct iterator as late as possible in case sourcemap invalid
|
---|
| 94 | absolute = join({ uri, query, isAbsolute, bases });
|
---|
| 95 |
|
---|
| 96 | if (typeof absolute === 'string') {
|
---|
| 97 | var relative = path.relative(directory, absolute)
|
---|
| 98 | .replace(/\\/g, '/'); // #6 - backslashes are not legal in URI
|
---|
| 99 |
|
---|
| 100 | return loaderUtils.urlToRequest(relative + query);
|
---|
| 101 | }
|
---|
| 102 | }
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | // everything else, including parentheses and quotation (where present) and media statements
|
---|
| 106 | return element;
|
---|
| 107 | }
|
---|
| 108 | };
|
---|
| 109 |
|
---|
| 110 | /**
|
---|
| 111 | * The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that
|
---|
| 112 | * dogma so we add path.isAbsolute() check to allow them.
|
---|
| 113 | *
|
---|
| 114 | * We also eliminate module relative (~) paths.
|
---|
| 115 | *
|
---|
| 116 | * @param {string|undefined} uri A uri string possibly empty or undefined
|
---|
| 117 | * @return {boolean} True for relative uri
|
---|
| 118 | */
|
---|
| 119 | function testIsRelative(uri) {
|
---|
| 120 | return !!uri && loaderUtils.isUrlRequest(uri, false) && !path.isAbsolute(uri) && (uri.indexOf('~') !== 0);
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | /**
|
---|
| 124 | * The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that
|
---|
| 125 | * dogma so we add path.isAbsolute() check to allow them.
|
---|
| 126 | *
|
---|
| 127 | * @param {string|undefined} uri A uri string possibly empty or undefined
|
---|
| 128 | * @return {boolean} True for absolute uri
|
---|
| 129 | */
|
---|
| 130 | function testIsAbsolute(uri) {
|
---|
| 131 | return !!uri && (typeof root === 'string') && loaderUtils.isUrlRequest(uri, root) &&
|
---|
| 132 | (/^\//.test(uri) || path.isAbsolute(uri));
|
---|
| 133 | }
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | module.exports = valueProcessor;
|
---|