fix: property handle external modules for CSS (#19917)

This commit is contained in:
Alexander Akait 2025-09-19 03:37:26 +03:00 committed by GitHub
parent 3ac31e24e3
commit 8e94386ee4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 254 additions and 139 deletions

View File

@ -110,35 +110,96 @@ class WebpackOptionsApply extends OptionsApply {
const NodeTargetPlugin = require("./node/NodeTargetPlugin");
new NodeTargetPlugin().apply(compiler);
}
if (options.externalsPresets.electronMain) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ElectronTargetPlugin("main").apply(compiler);
}
if (options.externalsPresets.electronPreload) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
// Handle external CSS `@import` and `url()`
if (options.experiments.css) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ElectronTargetPlugin("preload").apply(compiler);
}
if (options.externalsPresets.electronRenderer) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ExternalsPlugin(
"module",
({ request, dependencyType, contextInfo }, callback) => {
if (
contextInfo &&
/\.css(\?|$)/.test(contextInfo.issuer) &&
/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))
) {
if (dependencyType === "url") {
return callback(null, `asset ${request}`);
} else if (
dependencyType === "css-import" &&
options.experiments.css
) {
return callback(null, `css-import ${request}`);
}
}
new ElectronTargetPlugin("renderer").apply(compiler);
callback();
}
).apply(compiler);
}
}
if (
options.externalsPresets.electron &&
!options.externalsPresets.electronMain &&
!options.externalsPresets.electronPreload &&
!options.externalsPresets.electronRenderer
) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
if (options.externalsPresets.webAsync || options.externalsPresets.web) {
const type = options.externalsPresets.webAsync ? "import" : "module";
new ElectronTargetPlugin().apply(compiler);
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin(type, ({ request, dependencyType }, callback) => {
if (
/^(\/\/|https?:\/\/|#|std:|jsr:|npm:)/.test(
/** @type {string} */ (request)
)
) {
if (dependencyType === "url") {
return callback(null, `asset ${request}`);
} else if (
dependencyType === "css-import" &&
options.experiments.css
) {
return callback(null, `css-import ${request}`);
} else if (
/^(\/\/|https?:\/\/|std:|jsr:|npm:)/.test(
/** @type {string} */
(request)
)
) {
return callback(null, `${type} ${request}`);
}
}
callback();
}).apply(compiler);
}
if (options.externalsPresets.electron) {
if (options.externalsPresets.electronMain) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ElectronTargetPlugin("main").apply(compiler);
}
if (options.externalsPresets.electronPreload) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ElectronTargetPlugin("preload").apply(compiler);
}
if (options.externalsPresets.electronRenderer) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ElectronTargetPlugin("renderer").apply(compiler);
}
if (
!options.externalsPresets.electronMain &&
!options.externalsPresets.electronPreload &&
!options.externalsPresets.electronRenderer
) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin");
new ElectronTargetPlugin().apply(compiler);
}
}
if (options.externalsPresets.nwjs) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
@ -146,80 +207,6 @@ class WebpackOptionsApply extends OptionsApply {
new ExternalsPlugin("node-commonjs", "nw.gui").apply(compiler);
}
if (options.externalsPresets.webAsync) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin("import", ({ request, dependencyType }, callback) => {
if (dependencyType === "url") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `asset ${request}`);
}
} else if (options.experiments.css && dependencyType === "css-import") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `css-import ${request}`);
}
} else if (
options.experiments.css &&
/^(\/\/|https?:\/\/|std:)/.test(/** @type {string} */ (request))
) {
if (/^\.css(\?|$)/.test(/** @type {string} */ (request))) {
return callback(null, `css-import ${request}`);
}
return callback(null, `import ${request}`);
}
callback();
}).apply(compiler);
} else if (options.externalsPresets.web) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin("module", ({ request, dependencyType }, callback) => {
if (dependencyType === "url") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `asset ${request}`);
}
} else if (options.experiments.css && dependencyType === "css-import") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `css-import ${request}`);
}
} else if (
/^(\/\/|https?:\/\/|std:)/.test(/** @type {string} */ (request))
) {
if (
options.experiments.css &&
/^\.css((\?)|$)/.test(/** @type {string} */ (request))
) {
return callback(null, `css-import ${request}`);
}
return callback(null, `module ${request}`);
}
callback();
}).apply(compiler);
} else if (options.externalsPresets.node && options.experiments.css) {
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
const ExternalsPlugin = require("./ExternalsPlugin");
new ExternalsPlugin("module", ({ request, dependencyType }, callback) => {
if (dependencyType === "url") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `asset ${request}`);
}
} else if (dependencyType === "css-import") {
if (/^(\/\/|https?:\/\/|#)/.test(/** @type {string} */ (request))) {
return callback(null, `css-import ${request}`);
}
} else if (
/^(\/\/|https?:\/\/|std:)/.test(/** @type {string} */ (request))
) {
if (/^\.css(\?|$)/.test(/** @type {string} */ (request))) {
return callback(null, `css-import ${request}`);
}
return callback(null, `module ${request}`);
}
callback();
}).apply(compiler);
}
new ChunkPrefetchPreloadPlugin().apply(compiler);

View File

@ -0,0 +1,6 @@
@import "style2.modules.css";
@import "https://test.cases/path/../../../../configCases/css/external-in-web/dynamic-external.css";
.other {
color: blue;
}

View File

