Repository: webpack/webpack (JavaScript)

Issue:
Inlined SVG extracted with invalid filename
# Bug report

**What is the current behavior?**

When using `css-loader` with an SVG that is inlined the extracted SVG gets an invalid filename

**If the current behavior is a bug, please provide the steps to reproduce.**

1. Use `css-loader`

```js
({
    test: /\.css$/,
    use: [
        'style-loader',
        'css-loader'
    ]
});
```

2. Set `assetModuleFilename` to something

```js
{
  assetModuleFilename: 'media/[name].[contenthash:8][ext]'
}
```

3. Have CSS with inlined SVG

```css
.className {
  background: url(data:image/svg+xml;utf8,<svg width=%279px%27 height=%276px%27 viewBox=%270 0 9 6%27 version=%271.1%27 xmlns=%27http://www.w3.org/2000/svg%27 xmlns:xlink=%27http://www.w3.org/1999/xlink%27><g id=%27Artboard%27 stroke=%27none%27 stroke-width=%271%27 fill=%27none%27 fill-rule=%27evenodd%27 transform=%27translate%28-636.000000, -171.000000%29%27 fill-opacity=%270.368716033%27><g id=%27input%27 transform=%27translate%28172.000000, 37.000000%29%27 fill=%27%230E242F%27 fill-rule=%27nonzero%27><g id=%27Group-9%27 transform=%27translate%28323.000000, 127.000000%29%27><path d=%27M142.280245,7.23952813 C141.987305,6.92353472 141.512432,6.92361662 141.219585,7.23971106 C140.926739,7.5558055 140.926815,8.06821394 141.219755,8.38420735 L145.498801,13 L149.780245,8.38162071 C150.073185,8.0656273 150.073261,7.55321886 149.780415,7.23712442 C149.487568,6.92102998 149.012695,6.92094808 148.719755,7.23694149 L145.498801,10.7113732 L142.280245,7.23952813 Z%27 id=%27arrow%27></path></g></g></g></svg>)
}
```

4. Run webpack

The image gets placed in the output with a filename like `svg>.abc123` which breaks on a windows machine.

**What is the expected behavior?**

Webpack produces a file that doesn't break windows with `.svg` as the extension or omits the inlined SVG entirely

**Other relevant information:**
webpack version: 5.45.1
Node.js version: 14.16.0
Operating System: Windows



