Remove ReactFlightFB bundles (#28864)

Remove ReactFlightFB bundles
This commit is contained in:
Jan Kassens
2024-04-18 16:41:04 -04:00
committed by GitHub
parent 1f0701c11e
commit 1cd77a4ff7
12 changed files with 2 additions and 878 deletions

View File

@@ -1,15 +0,0 @@
/**
* 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
*/
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
export * from 'react-client/src/ReactFlightClientConsoleConfigPlain';
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
export * from 'react-server-dom-fb/src/ReactFlightClientConfigFBBundler';
export const usedWithSSR = false;

View File

@@ -1,112 +0,0 @@
/**
* 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
*/
import type {
Thenable,
FulfilledThenable,
RejectedThenable,
} from 'shared/ReactTypes';
export type ModuleLoading = mixed;
type ResolveClientReferenceFn<T> =
ClientReferenceMetadata => ClientReference<T>;
export type SSRModuleMap = {
resolveClientReference?: ResolveClientReferenceFn<any>,
};
export type ServerManifest = string;
export type {
ClientManifest,
ServerReferenceId,
ClientReferenceMetadata,
} from './ReactFlightReferencesFB';
import type {
ServerReferenceId,
ClientReferenceMetadata,
} from './ReactFlightReferencesFB';
export type ClientReference<T> = {
getModuleId: () => string,
load: () => Thenable<T>,
};
export function prepareDestinationForModule(
moduleLoading: ModuleLoading,
nonce: ?string,
metadata: ClientReferenceMetadata,
) {
return;
}
export function resolveClientReference<T>(
moduleMap: SSRModuleMap,
metadata: ClientReferenceMetadata,
): ClientReference<T> {
if (typeof moduleMap.resolveClientReference === 'function') {
return moduleMap.resolveClientReference(metadata);
} else {
throw new Error(
'Expected `resolveClientReference` to be defined on the moduleMap.',
);
}
}
export function resolveServerReference<T>(
config: ServerManifest,
id: ServerReferenceId,
): ClientReference<T> {
throw new Error('Not implemented');
}
const asyncModuleCache: Map<string, Thenable<any>> = new Map();
export function preloadModule<T>(
clientReference: ClientReference<T>,
): null | Thenable<any> {
const existingPromise = asyncModuleCache.get(clientReference.getModuleId());
if (existingPromise) {
if (existingPromise.status === 'fulfilled') {
return null;
}
return existingPromise;
} else {
const modulePromise: Thenable<T> = clientReference.load();
modulePromise.then(
value => {
const fulfilledThenable: FulfilledThenable<mixed> =
(modulePromise: any);
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
},
reason => {
const rejectedThenable: RejectedThenable<mixed> = (modulePromise: any);
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
},
);
asyncModuleCache.set(clientReference.getModuleId(), modulePromise);
return modulePromise;
}
}
export function requireModule<T>(clientReference: ClientReference<T>): T {
let module;
// We assume that preloadModule has been called before, which
// should have added something to the module cache.
const promise: any = asyncModuleCache.get(clientReference.getModuleId());
if (promise.status === 'fulfilled') {
module = promise.value;
} else {
throw promise.reason;
}
// We are currently only support default exports for client components
return module;
}

View File

@@ -1,91 +0,0 @@
/**
* 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
*/
import {enableBinaryFlight} from 'shared/ReactFeatureFlags';
import type {Thenable} from 'shared/ReactTypes';
import type {Response as FlightResponse} from 'react-client/src/ReactFlightClient';
import {
createResponse,
getRoot,
reportGlobalError,
processBinaryChunk,
close,
} from 'react-client/src/ReactFlightClient';
import type {SSRModuleMap} from './ReactFlightClientConfigFBBundler';
type Options = {
moduleMap: SSRModuleMap,
};
function createResponseFromOptions(options: void | Options) {
const moduleMap = options && options.moduleMap;
if (moduleMap == null) {
throw new Error('Expected `moduleMap` to be defined.');
}
return createResponse(moduleMap, null, undefined, undefined);
}
function processChunk(response: FlightResponse, chunk: string | Uint8Array) {
if (enableBinaryFlight) {
if (typeof chunk === 'string') {
throw new Error(
'`enableBinaryFlight` flag is enabled, expected a Uint8Array as input, got string.',
);
}
}
const buffer = typeof chunk !== 'string' ? chunk : encodeString(chunk);
processBinaryChunk(response, buffer);
}
function encodeString(string: string) {
const textEncoder = new TextEncoder();
return textEncoder.encode(string);
}
function startReadingFromStream(
response: FlightResponse,
stream: ReadableStream,
): void {
const reader = stream.getReader();
function progress({
done,
value,
}: {
done: boolean,
value: ?any,
...
}): void | Promise<void> {
if (done) {
close(response);
return;
}
const buffer: Uint8Array = (value: any);
processChunk(response, buffer);
return reader.read().then(progress).catch(error);
}
function error(e: any) {
reportGlobalError(response, e);
}
reader.read().then(progress).catch(error);
}
function createFromReadableStream<T>(
stream: ReadableStream,
options?: Options,
): Thenable<T> {
const response: FlightResponse = createResponseFromOptions(options);
startReadingFromStream(response, stream);
return getRoot(response);
}
export {createFromReadableStream};

View File

@@ -1,73 +0,0 @@
/**
* 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
*/
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
import type {
Destination,
Chunk,
PrecomputedChunk,
} from 'react-server/src/ReactServerStreamConfig';
import {setCheckIsClientReference} from './ReactFlightReferencesFB';
import {
createRequest,
startWork,
startFlowing,
} from 'react-server/src/ReactFlightServer';
import {setByteLengthOfChunkImplementation} from 'react-server/src/ReactServerStreamConfig';
export {
registerClientReference,
registerServerReference,
getRequestedClientReferencesKeys,
clearRequestedClientReferencesKeysSet,
setCheckIsClientReference,
} from './ReactFlightReferencesFB';
type Options = {
onError?: (error: mixed) => void,
};
function renderToDestination(
destination: Destination,
model: ReactClientValue,
options?: Options,
): void {
if (!configured) {
throw new Error(
'Please make sure to call `setConfig(...)` before calling `renderToDestination`.',
);
}
const request = createRequest(
model,
null,
options ? options.onError : undefined,
undefined,
undefined,
);
startWork(request);
startFlowing(request, destination);
}
type Config = {
byteLength: (chunk: Chunk | PrecomputedChunk) => number,
isClientReference: (reference: mixed) => boolean,
};
let configured = false;
function setConfig(config: Config): void {
setByteLengthOfChunkImplementation(config.byteLength);
setCheckIsClientReference(config.isClientReference);
configured = true;
}
export {renderToDestination, setConfig};

View File

@@ -1,90 +0,0 @@
/**
* 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
*/
export type ClientManifest = null;
// eslint-disable-next-line no-unused-vars
export type ServerReference<T> = string;
// eslint-disable-next-line no-unused-vars
export type ClientReference<T> = {
getModuleId(): ClientReferenceKey,
};
const requestedClientReferencesKeys = new Set<ClientReferenceKey>();
export type ClientReferenceKey = string;
export type ClientReferenceMetadata = {
moduleId: ClientReferenceKey,
exportName: string,
};
export type ServerReferenceId = string;
let checkIsClientReference: (clientReference: mixed) => boolean;
export function setCheckIsClientReference(
impl: (clientReference: mixed) => boolean,
): void {
checkIsClientReference = impl;
}
export function registerClientReference<T>(
clientReference: ClientReference<T>,
): void {}
export function isClientReference(reference: mixed): boolean {
if (checkIsClientReference == null) {
throw new Error('Expected implementation for checkIsClientReference.');
}
return checkIsClientReference(reference);
}
export function getClientReferenceKey<T>(
clientReference: ClientReference<T>,
): ClientReferenceKey {
const moduleId = clientReference.getModuleId();
requestedClientReferencesKeys.add(moduleId);
return clientReference.getModuleId();
}
export function resolveClientReferenceMetadata<T>(
config: ClientManifest,
clientReference: ClientReference<T>,
): ClientReferenceMetadata {
return {moduleId: clientReference.getModuleId(), exportName: 'default'};
}
export function registerServerReference<T>(
serverReference: ServerReference<T>,
id: string,
exportName: null | string,
): ServerReference<T> {
throw new Error('registerServerReference: Not Implemented.');
}
export function isServerReference<T>(reference: T): boolean {
throw new Error('isServerReference: Not Implemented.');
}
export function getServerReferenceId<T>(
config: ClientManifest,
serverReference: ServerReference<T>,
): ServerReferenceId {
throw new Error('getServerReferenceId: Not Implemented.');
}
export function getRequestedClientReferencesKeys(): $ReadOnlyArray<ClientReferenceKey> {
return Array.from(requestedClientReferencesKeys);
}
export function clearRequestedClientReferencesKeysSet(): void {
requestedClientReferencesKeys.clear();
}

View File

@@ -1,36 +0,0 @@
/**
* 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
*/
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
import type {ClientManifest, ServerReference} from './ReactFlightReferencesFB';
export type {
ClientManifest,
ClientReference,
ClientReferenceKey,
ClientReferenceMetadata,
ServerReference,
ServerReferenceId,
} from './ReactFlightReferencesFB';
export {
getClientReferenceKey,
isClientReference,
resolveClientReferenceMetadata,
isServerReference,
getServerReferenceId,
} from './ReactFlightReferencesFB';
export function getServerReferenceBoundArguments<T>(
config: ClientManifest,
serverReference: ServerReference<T>,
): null | Array<ReactClientValue> {
throw new Error('getServerReferenceBoundArguments: Not Implemented.');
}

View File

@@ -1,371 +0,0 @@
/**
* 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.
*
* @emails react-core
*/
'use strict';
// Polyfills for test environment
global.ReadableStream =
require('web-streams-polyfill/ponyfill/es6').ReadableStream;
global.TextEncoder = require('util').TextEncoder;
global.TextDecoder = require('util').TextDecoder;
// Don't wait before processing work on the server.
// TODO: we can replace this with FlightServer.act().
global.setImmediate = cb => cb();
let act;
let use;
let clientExports;
let moduleMap;
let React;
let ReactDOMClient;
let ReactServerDOMServer;
let ReactServerDOMClient;
let Suspense;
class Destination {
#buffer = '';
#controller = null;
constructor() {
const self = this;
this.stream = new ReadableStream({
start(controller) {
self.#controller = controller;
},
});
}
write(chunk) {
this.#buffer += chunk;
}
beginWriting() {}
completeWriting() {}
flushBuffered() {
if (!this.#controller) {
throw new Error('Expected a controller.');
}
this.#controller.enqueue(this.#buffer);
this.#buffer = '';
}
close() {}
onError() {}
}
class ClientReferenceImpl {
constructor(moduleId) {
this.moduleId = moduleId;
}
getModuleId() {
return this.moduleId;
}
}
describe('ReactFlightDOM for FB', () => {
beforeEach(() => {
// For this first reset we are going to load the dom-node version of react-server-dom-turbopack/server
// This can be thought of as essentially being the React Server Components scope with react-server
// condition
jest.resetModules();
jest.mock('react', () => require('react/src/ReactServerFB'));
jest.mock('shared/ReactFeatureFlags', () => {
jest.mock(
'ReactFeatureFlags',
() => jest.requireActual('shared/forks/ReactFeatureFlags.www-dynamic'),
{virtual: true},
);
return jest.requireActual('shared/forks/ReactFeatureFlags.www');
});
clientExports = value => {
return new ClientReferenceImpl(value.name);
};
moduleMap = {
resolveClientReference(metadata) {
throw new Error('Do not expect to load client components.');
},
};
ReactServerDOMServer = require('../ReactFlightDOMServerFB');
ReactServerDOMServer.setConfig({
byteLength: str => Buffer.byteLength(str),
isClientReference: reference => reference instanceof ClientReferenceImpl,
});
// This reset is to load modules for the SSR/Browser scope.
jest.resetModules();
__unmockReact();
act = require('internal-test-utils').act;
React = require('react');
use = React.use;
Suspense = React.Suspense;
ReactDOMClient = require('react-dom/client');
ReactServerDOMClient = require('../ReactFlightDOMClientFB');
});
it('should resolve HTML with renderToDestination', async () => {
function Text({children}) {
return <span>{children}</span>;
}
function HTML() {
return (
<div>
<Text>hello</Text>
<Text>world</Text>
</div>
);
}
function App() {
const model = {
html: <HTML />,
};
return model;
}
const destination = new Destination();
ReactServerDOMServer.renderToDestination(destination, <App />);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap,
},
);
const model = await response;
expect(model).toEqual({
html: (
<div>
<span>hello</span>
<span>world</span>
</div>
),
});
});
it('should resolve the root', async () => {
// Model
function Text({children}) {
return <span>{children}</span>;
}
function HTML() {
return (
<div>
<Text>hello</Text>
<Text>world</Text>
</div>
);
}
function RootModel() {
return {
html: <HTML />,
};
}
// View
function Message({response}) {
return <section>{use(response).html}</section>;
}
function App({response}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Message response={response} />
</Suspense>
);
}
const destination = new Destination();
ReactServerDOMServer.renderToDestination(destination, <RootModel />);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap,
},
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<App response={response} />);
});
expect(container.innerHTML).toBe(
'<section><div><span>hello</span><span>world</span></div></section>',
);
});
it('should not get confused by $', async () => {
// Model
function RootModel() {
return {text: '$1'};
}
// View
function Message({response}) {
return <p>{use(response).text}</p>;
}
function App({response}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Message response={response} />
</Suspense>
);
}
const destination = new Destination();
ReactServerDOMServer.renderToDestination(destination, <RootModel />);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap,
},
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<App response={response} />);
});
expect(container.innerHTML).toBe('<p>$1</p>');
});
it('should not get confused by @', async () => {
// Model
function RootModel() {
return {text: '@div'};
}
// View
function Message({response}) {
return <p>{use(response).text}</p>;
}
function App({response}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Message response={response} />
</Suspense>
);
}
const destination = new Destination();
ReactServerDOMServer.renderToDestination(destination, <RootModel />);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap,
},
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<App response={response} />);
});
expect(container.innerHTML).toBe('<p>@div</p>');
});
it('should be able to render a client component', async () => {
const Component = function ({greeting}) {
return greeting + ' World';
};
function Print({response}) {
return <p>{use(response)}</p>;
}
function App({response}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Print response={response} />
</Suspense>
);
}
const ClientComponent = clientExports(Component);
const destination = new Destination();
ReactServerDOMServer.renderToDestination(
destination,
<ClientComponent greeting={'Hello'} />,
moduleMap,
);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap: {
resolveClientReference(metadata) {
return {
getModuleId() {
return metadata.moduleId;
},
load() {
return Promise.resolve(Component);
},
};
},
},
},
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<App response={response} />);
});
expect(container.innerHTML).toBe('<p>Hello World</p>');
});
it('should render long strings', async () => {
// Model
const longString = 'Lorem Ipsum ❤️ '.repeat(100);
function RootModel() {
return {text: longString};
}
// View
function Message({response}) {
return <p>{use(response).text}</p>;
}
function App({response}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Message response={response} />
</Suspense>
);
}
const destination = new Destination();
ReactServerDOMServer.renderToDestination(destination, <RootModel />);
const response = ReactServerDOMClient.createFromReadableStream(
destination.stream,
{
moduleMap,
},
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<App response={response} />);
});
expect(container.innerHTML).toBe('<p>' + longString + '</p>');
});
// TODO: `registerClientComponent` need to be able to support this
it.skip('throws when accessing a member below the client exports', () => {
const ClientModule = clientExports({
Component: {deep: 'thing'},
});
function dotting() {
return ClientModule.Component.deep;
}
expect(dotting).toThrowError(
'Cannot access Component.deep on the server. ' +
'You cannot dot into a client module from a server component. ' +
'You can only pass the imported name through.',
);
});
});

