Repository: webpack/webpack (JavaScript)

Issue:
devtool source-map creates broken `webpack://` urls when `output.library.name` is an array containing a placeholder
<!-- Please don't delete this template because we'll close your issue -->
<!-- Before creating an issue please make sure you are using the latest version of webpack. -->

# Bug report

<!-- Please ask questions on StackOverflow or the webpack Gitter. -->
<!-- https://stackoverflow.com/questions/ask?tags=webpack -->
<!-- https://gitter.im/webpack/webpack -->
<!-- Issues which contain questions or support requests will be closed. -->

**What is the current behavior?**

When using an `output.library.name` containing a `[name]` placeholder, e.g. `['MyLib', '[name]']`, the source maps generated by `'devtool': 'source-map'` are broken. I get a bunch of JS console warnings like this when using my scripts:

```
Source map error: Error: Invalid URL: webpack://MyLib.[name]/webpack/bootstrap
```
![image](https://user-images.githubusercontent.com/19592990/145136953-f443a52e-5673-46e1-9819-151b1e5f4e68.png)

The problem seems to be that the `[name]` placeholder in `output.library.name` is being treated as literal in the source map URLs, rather than being filled in with the entry file name. Source maps aside, the JS does work as expected, with the placeholder being replaced in other contexts. As far as I can tell, this is a bug, as using the `[name]` placeholder in `output.library.name` is a [supported feature](https://github.com/webpack/webpack/commit/9e6b6deea4076322ae68d122c19e99bd4b9d162b).


**If the current behavior is a bug, please provide the steps to reproduce.**

1. Create an empty folder. Add the following files:

`webpack.config.js`:
```js
const path = require('path')

module.exports = {
	mode: 'production',
	context: path.resolve(__dirname, 'src'),
	entry: { foo: './foo.js' },
	output: {
		path: path.resolve(__dirname, 'build'),
		filename: '[name].js',
		library: { name: ['MyLib', '[name]'], type: 'window' },
		clean: true,
	},
	devtool: 'source-map',
	module: {
		rules: [{ test: /\.js$/u, use: ['babel-loader'] }],
	},
	cache: true,
}
```
`package.json`:
```json
{
	"browserslist": "last 5 Chrome versions, last 3 Firefox versions, last 3 Safari versions, last 3 ChromeAndroid versions, last 2 Android versions, last 2 FirefoxAndroid versions",
	"scripts": {
		"build": "webpack"
	},
	"devDependencies": {
		"@babel/core": "^7.16.0",
		"@babel/preset-env": "^7.16.4",
		"@babel/preset-react": "^7.16.0",
		"babel-loader": "^8.2.3",
		"prettier": "^2.5.1",
		"webpack": "^5.65.0",
		"webpack-cli": "^4.9.1"
	}
}
```
`babel.config.json`:
```json
{ "presets": [["@babel/preset-env", { "spec": true }]] }
```
`demo.html`:
```html
<html>
	<head>
		<meta charset="utf-8" />
		<script defer src="./build/foo.js"></script>
	</head>
	<body>Look at the JS dev console.</body>
</html>
```
`src/foo.js`:
```js
export function myThing() {
	console.log('beep')
}
```

2. Run `npm i && npm run build`.
3. Open `demo.html`.
4. Check the JS dev console.


<!-- A great way to do this is to provide your configuration via a GitHub repository -->
<!-- The most helpful is a minimal reproduction with instructions on how to reproduce -->
<!-- Repositories with too many files or large `webpack.config.js` files are not suitable -->
<!-- Please only add small code snippets directly into this issue -->
<!-- https://gist.github.com is a good place for longer code snippets -->
<!-- If your issue is caused by a plugin or loader, please create an issue on the loader/plugin repository instead -->

**What is the expected behavior?**

The source maps should contain working `webpack://` URLs with the `[name]` placeholder filled in as expected.

**Other relevant information:**
webpack version:  5.65.0
Node.js version: 16.13.1
Operating System: KDE Neon Linux



Hints:
Feel free to send a fix, should be not hard
I took a look, but since I've never looked at Webpack's code before, I'm a bit lost. If anyone with more experience wants to tackle this, please do, and thank you. Best I could gather is that `name` is absent from [the list here](https://github.com/webpack/webpack/blob/c854e30df7f879d9989a7ab7aa83d1f0d1fa6862/lib/ModuleFilenameHelpers.js#L172), and that may have something to do with it. I don't know how/where to get the value of name to use in that list.
@alexander-akait @ZebulanStanphill I want to tackle this, Can i take it ?
@TrickyPi Be my guest! :+1:
Yes, PR welcome
I can reproduce the problem. Seems more like a Firefox bug that it doesn't access the url.

`webpack://MyLib.[name]/webpack/bootstrap` is a valid url. It's a custom scheme, which basically allows anything after the scheme. You can also run `new URL("webpack://MyLib.[name]/webpack/bootstrap")` in JS and it parses the URL just fine.

It might not be a perfect default for the `output.devtoolNamespace` to include the raw `[name]` from the `output.library` option, but you can easily override it via e. g. `output.devtoolNamespace: "MyLibrary"`.

We can't replace the `[name]` with the entrypoint name, since
* chunks can be referenced by multiple entrypoints
* multiple entrypoints can be on a single page which could make it a race condition which name a source has

We could change the default for `devtoolNamespace` etc. to exclude `[name]`. That's here: https://github.com/webpack/webpack/blob/b3871e84122b6cc5298ad403ca46c3cd3df2a2b5/lib/config/defaults.js#L681-L697
Oh, i get it. It seems that sourceMap does not support the [name] placeholder contained in output.library.name .
Could we change the devtoolNamespace in here?  It seems more appropriate to change the default for devtoolNamespace.
https://github.com/webpack/webpack/blob/b3871e84122b6cc5298ad403ca46c3cd3df2a2b5/lib/config/defaults.js#L863
> Could we change the devtoolNamespace in here? It seems more appropriate to change the default for devtoolNamespace.

Technically yes, but actually the `output.uniqueName` default is already broken and you get also weird names for e. g. `output.chunkLoadingGlobal`. So better "fix" the `uniqueName` default directly, by fxing `getLibraryName`
okay, before "fixing" this problem, i have a confusion, is it necessary to "fix" the `uniqueName` default? since developer can via output.devtoolNamespace to avoid this problem.  If it's necessary, currently the solution i can think of is to exclude `[name]` in here,
https://github.com/webpack/webpack/blob/b3871e84122b6cc5298ad403ca46c3cd3df2a2b5/lib/config/defaults.js#L690
but i worry about it will cause unexpected problems, since it also change the default of `hotUpdateGlobal` etc.
> since it also change the default of `hotUpdateGlobal` etc.

You also want to avoid `[name]` in these options too.
> > since it also change the default of `hotUpdateGlobal` etc.
> 
> You also want to avoid `[name]` in these options too.

Okay

Patch:
diff --git a/lib/config/defaults.js b/lib/config/defaults.js
--- a/lib/config/defaults.js
+++ b/lib/config/defaults.js
@@ -715,7 +715,15 @@ const applyOutputDefaults = (
 	};
 
 	F(output, "uniqueName", () => {
-		const libraryName = getLibraryName(output.library);
+		const libraryName = getLibraryName(output.library).replace(
+			/^\[(\\*[\w:]+\\*)\](\.)|(\.)\[(\\*[\w:]+\\*)\](?=\.|$)|\[(\\*[\w:]+\\*)\]/g,
+			(m, a, d1, d2, b, c) => {
+				const content = a || b || c;
+				return content.startsWith("\\") && content.endsWith("\\")
+					? `${d2 || ""}[${content.slice(1, -1)}]${d1 || ""}`
+					: "";
+			}
+		);
 		if (libraryName) return libraryName;
 		const pkgPath = path.resolve(context, "package.json");
 		try {


Test patch:
diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js
--- a/test/Defaults.unittest.js
+++ b/test/Defaults.unittest.js
@@ -1054,6 +1054,197 @@ describe("Defaults", () => {
 		+     "uniqueName": "myLib.awesome",
 	`)
 	);
+	test(
+		"library contains [name] placeholder",
+		{
+			output: {
+				library: ["myLib", "[name]"]
+			}
+		},
+		e =>
+			e.toMatchInlineSnapshot(`
+			- Expected
+			+ Received
+
+			@@ ... @@
+			-     "chunkLoadingGlobal": "webpackChunkwebpack",
+			+     "chunkLoadingGlobal": "webpackChunkmyLib",
+			@@ ... @@
+			-     "devtoolNamespace": "webpack",
+			+     "devtoolNamespace": "myLib",
+			@@ ... @@
+			-     "enabledLibraryTypes": Array [],
+			+     "enabledLibraryTypes": Array [
+			+       "var",
+			+     ],
+			@@ ... @@
+			-     "hotUpdateGlobal": "webpackHotUpdatewebpack",
+			+     "hotUpdateGlobal": "webpackHotUpdatemyLib",
+			@@ ... @@
+			-     "library": undefined,
+			+     "library": Object {
+			+       "auxiliaryComment": undefined,
+			+       "export": undefined,
+			+       "name": Array [
+			+         "myLib",
+			+         "[name]",
+			+       ],
+			+       "type": "var",
+			+       "umdNamedDefine": undefined,
+			+     },
+			@@ ... @@
+			-     "uniqueName": "webpack",
+			+     "uniqueName": "myLib",
+		`)
+	);
+	test(
+		"library.name contains [name] placeholder",
+		{
+			output: {
+				library: {
+					name: ["my[name]Lib", "[name]", "lib"],
+					type: "var"
+				}
+			}
+		},
+		e =>
+			e.toMatchInlineSnapshot(`
+			- Expected
+			+ Received
+
+			@@ ... @@
+			-     "chunkLoadingGlobal": "webpackChunkwebpack",
+			+     "chunkLoadingGlobal": "webpackChunkmyLib_lib",
+			@@ ... @@
+			-     "devtoolNamespace": "webpack",
+			+     "devtoolNamespace": "myLib.lib",
+			@@ ... @@
+			-     "enabledLibraryTypes": Array [],
+			+     "enabledLibraryTypes": Array [
+			+       "var",
+			+     ],
+			@@ ... @@
+			-     "hotUpdateGlobal": "webpackHotUpdatewebpack",
+			+     "hotUpdateGlobal": "webpackHotUpdatemyLib_lib",
+			@@ ... @@
+			-     "library": undefined,
+			+     "library": Object {
+			+       "auxiliaryComment": undefined,
+			+       "export": undefined,
+			+       "name": Array [
+			+         "my[name]Lib",
+			+         "[name]",
+			+         "lib",
+			+       ],
+			+       "type": "var",
+			+       "umdNamedDefine": undefined,
+			+     },
+			@@ ... @@
+			-     "uniqueName": "webpack",
+			+     "uniqueName": "myLib.lib",
+		`)
+	);
+	test(
+		"library.name.root contains [name] placeholder",
+		{
+			output: {
+				library: {
+					name: {
+						root: ["[name]", "myLib"]
+					},
+					type: "var"
+				}
+			}
+		},
+		e =>
+			e.toMatchInlineSnapshot(`
+			- Expected
+			+ Received
+
+			@@ ... @@
+			-     "chunkLoadingGlobal": "webpackChunkwebpack",
+			+     "chunkLoadingGlobal": "webpackChunkmyLib",
+			@@ ... @@
+			-     "devtoolNamespace": "webpack",
+			+     "devtoolNamespace": "myLib",
+			@@ ... @@
+			-     "enabledLibraryTypes": Array [],
+			+     "enabledLibraryTypes": Array [
+			+       "var",
+			+     ],
+			@@ ... @@
+			-     "hotUpdateGlobal": "webpackHotUpdatewebpack",
+			+     "hotUpdateGlobal": "webpackHotUpdatemyLib",
+			@@ ... @@
+			-     "library": undefined,
+			+     "library": Object {
+			+       "auxiliaryComment": undefined,
+			+       "export": undefined,
+			+       "name": Object {
+			+         "root": Array [
+			+           "[name]",
+			+           "myLib",
+			+         ],
+			+       },
+			+       "type": "var",
+			+       "umdNamedDefine": undefined,
+			+     },
+			@@ ... @@
+			-     "uniqueName": "webpack",
+			+     "uniqueName": "myLib",
+		`)
+	);
+	test(
+		"library.name.root contains escaped placeholder",
+		{
+			output: {
+				library: {
+					name: {
+						root: ["[\\name\\]", "my[\\name\\]Lib[name]", "[\\name\\]"]
+					},
+					type: "var"
+				}
+			}
+		},
+		e =>
+			e.toMatchInlineSnapshot(`
+			- Expected
+			+ Received
+
+			@@ ... @@
+			-     "chunkLoadingGlobal": "webpackChunkwebpack",
+			+     "chunkLoadingGlobal": "webpackChunk_name_my_name_Lib_name_",
+			@@ ... @@
+			-     "devtoolNamespace": "webpack",
+			+     "devtoolNamespace": "[name].my[name]Lib.[name]",
+			@@ ... @@
+			-     "enabledLibraryTypes": Array [],
+			+     "enabledLibraryTypes": Array [
+			+       "var",
+			+     ],
+			@@ ... @@
+			-     "hotUpdateGlobal": "webpackHotUpdatewebpack",
+			+     "hotUpdateGlobal": "webpackHotUpdate_name_my_name_Lib_name_",
+			@@ ... @@
+			-     "library": undefined,
+			+     "library": Object {
+			+       "auxiliaryComment": undefined,
+			+       "export": undefined,
+			+       "name": Object {
+			+         "root": Array [
+			+           "[\\\\name\\\\]",
+			+           "my[\\\\name\\\\]Lib[name]",
+			+           "[\\\\name\\\\]",
+			+         ],
+			+       },
+			+       "type": "var",
+			+       "umdNamedDefine": undefined,
+			+     },
+			@@ ... @@
+			-     "uniqueName": "webpack",
+			+     "uniqueName": "[name].my[name]Lib.[name]",
+		`)
+	);
 	test("target node", { target: "node" }, e =>
 		e.toMatchInlineSnapshot(`
 		- Expected