css-loader 5.1.4 → 5.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [5.2.3](https://github.com/webpack-contrib/css-loader/compare/v5.2.2...v5.2.3) (2021-04-19)
6
+
7
+ ### Bug Fixes
8
+
9
+ * improve performance
10
+
11
+ ### [5.2.2](https://github.com/webpack-contrib/css-loader/compare/v5.2.1...v5.2.2) (2021-04-16)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * avoid escape nonASCII characters in local names ([0722733](https://github.com/webpack-contrib/css-loader/commit/072273308a8ab4b7efdae31440689dc81978ca1d))
17
+
18
+ ### [5.2.1](https://github.com/webpack-contrib/css-loader/compare/v5.2.0...v5.2.1) (2021-04-09)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * do not crash on unescaped svg data uri ([#1288](https://github.com/webpack-contrib/css-loader/issues/1288)) ([4f289c5](https://github.com/webpack-contrib/css-loader/commit/4f289c5e4df6c666fdf6dd3402560ae74d4bf7ee))
24
+
25
+ ## [5.2.0](https://github.com/webpack-contrib/css-loader/compare/v5.1.4...v5.2.0) (2021-03-24)
26
+
27
+
28
+ ### Features
29
+
30
+ * support async functions for `url` and `import` options ([#1277](https://github.com/webpack-contrib/css-loader/issues/1277)) ([c5062db](https://github.com/webpack-contrib/css-loader/commit/c5062db3fc849d882a07b9f2c9f66f00325c8896))
31
+
5
32
  ### [5.1.4](https://github.com/webpack-contrib/css-loader/compare/v5.1.3...v5.1.4) (2021-03-24)
6
33
 
7
34
 
@@ -11,25 +11,25 @@ var _utils = require("../utils");
11
11
 
12
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
13
 
14
- function visitor(result, parsedResults, node, key) {
14
+ function parseNode(atRule, key) {
15
15
  // Convert only top-level @import
16
- if (node.parent.type !== "root") {
16
+ if (atRule.parent.type !== "root") {
17
17
  return;
18
18
  }
19
19
 
20
- if (node.raws && node.raws.afterName && node.raws.afterName.trim().length > 0) {
21
- const lastCommentIndex = node.raws.afterName.lastIndexOf("/*");
22
- const matched = node.raws.afterName.slice(lastCommentIndex).match(_utils.webpackIgnoreCommentRegexp);
20
+ if (atRule.raws && atRule.raws.afterName && atRule.raws.afterName.trim().length > 0) {
21
+ const lastCommentIndex = atRule.raws.afterName.lastIndexOf("/*");
22
+ const matched = atRule.raws.afterName.slice(lastCommentIndex).match(_utils.WEBPACK_IGNORE_COMMENT_REGEXP);
23
23
 
24
24
  if (matched && matched[2] === "true") {
25
25
  return;
26
26
  }
27
27
  }
28
28
 
29
- const prevNode = node.prev();
29
+ const prevNode = atRule.prev();
30
30
 
31
31
  if (prevNode && prevNode.type === "comment") {
32
- const matched = prevNode.text.match(_utils.webpackIgnoreCommentRegexp);
32
+ const matched = prevNode.text.match(_utils.WEBPACK_IGNORE_COMMENT_REGEXP);
33
33
 
34
34
  if (matched && matched[2] === "true") {
35
35
  return;
@@ -37,23 +37,21 @@ function visitor(result, parsedResults, node, key) {
37
37
  } // Nodes do not exists - `@import url('http://') :root {}`
38
38
 
39
39
 
40
- if (node.nodes) {
41
- result.warn("It looks like you didn't end your @import statement correctly. Child nodes are attached to it.", {
42
- node
43
- });
44
- return;
40
+ if (atRule.nodes) {
41
+ const error = new Error("It looks like you didn't end your @import statement correctly. Child nodes are attached to it.");
42
+ error.node = atRule;
43
+ throw error;
45
44
  }
46
45
 
47
46
  const {
48
47
  nodes: paramsNodes
49
- } = (0, _postcssValueParser.default)(node[key]); // No nodes - `@import ;`
48
+ } = (0, _postcssValueParser.default)(atRule[key]); // No nodes - `@import ;`
50
49
  // Invalid type - `@import foo-bar;`
51
50
 
52
51
  if (paramsNodes.length === 0 || paramsNodes[0].type !== "string" && paramsNodes[0].type !== "function") {
53
- result.warn(`Unable to find uri in "${node.toString()}"`, {
54
- node
55
- });
56
- return;
52
+ const error = new Error(`Unable to find uri in "${atRule.toString()}"`);
53
+ error.node = atRule;
54
+ throw error;
57
55
  }
58
56
 
59
57
  let isStringValue;
@@ -65,30 +63,50 @@ function visitor(result, parsedResults, node, key) {
65
63
  } else {
66
64
  // Invalid function - `@import nourl(test.css);`
67
65
  if (paramsNodes[0].value.toLowerCase() !== "url") {
68
- result.warn(`Unable to find uri in "${node.toString()}"`, {
69
- node
70
- });
71
- return;
66
+ const error = new Error(`Unable to find uri in "${atRule.toString()}"`);
67
+ error.node = atRule;
68
+ throw error;
72
69
  }
73
70
 
74
71
  isStringValue = paramsNodes[0].nodes.length !== 0 && paramsNodes[0].nodes[0].type === "string";
75
72
  url = isStringValue ? paramsNodes[0].nodes[0].value : _postcssValueParser.default.stringify(paramsNodes[0].nodes);
73
+ }
74
+
75
+ url = (0, _utils.normalizeUrl)(url, isStringValue);
76
+ const isRequestable = (0, _utils.isUrlRequestable)(url);
77
+ let prefix;
78
+
79
+ if (isRequestable) {
80
+ const queryParts = url.split("!");
81
+
82
+ if (queryParts.length > 1) {
83
+ url = queryParts.pop();
84
+ prefix = queryParts.join("!");
85
+ }
76
86
  } // Empty url - `@import "";` or `@import url();`
77
87
 
78
88
 
79
89
  if (url.trim().length === 0) {
80
- result.warn(`Unable to find uri in "${node.toString()}"`, {
81
- node
82
- });
83
- return;
90
+ const error = new Error(`Unable to find uri in "${atRule.toString()}"`);
91
+ error.node = atRule;
92
+ throw error;
84
93
  }
85
94
 
86
- parsedResults.push({
87
- node,
95
+ const mediaNodes = paramsNodes.slice(1);
96
+ let media;
97
+
98
+ if (mediaNodes.length > 0) {
99
+ media = _postcssValueParser.default.stringify(mediaNodes).trim().toLowerCase();
100
+ } // eslint-disable-next-line consistent-return
101
+
102
+
103
+ return {
104
+ atRule,
105
+ prefix,
88
106
  url,
89
- isStringValue,
90
- mediaNodes: paramsNodes.slice(1)
91
- });
107
+ media,
108
+ isRequestable
109
+ };
92
110
  }
93
111
 
94
112
  const plugin = (options = {}) => {
@@ -96,123 +114,94 @@ const plugin = (options = {}) => {
96
114
  postcssPlugin: "postcss-import-parser",
97
115
 
98
116
  prepare(result) {
99
- const parsedResults = [];
117
+ const parsedAtRules = [];
100
118
  return {
101
119
  AtRule: {
102
120
  import(atRule) {
103
- visitor(result, parsedResults, atRule, "params");
121
+ let parsedAtRule;
122
+
123
+ try {
124
+ parsedAtRule = parseNode(atRule, "params", result);
125
+ } catch (error) {
126
+ result.warn(error.message, {
127
+ node: error.node
128
+ });
129
+ }
130
+
131
+ if (!parsedAtRule) {
132
+ return;
133
+ }
134
+
135
+ parsedAtRules.push(parsedAtRule);
104
136
  }
105
137
 
106
138
  },
107
139
 
108
140
  async OnceExit() {
109
- if (parsedResults.length === 0) {
141
+ if (parsedAtRules.length === 0) {
110
142
  return;
111
143
  }
112
144
 
113
- const imports = new Map();
114
- const tasks = [];
115
-
116
- for (const parsedResult of parsedResults) {
145
+ const resolvedAtRules = await Promise.all(parsedAtRules.map(async parsedAtRule => {
117
146
  const {
118
- node,
147
+ atRule,
148
+ isRequestable,
149
+ prefix,
119
150
  url,
120
- isStringValue,
121
- mediaNodes
122
- } = parsedResult;
123
- let normalizedUrl = url;
124
- let prefix = "";
125
- const isRequestable = (0, _utils.isUrlRequestable)(normalizedUrl);
126
-
127
- if (isRequestable) {
128
- const queryParts = normalizedUrl.split("!");
129
-
130
- if (queryParts.length > 1) {
131
- normalizedUrl = queryParts.pop();
132
- prefix = queryParts.join("!");
133
- }
134
-
135
- normalizedUrl = (0, _utils.normalizeUrl)(normalizedUrl, isStringValue); // Empty url after normalize - `@import '\
136
- // \
137
- // \
138
- // ';
151
+ media
152
+ } = parsedAtRule;
139
153
 
140
- if (normalizedUrl.trim().length === 0) {
141
- result.warn(`Unable to find uri in "${node.toString()}"`, {
142
- node
143
- }); // eslint-disable-next-line no-continue
154
+ if (options.filter) {
155
+ const needKeep = await options.filter(url, media);
144
156
 
145
- continue;
157
+ if (!needKeep) {
158
+ return null;
146
159
  }
147
160
  }
148
161
 
149
- let media;
150
-
151
- if (mediaNodes.length > 0) {
152
- media = _postcssValueParser.default.stringify(mediaNodes).trim().toLowerCase();
153
- }
154
-
155
- if (options.filter && !options.filter(normalizedUrl, media)) {
156
- // eslint-disable-next-line no-continue
157
- continue;
158
- }
159
-
160
- node.remove();
162
+ atRule.remove();
161
163
 
162
164
  if (isRequestable) {
163
- const request = (0, _utils.requestify)(normalizedUrl, options.rootContext);
164
- tasks.push((async () => {
165
- const {
166
- resolver,
167
- context
168
- } = options;
169
- const resolvedUrl = await (0, _utils.resolveRequests)(resolver, context, [...new Set([request, normalizedUrl])]);
170
- return {
171
- url: resolvedUrl,
172
- media,
173
- prefix,
174
- isRequestable
175
- };
176
- })());
177
- } else {
178
- tasks.push({
179
- url,
165
+ const request = (0, _utils.requestify)(url, options.rootContext);
166
+ const {
167
+ resolver,
168
+ context
169
+ } = options;
170
+ const resolvedUrl = await (0, _utils.resolveRequests)(resolver, context, [...new Set([request, url])]);
171
+ return {
172
+ url: resolvedUrl,
180
173
  media,
181
174
  prefix,
182
175
  isRequestable
183
- });
176
+ };
184
177
  }
185
- }
186
178
 
187
- const results = await Promise.all(tasks);
179
+ return {
180
+ url,
181
+ media,
182
+ prefix,
183
+ isRequestable
184
+ };
185
+ }));
186
+ const urlToNameMap = new Map();
187
+
188
+ for (let index = 0; index <= resolvedAtRules.length - 1; index++) {
189
+ const resolvedAtRule = resolvedAtRules[index];
190
+
191
+ if (!resolvedAtRule) {
192
+ // eslint-disable-next-line no-continue
193
+ continue;
194
+ }
188
195
 
189
- for (let index = 0; index <= results.length - 1; index++) {
190
196
  const {
191
197
  url,
192
198
  isRequestable,
193
199
  media
194
- } = results[index];
195
-
196
- if (isRequestable) {
197
- const {
198
- prefix
199
- } = results[index];
200
- const newUrl = prefix ? `${prefix}!${url}` : url;
201
- const importKey = newUrl;
202
- let importName = imports.get(importKey);
203
-
204
- if (!importName) {
205
- importName = `___CSS_LOADER_AT_RULE_IMPORT_${imports.size}___`;
206
- imports.set(importKey, importName);
207
- options.imports.push({
208
- importName,
209
- url: options.urlHandler(newUrl),
210
- index
211
- });
212
- }
200
+ } = resolvedAtRule;
213
201
 
202
+ if (!isRequestable) {
214
203
  options.api.push({
215
- importName,
204
+ url,
216
205
  media,
217
206
  index
218
207
  }); // eslint-disable-next-line no-continue
@@ -220,8 +209,24 @@ const plugin = (options = {}) => {
220
209
  continue;
221
210
  }
222
211
 
212
+ const {
213
+ prefix
214
+ } = resolvedAtRule;
215
+ const newUrl = prefix ? `${prefix}!${url}` : url;
216
+ let importName = urlToNameMap.get(newUrl);
217
+
218
+ if (!importName) {
219
+ importName = `___CSS_LOADER_AT_RULE_IMPORT_${urlToNameMap.size}___`;
220
+ urlToNameMap.set(newUrl, importName);
221
+ options.imports.push({
222
+ importName,
223
+ url: options.urlHandler(newUrl),
224
+ index
225
+ });
226
+ }
227
+
223
228
  options.api.push({
224
- url,
229
+ importName,
225
230
  media,
226
231
  index
227
232
  });
@@ -19,22 +19,6 @@ function getNodeFromUrlFunc(node) {
19
19
  return node.nodes && node.nodes[0];
20
20
  }
21
21
 
22
- function shouldHandleRule(rule, node, result) {
23
- // https://www.w3.org/TR/css-syntax-3/#typedef-url-token
24
- if (rule.url.replace(/^[\s]+|[\s]+$/g, "").length === 0) {
25
- result.warn(`Unable to find uri in '${node.toString()}'`, {
26
- node
27
- });
28
- return false;
29
- }
30
-
31
- if (!(0, _utils.isUrlRequestable)(rule.url)) {
32
- return false;
33
- }
34
-
35
- return true;
36
- }
37
-
38
22
  function getWebpackIgnoreCommentValue(index, nodes, inBetween) {
39
23
  if (index === 0 && typeof inBetween !== "undefined") {
40
24
  return inBetween;
@@ -61,21 +45,36 @@ function getWebpackIgnoreCommentValue(index, nodes, inBetween) {
61
45
  return;
62
46
  }
63
47
 
64
- const matched = prevValueNode.value.match(_utils.webpackIgnoreCommentRegexp);
48
+ const matched = prevValueNode.value.match(_utils.WEBPACK_IGNORE_COMMENT_REGEXP);
65
49
  return matched && matched[2] === "true";
66
50
  }
67
51
 
68
- function visitor(result, parsedResults, node, key) {
69
- if (!needParseDeclaration.test(node[key])) {
52
+ function shouldHandleURL(url, declaration, result) {
53
+ if (url.length === 0) {
54
+ result.warn(`Unable to find uri in '${declaration.toString()}'`, {
55
+ node: declaration
56
+ });
57
+ return false;
58
+ }
59
+
60
+ if (!(0, _utils.isUrlRequestable)(url)) {
61
+ return false;
62
+ }
63
+
64
+ return true;
65
+ }
66
+
67
+ function parseDeclaration(declaration, key, result) {
68
+ if (!needParseDeclaration.test(declaration[key])) {
70
69
  return;
71
70
  }
72
71
 
73
- const parsed = (0, _postcssValueParser.default)(node.raws && node.raws.value && node.raws.value.raw ? node.raws.value.raw : node[key]);
72
+ const parsed = (0, _postcssValueParser.default)(declaration.raws && declaration.raws.value && declaration.raws.value.raw ? declaration.raws.value.raw : declaration[key]);
74
73
  let inBetween;
75
74
 
76
- if (node.raws && node.raws.between) {
77
- const lastCommentIndex = node.raws.between.lastIndexOf("/*");
78
- const matched = node.raws.between.slice(lastCommentIndex).match(_utils.webpackIgnoreCommentRegexp);
75
+ if (declaration.raws && declaration.raws.between) {
76
+ const lastCommentIndex = declaration.raws.between.lastIndexOf("/*");
77
+ const matched = declaration.raws.between.slice(lastCommentIndex).match(_utils.WEBPACK_IGNORE_COMMENT_REGEXP);
79
78
 
80
79
  if (matched) {
81
80
  inBetween = matched[2] === "true";
@@ -83,10 +82,10 @@ function visitor(result, parsedResults, node, key) {
83
82
  }
84
83
 
85
84
  let isIgnoreOnDeclaration = false;
86
- const prevNode = node.prev();
85
+ const prevNode = declaration.prev();
87
86
 
88
87
  if (prevNode && prevNode.type === "comment") {
89
- const matched = prevNode.text.match(_utils.webpackIgnoreCommentRegexp);
88
+ const matched = prevNode.text.match(_utils.WEBPACK_IGNORE_COMMENT_REGEXP);
90
89
 
91
90
  if (matched) {
92
91
  isIgnoreOnDeclaration = matched[2] === "true";
@@ -94,6 +93,7 @@ function visitor(result, parsedResults, node, key) {
94
93
  }
95
94
 
96
95
  let needIgnore;
96
+ const parsedURLs = [];
97
97
  parsed.walk((valueNode, index, valueNodes) => {
98
98
  if (valueNode.type !== "function") {
99
99
  return;
@@ -115,23 +115,30 @@ function visitor(result, parsedResults, node, key) {
115
115
  nodes
116
116
  } = valueNode;
117
117
  const isStringValue = nodes.length !== 0 && nodes[0].type === "string";
118
- const url = isStringValue ? nodes[0].value : _postcssValueParser.default.stringify(nodes);
119
- const rule = {
120
- node: getNodeFromUrlFunc(valueNode),
121
- url,
122
- needQuotes: false,
123
- isStringValue
124
- };
118
+ let url = isStringValue ? nodes[0].value : _postcssValueParser.default.stringify(nodes);
119
+ url = (0, _utils.normalizeUrl)(url, isStringValue); // Do not traverse inside `url`
125
120
 
126
- if (shouldHandleRule(rule, node, result)) {
127
- parsedResults.push({
128
- node,
129
- rule,
130
- parsed
131
- });
132
- } // Do not traverse inside `url`
133
- // eslint-disable-next-line consistent-return
121
+ if (!shouldHandleURL(url, declaration, result)) {
122
+ // eslint-disable-next-line consistent-return
123
+ return false;
124
+ }
134
125
 
126
+ const queryParts = url.split("!");
127
+ let prefix;
128
+
129
+ if (queryParts.length > 1) {
130
+ url = queryParts.pop();
131
+ prefix = queryParts.join("!");
132
+ }
133
+
134
+ parsedURLs.push({
135
+ declaration,
136
+ parsed,
137
+ node: getNodeFromUrlFunc(valueNode),
138
+ prefix,
139
+ url,
140
+ needQuotes: false
141
+ }); // eslint-disable-next-line consistent-return
135
142
 
136
143
  return false;
137
144
  } else if (isImageSetFunc.test(valueNode.value)) {
@@ -158,21 +165,30 @@ function visitor(result, parsedResults, node, key) {
158
165
  nodes
159
166
  } = nNode;
160
167
  const isStringValue = nodes.length !== 0 && nodes[0].type === "string";
161
- const url = isStringValue ? nodes[0].value : _postcssValueParser.default.stringify(nodes);
162
- const rule = {
168
+ let url = isStringValue ? nodes[0].value : _postcssValueParser.default.stringify(nodes);
169
+ url = (0, _utils.normalizeUrl)(url, isStringValue); // Do not traverse inside `url`
170
+
171
+ if (!shouldHandleURL(url, declaration, result)) {
172
+ // eslint-disable-next-line consistent-return
173
+ return false;
174
+ }
175
+
176
+ const queryParts = url.split("!");
177
+ let prefix;
178
+
179
+ if (queryParts.length > 1) {
180
+ url = queryParts.pop();
181
+ prefix = queryParts.join("!");
182
+ }
183
+
184
+ parsedURLs.push({
185
+ declaration,
186
+ parsed,
163
187
  node: getNodeFromUrlFunc(nNode),
188
+ prefix,
164
189
  url,
165
- needQuotes: false,
166
- isStringValue
167
- };
168
-
169
- if (shouldHandleRule(rule, node, result)) {
170
- parsedResults.push({
171
- node,
172
- rule,
173
- parsed
174
- });
175
- }
190
+ needQuotes: false
191
+ });
176
192
  } else if (type === "string") {
177
193
  needIgnore = getWebpackIgnoreCommentValue(innerIndex, valueNode.nodes);
178
194
 
@@ -186,20 +202,29 @@ function visitor(result, parsedResults, node, key) {
186
202
  continue;
187
203
  }
188
204
 
189
- const rule = {
190
- node: nNode,
191
- url: value,
192
- needQuotes: true,
193
- isStringValue: true
194
- };
195
-
196
- if (shouldHandleRule(rule, node, result)) {
197
- parsedResults.push({
198
- node,
199
- rule,
200
- parsed
201
- });
205
+ let url = (0, _utils.normalizeUrl)(value, true); // Do not traverse inside `url`
206
+
207
+ if (!shouldHandleURL(url, declaration, result)) {
208
+ // eslint-disable-next-line consistent-return
209
+ return false;
210
+ }
211
+
212
+ const queryParts = url.split("!");
213
+ let prefix;
214
+
215
+ if (queryParts.length > 1) {
216
+ url = queryParts.pop();
217
+ prefix = queryParts.join("!");
202
218
  }
219
+
220
+ parsedURLs.push({
221
+ declaration,
222
+ parsed,
223
+ node: nNode,
224
+ prefix,
225
+ url,
226
+ needQuotes: true
227
+ });
203
228
  }
204
229
  } // Do not traverse inside `image-set`
205
230
  // eslint-disable-next-line consistent-return
@@ -207,7 +232,9 @@ function visitor(result, parsedResults, node, key) {
207
232
 
208
233
  return false;
209
234
  }
210
- });
235
+ }); // eslint-disable-next-line consistent-return
236
+
237
+ return parsedURLs;
211
238
  }
212
239
 
213
240
  const plugin = (options = {}) => {
@@ -215,39 +242,60 @@ const plugin = (options = {}) => {
215
242
  postcssPlugin: "postcss-url-parser",
216
243
 
217
244
  prepare(result) {
218
- const parsedResults = [];
245
+ const parsedDeclarations = [];
219
246
  return {
220
247
  Declaration(declaration) {
221
- visitor(result, parsedResults, declaration, "value");
248
+ const parsedURL = parseDeclaration(declaration, "value", result);
249
+
250
+ if (!parsedURL) {
251
+ return;
252
+ }
253
+
254
+ parsedDeclarations.push(...parsedURL);
222
255
  },
223
256
 
224
257
  async OnceExit() {
225
- if (parsedResults.length === 0) {
258
+ if (parsedDeclarations.length === 0) {
226
259
  return;
227
260
  }
228
261
 
229
- const tasks = [];
230
- const imports = new Map();
231
- const replacements = new Map();
232
- let hasUrlImportHelper = false;
233
-
234
- for (const parsedResult of parsedResults) {
262
+ const resolvedDeclarations = await Promise.all(parsedDeclarations.map(async parsedDeclaration => {
235
263
  const {
236
- url,
237
- isStringValue
238
- } = parsedResult.rule;
239
- let normalizedUrl = url;
240
- let prefix = "";
241
- const queryParts = normalizedUrl.split("!");
242
-
243
- if (queryParts.length > 1) {
244
- normalizedUrl = queryParts.pop();
245
- prefix = queryParts.join("!");
264
+ url
265
+ } = parsedDeclaration;
266
+
267
+ if (options.filter) {
268
+ const needKeep = await options.filter(url);
269
+
270
+ if (!needKeep) {
271
+ return null;
272
+ }
246
273
  }
247
274
 
248
- normalizedUrl = (0, _utils.normalizeUrl)(normalizedUrl, isStringValue);
275
+ const splittedUrl = url.split(/(\?)?#/);
276
+ const [pathname, query, hashOrQuery] = splittedUrl;
277
+ let hash = query ? "?" : "";
278
+ hash += hashOrQuery ? `#${hashOrQuery}` : "";
279
+ const request = (0, _utils.requestify)(pathname, options.rootContext);
280
+ const {
281
+ resolver,
282
+ context
283
+ } = options;
284
+ const resolvedUrl = await (0, _utils.resolveRequests)(resolver, context, [...new Set([request, url])]);
285
+ return { ...parsedDeclaration,
286
+ url: resolvedUrl,
287
+ hash
288
+ };
289
+ }));
290
+ const results = await Promise.all(resolvedDeclarations);
291
+ const urlToNameMap = new Map();
292
+ const urlToReplacementMap = new Map();
293
+ let hasUrlImportHelper = false;
249
294
 
250
- if (!options.filter(normalizedUrl)) {
295
+ for (let index = 0; index <= results.length - 1; index++) {
296
+ const item = results[index];
297
+
298
+ if (!item) {
251
299
  // eslint-disable-next-line no-continue
252
300
  continue;
253
301
  }
@@ -261,46 +309,16 @@ const plugin = (options = {}) => {
261
309
  hasUrlImportHelper = true;
262
310
  }
263
311
 
264
- const splittedUrl = normalizedUrl.split(/(\?)?#/);
265
- const [pathname, query, hashOrQuery] = splittedUrl;
266
- let hash = query ? "?" : "";
267
- hash += hashOrQuery ? `#${hashOrQuery}` : "";
268
- const request = (0, _utils.requestify)(pathname, options.rootContext);
269
- tasks.push((async () => {
270
- const {
271
- resolver,
272
- context
273
- } = options;
274
- const resolvedUrl = await (0, _utils.resolveRequests)(resolver, context, [...new Set([request, normalizedUrl])]);
275
- return {
276
- url: resolvedUrl,
277
- prefix,
278
- hash,
279
- parsedResult
280
- };
281
- })());
282
- }
283
-
284
- const results = await Promise.all(tasks);
285
-
286
- for (let index = 0; index <= results.length - 1; index++) {
287
312
  const {
288
313
  url,
289
- prefix,
290
- hash,
291
- parsedResult: {
292
- node,
293
- rule,
294
- parsed
295
- }
296
- } = results[index];
314
+ prefix
315
+ } = item;
297
316
  const newUrl = prefix ? `${prefix}!${url}` : url;
298
- const importKey = newUrl;
299
- let importName = imports.get(importKey);
317
+ let importName = urlToNameMap.get(newUrl);
300
318
 
301
319
  if (!importName) {
302
- importName = `___CSS_LOADER_URL_IMPORT_${imports.size}___`;
303
- imports.set(importKey, importName);
320
+ importName = `___CSS_LOADER_URL_IMPORT_${urlToNameMap.size}___`;
321
+ urlToNameMap.set(newUrl, importName);
304
322
  options.imports.push({
305
323
  importName,
306
324
  url: options.urlHandler(newUrl),
@@ -309,18 +327,19 @@ const plugin = (options = {}) => {
309
327
  }
310
328
 
311
329
  const {
330
+ hash,
312
331
  needQuotes
313
- } = rule;
332
+ } = item;
314
333
  const replacementKey = JSON.stringify({
315
334
  newUrl,
316
335
  hash,
317
336
  needQuotes
318
337
  });
319
- let replacementName = replacements.get(replacementKey);
338
+ let replacementName = urlToReplacementMap.get(replacementKey);
320
339
 
321
340
  if (!replacementName) {
322
- replacementName = `___CSS_LOADER_URL_REPLACEMENT_${replacements.size}___`;
323
- replacements.set(replacementKey, replacementName);
341
+ replacementName = `___CSS_LOADER_URL_REPLACEMENT_${urlToReplacementMap.size}___`;
342
+ urlToReplacementMap.set(replacementKey, replacementName);
324
343
  options.replacements.push({
325
344
  replacementName,
326
345
  importName,
@@ -330,11 +349,11 @@ const plugin = (options = {}) => {
330
349
  } // eslint-disable-next-line no-param-reassign
331
350
 
332
351
 
333
- rule.node.type = "word"; // eslint-disable-next-line no-param-reassign
352
+ item.node.type = "word"; // eslint-disable-next-line no-param-reassign
334
353
 
335
- rule.node.value = replacementName; // eslint-disable-next-line no-param-reassign
354
+ item.node.value = replacementName; // eslint-disable-next-line no-param-reassign
336
355
 
337
- node.value = parsed.toString();
356
+ item.declaration.value = item.parsed.toString();
338
357
  }
339
358
  }
340
359
 
package/dist/utils.js CHANGED
@@ -22,7 +22,7 @@ exports.resolveRequests = resolveRequests;
22
22
  exports.isUrlRequestable = isUrlRequestable;
23
23
  exports.sort = sort;
24
24
  exports.combineRequests = combineRequests;
25
- exports.webpackIgnoreCommentRegexp = void 0;
25
+ exports.WEBPACK_IGNORE_COMMENT_REGEXP = void 0;
26
26
 
27
27
  var _url = require("url");
28
28
 
@@ -30,8 +30,6 @@ var _path = _interopRequireDefault(require("path"));
30
30
 
31
31
  var _loaderUtils = require("loader-utils");
32
32
 
33
- var _cssesc = _interopRequireDefault(require("cssesc"));
34
-
35
33
  var _postcssModulesValues = _interopRequireDefault(require("postcss-modules-values"));
36
34
 
37
35
  var _postcssModulesLocalByDefault = _interopRequireDefault(require("postcss-modules-local-by-default"));
@@ -48,26 +46,137 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
48
46
  MIT License http://www.opensource.org/licenses/mit-license.php
49
47
  Author Tobias Koppers @sokra
50
48
  */
51
- const whitespace = "[\\x20\\t\\r\\n\\f]";
52
- const unescapeRegExp = new RegExp(`\\\\([\\da-f]{1,6}${whitespace}?|(${whitespace})|.)`, "ig");
53
- const matchNativeWin32Path = /^[A-Z]:[/\\]|^\\\\/i;
54
- const webpackIgnoreCommentRegexp = /webpackIgnore:(\s+)?(true|false)/;
55
- exports.webpackIgnoreCommentRegexp = webpackIgnoreCommentRegexp;
49
+ const WEBPACK_IGNORE_COMMENT_REGEXP = /webpackIgnore:(\s+)?(true|false)/; // eslint-disable-next-line no-useless-escape
50
+
51
+ exports.WEBPACK_IGNORE_COMMENT_REGEXP = WEBPACK_IGNORE_COMMENT_REGEXP;
52
+ const regexSingleEscape = /[ -,.\/:-@[\]\^`{-~]/;
53
+ const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
54
+
55
+ function escape(string) {
56
+ let output = "";
57
+ let counter = 0;
58
+
59
+ while (counter < string.length) {
60
+ // eslint-disable-next-line no-plusplus
61
+ const character = string.charAt(counter++);
62
+ let value; // eslint-disable-next-line no-control-regex
63
+
64
+ if (/[\t\n\f\r\x0B]/.test(character)) {
65
+ const codePoint = character.charCodeAt();
66
+ value = `\\${codePoint.toString(16).toUpperCase()} `;
67
+ } else if (character === "\\" || regexSingleEscape.test(character)) {
68
+ value = `\\${character}`;
69
+ } else {
70
+ value = character;
71
+ }
56
72
 
57
- function unescape(str) {
58
- return str.replace(unescapeRegExp, (_, escaped, escapedWhitespace) => {
59
- const high = `0x${escaped}` - 0x10000;
60
- /* eslint-disable line-comment-position */
61
- // NaN means non-codepoint
62
- // Workaround erroneous numeric interpretation of +"0x"
63
- // eslint-disable-next-line no-self-compare
64
-
65
- return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint
66
- String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair)
67
- // eslint-disable-next-line no-bitwise
68
- String.fromCharCode(high >> 10 | 0xd800, high & 0x3ff | 0xdc00);
69
- /* eslint-enable line-comment-position */
73
+ output += value;
74
+ }
75
+
76
+ const firstChar = string.charAt(0);
77
+
78
+ if (/^-[-\d]/.test(output)) {
79
+ output = `\\-${output.slice(1)}`;
80
+ } else if (/\d/.test(firstChar)) {
81
+ output = `\\3${firstChar} ${output.slice(1)}`;
82
+ } // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
83
+ // since they’re redundant. Note that this is only possible if the escape
84
+ // sequence isn’t preceded by an odd number of backslashes.
85
+
86
+
87
+ output = output.replace(regexExcessiveSpaces, ($0, $1, $2) => {
88
+ if ($1 && $1.length % 2) {
89
+ // It’s not safe to remove the space, so don’t.
90
+ return $0;
91
+ } // Strip the space.
92
+
93
+
94
+ return ($1 || "") + $2;
70
95
  });
96
+ return output;
97
+ }
98
+
99
+ function gobbleHex(str) {
100
+ const lower = str.toLowerCase();
101
+ let hex = "";
102
+ let spaceTerminated = false; // eslint-disable-next-line no-undefined
103
+
104
+ for (let i = 0; i < 6 && lower[i] !== undefined; i++) {
105
+ const code = lower.charCodeAt(i); // check to see if we are dealing with a valid hex char [a-f|0-9]
106
+
107
+ const valid = code >= 97 && code <= 102 || code >= 48 && code <= 57; // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
108
+
109
+ spaceTerminated = code === 32;
110
+
111
+ if (!valid) {
112
+ break;
113
+ }
114
+
115
+ hex += lower[i];
116
+ }
117
+
118
+ if (hex.length === 0) {
119
+ // eslint-disable-next-line no-undefined
120
+ return undefined;
121
+ }
122
+
123
+ const codePoint = parseInt(hex, 16);
124
+ const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff; // Add special case for
125
+ // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
126
+ // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
127
+
128
+ if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
129
+ return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)];
130
+ }
131
+
132
+ return [String.fromCodePoint(codePoint), hex.length + (spaceTerminated ? 1 : 0)];
133
+ }
134
+
135
+ const CONTAINS_ESCAPE = /\\/;
136
+
137
+ function unescape(str) {
138
+ const needToProcess = CONTAINS_ESCAPE.test(str);
139
+
140
+ if (!needToProcess) {
141
+ return str;
142
+ }
143
+
144
+ let ret = "";
145
+
146
+ for (let i = 0; i < str.length; i++) {
147
+ if (str[i] === "\\") {
148
+ const gobbled = gobbleHex(str.slice(i + 1, i + 7)); // eslint-disable-next-line no-undefined
149
+
150
+ if (gobbled !== undefined) {
151
+ ret += gobbled[0];
152
+ i += gobbled[1]; // eslint-disable-next-line no-continue
153
+
154
+ continue;
155
+ } // Retain a pair of \\ if double escaped `\\\\`
156
+ // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
157
+
158
+
159
+ if (str[i + 1] === "\\") {
160
+ ret += "\\";
161
+ i += 1; // eslint-disable-next-line no-continue
162
+
163
+ continue;
164
+ } // if \\ is at the end of the string retain it
165
+ // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
166
+
167
+
168
+ if (str.length === i + 1) {
169
+ ret += str[i];
170
+ } // eslint-disable-next-line no-continue
171
+
172
+
173
+ continue;
174
+ }
175
+
176
+ ret += str[i];
177
+ }
178
+
179
+ return ret;
71
180
  }
72
181
 
73
182
  function normalizePath(file) {
@@ -80,10 +189,9 @@ const filenameReservedRegex = /[<>:"/\\|?*]/g; // eslint-disable-next-line no-co
80
189
  const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g;
81
190
 
82
191
  function escapeLocalIdent(localident) {
83
- return (0, _cssesc.default)(localident // For `[hash]` placeholder
84
- .replace(/^((-?[0-9])|--)/, "_$1").replace(filenameReservedRegex, "-").replace(reControlChars, "-").replace(/\./g, "-"), {
85
- isIdentifier: true
86
- });
192
+ // TODO simplify in the next major release
193
+ return escape(localident // For `[hash]` placeholder
194
+ .replace(/^((-?[0-9])|--)/, "_$1").replace(filenameReservedRegex, "-").replace(reControlChars, "-").replace(/\./g, "-"));
87
195
  }
88
196
 
89
197
  function defaultGetLocalIdent(loaderContext, localIdentName, localName, options) {
@@ -100,6 +208,8 @@ function defaultGetLocalIdent(loaderContext, localIdentName, localName, options)
100
208
  return (0, _loaderUtils.interpolateName)(loaderContext, localIdentName, options);
101
209
  }
102
210
 
211
+ const NATIVE_WIN32_PATH = /^[A-Z]:[/\\]|^\\\\/i;
212
+
103
213
  function normalizeUrl(url, isStringValue) {
104
214
  let normalizedUrl = url.replace(/^( |\t\n|\r\n|\r|\f)*/g, "").replace(/( |\t\n|\r\n|\r|\f)*$/g, "");
105
215
 
@@ -107,11 +217,23 @@ function normalizeUrl(url, isStringValue) {
107
217
  normalizedUrl = normalizedUrl.replace(/\\(\n|\r\n|\r|\f)/g, "");
108
218
  }
109
219
 
110
- if (matchNativeWin32Path.test(url)) {
111
- return decodeURI(normalizedUrl);
220
+ if (NATIVE_WIN32_PATH.test(url)) {
221
+ try {
222
+ normalizedUrl = decodeURI(normalizedUrl);
223
+ } catch (error) {// Ignore
224
+ }
225
+
226
+ return normalizedUrl;
227
+ }
228
+
229
+ normalizedUrl = unescape(normalizedUrl);
230
+
231
+ try {
232
+ normalizedUrl = decodeURI(normalizedUrl);
233
+ } catch (error) {// Ignore
112
234
  }
113
235
 
114
- return decodeURI(unescape(normalizedUrl));
236
+ return normalizedUrl;
115
237
  }
116
238
 
117
239
  function requestify(url, rootContext) {
@@ -629,7 +751,7 @@ function isUrlRequestable(url) {
629
751
  } // Absolute URLs
630
752
 
631
753
 
632
- if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !matchNativeWin32Path.test(url)) {
754
+ if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !NATIVE_WIN32_PATH.test(url)) {
633
755
  return false;
634
756
  } // `#` URLs
635
757
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "css-loader",
3
- "version": "5.1.4",
3
+ "version": "5.2.3",
4
4
  "description": "css loader module for webpack",
5
5
  "license": "MIT",
6
6
  "repository": "webpack-contrib/css-loader",
@@ -43,32 +43,31 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "camelcase": "^6.2.0",
46
- "cssesc": "^3.0.0",
47
46
  "icss-utils": "^5.1.0",
48
47
  "loader-utils": "^2.0.0",
49
- "postcss": "^8.2.8",
48
+ "postcss": "^8.2.10",
50
49
  "postcss-modules-extract-imports": "^3.0.0",
51
50
  "postcss-modules-local-by-default": "^4.0.0",
52
51
  "postcss-modules-scope": "^3.0.0",
53
52
  "postcss-modules-values": "^4.0.0",
54
53
  "postcss-value-parser": "^4.1.0",
55
54
  "schema-utils": "^3.0.0",
56
- "semver": "^7.3.4"
55
+ "semver": "^7.3.5"
57
56
  },
58
57
  "devDependencies": {
59
- "@babel/cli": "^7.13.10",
60
- "@babel/core": "^7.13.10",
61
- "@babel/preset-env": "^7.13.10",
62
- "@commitlint/cli": "^12.0.1",
63
- "@commitlint/config-conventional": "^12.0.1",
58
+ "@babel/cli": "^7.13.14",
59
+ "@babel/core": "^7.13.15",
60
+ "@babel/preset-env": "^7.13.15",
61
+ "@commitlint/cli": "^12.1.1",
62
+ "@commitlint/config-conventional": "^12.1.1",
64
63
  "@webpack-contrib/eslint-config-webpack": "^3.0.0",
65
64
  "babel-jest": "^26.6.3",
66
65
  "cross-env": "^7.0.3",
67
66
  "del": "^6.0.0",
68
67
  "del-cli": "^3.0.1",
69
68
  "es-check": "^5.2.3",
70
- "eslint": "^7.22.0",
71
- "eslint-config-prettier": "^8.1.0",
69
+ "eslint": "^7.24.0",
70
+ "eslint-config-prettier": "^8.2.0",
72
71
  "eslint-plugin-import": "^2.22.1",
73
72
  "file-loader": "^6.2.0",
74
73
  "husky": "^4.3.8",
@@ -76,21 +75,21 @@
76
75
  "less": "^4.1.1",
77
76
  "less-loader": "^7.1.0",
78
77
  "lint-staged": "^10.5.4",
79
- "memfs": "^3.2.0",
80
- "mini-css-extract-plugin": "^1.3.9",
78
+ "memfs": "^3.2.2",
79
+ "mini-css-extract-plugin": "^1.4.1",
81
80
  "npm-run-all": "^4.1.5",
82
81
  "postcss-loader": "^4.0.4",
83
82
  "postcss-preset-env": "^6.7.0",
84
83
  "prettier": "^2.1.2",
85
84
  "sass": "^1.32.8",
86
85
  "sass-loader": "^10.1.0",
87
- "standard-version": "^9.1.1",
86
+ "standard-version": "^9.2.0",
88
87
  "strip-ansi": "^6.0.0",
89
88
  "style-loader": "^2.0.0",
90
89
  "stylus": "^0.54.8",
91
90
  "stylus-loader": "^4.3.0",
92
91
  "url-loader": "^4.1.1",
93
- "webpack": "^5.26.0"
92
+ "webpack": "^5.33.2"
94
93
  },
95
94
  "keywords": [
96
95
  "webpack",