Repository: webpack/webpack (JavaScript)

Issue:
__webpack_nonce__ fails due to imports getting hoisted above the assignment.
<!-- 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 attempting to setup CSP in our application, and the `__webpack_nonce__` assignment simply isn't working. After digging into the generated code, I discovered it's because all of the imports get hoisted above the assignment value, and thus get loaded before it ever gets set.

```js
// entry.js
__webpack_nonce__ = "foo";
import { createRoot } from 'react-dom/client';
import App from './App';

const element = document.getElementById('root');
const root = createRoot(element);
root.render(<App />);
```

This produces the following:

``` js
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var react_dom_client__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom/client */ "./node_modules/react-dom/client.js");
/* harmony import */ var _App__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./App */ "./src/App.js");
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! react/jsx-runtime */ "./node_modules/react/jsx-runtime.js");

__webpack_require__.nc = "foo";
const element = document.getElementById('root');
const root = (0,react_dom_client__WEBPACK_IMPORTED_MODULE_1__.createRoot)(element);
root.render( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_3__.jsx)(_App__WEBPACK_IMPORTED_MODULE_2__["default"], {}));
```

I can confirm that at every invocation of a script include, `__webpack_require__.nc` is undefined because it executed before the assignment.

**What is the expected behavior?**

I expect to be able to set the nonce, per the documentation, and have it be attached to the script tags that the runtime produces.


**Other relevant information:**
webpack version:  5.72.0
Node.js version: 16.14.0
Operating System: macOS 12.3
Additional tools: React 18, babel 7



Hints:
That is expected, just is ESM, move `__webpack_nonce__` to something like `import 'bootstrap-nonce.js'` and assign it
I tried that, still didn’t work. I also tried using dynamic imports to delay loading the App and react-dom.
this does not work since there is no additional chunks ( no `import()` ), but we should fix this anyway I think.
ah.. wait `__webpack_nonce__` will work for inner scripts.. you should set nonce to entrypoint by yourself ( maybe it is possible using html-webpack-plugin, if you use it )
_______

> I can confirm that at every invocation of a script include, __webpack_require__.nc is undefined because it executed before the assignment.

thats expected, there is no nonce yet..
The dynamic import is inside App, where I have a react lazy invocation. There’s five separate inner scripts being generated, and none of them will load when we enable csp.

we aren’t using the html plugin, we produce our own landing page using the manifest output. The entry script has a nonce and loads correctly, its when webpack tries to require the code split that things fail.

i seriously spent all day trying to make this thing work, I’m at the stage where my next step is to sed the entry bundle to inject the nonce myself.
Could you create a small repro I will take a look
Reproduction repo here: https://github.com/Twipped/webpack-nonce-repro
`npm install` and `npm start` should pop open a browser window that demonstrates the issue. If you use the console to navigate to `load script` and break at the check the value of `__webpack_require__.nc` on line 20, you'll see it's undefined, while `window.NONCE` does have a value present.
yeah, got a problem. I'll fix this

Patch:
diff --git a/lib/RuntimePlugin.js b/lib/RuntimePlugin.js
--- a/lib/RuntimePlugin.js
+++ b/lib/RuntimePlugin.js
@@ -26,6 +26,7 @@ const GlobalRuntimeModule = require("./runtime/GlobalRuntimeModule");
 const HasOwnPropertyRuntimeModule = require("./runtime/HasOwnPropertyRuntimeModule");
 const LoadScriptRuntimeModule = require("./runtime/LoadScriptRuntimeModule");
 const MakeNamespaceObjectRuntimeModule = require("./runtime/MakeNamespaceObjectRuntimeModule");
+const NonceRuntimeModule = require("./runtime/NonceRuntimeModule");
 const OnChunksLoadedRuntimeModule = require("./runtime/OnChunksLoadedRuntimeModule");
 const PublicPathRuntimeModule = require("./runtime/PublicPathRuntimeModule");
 const RelativeUrlRuntimeModule = require("./runtime/RelativeUrlRuntimeModule");
@@ -431,6 +432,12 @@ class RuntimePlugin {
 						return true;
 					}
 				});
+			compilation.hooks.runtimeRequirementInTree
+				.for(RuntimeGlobals.scriptNonce)
+				.tap("RuntimePlugin", chunk => {
+					compilation.addRuntimeModule(chunk, new NonceRuntimeModule());
+					return true;
+				});
 			// TODO webpack 6: remove CompatRuntimeModule
 			compilation.hooks.additionalTreeRuntimeRequirements.tap(
 				"RuntimePlugin",
diff --git a/lib/runtime/NonceRuntimeModule.js b/lib/runtime/NonceRuntimeModule.js
new file mode 100644
--- /dev/null
+++ b/lib/runtime/NonceRuntimeModule.js
@@ -0,0 +1,24 @@
+/*
+	MIT License http://www.opensource.org/licenses/mit-license.php
+	Author Ivan Kopeykin @vankop
+*/
+
+"use strict";
+
+const RuntimeGlobals = require("../RuntimeGlobals");
+const RuntimeModule = require("../RuntimeModule");
+
+class NonceRuntimeModule extends RuntimeModule {
+	constructor() {
+		super("nonce", RuntimeModule.STAGE_ATTACH);
+	}
+
+	/**
+	 * @returns {string} runtime code
+	 */
+	generate() {
+		return `${RuntimeGlobals.scriptNonce} = undefined;`;
+	}
+}
+
+module.exports = NonceRuntimeModule;


Test patch:
diff --git a/test/configCases/web/nonce/index.js b/test/configCases/web/nonce/index.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/web/nonce/index.js
@@ -0,0 +1,5 @@
+import "./nonce";
+
+it("should set nonce", () => {
+	expect(__webpack_nonce__).toBe("nonce");
+});
diff --git a/test/configCases/web/nonce/nonce.js b/test/configCases/web/nonce/nonce.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/web/nonce/nonce.js
@@ -0,0 +1 @@
+__webpack_nonce__ = "nonce";
diff --git a/test/configCases/web/nonce/webpack.config.js b/test/configCases/web/nonce/webpack.config.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/web/nonce/webpack.config.js
@@ -0,0 +1,7 @@
+const webpack = require("../../../../");
+/** @type {import("../../../../").Configuration} */
+module.exports = {
+	target: "web",
+	// plugin that intercepts __webpack_require__
+	plugins: [new webpack.HotModuleReplacementPlugin()]
+};