Repository: webpack/webpack (JavaScript)

Issue:
optimization.realContentHash does not respect output.hashSalt
<!-- 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?**
Assets with `[contenthash]` in the filename template do not produce different hashes when changing the value of the `output.hashSalt` webpack configuration property, if `optimization.realContentHash` is `true`.

**If the current behavior is a bug, please provide the steps to reproduce.**
Create a trivial webpack project with a `[contenthash]` in the output filename template and `optimization.realContentHash` set to `true`.
Vary the value of the `output.hashSalt` property and observe that emitted filenames don't change.

**What is the expected behavior?**
Changing the value of `output.hashSalt` should change the results of *all* filename hashes.

Inspection of the code here: https://github.com/webpack/webpack/blob/b67626c7b4ffed8737d195b27c8cea1e68d58134/lib/optimize/RealContentHashPlugin.js#L365-L368
Shows that `RealContentHashPlugin` does not look up `outputOptions.hashSalt` and apply it to the newly created Hash object.

**Other relevant information:**
webpack version:  5.75.0
Node.js version: 16.19.1
Operating System: Linux
Additional tools:



Hints:

Patch:
diff --git a/lib/optimize/RealContentHashPlugin.js b/lib/optimize/RealContentHashPlugin.js
--- a/lib/optimize/RealContentHashPlugin.js
+++ b/lib/optimize/RealContentHashPlugin.js
@@ -363,6 +363,9 @@ ${referencingAssets
 						let newHash = hooks.updateHash.call(assetsContent, oldHash);
 						if (!newHash) {
 							const hash = createHash(this._hashFunction);
+							if (compilation.outputOptions.hashSalt) {
+								hash.update(compilation.outputOptions.hashSalt);
+							}
 							for (const content of assetsContent) {
 								hash.update(content);
 							}


Test patch:
diff --git a/test/configCases/contenthash/salt/img.jpg b/test/configCases/contenthash/salt/img.jpg
new file mode 100644
diff --git a/test/configCases/contenthash/salt/index.js b/test/configCases/contenthash/salt/index.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/contenthash/salt/index.js
@@ -0,0 +1,5 @@
+import img from "./img.jpg";
+
+it("should compile", () => {
+	expect(typeof img).toBe("string");
+});
diff --git a/test/configCases/contenthash/salt/test.config.js b/test/configCases/contenthash/salt/test.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/contenthash/salt/test.config.js
@@ -0,0 +1,24 @@
+const findOutputFiles = require("../../../helpers/findOutputFiles");
+
+const allAssets = new Set();
+const allBundles = new Set();
+
+module.exports = {
+	findBundle: function(i, options) {
+		const bundle = findOutputFiles(options, new RegExp(`^bundle${i}`))[0];
+		allBundles.add(/\.([^.]+)\./.exec(bundle)[1]);
+
+		const assets = findOutputFiles(options, /^img/);
+		for (const asset of assets) {
+			allAssets.add(asset);
+		}
+
+		return `./${bundle}`;
+	},
+	afterExecute: () => {
+		// Since there are exactly 2 unique values of output.hashSalt,
+		// there should be exactly 2 unique output hashes for each file.
+		expect(allBundles.size).toBe(2);
+		expect(allAssets.size).toBe(2);
+	}
+};
diff --git a/test/configCases/contenthash/salt/webpack.config.js b/test/configCases/contenthash/salt/webpack.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/contenthash/salt/webpack.config.js
@@ -0,0 +1,48 @@
+/** @type {import("../../../../").Configuration[]} */
+module.exports = [
+	{
+		output: {
+			filename: "bundle0.[contenthash].js",
+			assetModuleFilename: "[name].[contenthash][ext]",
+			hashSalt: "1"
+		},
+		module: {
+			rules: [
+				{
+					test: /\.jpg$/,
+					type: "asset/resource"
+				}
+			]
+		}
+	},
+	{
+		output: {
+			filename: "bundle1.[contenthash].js",
+			assetModuleFilename: "[name].[contenthash][ext]",
+			hashSalt: "1"
+		},
+		module: {
+			rules: [
+				{
+					test: /\.jpg$/,
+					type: "asset/resource"
+				}
+			]
+		}
+	},
+	{
+		output: {
+			filename: "bundle2.[contenthash].js",
+			assetModuleFilename: "[name].[contenthash][ext]",
+			hashSalt: "2"
+		},
+		module: {
+			rules: [
+				{
+					test: /\.jpg$/,
+					type: "asset/resource"
+				}
+			]
+		}
+	}
+];