Skip to content

Commit c80c39f

Browse files
fix: handling urls in @import (#1016)
1 parent 30a9269 commit c80c39f

File tree

7 files changed

+216
-61
lines changed

7 files changed

+216
-61
lines changed

src/plugins/postcss-import-parser.js

+23-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
3-
import { isUrlRequest, urlToRequest } from 'loader-utils';
3+
import { isUrlRequest } from 'loader-utils';
44

5-
const pluginName = 'postcss-import-parser';
5+
import { normalizeUrl } from '../utils';
66

7-
function getArg(nodes) {
8-
return nodes.length !== 0 && nodes[0].type === 'string'
9-
? nodes[0].value
10-
: valueParser.stringify(nodes);
11-
}
7+
const pluginName = 'postcss-import-parser';
128

13-
function getUrl(node) {
9+
function getParsedValue(node) {
1410
if (node.type === 'function' && node.value.toLowerCase() === 'url') {
15-
return getArg(node.nodes);
11+
const { nodes } = node;
12+
const isStringValue = nodes.length !== 0 && nodes[0].type === 'string';
13+
const url = isStringValue ? nodes[0].value : valueParser.stringify(nodes);
14+
15+
return { url, isStringValue };
1616
}
1717

1818
if (node.type === 'string') {
19-
return node.value;
19+
const url = node.value;
20+
21+
return { url, isStringValue: true };
2022
}
2123

2224
return null;
@@ -29,14 +31,22 @@ function parseImport(params) {
2931
return null;
3032
}
3133

32-
let url = getUrl(nodes[0]);
34+
const value = getParsedValue(nodes[0]);
3335

34-
if (!url || url.trim().length === 0) {
36+
if (!value) {
37+
return null;
38+
}
39+
40+
let { url } = value;
41+
42+
if (url.trim().length === 0) {
3543
return null;
3644
}
3745

3846
if (isUrlRequest(url)) {
39-
url = urlToRequest(url);
47+
const { isStringValue } = value;
48+
49+
url = normalizeUrl(url, isStringValue);
4050
}
4151

4252
return {

src/plugins/postcss-url-parser.js

+22-27
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
3-
import { urlToRequest } from 'loader-utils';
43

5-
import { unescape } from '../utils';
4+
import { normalizeUrl } from '../utils';
65

76
const pluginName = 'postcss-url-parser';
87

@@ -21,13 +20,11 @@ function walkUrls(parsed, callback) {
2120
}
2221

2322
if (isUrlFunc.test(node.value)) {
24-
const isStringNode =
25-
node.nodes.length !== 0 && node.nodes[0].type === 'string';
26-
const url = isStringNode
27-
? node.nodes[0].value
28-
: valueParser.stringify(node.nodes);
23+
const { nodes } = node;
24+
const isStringValue = nodes.length !== 0 && nodes[0].type === 'string';
25+
const url = isStringValue ? nodes[0].value : valueParser.stringify(nodes);
2926

30-
callback(getNodeFromUrlFunc(node), url, false, isStringNode);
27+
callback(getNodeFromUrlFunc(node), url, false, isStringValue);
3128

3229
// Do not traverse inside `url`
3330
// eslint-disable-next-line consistent-return
@@ -36,18 +33,22 @@ function walkUrls(parsed, callback) {
3633

3734
if (isImageSetFunc.test(node.value)) {
3835
node.nodes.forEach((nNode) => {
39-
if (nNode.type === 'function' && isUrlFunc.test(nNode.value)) {
40-
const isStringNode =
41-
nNode.nodes.length !== 0 && nNode.nodes[0].type === 'string';
42-
const url = isStringNode
43-
? nNode.nodes[0].value
44-
: valueParser.stringify(nNode.nodes);
45-
46-
callback(getNodeFromUrlFunc(nNode), url, false, isStringNode);
36+
const { type, value } = nNode;
37+
38+
if (type === 'function' && isUrlFunc.test(value)) {
39+
const { nodes } = nNode;
40+
41+
const isStringValue =
42+
nodes.length !== 0 && nodes[0].type === 'string';
43+
const url = isStringValue
44+
? nodes[0].value
45+
: valueParser.stringify(nodes);
46+
47+
callback(getNodeFromUrlFunc(nNode), url, false, isStringValue);
4748
}
4849

49-
if (nNode.type === 'string') {
50-
callback(nNode, nNode.value, true, true);
50+
if (type === 'string') {
51+
callback(nNode, value, true, true);
5152
}
5253
});
5354

@@ -66,7 +67,7 @@ function getUrlsFromValue(value, result, filter, decl) {
6667
const parsed = valueParser(value);
6768
const urls = [];
6869

69-
walkUrls(parsed, (node, url, needQuotes, isStringNode) => {
70+
walkUrls(parsed, (node, url, needQuotes, isStringValue) => {
7071
if (url.trim().replace(/\\[\r\n]/g, '').length === 0) {
7172
result.warn(`Unable to find uri in '${decl ? decl.toString() : value}'`, {
7273
node: decl,
@@ -80,19 +81,13 @@ function getUrlsFromValue(value, result, filter, decl) {
8081
}
8182

8283
const splittedUrl = url.split(/(\?)?#/);
83-
let [normalizedUrl] = splittedUrl;
84-
const [, singleQuery, hashValue] = splittedUrl;
84+
const [urlWithoutHash, singleQuery, hashValue] = splittedUrl;
8585
const hash =
8686
singleQuery || hashValue
8787
? `${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}`
8888
: '';
8989

90-
// See https://drafts.csswg.org/css-values-4/#strings
91-
if (isStringNode && /\\[\n]/.test(normalizedUrl)) {
92-
normalizedUrl = normalizedUrl.replace(/\\[\n]/g, '');
93-
}
94-
95-
normalizedUrl = urlToRequest(decodeURIComponent(unescape(normalizedUrl)));
90+
const normalizedUrl = normalizeUrl(urlWithoutHash, isStringValue);
9691

9792
urls.push({ node, url: normalizedUrl, hash, needQuotes });
9893
});

src/utils.js

+33-19
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
*/
55
import path from 'path';
66

7-
import loaderUtils, { isUrlRequest, stringifyRequest } from 'loader-utils';
7+
import loaderUtils, {
8+
isUrlRequest,
9+
stringifyRequest,
10+
urlToRequest,
11+
} from 'loader-utils';
812
import normalizePath from 'normalize-path';
913
import cssesc from 'cssesc';
1014
import modulesValues from 'postcss-modules-values';
@@ -13,23 +17,6 @@ import extractImports from 'postcss-modules-extract-imports';
1317
import modulesScope from 'postcss-modules-scope';
1418
import camelCase from 'camelcase';
1519

16-
function getImportPrefix(loaderContext, importLoaders) {
17-
if (importLoaders === false) {
18-
return '';
19-
}
20-
21-
const numberImportedLoaders = parseInt(importLoaders, 10) || 0;
22-
const loadersRequest = loaderContext.loaders
23-
.slice(
24-
loaderContext.loaderIndex,
25-
loaderContext.loaderIndex + 1 + numberImportedLoaders
26-
)
27-
.map((x) => x.request)
28-
.join('!');
29-
30-
return `-!${loadersRequest}!`;
31-
}
32-
3320
const whitespace = '[\\x20\\t\\r\\n\\f]';
3421
const unescapeRegExp = new RegExp(
3522
`\\\\([\\da-f]{1,6}${whitespace}?|(${whitespace})|.)`,
@@ -90,6 +77,16 @@ function getLocalIdent(loaderContext, localIdentName, localName, options) {
9077
).replace(/\\\[local\\\]/gi, localName);
9178
}
9279

80+
function normalizeUrl(url, isStringValue) {
81+
let normalizedUrl = url;
82+
83+
if (isStringValue && /\\[\n]/.test(normalizedUrl)) {
84+
normalizedUrl = normalizedUrl.replace(/\\[\n]/g, '');
85+
}
86+
87+
return urlToRequest(decodeURIComponent(unescape(normalizedUrl)));
88+
}
89+
9390
function getFilter(filter, resourcePath, defaultFilter = null) {
9491
return (item) => {
9592
if (defaultFilter && !defaultFilter(item)) {
@@ -186,6 +183,23 @@ function normalizeSourceMap(map) {
186183
return newMap;
187184
}
188185

186+
function getImportPrefix(loaderContext, importLoaders) {
187+
if (importLoaders === false) {
188+
return '';
189+
}
190+
191+
const numberImportedLoaders = parseInt(importLoaders, 10) || 0;
192+
const loadersRequest = loaderContext.loaders
193+
.slice(
194+
loaderContext.loaderIndex,
195+
loaderContext.loaderIndex + 1 + numberImportedLoaders
196+
)
197+
.map((x) => x.request)
198+
.join('!');
199+
200+
return `-!${loadersRequest}!`;
201+
}
202+
189203
function getImportCode(
190204
loaderContext,
191205
imports,
@@ -395,7 +409,7 @@ function getExportCode(
395409
}
396410

397411
export {
398-
unescape,
412+
normalizeUrl,
399413
getFilter,
400414
getModulesPlugins,
401415
normalizeSourceMap,

0 commit comments

Comments
 (0)