Repository: webpack/webpack (JavaScript)

Issue:
When set via browserslist query, can't output ESM with node `12.22` target
<!-- 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?**

I'm outputting ESM (`experiments.outputModule: true` & `output.library: "module"`).

When having `target: "browserslist:node 12.22"` in my `webpack.config.js`, I get the following error:
```
Error: For the selected environment is no default ESM chunk format available:
ESM exports can be chosen when 'import()' is available.
JSONP Array push can be chosen when 'document' is available.
Make sure that your 'browserslist' includes only platforms that support these features or select an appropriate 'target' to allow selecting a chunk format by default. Alternatively specify the 'output.chunkFormat' directly.
```

If I replace the above line with `target: "node12.22"`, the error disappear.

**If the current behavior is a bug, please provide the steps to reproduce.**

See above (but can provide a minimal example if necessary).

<!-- 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?**

Successful compilation.

<!-- "It should work" is not a helpful explanation -->
<!-- Explain exactly how it should behave -->

**Other relevant information:**
webpack version: `5.68.0`
Node.js version: `17.4.0`
Operating System: MacOS Monterey
Additional tools: N/A



Hints:
hm.. in browserlist we support from `node 13.14` from `node` target from `12`...
Why is that? 🤔
ESM support has been back ported to some minor node.js 12 version.
We need to update that. You can send a PR
Will do!

Patch:
diff --git a/lib/config/browserslistTargetHandler.js b/lib/config/browserslistTargetHandler.js
--- a/lib/config/browserslistTargetHandler.js
+++ b/lib/config/browserslistTargetHandler.js
@@ -121,8 +121,7 @@ const resolve = browsers => {
 		// baidu: Not supported
 		// and_uc: Not supported
 		// kaios: Not supported
-		// Since Node.js 13.14.0 no warning about usage, but it was added 8.5.0 with some limitations and it was improved in 12.0.0 and 13.2.0
-		node: [13, 14]
+		node: [12, 17]
 	});
 
 	return {
@@ -248,8 +247,7 @@ const resolve = browsers => {
 			// baidu: Not supported
 			// and_uc: Not supported
 			// kaios: Not supported
-			// Since Node.js 13.14.0 no warning about usage, but it was added 8.5.0 with some limitations and it was improved in 12.0.0 and 13.2.0
-			node: [13, 14]
+			node: [12, 17]
 		}),
 		dynamicImport: es6DynamicImport,
 		dynamicImportInWorker: es6DynamicImport && !anyNode,