Hints:
shouldn't the asset stay an data url anyway? Did you change that to an real asset?
I used the filter url function to not include it, but I feel webpack should do that by default ootb. I hand the realized it happened until our window build broke. 
@hipstersmoothie I don't understand you, what is mean `doesn't break windows`
The build worked fine on Mac but when we tried to build our app on windows we got this error (presumably because `>` isn't a valid character in a filename)

![CleanShot 2021-07-22 at 17 09 42](https://user-images.githubusercontent.com/1192452/126723848-716d3466-f8d9-44e9-b49b-bb53cd9ca5e9.png)

So either producing a filename that works on all platforms _or_ not extracting the inlined SVG at all would be better default behaviors.
> So either producing a filename that works on all platforms or not extracting the inlined SVG at all would be better default behaviors.

You can setup it on own side
Yeah I understand that and I've done that, I just think that this isn't an error a user should have to encounter and notice, webpack + it's loaders out of the box shouldn't produce this bug. Or there should at least be a note somewhere in the docs explaining that you must configure it. I'm willing to make a PR for either approach. 

The bug happens [here](https://github.com/webpack/webpack/blob/74a16d080717f43c2cced3b7252d1fbf2a4eb4ee/lib/TemplatedPathPlugin.js#L123) when webpack runs `basename` on the SVG resulting in `</svg>` being the last part of the path so `svg>` becomes the filename.
> The bug happens here when webpack runs basename on the SVG resulting in </svg> being the last part of the path so svg> becomes the filename.

We should fix it, and yes you can send a PR to docs
<!-- identifier: inactive-warning -->

**This issue had no activity for at least three months.**

It's subject to automatic issue closing if there is no activity in the next 15 days.
bump
@alexander-akait should `hash` to be treated as `name` here? also should webpack try to determine extension?

Yep, I think it is good idea

Patch:
diff --git a/lib/TemplatedPathPlugin.js b/lib/TemplatedPathPlugin.js
--- a/lib/TemplatedPathPlugin.js
+++ b/lib/TemplatedPathPlugin.js
@@ -5,6 +5,7 @@
 
 "use strict";
 
+const mime = require("mime-types");
 const { basename, extname } = require("path");
 const util = require("util");
 const Chunk = require("./Chunk");
@@ -117,29 +118,53 @@ const replacePathVariables = (path, data, assetInfo) => {
 	// [name] - file
 	// [ext] - .js
 	if (typeof data.filename === "string") {
-		const { path: file, query, fragment } = parseResource(data.filename);
-
-		const ext = extname(file);
-		const base = basename(file);
-		const name = base.slice(0, base.length - ext.length);
-		const path = file.slice(0, file.length - base.length);
-
-		replacements.set("file", replacer(file));
-		replacements.set("query", replacer(query, true));
-		replacements.set("fragment", replacer(fragment, true));
-		replacements.set("path", replacer(path, true));
-		replacements.set("base", replacer(base));
-		replacements.set("name", replacer(name));
-		replacements.set("ext", replacer(ext, true));
-		// Legacy
-		replacements.set(
-			"filebase",
-			deprecated(
-				replacer(base),
-				"[filebase] is now [base]",
-				"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
-			)
-		);
+		// check that filename is data uri
+		let match = data.filename.match(/^data:([^;,]+)/);
+		if (match) {
+			const ext = mime.extension(match[1]);
+			const emptyReplacer = replacer("", true);
+
+			replacements.set("file", emptyReplacer);
+			replacements.set("query", emptyReplacer);
+			replacements.set("fragment", emptyReplacer);
+			replacements.set("path", emptyReplacer);
+			replacements.set("base", emptyReplacer);
+			replacements.set("name", emptyReplacer);
+			replacements.set("ext", replacer(ext ? `.${ext}` : "", true));
+			// Legacy
+			replacements.set(
+				"filebase",
+				deprecated(
+					emptyReplacer,
+					"[filebase] is now [base]",
+					"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
+				)
+			);
+		} else {
+			const { path: file, query, fragment } = parseResource(data.filename);
+
+			const ext = extname(file);
+			const base = basename(file);
+			const name = base.slice(0, base.length - ext.length);
+			const path = file.slice(0, file.length - base.length);
+
+			replacements.set("file", replacer(file));
+			replacements.set("query", replacer(query, true));
+			replacements.set("fragment", replacer(fragment, true));
+			replacements.set("path", replacer(path, true));
+			replacements.set("base", replacer(base));
+			replacements.set("name", replacer(name));
+			replacements.set("ext", replacer(ext, true));
+			// Legacy
+			replacements.set(
+				"filebase",
+				deprecated(
+					replacer(base),
+					"[filebase] is now [base]",
+					"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
+				)
+			);
+		}
 	}
 
 	// Compilation context


Test patch:
diff --git a/test/configCases/asset-modules/resource-from-data-uri/index.js b/test/configCases/asset-modules/resource-from-data-uri/index.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/asset-modules/resource-from-data-uri/index.js
@@ -0,0 +1,5 @@
+import asset from "data:image/svg+xml;utf8,<svg><title>icon-square-small</title></svg>"
+
+it("should compile with correct filename", () => {
+	expect(asset).toMatch(/public\/media\/\.[0-9a-zA-Z]{8}\.svg/);
+});
diff --git a/test/configCases/asset-modules/resource-from-data-uri/webpack.config.js b/test/configCases/asset-modules/resource-from-data-uri/webpack.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/asset-modules/resource-from-data-uri/webpack.config.js
@@ -0,0 +1,16 @@
+/** @type {import("../../../../").Configuration} */
+module.exports = {
+	output: {
+		assetModuleFilename: "media/[name].[contenthash:8][ext]",
+		publicPath: "public/"
+	},
+	module: {
+		rules: [
+			{
+				mimetype: "image/svg+xml",
+				type: "asset/resource"
+			}
+		]
+	},
+	target: "web"
+};