mirror of https://github.com/webpack/webpack.git
				
				
				
			fix: property handle external modules for CSS (#19917)
This commit is contained in:
		
							parent
							
								
									3ac31e24e3
								
							
						
					
					
						commit
						8e94386ee4
					
				|  | @ -110,7 +110,68 @@ class WebpackOptionsApply extends OptionsApply { | ||||||
| 			const NodeTargetPlugin = require("./node/NodeTargetPlugin"); | 			const NodeTargetPlugin = require("./node/NodeTargetPlugin"); | ||||||
| 
 | 
 | ||||||
| 			new NodeTargetPlugin().apply(compiler); | 			new NodeTargetPlugin().apply(compiler); | ||||||
|  | 
 | ||||||
|  | 			// 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 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}`); | ||||||
| 							} | 							} | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						callback(); | ||||||
|  | 					} | ||||||
|  | 				).apply(compiler); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (options.externalsPresets.webAsync || options.externalsPresets.web) { | ||||||
|  | 			const type = options.externalsPresets.webAsync ? "import" : "module"; | ||||||
|  | 
 | ||||||
|  | 			// @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) { | 			if (options.externalsPresets.electronMain) { | ||||||
| 				// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
 | 				// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
 | ||||||
| 				const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin"); | 				const ElectronTargetPlugin = require("./electron/ElectronTargetPlugin"); | ||||||
|  | @ -130,7 +191,6 @@ class WebpackOptionsApply extends OptionsApply { | ||||||
| 				new ElectronTargetPlugin("renderer").apply(compiler); | 				new ElectronTargetPlugin("renderer").apply(compiler); | ||||||
| 			} | 			} | ||||||
| 			if ( | 			if ( | ||||||
| 			options.externalsPresets.electron && |  | ||||||
| 				!options.externalsPresets.electronMain && | 				!options.externalsPresets.electronMain && | ||||||
| 				!options.externalsPresets.electronPreload && | 				!options.externalsPresets.electronPreload && | ||||||
| 				!options.externalsPresets.electronRenderer | 				!options.externalsPresets.electronRenderer | ||||||
|  | @ -140,86 +200,13 @@ class WebpackOptionsApply extends OptionsApply { | ||||||
| 
 | 
 | ||||||
| 				new ElectronTargetPlugin().apply(compiler); | 				new ElectronTargetPlugin().apply(compiler); | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 		if (options.externalsPresets.nwjs) { | 		if (options.externalsPresets.nwjs) { | ||||||
| 			// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
 | 			// @ts-expect-error https://github.com/microsoft/TypeScript/issues/41697
 | ||||||
| 			const ExternalsPlugin = require("./ExternalsPlugin"); | 			const ExternalsPlugin = require("./ExternalsPlugin"); | ||||||
| 
 | 
 | ||||||
| 			new ExternalsPlugin("node-commonjs", "nw.gui").apply(compiler); | 			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); | 		new ChunkPrefetchPreloadPlugin().apply(compiler); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | @import "style2.modules.css"; | ||||||
|  | @import "https://test.cases/path/../../../../configCases/css/external-in-web/dynamic-external.css"; | ||||||
|  | 
 | ||||||
|  | .other { | ||||||
|  | 	color: blue; | ||||||
|  | } | ||||||
|  | @ -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"/] | ||||||
|  | ]; | ||||||
|  | @ -1,6 +1,27 @@ | ||||||
| it("should import an external css", done => { | import * as style from "./style.module.css"; | ||||||
| 	import("../external/style.css").then(x => { | import * as style1 from "https://test.cases/external.css"; | ||||||
| 		expect(x).toEqual({}); | 
 | ||||||
|  | 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(); | ||||||
| 	}, done); | 	}, done); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -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 []; | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | @import "https://test.cases/path/../../../../configCases/css/external-in-web/external.css"; | ||||||
|  | 
 | ||||||
|  | .class { | ||||||
|  | 	padding: 10px; | ||||||
|  | } | ||||||
|  | @ -3,11 +3,30 @@ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| 
 | 
 | ||||||
| /** @type {import("../../../../").Configuration} */ | /** @type {import("../../../../").Configuration} */ | ||||||
| module.exports = { | module.exports = [ | ||||||
|  | 	{ | ||||||
| 		context: path.join(__dirname, "../external"), | 		context: path.join(__dirname, "../external"), | ||||||
| 		entry: "../external-in-node/index.js", | 		entry: "../external-in-node/index.js", | ||||||
| 		target: "node", | 		target: "node", | ||||||
|  | 		optimization: { | ||||||
|  | 			chunkIds: "named", | ||||||
|  | 			moduleIds: "named" | ||||||
|  | 		}, | ||||||
| 		experiments: { | 		experiments: { | ||||||
| 			css: true | 			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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | body { | ||||||
|  | 	color: red; | ||||||
|  | } | ||||||
|  | @ -1,2 +1,2 @@ | ||||||
| @import "style2.css"; | @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"; | ||||||
|  | @ -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); | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | @import "https://test.cases/path/../../../../configCases/css/external-in-web/external.css"; | ||||||
|  | 
 | ||||||
|  | body { | ||||||
|  | 	padding: 10px; | ||||||
|  | } | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
|  | @ -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"]; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | body { | ||||||
|  | 	border: 10px red solid; | ||||||
|  | } | ||||||
|  | @ -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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | ]; | ||||||
|  | @ -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); |  | ||||||
| }); |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| module.exports = { |  | ||||||
| 	findBundle() { |  | ||||||
| 		return ["125.bundle0.js", "bundle0.js"]; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| /** @type {import("../../../../").Configuration} */ |  | ||||||
| module.exports = { |  | ||||||
| 	target: "web", |  | ||||||
| 	experiments: { |  | ||||||
| 		css: true |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
		Loading…
	
		Reference in New Issue