UNPKG

4.41 kBJavaScriptView Raw
1/*
2 * MIT License http://opensource.org/licenses/MIT
3 * Author: Ben Holloway @bholloway
4 */
5'use strict';
6
7var path = require('path'),
8 loaderUtils = require('loader-utils');
9
10/**
11 * Create a value processing function for a given file path.
12 *
13 * @param {string} filename The current file being processed
14 * @param {{absolute:string, keepQuery:boolean, join:function, root:string}} options Options hash
15 * @return {function} value processing function
16 */
17function valueProcessor(filename, options) {
18 var URL_STATEMENT_REGEX = /(url\s*\()\s*(?:(['"])((?:(?!\2).)*)(\2)|([^'"](?:(?!\)).)*[^'"]))\s*(\))/g;
19 var directory = path.dirname(filename);
20 var join = options.join(filename, options);
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 {string|Iterator.<string>} candidate An absolute path that may be the correct base or an Iterator thereof
27 */
28 return function transformValue(value, candidate) {
29
30 // allow multiple url() values in the declaration
31 // split by url statements and process the content
32 // additional capture groups are needed to match quotations correctly
33 // escaped quotations are not considered
34 return value
35 .split(URL_STATEMENT_REGEX)
36 .map(eachSplitOrGroup)
37 .join('');
38
39 /**
40 * Encode the content portion of <code>url()</code> statements.
41 * There are 4 capture groups in the split making every 5th unmatched.
42 *
43 * @param {string} token A single split item
44 * @param {number} i The index of the item in the split
45 * @param {Array} arr The array of split values
46 * @returns {string} Every 3 or 5 items is an encoded url everything else is as is
47 */
48 function eachSplitOrGroup(token, i, arr) {
49
50 // we can get groups as undefined under certain match circumstances
51 var initialised = token || '';
52
53 // the content of the url() statement is either in group 3 or group 5
54 var mod = i % 7;
55 if ((mod === 3) || (mod === 5)) {
56
57 // detect quoted url and unescape backslashes
58 var before = arr[i - 1],
59 after = arr[i + 1],
60 isQuoted = (before === after) && ((before === '\'') || (before === '"')),
61 unescaped = isQuoted ? initialised.replace(/\\{2}/g, '\\') : initialised;
62
63 // split into uri and query/hash and then find the absolute path to the uri
64 var split = unescaped.split(/([?#])/g),
65 uri = split[0],
66 absolute = testIsRelative(uri) && join(uri, candidate) || testIsAbsolute(uri) && join(uri),
67 query = options.keepQuery ? split.slice(1).join('') : '';
68
69 // use the absolute path in absolute mode or else relative path (or default to initialised)
70 // #6 - backslashes are not legal in URI
71 if (!absolute) {
72 return initialised;
73 } else if (options.absolute) {
74 return absolute.replace(/\\/g, '/') + query;
75 } else {
76 return loaderUtils.urlToRequest(
77 path.relative(directory, absolute).replace(/\\/g, '/') + query
78 );
79 }
80 }
81 // everything else, including parentheses and quotation (where present) and media statements
82 else {
83 return initialised;
84 }
85 }
86 };
87
88 /**
89 * The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that
90 * dogma so we add path.isAbsolute() check to allow them.
91 *
92 * We also eliminate module relative (~) paths.
93 *
94 * @param {string|undefined} uri A uri string possibly empty or undefined
95 * @return {boolean} True for relative uri
96 */
97 function testIsRelative(uri) {
98 return !!uri && loaderUtils.isUrlRequest(uri, false) && !path.isAbsolute(uri) && (uri.indexOf('~') !== 0);
99 }
100
101 /**
102 * The loaderUtils.isUrlRequest() doesn't support windows absolute paths on principle. We do not subscribe to that
103 * dogma so we add path.isAbsolute() check to allow them.
104 *
105 * @param {string|undefined} uri A uri string possibly empty or undefined
106 * @return {boolean} True for absolute uri
107 */
108 function testIsAbsolute(uri) {
109 return !!uri && (typeof options.root === 'string') && loaderUtils.isUrlRequest(uri, options.root) &&
110 (/^\//.test(uri) || path.isAbsolute(uri));
111 }
112}
113
114module.exports = valueProcessor;