View File

@@ -1,18 +0,0 @@
/**
* 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
*/
import type {Request} from 'react-server/src/ReactFlightServer';
export * from 'react-server-dom-fb/src/ReactFlightServerConfigFBBundler';
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
export const supportsRequestStorage = false;
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
export * from '../ReactFlightServerConfigDebugNoop';

View File

@@ -1,11 +0,0 @@
/**
* 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
*/
export * from './ReactServer';
export {jsx, jsxs, jsxDEV} from './jsx/ReactJSX';

View File

@@ -96,18 +96,6 @@ const bundles = [
externals: [],
},
/******* Isomorphic Shared Subset for FB *******/
{
bundleTypes: __EXPERIMENTAL__ ? [FB_WWW_DEV, FB_WWW_PROD] : [],
moduleType: ISOMORPHIC,
entry: 'react/src/ReactServerFB.js',
condition: 'react-server',
global: 'ReactServer',
minifyWithProdErrorCodes: true,
wrapWithModuleBoundaries: false,
externals: [],
},
/******* React JSX Runtime *******/
{
bundleTypes: [
@@ -596,29 +584,6 @@ const bundles = [
externals: ['acorn'],
},
/******* React Server DOM FB Server *******/
{
bundleTypes: __EXPERIMENTAL__ ? [FB_WWW_DEV, FB_WWW_PROD] : [],
moduleType: RENDERER,
entry: 'react-server-dom-fb/src/ReactFlightDOMServerFB.js',
condition: 'react-server',
global: 'ReactFlightDOMServer',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'react-dom'],
},
/******* React Server DOM FB Client *******/
{
bundleTypes: __EXPERIMENTAL__ ? [FB_WWW_DEV, FB_WWW_PROD] : [],
moduleType: RENDERER,
entry: 'react-server-dom-fb/src/ReactFlightDOMClientFB.js',
global: 'ReactFlightDOMClient',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'react-dom'],
},
/******* React Suspense Test Utils *******/
{
bundleTypes: [NODE_ES2015],

View File

@@ -62,10 +62,7 @@ const forks = Object.freeze({
if (entry === 'react') {
return './packages/react/src/ReactSharedInternalsClient.js';
}
if (
entry === 'react/src/ReactServer.js' ||
entry === 'react/src/ReactServerFB.js'
) {
if (entry === 'react/src/ReactServer.js') {
return './packages/react/src/ReactSharedInternalsServer.js';
}
if (bundle.condition === 'react-server') {

View File

@@ -405,34 +405,13 @@ module.exports = [
'react-dom',
'react-dom/src/ReactDOMServer.js',
'react-dom-bindings',
'react-server-dom-fb/src/ReactDOMServerFB.js',
'react-server-dom-fb',
'shared/ReactDOMSharedInternals',
],
isFlowTyped: true,
isServerSupported: true,
isFlightSupported: false,
},
{
shortName: 'dom-fb-experimental',
entryPoints: [
'react-server-dom-fb/src/ReactFlightDOMClientFB.js',
'react-server-dom-fb/src/ReactFlightDOMServerFB.js',
],
paths: [
'react-dom',
'react-dom-bindings',
'react-server-dom-fb/src/ReactFlightClientConfigFBBundler.js',
'react-server-dom-fb/src/ReactFlightClientConfigFBBundler.js',
'react-server-dom-fb/src/ReactFlightReferencesFB.js',
'react-server-dom-fb/src/ReactFlightServerConfigFBBundler.js',
'react-server-dom-fb/src/ReactFlightDOMClientFB.js',
'react-server-dom-fb/src/ReactFlightDOMServerFB.js',
'shared/ReactDOMSharedInternals',
],
isFlowTyped: true,
isServerSupported: true,
isFlightSupported: true,
},
{
shortName: 'native',
entryPoints: ['react-native-renderer'],