@samitouri / QOS-React-2 / commits / b12bea62d9

Preinits should support a nonce option (#26744)

Currently there is no way to provide a nonce when using `ReactDOM.preinit(..., { as: 'script' })` This PR adds `nonce?: string` as an option While implementing this PR I added a test to also show you can pass `integrity`. This test isn't directly related to the nonce change.

Josh Story committed Apr 28, 2023 at 15:57 UTC b12bea62d9cfd9a925f28cb2c93daeda3865a64e
4 files changed +105
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+2
@@ -2203,6 +2203,7 @@ type PreinitOptions = {
2203 precedence?: string,
2204 crossOrigin?: string,
2205 integrity?: string,
2206 + nonce?: string,
2207 };
2208 function preinit(href: string, options: PreinitOptions) {
2209 if (!enableFloat) {
@@ -2355,6 +2356,7 @@ function scriptPropsFromPreinitOptions(
2356 async: true,
2357 crossOrigin: options.crossOrigin,
2358 integrity: options.integrity,
2359 + nonce: options.nonce,
2360 };
2361 }
2362
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+2
@@ -5110,6 +5110,7 @@ type PreinitOptions = {
5110 precedence?: string,
5111 crossOrigin?: string,
5112 integrity?: string,
5113 + nonce?: string,
5114 };
5115 function preinit(href: string, options: PreinitOptions): void {
5116 if (!enableFloat) {
@@ -5449,6 +5450,7 @@ function scriptPropsFromPreinitOptions(
5450 async: true,
5451 crossOrigin: options.crossOrigin,
5452 integrity: options.integrity,
5453 + nonce: options.nonce,
5454 };
5455 }
5456
packages/react-dom/src/ReactDOMDispatcher.js
+1
@@ -20,6 +20,7 @@ export type PreinitOptions = {
20 precedence?: string,
21 crossOrigin?: string,
22 integrity?: string,
23 + nonce?: string,
24 };
25
26 export type HostDispatcher = {
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+100
@@ -4309,6 +4309,106 @@ body {
4309 'ReactDOM.preinit(): For `href` "foo", the options provided conflict with another call to `ReactDOM.preinit("foo", { as: "script", ... })`. React will always use the options it first encounters when preinitializing a hoistable script for a given `href` and any later options will be ignored if different. Try updating all calls to `ReactDOM.preinit()` for a given `href` to use the same options, or only call `ReactDOM.preinit()` once per `href`.\n "integrity" option value: "some hash", missing from original options\n "crossOrigin" option value: "anonymous", original option value: "use-credentials"',
4310 ]);
4311 });
4312 +
4313 + it('accepts a `nonce` option for `as: "script"`', async () => {
4314 + function Component({src}) {
4315 + ReactDOM.preinit(src, {as: 'script', nonce: 'R4nD0m'});
4316 + return 'hello';
4317 + }
4318 +
4319 + await act(() => {
4320 + renderToPipeableStream(
4321 + <html>
4322 + <body>
4323 + <Component src="foo" />
4324 + </body>
4325 + </html>,
4326 + {
4327 + nonce: 'R4nD0m',
4328 + },
4329 + ).pipe(writable);
4330 + });
4331 +
4332 + expect(getMeaningfulChildren(document)).toEqual(
4333 + <html>
4334 + <head>
4335 + <script async="" src="foo" nonce="R4nD0m" />
4336 + </head>
4337 + <body>hello</body>
4338 + </html>,
4339 + );
4340 +
4341 + await clientAct(() => {
4342 + ReactDOMClient.hydrateRoot(
4343 + document,
4344 + <html>
4345 + <body>
4346 + <Component src="bar" />
4347 + </body>
4348 + </html>,
4349 + );
4350 + });
4351 +
4352 + expect(getMeaningfulChildren(document)).toEqual(
4353 + <html>
4354 + <head>
4355 + <script async="" src="foo" nonce="R4nD0m" />
4356 + <script async="" src="bar" nonce="R4nD0m" />
4357 + </head>
4358 + <body>hello</body>
4359 + </html>,
4360 + );
4361 + });
4362 +
4363 + it('accepts an `integrity` option for `as: "script"`', async () => {
4364 + function Component({src, hash}) {
4365 + ReactDOM.preinit(src, {as: 'script', integrity: hash});
4366 + return 'hello';
4367 + }
4368 +
4369 + await act(() => {
4370 + renderToPipeableStream(
4371 + <html>
4372 + <body>
4373 + <Component src="foo" hash="foo hash" />
4374 + </body>
4375 + </html>,
4376 + {
4377 + nonce: 'R4nD0m',
4378 + },
4379 + ).pipe(writable);
4380 + });
4381 +
4382 + expect(getMeaningfulChildren(document)).toEqual(
4383 + <html>
4384 + <head>
4385 + <script async="" src="foo" integrity="foo hash" />
4386 + </head>
4387 + <body>hello</body>
4388 + </html>,
4389 + );
4390 +
4391 + await clientAct(() => {
4392 + ReactDOMClient.hydrateRoot(
4393 + document,
4394 + <html>
4395 + <body>
4396 + <Component src="bar" hash="bar hash" />
4397 + </body>
4398 + </html>,
4399 + );
4400 + });
4401 +
4402 + expect(getMeaningfulChildren(document)).toEqual(
4403 + <html>
4404 + <head>
4405 + <script async="" src="foo" integrity="foo hash" />
4406 + <script async="" src="bar" integrity="bar hash" />
4407 + </head>
4408 + <body>hello</body>
4409 + </html>,
4410 + );
4411 + });
4412 });
4413
4414 describe('Stylesheet Resources', () => {