@@ -272,7 +270,7 @@ const resolve = browsers => {
 			// baidu: Unknown support
 			// and_uc: Unknown support
 			// kaios: Unknown support
-			node: [12, 0]
+			node: 12
 		}),
 		optionalChaining: rawChecker({
 			chrome: 80,


Test patch:
diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js
--- a/test/Defaults.unittest.js
+++ b/test/Defaults.unittest.js
@@ -13,64 +13,64 @@ const quoteMeta = str => {
 	return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
 };
 
-describe("Defaults", () => {
-	const cwd = process.cwd();
-	const cwdRegExp = new RegExp(
-		`${quoteMeta(cwd)}((?:\\\\)?(?:[a-zA-Z.\\-_]+\\\\)*)`,
-		"g"
-	);
-	const escapedCwd = JSON.stringify(cwd).slice(1, -1);
-	const escapedCwdRegExp = new RegExp(
-		`${quoteMeta(escapedCwd)}((?:\\\\\\\\)?(?:[a-zA-Z.\\-_]+\\\\\\\\)*)`,
-		"g"
-	);
-	const normalize = str => {
-		if (cwd.startsWith("/")) {
-			str = str.replace(new RegExp(quoteMeta(cwd), "g"), "<cwd>");
-		} else {
-			str = str.replace(cwdRegExp, (m, g) => `<cwd>${g.replace(/\\/g, "/")}`);
-			str = str.replace(
-				escapedCwdRegExp,
-				(m, g) => `<cwd>${g.replace(/\\\\/g, "/")}`
-			);
-		}
-		str = str.replace(/@@ -\d+,\d+ \+\d+,\d+ @@/g, "@@ ... @@");
-		return str;
-	};
+const cwd = process.cwd();
+const cwdRegExp = new RegExp(
+	`${quoteMeta(cwd)}((?:\\\\)?(?:[a-zA-Z.\\-_]+\\\\)*)`,
+	"g"
+);
+const escapedCwd = JSON.stringify(cwd).slice(1, -1);
+const escapedCwdRegExp = new RegExp(
+	`${quoteMeta(escapedCwd)}((?:\\\\\\\\)?(?:[a-zA-Z.\\-_]+\\\\\\\\)*)`,
+	"g"
+);
+const normalize = str => {
+	if (cwd.startsWith("/")) {
+		str = str.replace(new RegExp(quoteMeta(cwd), "g"), "<cwd>");
+	} else {
+		str = str.replace(cwdRegExp, (m, g) => `<cwd>${g.replace(/\\/g, "/")}`);
+		str = str.replace(
+			escapedCwdRegExp,
+			(m, g) => `<cwd>${g.replace(/\\\\/g, "/")}`
+		);
+	}
+	str = str.replace(/@@ -\d+,\d+ \+\d+,\d+ @@/g, "@@ ... @@");
+	return str;
+};
 
-	class Diff {
-		constructor(value) {
-			this.value = value;
-		}
+class Diff {
+	constructor(value) {
+		this.value = value;
 	}
+}
 
-	expect.addSnapshotSerializer({
-		test(value) {
-			return value instanceof Diff;
-		},
-		print(received) {
-			return normalize(received.value);
-		}
-	});
+expect.addSnapshotSerializer({
+	test(value) {
+		return value instanceof Diff;
+	},
+	print(received) {
+		return normalize(received.value);
+	}
+});
 
-	expect.addSnapshotSerializer({
-		test(value) {
-			return typeof value === "string";
-		},
-		print(received) {
-			return JSON.stringify(normalize(received));
-		}
-	});
+expect.addSnapshotSerializer({
+	test(value) {
+		return typeof value === "string";
+	},
+	print(received) {
+		return JSON.stringify(normalize(received));
+	}
+});
 
-	const getDefaultConfig = config => {
-		const { applyWebpackOptionsDefaults, getNormalizedWebpackOptions } =
-			require("..").config;
-		config = getNormalizedWebpackOptions(config);
-		applyWebpackOptionsDefaults(config);
-		process.chdir(cwd);
-		return config;
-	};
+const getDefaultConfig = config => {
+	const { applyWebpackOptionsDefaults, getNormalizedWebpackOptions } =
+		require("..").config;
+	config = getNormalizedWebpackOptions(config);
+	applyWebpackOptionsDefaults(config);
+	process.chdir(cwd);
+	return config;
+};
 
+describe("snapshots", () => {
 	const baseConfig = getDefaultConfig({ mode: "none" });
 
 	it("should have the correct base config", () => {
@@ -2018,3 +2018,27 @@ describe("Defaults", () => {
 		`)
 	);
 });
+
+it("should result in the same target options for same target", () => {
+	const inlineTarget = getDefaultConfig({ target: "node12.17" });
+	const browserslistTarget = getDefaultConfig({
+		target: "browserslist: node 12.17"
+	});
+	const diff = stripAnsi(
+		jestDiff(inlineTarget, browserslistTarget, {
+			expand: false,
+			contextLines: 0
+		})
+	);
+
+	expect(inlineTarget.output.environment.module).toBe(true);
+	expect(inlineTarget.output.environment.dynamicImport).toBe(true);
+	expect(new Diff(diff)).toMatchInlineSnapshot(`
+		- Expected
+		+ Received
+
+		@@ ... @@
+		-   "target": "node12.17",
+		+   "target": "browserslist: node 12.17",
+	`);
+});
diff --git a/test/__snapshots__/target-browserslist.unittest.js.snap b/test/__snapshots__/target-browserslist.unittest.js.snap
--- a/test/__snapshots__/target-browserslist.unittest.js.snap
+++ b/test/__snapshots__/target-browserslist.unittest.js.snap
@@ -298,7 +298,7 @@ Object {
   "const": true,
   "destructuring": true,
   "document": null,
-  "dynamicImport": false,
+  "dynamicImport": true,
   "dynamicImportInWorker": false,
   "electron": false,
   "fetchWasm": null,
@@ -307,7 +307,7 @@ Object {
   "globalThis": true,
   "importScripts": false,
   "importScriptsInWorker": true,
-  "module": false,
+  "module": true,
   "node": null,
   "nodeBuiltins": null,
   "nwjs": false,
@@ -675,7 +675,7 @@ Object {
   "const": true,
   "destructuring": true,
   "document": false,
-  "dynamicImport": false,
+  "dynamicImport": true,
   "dynamicImportInWorker": false,
   "electron": false,
   "fetchWasm": false,
@@ -684,7 +684,7 @@ Object {
   "globalThis": true,
   "importScripts": false,
   "importScriptsInWorker": true,
-  "module": false,
+  "module": true,
   "node": true,
   "nodeBuiltins": true,
   "nwjs": false,
diff --git a/test/configCases/ecmaVersion/browserslist/webpack.config.js b/test/configCases/ecmaVersion/browserslist/webpack.config.js
--- a/test/configCases/ecmaVersion/browserslist/webpack.config.js
+++ b/test/configCases/ecmaVersion/browserslist/webpack.config.js
@@ -10,9 +10,9 @@ module.exports = {
 			  "bigIntLiteral": true,
 			  "const": true,
 			  "destructuring": true,
-			  "dynamicImport": false,
+			  "dynamicImport": true,
 			  "forOf": true,
-			  "module": false,
+			  "module": true,
 			}
 		`);
 				expect(compilation.options.externalsPresets).toMatchInlineSnapshot(`