fix: css url public path
|  | @ -5,7 +5,11 @@ | |||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const { ConcatSource, PrefixSource } = require("webpack-sources"); | ||||
| const { | ||||
| 	ConcatSource, | ||||
| 	PrefixSource, | ||||
| 	ReplaceSource | ||||
| } = require("webpack-sources"); | ||||
| const CssModule = require("../CssModule"); | ||||
| const HotUpdateChunk = require("../HotUpdateChunk"); | ||||
| const { | ||||
|  | @ -26,6 +30,7 @@ const StaticExportsDependency = require("../dependencies/StaticExportsDependency | |||
| const { compareModulesByIdentifier } = require("../util/comparators"); | ||||
| const createSchemaValidation = require("../util/create-schema-validation"); | ||||
| const createHash = require("../util/createHash"); | ||||
| const { getUndoPath } = require("../util/identifier"); | ||||
| const memoize = require("../util/memoize"); | ||||
| const nonNumericOnlyHash = require("../util/nonNumericOnlyHash"); | ||||
| const CssExportsGenerator = require("./CssExportsGenerator"); | ||||
|  | @ -356,6 +361,24 @@ class CssModulesPlugin { | |||
| 
 | ||||
| 					/** @type {CssModule[] | undefined} */ | ||||
| 					const modules = orderedCssModulesPerChunk.get(chunk); | ||||
| 					const { path: filename, info } = compilation.getPathWithInfo( | ||||
| 						CssModulesPlugin.getChunkFilenameTemplate( | ||||
| 							chunk, | ||||
| 							compilation.outputOptions | ||||
| 						), | ||||
| 						{ | ||||
| 							hash, | ||||
| 							runtime: chunk.runtime, | ||||
| 							chunk, | ||||
| 							contentHashType: "css" | ||||
| 						} | ||||
| 					); | ||||
| 					const publicPath = | ||||
| 						compilation.outputOptions.publicPath === "auto" | ||||
| 							? getUndoPath(filename, compilation.outputOptions.path, false) | ||||
| 							: compilation.getAssetPath(compilation.outputOptions.publicPath, { | ||||
| 									hash: compilation.hash | ||||
| 							  }); | ||||
| 					if (modules !== undefined) { | ||||
| 						result.push({ | ||||
| 							render: () => | ||||
|  | @ -366,18 +389,11 @@ class CssModulesPlugin { | |||
| 									uniqueName: compilation.outputOptions.uniqueName, | ||||
| 									cssHeadDataCompression: | ||||
| 										compilation.outputOptions.cssHeadDataCompression, | ||||
| 									publicPath, | ||||
| 									modules | ||||
| 								}), | ||||
| 							filenameTemplate: CssModulesPlugin.getChunkFilenameTemplate( | ||||
| 								chunk, | ||||
| 								compilation.outputOptions | ||||
| 							), | ||||
| 							pathOptions: { | ||||
| 								hash, | ||||
| 								runtime: chunk.runtime, | ||||
| 								chunk, | ||||
| 								contentHashType: "css" | ||||
| 							}, | ||||
| 							filename, | ||||
| 							info, | ||||
| 							identifier: `css${chunk.id}`, | ||||
| 							hash: chunk.contentHash.css | ||||
| 						}); | ||||
|  | @ -576,6 +592,7 @@ class CssModulesPlugin { | |||
| 	 * @param {Object} options options | ||||
| 	 * @param {string | undefined} options.uniqueName unique name | ||||
| 	 * @param {boolean | undefined} options.cssHeadDataCompression compress css head data | ||||
| 	 * @param {string} options.publicPath public path | ||||
| 	 * @param {Chunk} options.chunk chunk | ||||
| 	 * @param {ChunkGraph} options.chunkGraph chunk graph | ||||
| 	 * @param {CodeGenerationResults} options.codeGenerationResults code generation results | ||||
|  | @ -585,6 +602,7 @@ class CssModulesPlugin { | |||
| 	renderChunk({ | ||||
| 		uniqueName, | ||||
| 		cssHeadDataCompression, | ||||
| 		publicPath, | ||||
| 		chunk, | ||||
| 		chunkGraph, | ||||
| 		codeGenerationResults, | ||||
|  | @ -604,6 +622,24 @@ class CssModulesPlugin { | |||
| 							codeGenResult.sources.get("css-import") | ||||
| 					); | ||||
| 
 | ||||
| 				let matches = [ | ||||
| 					...moduleSource | ||||
| 						.source() | ||||
| 						.toString() | ||||
| 						.matchAll(new RegExp(CssUrlDependency.PUBLIC_PATH_PLACEHOLDER, "g")) | ||||
| 				]; | ||||
| 				if (matches.length > 0) { | ||||
| 					const replaceSource = new ReplaceSource(moduleSource); | ||||
| 					for (const match of matches) { | ||||
| 						replaceSource.replace( | ||||
| 							match.index, | ||||
| 							(match.index += match[0].length - 1), | ||||
| 							publicPath | ||||
| 						); | ||||
| 					} | ||||
| 					moduleSource = replaceSource; | ||||
| 				} | ||||
| 
 | ||||
| 				let inheritance = [[module.cssLayer, module.supports, module.media]]; | ||||
| 
 | ||||
| 				if (module.inheritance) { | ||||
|  |  | |||
|  | @ -29,6 +29,8 @@ const getIgnoredRawDataUrlModule = memoize(() => { | |||
| 	return new RawDataUrlModule("data:,", `ignored-asset`, `(ignored asset)`); | ||||
| }); | ||||
| 
 | ||||
| const PUBLIC_PATH_PLACEHOLDER = "__CSS_URL_DEPENDENCY_PUBLIC_PATH__"; | ||||
| 
 | ||||
| class CssUrlDependency extends ModuleDependency { | ||||
| 	/** | ||||
| 	 * @param {string} request request | ||||
|  | @ -134,7 +136,7 @@ CssUrlDependency.Template = class CssUrlDependencyTemplate extends ( | |||
| 			case "string": | ||||
| 				newValue = cssEscapeString( | ||||
| 					runtimeTemplate.assetUrl({ | ||||
| 						publicPath: "", | ||||
| 						publicPath: PUBLIC_PATH_PLACEHOLDER, | ||||
| 						module: /** @type {Module} */ (moduleGraph.getModule(dep)), | ||||
| 						codeGenerationResults | ||||
| 					}) | ||||
|  | @ -143,7 +145,7 @@ CssUrlDependency.Template = class CssUrlDependencyTemplate extends ( | |||
| 			case "url": | ||||
| 				newValue = `url(${cssEscapeString( | ||||
| 					runtimeTemplate.assetUrl({ | ||||
| 						publicPath: "", | ||||
| 						publicPath: PUBLIC_PATH_PLACEHOLDER, | ||||
| 						module: /** @type {Module} */ (moduleGraph.getModule(dep)), | ||||
| 						codeGenerationResults | ||||
| 					}) | ||||
|  | @ -162,3 +164,4 @@ CssUrlDependency.Template = class CssUrlDependencyTemplate extends ( | |||
| makeSerializable(CssUrlDependency, "webpack/lib/dependencies/CssUrlDependency"); | ||||
| 
 | ||||
| module.exports = CssUrlDependency; | ||||
| module.exports.PUBLIC_PATH_PLACEHOLDER = PUBLIC_PATH_PLACEHOLDER; | ||||
|  |  | |||
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
|  | @ -0,0 +1,18 @@ | |||
| const testCase = (tagName, impFn) => { | ||||
| 	it(`should be able to handle styles in ${tagName}.css`, done => { | ||||
| 		const element = document.createElement(tagName); | ||||
| 		document.body.appendChild(element); | ||||
| 		impFn().then(x => { | ||||
| 			try { | ||||
| 				expect(x).toEqual(nsObj({})); | ||||
| 				const style = getComputedStyle(element); | ||||
| 				expect(style).toMatchSnapshot(); | ||||
| 				done(); | ||||
| 			} catch (e) { | ||||
| 				done(e); | ||||
| 			} | ||||
| 		}, done); | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| testCase("div", () => import("./spacing.css")); | ||||
|  | @ -0,0 +1,5 @@ | |||
| @import url("#test"); | ||||
| 
 | ||||
| .nested { | ||||
| 	background: url('./img.png'); | ||||
| } | ||||
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 76 KiB | 
							
								
								
									
										
											BIN
										
									
								
								test/configCases/css/urls-css-filename/node_modules/package/img.png
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						| After Width: | Height: | Size: 76 KiB | 
							
								
								
									
										4
									
								
								test/configCases/css/urls-css-filename/node_modules/package/package.json
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						|  | @ -0,0 +1,4 @@ | |||
| { | ||||
|   "name": "package", | ||||
|   "version": "1.0.0" | ||||
| } | ||||
| After Width: | Height: | Size: 76 KiB | 
|  | @ -0,0 +1,32 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	target: "web", | ||||
| 	mode: "development", | ||||
| 	devtool: false, | ||||
| 	experiments: { | ||||
| 		css: true | ||||
| 	}, | ||||
| 	output: { | ||||
| 		cssFilename: "css/[name].css", | ||||
| 		cssChunkFilename: "css/async/[name].css", | ||||
| 		assetModuleFilename: "asset/[name].[hash][ext][query][fragment]" | ||||
| 	}, | ||||
| 	optimization: { | ||||
| 		splitChunks: { | ||||
| 			cacheGroups: { | ||||
| 				assetFixHack: { | ||||
| 					type: "asset/resource", | ||||
| 					chunks: "all", | ||||
| 					name: "main", | ||||
| 					enforce: true | ||||
| 				}, | ||||
| 				assetFixHack1: { | ||||
| 					type: "asset/inline", | ||||
| 					chunks: "all", | ||||
| 					name: "main", | ||||
| 					enforce: true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||