[ServerRenderer] Setup for adding data attributes streaming format (#25567)

This commit is contained in:
mofeiZ
2022-11-01 12:04:50 -04:00
committed by GitHub
parent ab075a2324
commit 6883d79445
5 changed files with 291 additions and 180 deletions
+99 -123
View File
@@ -8,6 +8,7 @@
*/
'use strict';
import {replaceScriptsAndMove, mergeOptions} from '../test-utils/FizzTestUtils';
let JSDOM;
let Stream;
@@ -30,6 +31,7 @@ let container;
let buffer = '';
let hasErrored = false;
let fatalError = undefined;
const renderOptions = {};
describe('ReactDOMFizzServer', () => {
beforeEach(() => {
@@ -134,17 +136,7 @@ describe('ReactDOMFizzServer', () => {
container.nodeName === '#document' ? container.body : container;
while (fakeBody.firstChild) {
const node = fakeBody.firstChild;
if (
node.nodeName === 'SCRIPT' &&
(CSPnonce === null || node.getAttribute('nonce') === CSPnonce)
) {
const script = document.createElement('script');
script.textContent = node.textContent;
fakeBody.removeChild(node);
parent.appendChild(script);
} else {
parent.appendChild(node);
}
await replaceScriptsAndMove(window, CSPnonce, node, parent);
}
}
@@ -170,6 +162,7 @@ describe('ReactDOMFizzServer', () => {
document = jsdom.window.document;
container = document;
buffer = '';
await replaceScriptsAndMove(window, CSPnonce, document.documentElement);
}
function getVisibleChildren(element) {
@@ -179,6 +172,7 @@ describe('ReactDOMFizzServer', () => {
if (node.nodeType === 1) {
if (
node.tagName !== 'SCRIPT' &&
node.tagName !== 'script' &&
node.tagName !== 'TEMPLATE' &&
node.tagName !== 'template' &&
!node.hasAttribute('hidden') &&
@@ -288,6 +282,13 @@ describe('ReactDOMFizzServer', () => {
const As = as;
return <As>{readText(text)}</As>;
}
function renderToPipeableStream(jsx, options) {
// Merge options with renderOptions, which may contain featureFlag specific behavior
return ReactDOMFizzServer.renderToPipeableStream(
jsx,
mergeOptions(options, renderOptions),
);
}
it('should asynchronously load a lazy component', async () => {
let resolveA;
@@ -313,7 +314,7 @@ describe('ReactDOMFizzServer', () => {
};
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<div>
<Suspense fallback={<Text text="Loading..." />}>
@@ -379,7 +380,7 @@ describe('ReactDOMFizzServer', () => {
// Server-side
const [App, resolve] = makeApp();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(
@@ -432,7 +433,7 @@ describe('ReactDOMFizzServer', () => {
});
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<Suspense fallback={<Text text="Loading..." />}>
<Lazy text="Hello" />
@@ -492,13 +493,10 @@ describe('ReactDOMFizzServer', () => {
loggedErrors.length = 0;
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App isClient={false} />,
{
bootstrapScriptContent: '__INIT__();',
onError,
},
);
const {pipe} = renderToPipeableStream(<App isClient={false} />, {
bootstrapScriptContent: '__INIT__();',
onError,
});
pipe(writable);
});
expect(loggedErrors).toEqual([]);
@@ -554,7 +552,7 @@ describe('ReactDOMFizzServer', () => {
});
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<Suspense fallback={<Text text="Loading..." />}>
{lazyElement}
@@ -567,7 +565,7 @@ describe('ReactDOMFizzServer', () => {
// Because there is no content inside the Suspense boundary that could've
// been written, we expect to not see any additional partial data flushed
// yet.
expect(container.firstChild.nextSibling).toBe(null);
expect(container.childNodes.length).toBe(1);
await act(async () => {
resolveElement({default: <Text text="Hello" />});
});
@@ -603,7 +601,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App isClient={false} />,
{
@@ -699,7 +697,7 @@ describe('ReactDOMFizzServer', () => {
loggedErrors.length = 0;
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App />,
{
@@ -767,7 +765,7 @@ describe('ReactDOMFizzServer', () => {
loggedErrors.length = 0;
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App />,
{
@@ -823,7 +821,7 @@ describe('ReactDOMFizzServer', () => {
it('should asynchronously load the suspense boundary', async () => {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<Suspense fallback={<Text text="Loading..." />}>
<AsyncText text="Hello World" />
@@ -862,7 +860,7 @@ describe('ReactDOMFizzServer', () => {
};
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
bootstrapScriptContent: '__INIT__();',
});
pipe(writable);
@@ -936,7 +934,7 @@ describe('ReactDOMFizzServer', () => {
// We originally suspend the boundary and start streaming the loading state.
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App />,
{
@@ -1018,9 +1016,7 @@ describe('ReactDOMFizzServer', () => {
// We originally suspend the boundary and start streaming the loading state.
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App showMore={false} />,
);
const {pipe} = renderToPipeableStream(<App showMore={false} />);
pipe(writable);
});
@@ -1093,7 +1089,7 @@ describe('ReactDOMFizzServer', () => {
let controls;
await act(async () => {
controls = ReactDOMFizzServer.renderToPipeableStream(<App />, {onError});
controls = renderToPipeableStream(<App />, {onError});
controls.pipe(writable);
});
@@ -1163,7 +1159,7 @@ describe('ReactDOMFizzServer', () => {
};
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
// We use two nested boundaries to flush out coverage of an old reentrancy bug.
<Suspense fallback="Loading...">
<Suspense fallback={<Text text="Loading A..." />}>
@@ -1187,7 +1183,7 @@ describe('ReactDOMFizzServer', () => {
});
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<Suspense fallback={<Text text="Loading B..." />}>
<Text text="This will show B: " />
<div>
@@ -1283,7 +1279,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -1370,7 +1366,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -1423,7 +1419,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App />,
{
@@ -1509,7 +1505,7 @@ describe('ReactDOMFizzServer', () => {
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<A />);
const {pipe} = renderToPipeableStream(<A />);
pipe(writable);
});
@@ -1607,7 +1603,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<TestProvider ctx="A">
<div>
<Suspense fallback={[<Text text="Loading: " />, <TestConsumer />]}>
@@ -1664,7 +1660,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<PrintA />
<div>
@@ -1725,7 +1721,7 @@ describe('ReactDOMFizzServer', () => {
const loggedErrors = [];
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<PrintA />
<div>
@@ -1793,7 +1789,7 @@ describe('ReactDOMFizzServer', () => {
let controls;
await act(async () => {
controls = ReactDOMFizzServer.renderToPipeableStream(
controls = renderToPipeableStream(
<App isClient={false} />,
{
@@ -1876,7 +1872,7 @@ describe('ReactDOMFizzServer', () => {
it('should be able to abort the fallback if the main content finishes first', async () => {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<Suspense fallback={<Text text="Loading Outer" />}>
<div>
<Suspense
@@ -1973,9 +1969,7 @@ describe('ReactDOMFizzServer', () => {
await jest.runAllTimers();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App isClient={false} />,
);
const {pipe} = renderToPipeableStream(<App isClient={false} />);
pipe(writable);
});
@@ -2085,7 +2079,7 @@ describe('ReactDOMFizzServer', () => {
const loggedErrors = [];
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<Suspense fallback="Loading...">
<App />
</Suspense>,
@@ -2170,7 +2164,7 @@ describe('ReactDOMFizzServer', () => {
}
const loggedErrors = [];
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<Suspense fallback="Loading...">
<App />
</Suspense>,
@@ -2259,7 +2253,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(Scheduler).toHaveYielded(['Yay!']);
@@ -2349,7 +2343,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(Scheduler).toHaveYielded(['Yay!']);
@@ -2439,7 +2433,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(Scheduler).toHaveYielded(['Yay!']);
@@ -2492,7 +2486,7 @@ describe('ReactDOMFizzServer', () => {
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError(error) {
Scheduler.unstable_yieldValue('[s!] ' + error.message);
},
@@ -2577,14 +2571,11 @@ describe('ReactDOMFizzServer', () => {
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App color="red" />,
{
onError(error) {
Scheduler.unstable_yieldValue('[s!] ' + error.message);
},
const {pipe} = renderToPipeableStream(<App color="red" />, {
onError(error) {
Scheduler.unstable_yieldValue('[s!] ' + error.message);
},
);
});
pipe(writable);
});
expect(Scheduler).toHaveYielded(['[s!] Oops.']);
@@ -2678,7 +2669,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App fallbackText="Loading..." />,
{
onError(error) {
@@ -2807,7 +2798,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(Scheduler).toHaveYielded(['Yay!']);
@@ -2959,7 +2950,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(Scheduler).toHaveYielded(['A', 'B']);
@@ -3025,7 +3016,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<Foo />);
const {pipe} = renderToPipeableStream(<Foo />);
pipe(writable);
});
@@ -3047,9 +3038,7 @@ describe('ReactDOMFizzServer', () => {
]).map(item => <li key={item.get('value')}>{item.get('name')}</li>);
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<ul>{mappedJSX}</ul>,
);
const {pipe} = renderToPipeableStream(<ul>{mappedJSX}</ul>);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(
@@ -3081,10 +3070,7 @@ describe('ReactDOMFizzServer', () => {
let abort;
const loggedErrors = [];
await act(async () => {
const {
pipe,
abort: abortImpl,
} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe, abort: abortImpl} = renderToPipeableStream(<App />, {
onError(error) {
// In this test we contrive erroring with strings so we push the error whereas in most
// other tests we contrive erroring with Errors and push the message.
@@ -3167,10 +3153,7 @@ describe('ReactDOMFizzServer', () => {
let abort;
const loggedErrors = [];
await act(async () => {
const {
pipe,
abort: abortImpl,
} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe, abort: abortImpl} = renderToPipeableStream(<App />, {
onError(error) {
loggedErrors.push(error.message);
return 'a digest';
@@ -3232,7 +3215,7 @@ describe('ReactDOMFizzServer', () => {
it('warns in dev if you access digest from errorInfo in onRecoverableError', async () => {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<div>
<Suspense fallback={'loading...'}>
<AsyncText text={'hello'} />
@@ -3309,7 +3292,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError,
});
pipe(writable);
@@ -3357,7 +3340,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError,
});
pipe(writable);
@@ -3397,7 +3380,7 @@ describe('ReactDOMFizzServer', () => {
loggedErrors.length = 0;
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError,
});
pipe(writable);
@@ -3435,7 +3418,7 @@ describe('ReactDOMFizzServer', () => {
it('accepts an integrity property for bootstrapScripts and bootstrapModules', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3494,7 +3477,7 @@ describe('ReactDOMFizzServer', () => {
const stringWithScriptsInIt =
'prescription pre<scription pre<Scription pre</scRipTion pre</ScripTion </script><script><!-- <script> -->';
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<div />, {
const {pipe} = renderToPipeableStream(<div />, {
bootstrapScriptContent:
'window.__test_outlet = "This should have been replaced";var x = "' +
stringWithScriptsInIt +
@@ -3515,7 +3498,7 @@ describe('ReactDOMFizzServer', () => {
el.textContent = '{"one":1,\u2028\u2029"two":2}';
const stringWithLSAndPSCharacters = el.textContent;
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<div />, {
const {pipe} = renderToPipeableStream(<div />, {
bootstrapScriptContent:
'let x = ' +
stringWithLSAndPSCharacters +
@@ -3536,7 +3519,7 @@ describe('ReactDOMFizzServer', () => {
// this boolean expression will be cast to a number due to the bitwise &. we will look for a truthy value (1) below
const booleanLogicString = '1 < 2 & 3 > 1';
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<div />, {
const {pipe} = renderToPipeableStream(<div />, {
bootstrapScriptContent:
'let x = ' + booleanLogicString + '; window.__test_outlet = x;',
});
@@ -3549,7 +3532,7 @@ describe('ReactDOMFizzServer', () => {
// @gate enableFizzExternalRuntime
it('supports option to load runtime as an external script', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3608,7 +3591,7 @@ describe('ReactDOMFizzServer', () => {
const [ServerApp, serverResolve] = makeApp();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<ServerApp />);
const {pipe} = renderToPipeableStream(<ServerApp />);
pipe(writable);
});
await act(() => {
@@ -3684,9 +3667,7 @@ describe('ReactDOMFizzServer', () => {
const [ServerApp, serverResolve] = makeApp();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<ServerApp text="initial" />,
);
const {pipe} = renderToPipeableStream(<ServerApp text="initial" />);
pipe(writable);
});
await act(() => {
@@ -3763,9 +3744,7 @@ describe('ReactDOMFizzServer', () => {
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App text="initial" />,
);
const {pipe} = renderToPipeableStream(<App text="initial" />);
pipe(writable);
});
@@ -3854,7 +3833,7 @@ describe('ReactDOMFizzServer', () => {
};
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -3946,7 +3925,7 @@ describe('ReactDOMFizzServer', () => {
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4077,7 +4056,7 @@ describe('ReactDOMFizzServer', () => {
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4200,7 +4179,7 @@ describe('ReactDOMFizzServer', () => {
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4271,7 +4250,7 @@ describe('ReactDOMFizzServer', () => {
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4317,7 +4296,7 @@ describe('ReactDOMFizzServer', () => {
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4372,7 +4351,7 @@ describe('ReactDOMFizzServer', () => {
);
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html data-html="html">
<AsyncNoOutput />
<AsyncHead />
@@ -4413,7 +4392,7 @@ describe('ReactDOMFizzServer', () => {
});
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -4470,9 +4449,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App name="Foo" />,
);
const {pipe} = renderToPipeableStream(<App name="Foo" />);
pipe(writable);
});
@@ -4510,9 +4487,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
<App name="Foo" />,
);
const {pipe} = renderToPipeableStream(<App name="Foo" />);
pipe(writable);
});
@@ -4562,7 +4537,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -4607,7 +4582,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
await afterImmediate();
await act(() => resolveText('ello'));
pipe(writable);
@@ -4651,7 +4626,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
await afterImmediate();
await act(() => resolveText('world'));
pipe(writable);
@@ -4689,7 +4664,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
await afterImmediate();
await act(() => resolveText('world'));
pipe(writable);
@@ -4738,7 +4713,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
await afterImmediate();
await act(() => resolveText('world'));
pipe(writable);
@@ -4823,12 +4798,13 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
await afterImmediate();
await act(() => resolveText('world'));
pipe(writable);
});
// strip inserted external runtime
expect(container.innerHTML).toEqual(
'<div><!--$-->hello<!-- -->world<!-- --><!--/$--><!--$-->world<!-- --><!--/$--><!--$-->hello<!-- -->world<!-- --><br><!--/$--><!--$-->world<!-- --><br><!--/$--></div>',
);
@@ -4879,7 +4855,7 @@ describe('ReactDOMFizzServer', () => {
prepareJSDOMForTitle();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(<title>hello</title>);
@@ -4903,7 +4879,7 @@ describe('ReactDOMFizzServer', () => {
prepareJSDOMForTitle();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(<title>hello</title>);
@@ -4941,7 +4917,7 @@ describe('ReactDOMFizzServer', () => {
prepareJSDOMForTitle();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
if (__DEV__) {
@@ -5025,7 +5001,7 @@ describe('ReactDOMFizzServer', () => {
prepareJSDOMForTitle();
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
if (__DEV__) {
@@ -5086,7 +5062,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -5146,7 +5122,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
@@ -5185,7 +5161,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -5254,7 +5230,7 @@ describe('ReactDOMFizzServer', () => {
const reportedServerErrors = [];
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError(error) {
reportedServerErrors.push(error);
},
@@ -5315,7 +5291,7 @@ describe('ReactDOMFizzServer', () => {
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual('Hi');
@@ -5342,7 +5318,7 @@ describe('ReactDOMFizzServer', () => {
);
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(<button>0</button>);
@@ -5373,7 +5349,7 @@ describe('ReactDOMFizzServer', () => {
let caughtError;
try {
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
const {pipe} = renderToPipeableStream(<App />, {
onError(e) {
reportedServerErrors.push(e);
},
@@ -5402,7 +5378,7 @@ describe('ReactDOMFizzServer', () => {
}
}
await act(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getVisibleChildren(container)).toEqual(<div />);
+58 -55
View File
@@ -8,6 +8,7 @@
*/
'use strict';
import {replaceScriptsAndMove, mergeOptions} from '../test-utils/FizzTestUtils';
let JSDOM;
let Stream;
@@ -25,6 +26,7 @@ let container;
let buffer = '';
let hasErrored = false;
let fatalError = undefined;
const renderOptions = {};
describe('ReactDOMFloat', () => {
beforeEach(() => {
@@ -100,17 +102,7 @@ describe('ReactDOMFloat', () => {
container.nodeName === '#document' ? container.body : container;
while (fakeBody.firstChild) {
const node = fakeBody.firstChild;
if (
node.nodeName === 'SCRIPT' &&
(CSPnonce === null || node.getAttribute('nonce') === CSPnonce)
) {
const script = document.createElement('script');
script.textContent = node.textContent;
fakeBody.removeChild(node);
parent.appendChild(script);
} else {
parent.appendChild(node);
}
await replaceScriptsAndMove(document.defaultView, CSPnonce, node, parent);
}
}
@@ -135,6 +127,7 @@ describe('ReactDOMFloat', () => {
document = jsdom.window.document;
container = document;
buffer = '';
await replaceScriptsAndMove(jsdom.window, null, document.documentElement);
}
function getMeaningfulChildren(element) {
@@ -148,6 +141,8 @@ describe('ReactDOMFloat', () => {
node.hasAttribute('data-meaningful') ||
(node.tagName === 'SCRIPT' &&
node.hasAttribute('src') &&
node.getAttribute('src') !==
renderOptions.unstable_externalRuntimeSrc &&
node.hasAttribute('async')) ||
(node.tagName !== 'SCRIPT' &&
node.tagName !== 'TEMPLATE' &&
@@ -235,6 +230,14 @@ describe('ReactDOMFloat', () => {
return readText(text);
}
function renderToPipeableStream(jsx, options) {
// Merge options with renderOptions, which may contain featureFlag specific behavior
return ReactDOMFizzServer.renderToPipeableStream(
jsx,
mergeOptions(options, renderOptions),
);
}
// @gate enableFloat
it('can render resources before singletons', async () => {
const root = ReactDOMClient.createRoot(document);
@@ -569,7 +572,7 @@ describe('ReactDOMFloat', () => {
});
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<title>foo</title>
<html>
@@ -635,7 +638,7 @@ describe('ReactDOMFloat', () => {
return 'foo';
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -786,7 +789,7 @@ describe('ReactDOMFloat', () => {
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<ServerApp />);
const {pipe} = renderToPipeableStream(<ServerApp />);
pipe(writable);
});
expect(getMeaningfulChildren(document)).toEqual(
@@ -846,7 +849,7 @@ describe('ReactDOMFloat', () => {
return 'foo';
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -877,7 +880,7 @@ describe('ReactDOMFloat', () => {
return 'foo';
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -996,7 +999,7 @@ describe('ReactDOMFloat', () => {
);
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<App srcs={['server', 'shared']} />,
);
pipe(writable);
@@ -1148,7 +1151,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('supports preconnects, prefetc-dns, and arbitrary other link types', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -1259,7 +1262,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can render <base> as a Resource', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -1317,7 +1320,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can render icons and apple-touch-icons as Resources', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<html>
<head />
@@ -1371,7 +1374,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can hydrate the right instances for deeply nested structured metas', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<html>
<head />
@@ -1463,7 +1466,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can insert meta tags in the expected location', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<html>
<head />
@@ -1615,7 +1618,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can render meta tags with og properties with structured data', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<html>
<head />
@@ -1778,7 +1781,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can render meta tags as resources', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<html>
<head />
@@ -1843,7 +1846,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('can rendering title tags anywhere in the tree', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<title>before</title>
<>
@@ -1918,7 +1921,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('prepends new titles on the client so newer ones override older ones, including orphaned server rendered titles', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<title>server</title>
@@ -2032,7 +2035,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat && enableHostSingletons && (enableClientRenderFallbackOnTextMismatch || !__DEV__)
it('can render a title before a singleton even if that singleton clears its contents', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<>
<title>foo</title>
<html>
@@ -2097,7 +2100,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('treats link rel stylesheet elements as a style resource when it includes a precedence when server rendering', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2150,7 +2153,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('treats link rel stylesheet elements as a style resource when it includes a precedence when hydrating', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2188,7 +2191,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('preloads stylesheets without a precedence prop when server rendering', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2216,7 +2219,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('hoists style resources to the correct precedence', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2311,7 +2314,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
it('retains styles even when a new html, head, and/body mount', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2363,7 +2366,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat && !enableHostSingletons
it('retains styles even when a new html, head, and/body mount - without HostSingleton', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2468,7 +2471,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('treats async scripts without onLoad or onError as Resources', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2553,7 +2556,7 @@ describe('ReactDOMFloat', () => {
);
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
@@ -2636,7 +2639,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('treats stylesheet links with a precedence as a resource', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2689,7 +2692,7 @@ describe('ReactDOMFloat', () => {
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -2745,7 +2748,7 @@ describe('ReactDOMFloat', () => {
ReactDOM.preinit('preset', {as: 'style', precedence: 'preset'});
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3109,7 +3112,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('normalizes style resource precedence for all boundaries inlined as part of the shell flush', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3199,7 +3202,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('style resources are inserted according to precedence order on the client', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3328,7 +3331,7 @@ describe('ReactDOMFloat', () => {
return null;
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3406,7 +3409,7 @@ describe('ReactDOMFloat', () => {
return children;
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3529,7 +3532,7 @@ describe('ReactDOMFloat', () => {
return children;
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3761,7 +3764,7 @@ describe('ReactDOMFloat', () => {
}
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});
expect(getMeaningfulChildren(document)).toEqual(
@@ -3869,7 +3872,7 @@ describe('ReactDOMFloat', () => {
return children;
}
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -3998,7 +4001,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -4082,7 +4085,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<Component scenarios={scenarios} />
@@ -4719,7 +4722,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<link
@@ -4832,7 +4835,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<link
@@ -4945,7 +4948,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<link
@@ -5056,7 +5059,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<script src="foo" async={true} data-foo="a current value" />
@@ -5106,7 +5109,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<link
@@ -5201,7 +5204,7 @@ describe('ReactDOMFloat', () => {
};
try {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head>
<link
@@ -5262,7 +5265,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('escapes hrefs when selecting matching elements in the document when rendering Resources', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -5324,7 +5327,7 @@ describe('ReactDOMFloat', () => {
// @gate enableFloat
it('escapes hrefs when selecting matching elements in the document when using preload and preinit', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
const {pipe} = renderToPipeableStream(
<html>
<head />
<body>
@@ -1,10 +1,17 @@
// TODO: Add Flow types
/**
* This file is compiled to a standalone browser script by rollup and loaded by Fizz
* clients. Therefore, it should be fast and not have many external dependencies.
* @flow
*/
// Imports are resolved statically by the closure compiler in release bundles
// and by rollup in jest unit tests
import {
clientRenderBoundary,
completeBoundaryWithStyles,
completeBoundary,
completeSegment,
} from 'react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSet';
} from '../../../react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSet';
// Intentionally does nothing. Implementation will be added in future PR.
// eslint-disable-next-line no-unused-vars
+123
View File
@@ -0,0 +1,123 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
'use strict';
import * as tmp from 'tmp';
import * as fs from 'fs';
import replace from 'rollup-plugin-replace';
import {rollup} from 'rollup';
const rollupCache: Map<string, string | null> = new Map();
// Utility function to read and bundle a standalone browser script
async function getRollupResult(scriptSrc: string): Promise<string | null> {
const cachedResult = rollupCache.get(scriptSrc);
if (cachedResult !== undefined) {
return cachedResult;
}
let tmpFile;
try {
tmpFile = tmp.fileSync();
const rollupConfig = {
input: require.resolve(scriptSrc),
onwarn: console.warn,
plugins: [replace({__DEV__: 'true'})],
output: {
externalLiveBindings: false,
freeze: false,
interop: false,
esModule: false,
},
};
const outputConfig = {
file: tmpFile.name,
format: 'iife',
};
const bundle = await rollup(rollupConfig);
await bundle.write(outputConfig);
const bundleBuffer = Buffer.alloc(4096);
let bundleStr = '';
while (true) {
// $FlowFixMe[incompatible-call]
const bytes = fs.readSync(tmpFile.fd, bundleBuffer);
if (bytes <= 0) {
break;
}
bundleStr += bundleBuffer.slice(0, bytes).toString();
}
rollupCache.set(scriptSrc, bundleStr);
return bundleStr;
} catch (e) {
rollupCache.set(scriptSrc, null);
return null;
} finally {
if (tmpFile) {
tmpFile.removeCallback();
}
}
}
// Utility function to process received HTML nodes and execute
// embedded scripts by:
// 1. Matching nonce attributes and moving node into an existing
// parent container (if passed)
// 2. Resolving scripts with sources
async function replaceScriptsAndMove(
window: any,
CSPnonce: string | null,
node: Node,
parent: Node | null,
) {
if (
node.nodeType === 1 &&
(node.nodeName === 'SCRIPT' || node.nodeName === 'script')
) {
// $FlowFixMe[incompatible-cast]
const element = (node: HTMLElement);
const script = window.document.createElement('SCRIPT');
const scriptSrc = element.getAttribute('src');
if (scriptSrc) {
const rollupOutput = await getRollupResult(scriptSrc);
if (rollupOutput) {
// Manually call eval(...) here, since changing the HTML text content
// may interfere with hydration
window.eval(rollupOutput);
}
for (let i = 0; i < element.attributes.length; i++) {
const attr = element.attributes.item(i);
script.setAttribute(attr.name, attr.value);
}
} else if (element === null || element.getAttribute('nonce') === CSPnonce) {
script.textContent = node.textContent;
}
if (parent) {
element.parentNode?.removeChild(element);
parent.appendChild(script);
} else {
element.parentNode?.replaceChild(script, element);
}
} else {
for (let i = 0; i < node.childNodes.length; i++) {
const inner = node.childNodes[i];
await replaceScriptsAndMove(window, CSPnonce, inner, null);
}
if (parent != null) {
parent.appendChild(node);
}
}
}
function mergeOptions(options: Object, defaultOptions: Object): Object {
return {
...defaultOptions,
...options,
};
}
export {replaceScriptsAndMove, mergeOptions};
+2
View File
@@ -128,6 +128,8 @@ export const enableUseMemoCacheHook = __EXPERIMENTAL__;
export const enableUseEventHook = __EXPERIMENTAL__;
// Test in www before enabling in open source.
// Enables DOM-server to stream its instruction set as data-attributes
// (handled with an MutationObserver) instead of inline-scripts
export const enableFizzExternalRuntime = false;
// -----------------------------------------------------------------------------