Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support absolute URL, when experiments.buildHttp enabled #1389

Merged
merged 2 commits into from Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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");
});
});