Skip to content

Commit 875b274

Browse files
feat: resolve data URI (#1328)
1 parent 09675d9 commit 875b274

File tree

7 files changed

+618
-476
lines changed

7 files changed

+618
-476
lines changed

src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export default async function loader(content, map, meta) {
8787

8888
if (shouldUseURLPlugin(options)) {
8989
const needToResolveURL = !options.esModule;
90+
const isSupportDataURLInNewURL =
91+
options.esModule && Boolean("fsStartTime" in this._compiler);
9092

9193
plugins.push(
9294
urlParser({
@@ -101,6 +103,8 @@ export default async function loader(content, map, meta) {
101103
: // eslint-disable-next-line no-undefined
102104
undefined,
103105
urlHandler: (url) => stringifyRequest(this, url),
106+
// Support data urls as input in new URL added in webpack@5.38.0
107+
isSupportDataURLInNewURL,
104108
})
105109
);
106110
}

src/plugins/postcss-url-parser.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
normalizeUrl,
66
requestify,
77
isUrlRequestable,
8+
isDataUrl,
89
WEBPACK_IGNORE_COMMENT_REGEXP,
910
} from "../utils";
1011

@@ -47,7 +48,7 @@ function getWebpackIgnoreCommentValue(index, nodes, inBetween) {
4748
return matched && matched[2] === "true";
4849
}
4950

50-
function shouldHandleURL(url, declaration, result) {
51+
function shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL) {
5152
if (url.length === 0) {
5253
result.warn(`Unable to find uri in '${declaration.toString()}'`, {
5354
node: declaration,
@@ -56,14 +57,24 @@ function shouldHandleURL(url, declaration, result) {
5657
return false;
5758
}
5859

60+
if (isDataUrl(url) && isSupportDataURLInNewURL) {
61+
try {
62+
decodeURIComponent(url);
63+
} catch (ignoreError) {
64+
return false;
65+
}
66+
67+
return true;
68+
}
69+
5970
if (!isUrlRequestable(url)) {
6071
return false;
6172
}
6273

6374
return true;
6475
}
6576

66-
function parseDeclaration(declaration, key, result) {
77+
function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
6778
if (!needParseDeclaration.test(declaration[key])) {
6879
return;
6980
}
@@ -130,7 +141,9 @@ function parseDeclaration(declaration, key, result) {
130141
url = normalizeUrl(url, isStringValue);
131142

132143
// Do not traverse inside `url`
133-
if (!shouldHandleURL(url, declaration, result)) {
144+
if (
145+
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
146+
) {
134147
// eslint-disable-next-line consistent-return
135148
return false;
136149
}
@@ -186,7 +199,9 @@ function parseDeclaration(declaration, key, result) {
186199
url = normalizeUrl(url, isStringValue);
187200

188201
// Do not traverse inside `url`
189-
if (!shouldHandleURL(url, declaration, result)) {
202+
if (
203+
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
204+
) {
190205
// eslint-disable-next-line consistent-return
191206
return false;
192207
}
@@ -229,7 +244,9 @@ function parseDeclaration(declaration, key, result) {
229244
let url = normalizeUrl(value, true);
230245

231246
// Do not traverse inside `url`
232-
if (!shouldHandleURL(url, declaration, result)) {
247+
if (
248+
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
249+
) {
233250
// eslint-disable-next-line consistent-return
234251
return false;
235252
}
@@ -271,7 +288,13 @@ const plugin = (options = {}) => {
271288

272289
return {
273290
Declaration(declaration) {
274-
const parsedURL = parseDeclaration(declaration, "value", result);
291+
const { isSupportDataURLInNewURL } = options;
292+
const parsedURL = parseDeclaration(
293+
declaration,
294+
"value",
295+
result,
296+
isSupportDataURLInNewURL
297+
);
275298

276299
if (!parsedURL) {
277300
return;
@@ -292,10 +315,16 @@ const plugin = (options = {}) => {
292315
const needKeep = await options.filter(url);
293316

294317
if (!needKeep) {
318+
// eslint-disable-next-line consistent-return
295319
return;
296320
}
297321
}
298322

323+
if (isDataUrl(url)) {
324+
// eslint-disable-next-line consistent-return
325+
return parsedDeclaration;
326+
}
327+
299328
const splittedUrl = url.split(/(\?)?#/);
300329
const [pathname, query, hashOrQuery] = splittedUrl;
301330

@@ -320,6 +349,7 @@ const plugin = (options = {}) => {
320349
]);
321350

322351
if (!resolvedUrl) {
352+
// eslint-disable-next-line consistent-return
323353
return;
324354
}
325355

src/utils.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,18 @@ function defaultGetLocalIdent(
390390
return loaderContext._compilation.getPath(localIdentName, data);
391391
}
392392

393+
function fixedEncodeURIComponent(str) {
394+
return str.replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16)}`);
395+
}
396+
397+
function isDataUrl(url) {
398+
if (/^data:/i.test(url)) {
399+
return true;
400+
}
401+
402+
return false;
403+
}
404+
393405
const NATIVE_WIN32_PATH = /^[A-Z]:[/\\]|^\\\\/i;
394406

395407
function normalizeUrl(url, isStringValue) {
@@ -413,6 +425,11 @@ function normalizeUrl(url, isStringValue) {
413425

414426
normalizedUrl = unescape(normalizedUrl);
415427

428+
if (isDataUrl(url)) {
429+
// Todo fixedEncodeURIComponent is workaround. Webpack resolver shouldn't handle "!" in dataURL
430+
return fixedEncodeURIComponent(normalizedUrl);
431+
}
432+
416433
try {
417434
normalizedUrl = decodeURI(normalizedUrl);
418435
} catch (error) {
@@ -1099,4 +1116,5 @@ export {
10991116
combineRequests,
11001117
camelCase,
11011118
stringifyRequest,
1119+
isDataUrl,
11021120
};

0 commit comments

Comments
 (0)