Skip to content

Commit 1d2c855

Browse files
acdliterickhanlonii
authored andcommitted
Add option to load Fizz runtime from external file (#25499)
* Add feature flag for external Fizz runtime Only enabled for www for now * Add option to load Fizz runtime from external file When unstable_externalRuntimeSrc is provided, React will inject a script tag that points to the provided URL. Then, instead of emitting inline scripts, the Fizz stream will emit HTML nodes with data attributes that encode the instructions. The external runtime will detect these with a mutation observer and translate them into runtime commands. This part isn't implemented in this PR, though — all this does is set up the option to use an external runtime, and inject the script tag. The external runtime is injected at the same time as bootstrap scripts.
1 parent f2d94fd commit 1d2c855

14 files changed

+73
-0
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
enableFilterEmptyStringAttributesDOM,
2424
enableCustomElementPropertySupport,
2525
enableFloat,
26+
enableFizzExternalRuntime,
2627
} from 'shared/ReactFeatureFlags';
2728

2829
import type {
@@ -157,6 +158,7 @@ export function createResponseState(
157158
bootstrapScriptContent: string | void,
158159
bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
159160
bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
161+
externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
160162
): ResponseState {
161163
const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
162164
const inlineScriptWithNonce =
@@ -173,6 +175,29 @@ export function createResponseState(
173175
endInlineScript,
174176
);
175177
}
178+
if (enableFizzExternalRuntime) {
179+
if (externalRuntimeConfig !== undefined) {
180+
const src =
181+
typeof externalRuntimeConfig === 'string'
182+
? externalRuntimeConfig
183+
: externalRuntimeConfig.src;
184+
const integrity =
185+
typeof externalRuntimeConfig === 'string'
186+
? undefined
187+
: externalRuntimeConfig.integrity;
188+
bootstrapChunks.push(
189+
startScriptSrc,
190+
stringToChunk(escapeTextForBrowser(src)),
191+
);
192+
if (integrity) {
193+
bootstrapChunks.push(
194+
scriptIntegirty,
195+
stringToChunk(escapeTextForBrowser(integrity)),
196+
);
197+
}
198+
bootstrapChunks.push(endAsyncScript);
199+
}
200+
}
176201
if (bootstrapScripts !== undefined) {
177202
for (let i = 0; i < bootstrapScripts.length; i++) {
178203
const scriptConfig = bootstrapScripts[i];

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,6 +3546,36 @@ describe('ReactDOMFizzServer', () => {
35463546
});
35473547
});
35483548

3549+
// @gate enableFizzExternalRuntime
3550+
it('supports option to load runtime as an external script', async () => {
3551+
await actIntoEmptyDocument(() => {
3552+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
3553+
<html>
3554+
<head />
3555+
<body>
3556+
<div>hello world</div>
3557+
</body>
3558+
</html>,
3559+
{
3560+
unstable_externalRuntimeSrc: 'src-of-external-runtime',
3561+
},
3562+
);
3563+
pipe(writable);
3564+
});
3565+
3566+
expect(getVisibleChildren(document)).toEqual(
3567+
<html>
3568+
<head />
3569+
<body>
3570+
<div>hello world</div>
3571+
</body>
3572+
</html>,
3573+
);
3574+
expect(
3575+
Array.from(document.getElementsByTagName('script')).map(n => n.outerHTML),
3576+
).toEqual(['<script src="src-of-external-runtime" async=""></script>']);
3577+
});
3578+
35493579
it('#24384: Suspending should halt hydration warnings and not emit any if hydration completes successfully after unsuspending', async () => {
35503580
const makeApp = () => {
35513581
let resolve, resolved;

packages/react-dom/src/server/ReactDOMFizzServerBrowser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Options = {
3434
progressiveChunkSize?: number,
3535
signal?: AbortSignal,
3636
onError?: (error: mixed) => ?string,
37+
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
3738
};
3839

3940
// TODO: Move to sub-classing ReadableStream.
@@ -86,6 +87,7 @@ function renderToReadableStream(
8687
options ? options.bootstrapScriptContent : undefined,
8788
options ? options.bootstrapScripts : undefined,
8889
options ? options.bootstrapModules : undefined,
90+
options ? options.unstable_externalRuntimeSrc : undefined,
8991
),
9092
createRootFormatContext(options ? options.namespaceURI : undefined),
9193
options ? options.progressiveChunkSize : undefined,

packages/react-dom/src/server/ReactDOMFizzServerNode.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Options = {
4747
onShellError?: (error: mixed) => void,
4848
onAllReady?: () => void,
4949
onError?: (error: mixed) => ?string,
50+
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
5051
};
5152

5253
type PipeableStream = {
@@ -65,6 +66,7 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
6566
options ? options.bootstrapScriptContent : undefined,
6667
options ? options.bootstrapScripts : undefined,
6768
options ? options.bootstrapModules : undefined,
69+
options ? options.unstable_externalRuntimeSrc : undefined,
6870
),
6971
createRootFormatContext(options ? options.namespaceURI : undefined),
7072
options ? options.progressiveChunkSize : undefined,

packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Options = {
3333
progressiveChunkSize?: number,
3434
signal?: AbortSignal,
3535
onError?: (error: mixed) => ?string,
36+
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
3637
};
3738

3839
type StaticResult = {
@@ -71,6 +72,7 @@ function prerender(
7172
options ? options.bootstrapScriptContent : undefined,
7273
options ? options.bootstrapScripts : undefined,
7374
options ? options.bootstrapModules : undefined,
75+
options ? options.unstable_externalRuntimeSrc : undefined,
7476
),
7577
createRootFormatContext(options ? options.namespaceURI : undefined),
7678
options ? options.progressiveChunkSize : undefined,

packages/react-dom/src/server/ReactDOMFizzStaticNode.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Options = {
3535
progressiveChunkSize?: number,
3636
signal?: AbortSignal,
3737
onError?: (error: mixed) => ?string,
38+
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
3839
};
3940

4041
type StaticResult = {
@@ -86,6 +87,7 @@ function prerenderToNodeStreams(
8687
options ? options.bootstrapScriptContent : undefined,
8788
options ? options.bootstrapScripts : undefined,
8889
options ? options.bootstrapModules : undefined,
90+
options ? options.unstable_externalRuntimeSrc : undefined,
8991
),
9092
createRootFormatContext(options ? options.namespaceURI : undefined),
9193
options ? options.progressiveChunkSize : undefined,

packages/shared/ReactFeatureFlags.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ export const enableUseMemoCacheHook = __EXPERIMENTAL__;
125125

126126
export const enableUseEventHook = __EXPERIMENTAL__;
127127

128+
// Test in www before enabling in open source.
129+
export const enableFizzExternalRuntime = false;
130+
128131
// -----------------------------------------------------------------------------
129132
// Chopping Block
130133
//

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export const enableFloat = false;
8686
export const enableHostSingletons = false;
8787

8888
export const useModernStrictMode = false;
89+
export const enableFizzExternalRuntime = false;
8990

9091
// Flow magic to verify the exports of this file match the original version.
9192
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export const enableFloat = false;
7676
export const enableHostSingletons = false;
7777

7878
export const useModernStrictMode = false;
79+
export const enableFizzExternalRuntime = false;
7980

8081
// Flow magic to verify the exports of this file match the original version.
8182
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export const enableFloat = false;
7676
export const enableHostSingletons = false;
7777

7878
export const useModernStrictMode = false;
79+
export const enableFizzExternalRuntime = false;
7980

8081
// Flow magic to verify the exports of this file match the original version.
8182
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export const enableFloat = false;
7878
export const enableHostSingletons = false;
7979

8080
export const useModernStrictMode = false;
81+
export const enableFizzExternalRuntime = false;
8182

8283
// Flow magic to verify the exports of this file match the original version.
8384
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.testing.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export const enableFloat = false;
7676
export const enableHostSingletons = false;
7777

7878
export const useModernStrictMode = false;
79+
export const enableFizzExternalRuntime = false;
7980

8081
// Flow magic to verify the exports of this file match the original version.
8182
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.testing.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export const enableFloat = false;
7777
export const enableHostSingletons = false;
7878

7979
export const useModernStrictMode = false;
80+
export const enableFizzExternalRuntime = false;
8081

8182
// Flow magic to verify the exports of this file match the original version.
8283
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export const enableUseMutableSource = true;
113113
export const enableCustomElementPropertySupport = __EXPERIMENTAL__;
114114

115115
export const useModernStrictMode = false;
116+
export const enableFizzExternalRuntime = true;
116117

117118
// Flow magic to verify the exports of this file match the original version.
118119
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

0 commit comments

Comments
 (0)