Skip to content

Commit b12bea6

Browse files
authored
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.
1 parent efbd685 commit b12bea6

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,7 @@ type PreinitOptions = {
22032203
precedence?: string,
22042204
crossOrigin?: string,
22052205
integrity?: string,
2206+
nonce?: string,
22062207
};
22072208
function preinit(href: string, options: PreinitOptions) {
22082209
if (!enableFloat) {
@@ -2355,6 +2356,7 @@ function scriptPropsFromPreinitOptions(
23552356
async: true,
23562357
crossOrigin: options.crossOrigin,
23572358
integrity: options.integrity,
2359+
nonce: options.nonce,
23582360
};
23592361
}
23602362

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5110,6 +5110,7 @@ type PreinitOptions = {
51105110
precedence?: string,
51115111
crossOrigin?: string,
51125112
integrity?: string,
5113+
nonce?: string,
51135114
};
51145115
function preinit(href: string, options: PreinitOptions): void {
51155116
if (!enableFloat) {
@@ -5449,6 +5450,7 @@ function scriptPropsFromPreinitOptions(
54495450
async: true,
54505451
crossOrigin: options.crossOrigin,
54515452
integrity: options.integrity,
5453+
nonce: options.nonce,
54525454
};
54535455
}
54545456

packages/react-dom/src/ReactDOMDispatcher.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type PreinitOptions = {
2020
precedence?: string,
2121
crossOrigin?: string,
2222
integrity?: string,
23+
nonce?: string,
2324
};
2425

2526
export type HostDispatcher = {

packages/react-dom/src/__tests__/ReactDOMFloat-test.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4309,6 +4309,106 @@ body {
43094309
'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"',
43104310
]);
43114311
});
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+
});
43124412
});
43134413

43144414
describe('Stylesheet Resources', () => {

0 commit comments

Comments
 (0)