Skip to content

Commit 8946be4

Browse files
feat: support absolute URL in url() when experiments.buildHttp enabled (#1389)
1 parent fee0582 commit 8946be4

13 files changed

+2189
-1939
lines changed

package-lock.json

+2,069-1,909
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"del": "^6.0.0",
6464
"del-cli": "^4.0.1",
6565
"es-check": "^6.0.0",
66-
"eslint": "^7.30.0",
66+
"eslint": "^8.1.0",
6767
"eslint-config-prettier": "^8.3.0",
6868
"eslint-plugin-import": "^2.23.4",
6969
"file-loader": "^6.2.0",

src/index.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,24 @@ export default async function loader(content, map, meta) {
5454
const importPluginImports = [];
5555
const importPluginApi = [];
5656

57+
let isSupportAbsoluteURL = false;
58+
59+
// TODO enable by default in the next major release
60+
if (
61+
this._compilation &&
62+
this._compilation.options &&
63+
this._compilation.options.experiments &&
64+
this._compilation.options.experiments.buildHttp
65+
) {
66+
isSupportAbsoluteURL = true;
67+
}
68+
const isSupportDataURL =
69+
options.esModule && Boolean("fsStartTime" in this._compiler);
70+
5771
if (shouldUseImportPlugin(options)) {
5872
plugins.push(
5973
importParser({
74+
isSupportAbsoluteURL,
6075
isCSSStyleSheet: options.exportType === "css-style-sheet",
6176
loaderContext: this,
6277
imports: importPluginImports,
@@ -75,11 +90,11 @@ export default async function loader(content, map, meta) {
7590

7691
if (shouldUseURLPlugin(options)) {
7792
const needToResolveURL = !options.esModule;
78-
const isSupportDataURLInNewURL =
79-
options.esModule && Boolean("fsStartTime" in this._compiler);
8093

8194
plugins.push(
8295
urlParser({
96+
isSupportAbsoluteURL,
97+
isSupportDataURL,
8398
imports: urlPluginImports,
8499
replacements,
85100
context: this.context,
@@ -92,7 +107,6 @@ export default async function loader(content, map, meta) {
92107
undefined,
93108
urlHandler: (url) => stringifyRequest(this, url),
94109
// Support data urls as input in new URL added in webpack@5.38.0
95-
isSupportDataURLInNewURL,
96110
})
97111
);
98112
}

src/plugins/postcss-url-parser.js

+12-20
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function getWebpackIgnoreCommentValue(index, nodes, inBetween) {
4848
return matched && matched[2] === "true";
4949
}
5050

51-
function shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL) {
51+
function shouldHandleURL(url, declaration, result, options = {}) {
5252
if (url.length === 0) {
5353
result.warn(`Unable to find uri in '${declaration.toString()}'`, {
5454
node: declaration,
@@ -57,7 +57,7 @@ function shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL) {
5757
return false;
5858
}
5959

60-
if (isDataUrl(url) && isSupportDataURLInNewURL) {
60+
if (isDataUrl(url) && options.isSupportDataURL) {
6161
try {
6262
decodeURIComponent(url);
6363
} catch (ignoreError) {
@@ -67,14 +67,14 @@ function shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL) {
6767
return true;
6868
}
6969

70-
if (!isUrlRequestable(url)) {
70+
if (!isUrlRequestable(url, options.isSupportAbsoluteURL)) {
7171
return false;
7272
}
7373

7474
return true;
7575
}
7676

77-
function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
77+
function parseDeclaration(declaration, key, result, options) {
7878
if (!needParseDeclaration.test(declaration[key])) {
7979
return;
8080
}
@@ -141,9 +141,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
141141
url = normalizeUrl(url, isStringValue);
142142

143143
// Do not traverse inside `url`
144-
if (
145-
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
146-
) {
144+
if (!shouldHandleURL(url, declaration, result, options)) {
147145
// eslint-disable-next-line consistent-return
148146
return false;
149147
}
@@ -199,9 +197,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
199197
url = normalizeUrl(url, isStringValue);
200198

201199
// Do not traverse inside `url`
202-
if (
203-
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
204-
) {
200+
if (!shouldHandleURL(url, declaration, result, options)) {
205201
// eslint-disable-next-line consistent-return
206202
return false;
207203
}
@@ -244,9 +240,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
244240
let url = normalizeUrl(value, true);
245241

246242
// Do not traverse inside `url`
247-
if (
248-
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
249-
) {
243+
if (!shouldHandleURL(url, declaration, result, options)) {
250244
// eslint-disable-next-line consistent-return
251245
return false;
252246
}
@@ -288,13 +282,11 @@ const plugin = (options = {}) => {
288282

289283
return {
290284
Declaration(declaration) {
291-
const { isSupportDataURLInNewURL } = options;
292-
const parsedURL = parseDeclaration(
293-
declaration,
294-
"value",
295-
result,
296-
isSupportDataURLInNewURL
297-
);
285+
const { isSupportDataURL, isSupportAbsoluteURL } = options;
286+
const parsedURL = parseDeclaration(declaration, "value", result, {
287+
isSupportDataURL,
288+
isSupportAbsoluteURL,
289+
});
298290

299291
if (!parsedURL) {
300292
return;

src/runtime/api.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ module.exports = (cssWithMappingToString) => {
5151
const alreadyImportedModules = {};
5252

5353
if (dedupe) {
54-
for (let i = 0; i < this.length; i++) {
55-
const id = this[i][0];
54+
for (let k = 0; k < this.length; k++) {
55+
const id = this[k][0];
5656

5757
if (id != null) {
5858
alreadyImportedModules[id] = true;
5959
}
6060
}
6161
}
6262

63-
for (let i = 0; i < modules.length; i++) {
64-
const item = [].concat(modules[i]);
63+
for (let k = 0; k < modules.length; k++) {
64+
const item = [].concat(modules[k]);
6565

6666
if (dedupe && alreadyImportedModules[item[0]]) {
6767
continue;

src/utils.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ async function resolveRequests(resolve, context, possibleRequests) {
11891189
});
11901190
}
11911191

1192-
function isUrlRequestable(url) {
1192+
function isUrlRequestable(url, isSupportAbsoluteURL) {
11931193
// Protocol-relative URLs
11941194
if (/^\/\//.test(url)) {
11951195
return false;
@@ -1202,6 +1202,10 @@ function isUrlRequestable(url) {
12021202

12031203
// Absolute URLs
12041204
if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !NATIVE_WIN32_PATH.test(url)) {
1205+
if (isSupportAbsoluteURL) {
1206+
return true;
1207+
}
1208+
12051209
return false;
12061210
}
12071211

test/__snapshots__/url-option.test.js.snap

+37
Original file line numberDiff line numberDiff line change
@@ -2538,6 +2538,43 @@ Warning
25382538
]
25392539
`;
25402540

2541+
exports[`"url" option should work with absolute URLs: errors 1`] = `Array []`;
2542+
2543+
exports[`"url" option should work with absolute URLs: module 1`] = `
2544+
"// Imports
2545+
import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from \\"../../../src/runtime/noSourceMaps.js\\";
2546+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
2547+
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../src/runtime/getUrl.js\\";
2548+
var ___CSS_LOADER_URL_IMPORT_0___ = new URL(\\"https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png\\", import.meta.url);
2549+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___);
2550+
___CSS_LOADER_EXPORT___.push([module.id, \\"@import url(https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css);\\"]);
2551+
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
2552+
// Module
2553+
___CSS_LOADER_EXPORT___.push([module.id, \\"a {\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
2554+
// Exports
2555+
export default ___CSS_LOADER_EXPORT___;
2556+
"
2557+
`;
2558+
2559+
exports[`"url" option should work with absolute URLs: result 1`] = `
2560+
Array [
2561+
Array [
2562+
"./url/absolute-url.css",
2563+
"@import url(https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css);",
2564+
],
2565+
Array [
2566+
"./url/absolute-url.css",
2567+
"a {
2568+
background: url(replaced_file_protocol_/webpack/public/path/img.png);
2569+
}
2570+
",
2571+
"",
2572+
],
2573+
]
2574+
`;
2575+
2576+
exports[`"url" option should work with absolute URLs: warnings 1`] = `Array []`;
2577+
25412578
exports[`"url" option should work with mini-css-extract-plugin: css 1`] = `
25422579
"/*!*****************************************************************************!*\\\\
25432580
!*** css ../../src/index.js??ruleSet[1].rules[0].use[1]!./url/imported.css ***!

test/fixtures/url/absolute-url.css

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css");
2+
3+
a {
4+
background: url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png");
5+
}

test/fixtures/url/absolute-url.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import css from './absolute-url.css';
2+
3+
__export__ = css;
4+
5+
export default css;

test/lock-files/lock.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png": {
3+
"integrity": "sha512-bHqIPBYwzPsVLYcTDqJzwgvIaxLjmezufiCVXAMI0Naelf3eWVdydMA40hXbSuB0dZCGjCepuGaI7Ze8kLM+Ew==",
4+
"contentType": "image/png"
5+
},
6+
"version": 1
7+
}

test/sourceMap-option.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ describe('"sourceMap" option', () => {
498498
(assetName) => /\.js$/.test(assetName)
499499
);
500500

501-
expect(chunkName).toBe("main.043d33a99a1aeaa533ff.bundle.js");
501+
expect(chunkName).toBe("main.25338c5bc4249008fa47.bundle.js");
502502
expect(
503503
getModuleSource("fixtures/source-map/basic.css", stats)
504504
).toMatchSnapshot("module");

test/url-option.test.js

+26
Original file line numberDiff line numberDiff line change
@@ -562,4 +562,30 @@ describe('"url" option', () => {
562562
expect(getWarnings(stats)).toMatchSnapshot("warnings");
563563
expect(getErrors(stats)).toMatchSnapshot("errors");
564564
});
565+
566+
it("should work with absolute URLs", async () => {
567+
const compiler = getCompiler(
568+
"./url/absolute-url.js",
569+
{},
570+
{
571+
experiments: {
572+
buildHttp: {
573+
allowedUris: [() => true],
574+
lockfileLocation: path.resolve(__dirname, "./lock-files/lock.json"),
575+
cacheLocation: path.resolve(__dirname, "./lock-files"),
576+
},
577+
},
578+
}
579+
);
580+
const stats = await compile(compiler);
581+
582+
expect(getModuleSource("./url/absolute-url.css", stats)).toMatchSnapshot(
583+
"module"
584+
);
585+
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
586+
"result"
587+
);
588+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
589+
expect(getErrors(stats)).toMatchSnapshot("errors");
590+
});
565591
});

0 commit comments

Comments
 (0)