@ -0,0 +1,12 @@
"use strict";
module.exports = [
[
/UnhandledSchemeError: Reading from "https:\/\/test\.cases\/url-external\.css"/
],
[/UnhandledSchemeError: Reading from "https:\/\/test\.cases\/external\.css"/],
[
/UnhandledSchemeError: Reading from "https:\/\/test\.cases\/url-external\.css"/
],
[/UnhandledSchemeError: Reading from "https:\/\/test\.cases\/external\.css"/]
];

View File

@ -1,6 +1,27 @@
it("should import an external css", done => {
import("../external/style.css").then(x => {
expect(x).toEqual({});
import * as style from "./style.module.css";
import * as style1 from "https://test.cases/external.css";
it("should import an external CSS inside CSS", () => {
expect(style).toEqual(
nsObj({
class: "_external-in-node_style_module_css-class"
})
);
});
it("should work with an external URL", () => {
const url = new URL("https://test.cases/url-external.css", import.meta.url);
expect(url.toString().endsWith("url-external.css")).toBe(true);
});
it("should import an external css dynamically", done => {
import("./dynamic.modules.css").then(x => {
expect(x).toEqual(
nsObj({
other: "_external-in-node_dynamic_modules_css-other"
})
);
done();
}, done);
});

View File

@ -0,0 +1,12 @@
"use strict";
module.exports = (options) => {
if (options[0].cache && options[0].cache.type === "filesystem") {
return [
/Pack got invalid because of write to/,
/Pack got invalid because of write to/
];
}
return [];
};

View File

@ -0,0 +1,5 @@
@import "https://test.cases/path/../../../../configCases/css/external-in-web/external.css";
.class {
padding: 10px;
}

View File

@ -3,11 +3,30 @@
const path = require("path");
/** @type {import("../../../../").Configuration} */
module.exports = {
context: path.join(__dirname, "../external"),
entry: "../external-in-node/index.js",
target: "node",
experiments: {
css: true
module.exports = [
{
context: path.join(__dirname, "../external"),
entry: "../external-in-node/index.js",
target: "node",
optimization: {
chunkIds: "named",
moduleIds: "named"
},
experiments: {
css: true
}
},
{
context: path.join(__dirname, "../external"),
entry: "../external-in-node/index.js",
target: "node",
optimization: {
chunkIds: "named",
moduleIds: "named"
},
experiments: {
css: true,
outputModule: true
}
}
};
];

View File

@ -0,0 +1,3 @@
body {
color: red;
}

View File

@ -1,2 +1,2 @@
@import "style2.css";
@import "https://test.cases/path/../../../../configCases/css/external/external.css";
@import "https://test.cases/path/../../../../configCases/css/external-in-web/dynamic-external.css";

View File

@ -0,0 +1,31 @@
import * as style from "./style.css";
it("should import an external CSS inside CSS", () => {
const bodyStyle = getComputedStyle(document.body);
expect(bodyStyle.getPropertyValue("color")).toBe(" green");
expect(bodyStyle.getPropertyValue("padding")).toBe(" 10px");
});
// import * as style1 from "http://test.com/import.css";
it("should work with an external URL", () => {
const url = new URL("https://test.cases/url-external.css", import.meta.url);
expect(url.toString().endsWith("url-external.css")).toBe(true);
});
it("should import an external css dynamically", done => {
import("./dynamic.css").then(x => {
expect(x).toEqual({});
const bodyStyle = getComputedStyle(document.body);
expect(bodyStyle.getPropertyValue("color")).toBe(" red");
expect(bodyStyle.getPropertyValue("background")).toBe(
" url(//example.com/image.png) url(https://example.com/image.png)"
);
expect(bodyStyle.getPropertyValue("background-image")).toBe(
" url(http://example.com/image.png)"
);
done();
}, done);
});

View File

@ -0,0 +1,5 @@
@import "https://test.cases/path/../../../../configCases/css/external-in-web/external.css";
body {
padding: 10px;
}

View File

@ -0,0 +1,4 @@
body {
background: url(//example.com/image.png) url(https://example.com/image.png);
background-image: url(http://example.com/image.png);
}

View File

@ -0,0 +1,13 @@
"use strict";
module.exports = {
moduleScope(scope, stats) {
const link = scope.window.document.createElement("link");
link.rel = "stylesheet";
link.href = stats.experiments.outputModule ? "bundle1.css" : "bundle0.css";
scope.window.document.head.appendChild(link);
},
findBundle(i) {
return i === 0 ? ["dynamic_css.bundle0.js", "bundle0.js"] : ["bundle1.mjs"];
}
};

View File

@ -0,0 +1,3 @@
body {
border: 10px red solid;
}

View File

@ -0,0 +1,24 @@
"use strict";
/** @type {import("../../../../").Configuration} */
module.exports = [
{
target: "web",
optimization: {
chunkIds: "named"
},
experiments: {
css: true
}
},
{
target: "web",
optimization: {
chunkIds: "named"
},
experiments: {
css: true,
outputModule: true
}
}
];

View File

@ -1,14 +0,0 @@
it("should import an external css", done => {
import("./style.css").then(x => {
expect(x).toEqual({});
const style = getComputedStyle(document.body);
expect(style.getPropertyValue("color")).toBe(" green");
expect(style.getPropertyValue("background")).toBe(
" url(//example.com/image.png) url(https://example.com/image.png)"
);
expect(style.getPropertyValue("background-image")).toBe(
" url(http://example.com/image.png)"
);
done();
}, done);
});

View File

@ -1,7 +0,0 @@
"use strict";
module.exports = {
findBundle() {
return ["125.bundle0.js", "bundle0.js"];
}
};

View File

@ -1,9 +0,0 @@
"use strict";
/** @type {import("../../../../").Configuration} */
module.exports = {
target: "web",
experiments: {
css: true
}
};