Skip to content

Commit

Permalink
feat: support absolute URL in url() when experiments.buildHttp en…
Browse files Browse the repository at this point in the history
…abled (#1389)
  • Loading branch information
alexander-akait committed Oct 26, 2021
1 parent fee0582 commit 8946be4
Show file tree
Hide file tree
Showing 13 changed files with 2,189 additions and 1,939 deletions.
3,978 changes: 2,069 additions & 1,909 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -63,7 +63,7 @@
"del": "^6.0.0",
"del-cli": "^4.0.1",
"es-check": "^6.0.0",
"eslint": "^7.30.0",
"eslint": "^8.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"file-loader": "^6.2.0",
Expand Down
20 changes: 17 additions & 3 deletions src/index.js
Expand Up @@ -54,9 +54,24 @@ export default async function loader(content, map, meta) {
const importPluginImports = [];
const importPluginApi = [];

let isSupportAbsoluteURL = false;

// TODO enable by default in the next major release
if (
this._compilation &&
this._compilation.options &&
this._compilation.options.experiments &&
this._compilation.options.experiments.buildHttp
) {
isSupportAbsoluteURL = true;
}
const isSupportDataURL =
options.esModule && Boolean("fsStartTime" in this._compiler);

if (shouldUseImportPlugin(options)) {
plugins.push(
importParser({
isSupportAbsoluteURL,
isCSSStyleSheet: options.exportType === "css-style-sheet",
loaderContext: this,
imports: importPluginImports,
Expand All @@ -75,11 +90,11 @@ export default async function loader(content, map, meta) {

if (shouldUseURLPlugin(options)) {
const needToResolveURL = !options.esModule;
const isSupportDataURLInNewURL =
options.esModule && Boolean("fsStartTime" in this._compiler);

plugins.push(
urlParser({
isSupportAbsoluteURL,
isSupportDataURL,
imports: urlPluginImports,
replacements,
context: this.context,
Expand All @@ -92,7 +107,6 @@ export default async function loader(content, map, meta) {
undefined,
urlHandler: (url) => stringifyRequest(this, url),
// Support data urls as input in new URL added in webpack@5.38.0
isSupportDataURLInNewURL,
})
);
}
Expand Down
32 changes: 12 additions & 20 deletions src/plugins/postcss-url-parser.js
Expand Up @@ -48,7 +48,7 @@ function getWebpackIgnoreCommentValue(index, nodes, inBetween) {
return matched && matched[2] === "true";
}

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

if (isDataUrl(url) && isSupportDataURLInNewURL) {
if (isDataUrl(url) && options.isSupportDataURL) {
try {
decodeURIComponent(url);
} catch (ignoreError) {
Expand All @@ -67,14 +67,14 @@ function shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL) {
return true;
}

if (!isUrlRequestable(url)) {
if (!isUrlRequestable(url, options.isSupportAbsoluteURL)) {
return false;
}

return true;
}

function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
function parseDeclaration(declaration, key, result, options) {
if (!needParseDeclaration.test(declaration[key])) {
return;
}
Expand Down Expand Up @@ -141,9 +141,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
url = normalizeUrl(url, isStringValue);

// Do not traverse inside `url`
if (
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
) {
if (!shouldHandleURL(url, declaration, result, options)) {
// eslint-disable-next-line consistent-return
return false;
}
Expand Down Expand Up @@ -199,9 +197,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
url = normalizeUrl(url, isStringValue);

// Do not traverse inside `url`
if (
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
) {
if (!shouldHandleURL(url, declaration, result, options)) {
// eslint-disable-next-line consistent-return
return false;
}
Expand Down Expand Up @@ -244,9 +240,7 @@ function parseDeclaration(declaration, key, result, isSupportDataURLInNewURL) {
let url = normalizeUrl(value, true);

// Do not traverse inside `url`
if (
!shouldHandleURL(url, declaration, result, isSupportDataURLInNewURL)
) {
if (!shouldHandleURL(url, declaration, result, options)) {
// eslint-disable-next-line consistent-return
return false;
}
Expand Down Expand Up @@ -288,13 +282,11 @@ const plugin = (options = {}) => {

return {
Declaration(declaration) {
const { isSupportDataURLInNewURL } = options;
const parsedURL = parseDeclaration(
declaration,
"value",
result,
isSupportDataURLInNewURL
);
const { isSupportDataURL, isSupportAbsoluteURL } = options;
const parsedURL = parseDeclaration(declaration, "value", result, {
isSupportDataURL,
isSupportAbsoluteURL,
});

if (!parsedURL) {
return;
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/api.js
Expand Up @@ -51,17 +51,17 @@ module.exports = (cssWithMappingToString) => {
const alreadyImportedModules = {};

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

if (id != null) {
alreadyImportedModules[id] = true;
}
}
}

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

if (dedupe && alreadyImportedModules[item[0]]) {
continue;
Expand Down
6 changes: 5 additions & 1 deletion src/utils.js
Expand Up @@ -1189,7 +1189,7 @@ async function resolveRequests(resolve, context, possibleRequests) {
});
}

function isUrlRequestable(url) {
function isUrlRequestable(url, isSupportAbsoluteURL) {
// Protocol-relative URLs
if (/^\/\//.test(url)) {
return false;
Expand All @@ -1202,6 +1202,10 @@ function isUrlRequestable(url) {

// Absolute URLs
if (/^[a-z][a-z0-9+.-]*:/i.test(url) && !NATIVE_WIN32_PATH.test(url)) {
if (isSupportAbsoluteURL) {
return true;
}

return false;
}

Expand Down
37 changes: 37 additions & 0 deletions test/__snapshots__/url-option.test.js.snap
Expand Up @@ -2538,6 +2538,43 @@ Warning
]
`;

exports[`"url" option should work with absolute URLs: errors 1`] = `Array []`;

exports[`"url" option should work with absolute URLs: module 1`] = `
"// Imports
import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from \\"../../../src/runtime/noSourceMaps.js\\";
import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\";
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../src/runtime/getUrl.js\\";
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);
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___);
___CSS_LOADER_EXPORT___.push([module.id, \\"@import url(https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css);\\"]);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
// Module
___CSS_LOADER_EXPORT___.push([module.id, \\"a {\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\", \\"\\"]);
// Exports
export default ___CSS_LOADER_EXPORT___;
"
`;

exports[`"url" option should work with absolute URLs: result 1`] = `
Array [
Array [
"./url/absolute-url.css",
"@import url(https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css);",
],
Array [
"./url/absolute-url.css",
"a {
background: url(replaced_file_protocol_/webpack/public/path/img.png);
}
",
"",
],
]
`;

exports[`"url" option should work with absolute URLs: warnings 1`] = `Array []`;

exports[`"url" option should work with mini-css-extract-plugin: css 1`] = `
"/*!*****************************************************************************!*\\\\
!*** css ../../src/index.js??ruleSet[1].rules[0].use[1]!./url/imported.css ***!
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/url/absolute-url.css
@@ -0,0 +1,5 @@
@import url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/imported.css");

a {
background: url("https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png");
}
5 changes: 5 additions & 0 deletions test/fixtures/url/absolute-url.js
@@ -0,0 +1,5 @@
import css from './absolute-url.css';

__export__ = css;

export default css;
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions test/lock-files/lock.json
@@ -0,0 +1,7 @@
{
"https://raw.githubusercontent.com/webpack-contrib/css-loader/master/test/fixtures/url/img.png": {
"integrity": "sha512-bHqIPBYwzPsVLYcTDqJzwgvIaxLjmezufiCVXAMI0Naelf3eWVdydMA40hXbSuB0dZCGjCepuGaI7Ze8kLM+Ew==",
"contentType": "image/png"
},
"version": 1
}
2 changes: 1 addition & 1 deletion test/sourceMap-option.test.js
Expand Up @@ -498,7 +498,7 @@ describe('"sourceMap" option', () => {
(assetName) => /\.js$/.test(assetName)
);

expect(chunkName).toBe("main.043d33a99a1aeaa533ff.bundle.js");
expect(chunkName).toBe("main.25338c5bc4249008fa47.bundle.js");
expect(
getModuleSource("fixtures/source-map/basic.css", stats)
).toMatchSnapshot("module");
Expand Down
26 changes: 26 additions & 0 deletions test/url-option.test.js
Expand Up @@ -562,4 +562,30 @@ describe('"url" option', () => {
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});

it("should work with absolute URLs", async () => {
const compiler = getCompiler(
"./url/absolute-url.js",
{},
{
experiments: {
buildHttp: {
allowedUris: [() => true],
lockfileLocation: path.resolve(__dirname, "./lock-files/lock.json"),
cacheLocation: path.resolve(__dirname, "./lock-files"),
},
},
}
);
const stats = await compile(compiler);

expect(getModuleSource("./url/absolute-url.css", stats)).toMatchSnapshot(
"module"
);
expect(getExecutedCode("main.bundle.js", compiler, stats)).toMatchSnapshot(
"result"
);
expect(getWarnings(stats)).toMatchSnapshot("warnings");
expect(getErrors(stats)).toMatchSnapshot("errors");
});
});

0 comments on commit 8946be4

Please sign in to comment.