Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion types/react-dom/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,54 @@ declare global {
namespace NodeJS {
// tslint:disable-next-line:no-empty-interface
interface ReadableStream {}

// tslint:disable-next-line:no-empty-interface
interface WritableStream {}
}

/**
* Stub for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
*/
// tslint:disable-next-line:no-empty-interface
interface AbortSignal {}

/**
* Stub for https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
*/
// tslint:disable-next-line:no-empty-interface
interface ReadableStream {}
}

import { ReactElement } from 'react';
import { ReactElement, ReactNode } from 'react';

export interface RenderToPipeableStreamOptions {
identifierPrefix?: string;
namespaceURI?: string;
nonce?: string;
bootstrapScriptContent?: string;
bootstrapScripts?: string[];
bootstrapModules?: string[];
progressiveChunkSize?: number;
onShellReady?: () => void;
onShellError?: (error: unknown) => void;
onAllReady?: () => void;
onError?: (error: unknown) => void;
}

export interface PipeableStream {
abort(): void;
pipe<Writable extends NodeJS.WritableStream>(destination: Writable): Writable;
}

/**
* Only available in the environments with [Node.js Streams](https://nodejs.dev/learn/nodejs-streams).
*
* @see [API](https://reactjs.org/docs/react-dom-server.html#rendertopipeablestream)
*
* @param children
* @param options
*/
export function renderToPipeableStream(children: ReactNode, options?: RenderToPipeableStreamOptions): PipeableStream;

/**
* Render a React element to its initial HTML. This should only be used on the server.
Expand All @@ -24,6 +68,8 @@ export function renderToString(element: ReactElement): string;
* Render a React element to its initial HTML. Returns a Readable stream that outputs
* an HTML string. The HTML output by this stream is exactly equal to what
* `ReactDOMServer.renderToString()` would return.
*
* @deprecated
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/
export function renderToNodeStream(element: ReactElement): NodeJS.ReadableStream;

Expand All @@ -42,6 +88,32 @@ export function renderToStaticMarkup(element: ReactElement): string;
*/
export function renderToStaticNodeStream(element: ReactElement): NodeJS.ReadableStream;

export interface RenderToReadableStreamOptions {
identifierPrefix?: string;
namespaceURI?: string;
nonce?: string;
bootstrapScriptContent?: string;
bootstrapScripts?: string[];
bootstrapModules?: string[];
progressiveChunkSize?: number;
signal?: AbortSignal;
onError?: (error: unknown) => void;
}

export interface ReactDOMServerReadableStream extends ReadableStream {
allReady: Promise<void>;
}

/**
* Only available in the environments with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) (this includes browsers, Deno, and some modern edge runtimes).
*
* @see [API](https://reactjs.org/docs/react-dom-server.html#rendertoreadablestream)
*/
export function renderToReadableStream(
children: ReactNode,
options?: RenderToReadableStreamOptions,
): Promise<ReactDOMServerReadableStream>;

export const version: string;

export as namespace ReactDOMServer;
81 changes: 73 additions & 8 deletions types/react-dom/test/react-dom-tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as ReactTestUtils from 'react-dom/test-utils';
declare function describe(desc: string, f: () => void): void;
declare function it(desc: string, f: () => void): void;

class TestComponent extends React.Component<{x: string}> { }
class TestComponent extends React.Component<{ x: string }> {}

describe('ReactDOM', () => {
it('render', () => {
Expand Down Expand Up @@ -94,15 +94,12 @@ describe('ReactDOMServer', () => {
describe('React dom test utils', () => {
it('Simulate', () => {
const element = document.createElement('div');
const dom = ReactDOM.render(
React.createElement('input', { type: 'text' }),
element
) as Element;
const dom = ReactDOM.render(React.createElement('input', { type: 'text' }), element) as Element;
const node = ReactDOM.findDOMNode(dom) as HTMLInputElement;

node.value = 'giraffe';
ReactTestUtils.Simulate.change(node);
ReactTestUtils.Simulate.keyDown(node, { key: "Enter", keyCode: 13, which: 13 });
ReactTestUtils.Simulate.keyDown(node, { key: 'Enter', keyCode: 13, which: 13 });
});

it('renderIntoDocument', () => {
Expand Down Expand Up @@ -209,7 +206,7 @@ describe('React dom test utils', () => {
// tslint:disable-next-line no-void-expression
const result = ReactTestUtils.act(() => {});
// $ExpectError
result.then((x) => {});
result.then(x => {});
});
});
describe('with async callback', () => {
Expand All @@ -222,7 +219,7 @@ describe('React dom test utils', () => {
});
it('returns a Promise-like', () => {
const result = ReactTestUtils.act(async () => {});
result.then((x) => {});
result.then(x => {});
});
});
});
Expand Down Expand Up @@ -252,3 +249,71 @@ function hydrateRoot() {
identifierPrefix: 'react-18-app',
});
}

/**
* source:
*/
function pipeableStreamDocumentedExample() {
function App() {
return null;
}

interface Response extends NodeJS.WritableStream {
send(content: string): void;
setHeader(key: string, value: unknown): void;
statusCode: number;
}

let didError = false;
const res: Response = {} as any;
const stream = ReactDOMServer.renderToPipeableStream(<App />, {
onShellReady() {
res.statusCode = didError ? 500 : 200;
res.setHeader('Content-type', 'text/html');
stream.pipe(res);
},
onShellError(error) {
res.statusCode = 500;
res.send('<!doctype html><p>Loading...</p><script src="clientrender.js"></script>');
},
onAllReady() {},
onError(err) {
didError = true;
console.error(err);
},
});
}

/**
* source: https://reactjs.org/docs/react-dom-server.html#rendertoreadablestream
*/
async function readableStreamDocumentedExample() {
const controller = new AbortController();
let didError = false;
try {
const stream = await ReactDOMServer.renderToReadableStream(
<html>
<body>Success</body>
</html>,
{
signal: controller.signal,
onError(error) {
didError = true;
console.error(error);
},
},
);

await stream.allReady;

return new Response(stream, {
status: didError ? 500 : 200,
headers: { 'Content-Type': 'text/html' },
});
} catch (error) {
return new Response('<!doctype html><p>Loading...</p><script src="clientrender.js"></script>', {
status: 500,
headers: { 'Content-Type': 'text/html' },
});
}
}