react-streaming
Advanced tools
Comparing version 0.3.19 to 0.3.20
export { renderToStream }; | ||
export { disable }; | ||
export { renderToNodeStream_set }; | ||
import React from 'react'; | ||
import type { renderToPipeableStream as RenderToPipeableStream, renderToReadableStream as RenderToReadableStream } from 'react-dom/server'; | ||
import { Pipe } from './renderToStream/createPipeWrapper'; | ||
import type { Pipe } from './renderToStream/createPipeWrapper'; | ||
import { SeoStrategy } from './renderToStream/resolveSeoStrategy'; | ||
import type { renderToNodeStream as renderToNodeStream_ } from './renderToStream/renderToNodeStream'; | ||
declare type Options = { | ||
@@ -29,1 +31,2 @@ webStream?: boolean; | ||
declare function renderToStream(element: React.ReactNode, options?: Options): Promise<Result>; | ||
declare function renderToNodeStream_set(renderToNodeStream: typeof renderToNodeStream_): void; |
@@ -29,3 +29,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.disable = exports.renderToStream = void 0; | ||
exports.renderToNodeStream_set = exports.disable = exports.renderToStream = void 0; | ||
const react_1 = __importDefault(require("react")); | ||
@@ -35,9 +35,9 @@ const server_1 = __importStar(require("react-dom/server")); | ||
const useStream_1 = require("./useStream"); | ||
const createPipeWrapper_1 = require("./renderToStream/createPipeWrapper"); | ||
const createReadableWrapper_1 = require("./renderToStream/createReadableWrapper"); | ||
const resolveSeoStrategy_1 = require("./renderToStream/resolveSeoStrategy"); | ||
const utils_1 = require("./utils"); | ||
const loadNodeStreamModule_1 = require("./renderToStream/loadNodeStreamModule"); | ||
const import_1 = __importDefault(require("@brillout/import")); | ||
const debug = (0, utils_1.createDebugger)('react-streaming:flow'); | ||
const renderToWebStream_1 = require("./renderToStream/renderToWebStream"); | ||
const misc_1 = require("./renderToStream/misc"); | ||
const globalObject = (0, utils_1.getGlobalObject)('renderToStream.ts', { | ||
renderToNodeStream: null | ||
}); | ||
assertReact(); | ||
@@ -65,11 +65,11 @@ const globalConfig = (globalThis.__react_streaming = globalThis | ||
const disable = globalConfig.disable || ((_a = options.disable) !== null && _a !== void 0 ? _a : (0, resolveSeoStrategy_1.resolveSeoStrategy)(options).disableStream); | ||
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !(await (0, loadNodeStreamModule_1.nodeStreamModuleIsAvailable)()); | ||
debug(`disable === ${disable} && webStream === ${webStream}`); | ||
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !globalObject.renderToNodeStream; | ||
(0, misc_1.debugFlow)(`disable === ${disable} && webStream === ${webStream}`); | ||
let result; | ||
const resultPartial = { disabled: disable }; | ||
if (!webStream) { | ||
result = Object.assign(Object.assign({}, resultPartial), (await renderToNodeStream(element, disable, options))); | ||
result = Object.assign(Object.assign({}, resultPartial), (await globalObject.renderToNodeStream(element, disable, options))); | ||
} | ||
else { | ||
result = Object.assign(Object.assign({}, resultPartial), (await renderToWebStream(element, disable, options))); | ||
result = Object.assign(Object.assign({}, resultPartial), (await (0, renderToWebStream_1.renderToWebStream)(element, disable, options))); | ||
} | ||
@@ -79,141 +79,10 @@ injectToStream = result.injectToStream; | ||
buffer.length = 0; | ||
debug('promise `await renderToStream()` resolved'); | ||
(0, misc_1.debugFlow)('promise `await renderToStream()` resolved'); | ||
return result; | ||
} | ||
exports.renderToStream = renderToStream; | ||
async function renderToNodeStream(element, disable, options) { | ||
var _a; | ||
debug('creating Node.js Stream Pipe'); | ||
let onAllReady; | ||
const allReady = new Promise((r) => { | ||
onAllReady = () => r(); | ||
}); | ||
let onShellReady; | ||
const shellReady = new Promise((r) => { | ||
onShellReady = () => r(); | ||
}); | ||
let didError = false; | ||
let firstErr = null; | ||
let reactBug = null; | ||
const onError = (err) => { | ||
debug('[react] onError() / onShellError()'); | ||
didError = true; | ||
firstErr !== null && firstErr !== void 0 ? firstErr : (firstErr = err); | ||
onShellReady(); | ||
afterReactBugCatch(() => { | ||
var _a; | ||
// Is not a React internal error (i.e. a React bug) | ||
if (err !== reactBug) { | ||
(_a = options.onBoundaryError) === null || _a === void 0 ? void 0 : _a.call(options, err); | ||
} | ||
}); | ||
}; | ||
const renderToPipeableStream = (_a = options.renderToPipeableStream) !== null && _a !== void 0 ? _a : | ||
// We don't directly use import() because it shouldn't be bundled for Cloudflare Workers: the module react-dom/server.node contains a require('stream') which fails on Cloudflare Workers | ||
(await (0, import_1.default)('react-dom/server.node')).renderToPipeableStream; | ||
assertReactImport(renderToPipeableStream, 'renderToPipeableStream'); | ||
const { pipe: pipeOriginal } = renderToPipeableStream(element, { | ||
onShellReady() { | ||
debug('[react] onShellReady()'); | ||
onShellReady(); | ||
}, | ||
onAllReady() { | ||
debug('[react] onAllReady()'); | ||
onShellReady(); | ||
onAllReady(); | ||
}, | ||
onShellError: onError, | ||
onError | ||
}); | ||
let promiseResolved = false; | ||
const { pipeForUser, injectToStream, streamEnd } = await (0, createPipeWrapper_1.createPipeWrapper)(pipeOriginal, { | ||
onReactBug(err) { | ||
debug('react bug'); | ||
didError = true; | ||
firstErr !== null && firstErr !== void 0 ? firstErr : (firstErr = err); | ||
reactBug = err; | ||
// Only log if it wasn't used as rejection for `await renderToStream()` | ||
if (reactBug !== firstErr || promiseResolved) { | ||
console.error(reactBug); | ||
} | ||
} | ||
}); | ||
await shellReady; | ||
if (didError) | ||
throw firstErr; | ||
if (disable) | ||
await allReady; | ||
if (didError) | ||
throw firstErr; | ||
promiseResolved = true; | ||
return { | ||
pipe: pipeForUser, | ||
readable: null, | ||
streamEnd: wrapStreamEnd(streamEnd, didError), | ||
injectToStream | ||
}; | ||
function renderToNodeStream_set(renderToNodeStream) { | ||
globalObject.renderToNodeStream = renderToNodeStream; | ||
} | ||
async function renderToWebStream(element, disable, options) { | ||
var _a; | ||
debug('creating Web Stream Pipe'); | ||
let didError = false; | ||
let firstErr = null; | ||
let reactBug = null; | ||
const onError = (err) => { | ||
didError = true; | ||
firstErr = firstErr || err; | ||
afterReactBugCatch(() => { | ||
var _a; | ||
// Is not a React internal error (i.e. a React bug) | ||
if (err !== reactBug) { | ||
(_a = options.onBoundaryError) === null || _a === void 0 ? void 0 : _a.call(options, err); | ||
} | ||
}); | ||
}; | ||
const renderToReadableStream = (_a = options.renderToReadableStream) !== null && _a !== void 0 ? _a : | ||
// We directly use import() because it needs to be bundled for Cloudflare Workers | ||
(await import('react-dom/server.browser')).renderToReadableStream; | ||
assertReactImport(renderToReadableStream, 'renderToReadableStream'); | ||
const readableOriginal = await renderToReadableStream(element, { onError }); | ||
const { allReady } = readableOriginal; | ||
let promiseResolved = false; | ||
// Upon React internal errors (i.e. React bugs), React rejects `allReady`. | ||
// React doesn't reject `allReady` upon boundary errors. | ||
allReady.catch((err) => { | ||
debug('react bug'); | ||
didError = true; | ||
firstErr = firstErr || err; | ||
reactBug = err; | ||
// Only log if it wasn't used as rejection for `await renderToStream()` | ||
if (reactBug !== firstErr || promiseResolved) { | ||
console.error(reactBug); | ||
} | ||
}); | ||
if (didError) | ||
throw firstErr; | ||
if (disable) | ||
await allReady; | ||
if (didError) | ||
throw firstErr; | ||
const { readableForUser, streamEnd, injectToStream } = (0, createReadableWrapper_1.createReadableWrapper)(readableOriginal); | ||
promiseResolved = true; | ||
return { | ||
readable: readableForUser, | ||
pipe: null, | ||
streamEnd: wrapStreamEnd(streamEnd, didError), | ||
injectToStream | ||
}; | ||
} | ||
// Needed for the hacky solution to workaround https://github.com/facebook/react/issues/24536 | ||
function afterReactBugCatch(fn) { | ||
setTimeout(() => { | ||
fn(); | ||
}, 0); | ||
} | ||
function wrapStreamEnd(streamEnd, didError) { | ||
return (streamEnd | ||
// Needed because of the `afterReactBugCatch()` hack above, otherwise `onBoundaryError` triggers after `streamEnd` resolved | ||
.then(() => new Promise((r) => setTimeout(r, 0))) | ||
.then(() => !didError)); | ||
} | ||
exports.renderToNodeStream_set = renderToNodeStream_set; | ||
// To debug wrong peer dependency loading: | ||
@@ -228,5 +97,1 @@ // - https://stackoverflow.com/questions/21056748/seriously-debugging-node-js-cannot-find-module-xyz-abcd | ||
} | ||
function assertReactImport(fn, fnName) { | ||
(0, utils_1.assert)(typeof fn === 'function'); | ||
(0, utils_1.assertUsage)(fn, `Couldn't import ${fnName}() from 'react-dom'`); | ||
} |
@@ -6,6 +6,5 @@ "use strict"; | ||
const createBuffer_1 = require("./createBuffer"); | ||
const loadNodeStreamModule_1 = require("./loadNodeStreamModule"); | ||
const debug = (0, utils_1.createDebugger)('react-streaming:createPipeWrapper'); | ||
const stream_1 = require("stream"); | ||
async function createPipeWrapper(pipeFromReact, { onReactBug }) { | ||
const { Writable } = await (0, loadNodeStreamModule_1.loadNodeStreamModule)(); | ||
const { pipeForUser, streamEnd } = createPipeForUser(); | ||
@@ -24,3 +23,3 @@ const streamOperations = { | ||
const pipeForUser = (writableFromUser) => { | ||
const writableForReact = new Writable({ | ||
const writableForReact = new stream_1.Writable({ | ||
write(chunk, encoding, callback) { | ||
@@ -27,0 +26,0 @@ debug('write'); |
@@ -5,3 +5,3 @@ "use strict"; | ||
const getGlobalObject_1 = require("./getGlobalObject"); | ||
const PROJECT_VERSION = '0.3.19'; | ||
const PROJECT_VERSION = '0.3.20'; | ||
const projectInfo = { | ||
@@ -8,0 +8,0 @@ projectName: 'react-streaming', |
export { renderToStream }; | ||
export { disable }; | ||
export { renderToNodeStream_set }; | ||
import React from 'react'; | ||
import type { renderToPipeableStream as RenderToPipeableStream, renderToReadableStream as RenderToReadableStream } from 'react-dom/server'; | ||
import { Pipe } from './renderToStream/createPipeWrapper'; | ||
import type { Pipe } from './renderToStream/createPipeWrapper'; | ||
import { SeoStrategy } from './renderToStream/resolveSeoStrategy'; | ||
import type { renderToNodeStream as renderToNodeStream_ } from './renderToStream/renderToNodeStream'; | ||
declare type Options = { | ||
@@ -29,1 +31,2 @@ webStream?: boolean; | ||
declare function renderToStream(element: React.ReactNode, options?: Options): Promise<Result>; | ||
declare function renderToNodeStream_set(renderToNodeStream: typeof renderToNodeStream_): void; |
export { renderToStream }; | ||
export { disable }; | ||
export { renderToNodeStream_set }; | ||
import React from 'react'; | ||
@@ -7,9 +8,9 @@ import ReactDOMServer, { version as reactDomVersion } from 'react-dom/server'; | ||
import { StreamProvider } from './useStream'; | ||
import { createPipeWrapper } from './renderToStream/createPipeWrapper'; | ||
import { createReadableWrapper } from './renderToStream/createReadableWrapper'; | ||
import { resolveSeoStrategy } from './renderToStream/resolveSeoStrategy'; | ||
import { assert, assertUsage, createDebugger } from './utils'; | ||
import { nodeStreamModuleIsAvailable } from './renderToStream/loadNodeStreamModule'; | ||
import import_ from '@brillout/import'; | ||
const debug = createDebugger('react-streaming:flow'); | ||
import { assert, assertUsage, getGlobalObject } from './utils'; | ||
import { renderToWebStream } from './renderToStream/renderToWebStream'; | ||
import { debugFlow } from './renderToStream/misc'; | ||
const globalObject = getGlobalObject('renderToStream.ts', { | ||
renderToNodeStream: null | ||
}); | ||
assertReact(); | ||
@@ -36,8 +37,8 @@ const globalConfig = (globalThis.__react_streaming = globalThis | ||
const disable = globalConfig.disable || ((_a = options.disable) !== null && _a !== void 0 ? _a : resolveSeoStrategy(options).disableStream); | ||
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !(await nodeStreamModuleIsAvailable()); | ||
debug(`disable === ${disable} && webStream === ${webStream}`); | ||
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !globalObject.renderToNodeStream; | ||
debugFlow(`disable === ${disable} && webStream === ${webStream}`); | ||
let result; | ||
const resultPartial = { disabled: disable }; | ||
if (!webStream) { | ||
result = { ...resultPartial, ...(await renderToNodeStream(element, disable, options)) }; | ||
result = { ...resultPartial, ...(await globalObject.renderToNodeStream(element, disable, options)) }; | ||
} | ||
@@ -50,140 +51,8 @@ else { | ||
buffer.length = 0; | ||
debug('promise `await renderToStream()` resolved'); | ||
debugFlow('promise `await renderToStream()` resolved'); | ||
return result; | ||
} | ||
async function renderToNodeStream(element, disable, options) { | ||
var _a; | ||
debug('creating Node.js Stream Pipe'); | ||
let onAllReady; | ||
const allReady = new Promise((r) => { | ||
onAllReady = () => r(); | ||
}); | ||
let onShellReady; | ||
const shellReady = new Promise((r) => { | ||
onShellReady = () => r(); | ||
}); | ||
let didError = false; | ||
let firstErr = null; | ||
let reactBug = null; | ||
const onError = (err) => { | ||
debug('[react] onError() / onShellError()'); | ||
didError = true; | ||
firstErr !== null && firstErr !== void 0 ? firstErr : (firstErr = err); | ||
onShellReady(); | ||
afterReactBugCatch(() => { | ||
var _a; | ||
// Is not a React internal error (i.e. a React bug) | ||
if (err !== reactBug) { | ||
(_a = options.onBoundaryError) === null || _a === void 0 ? void 0 : _a.call(options, err); | ||
} | ||
}); | ||
}; | ||
const renderToPipeableStream = (_a = options.renderToPipeableStream) !== null && _a !== void 0 ? _a : | ||
// We don't directly use import() because it shouldn't be bundled for Cloudflare Workers: the module react-dom/server.node contains a require('stream') which fails on Cloudflare Workers | ||
(await import_('react-dom/server.node')).renderToPipeableStream; | ||
assertReactImport(renderToPipeableStream, 'renderToPipeableStream'); | ||
const { pipe: pipeOriginal } = renderToPipeableStream(element, { | ||
onShellReady() { | ||
debug('[react] onShellReady()'); | ||
onShellReady(); | ||
}, | ||
onAllReady() { | ||
debug('[react] onAllReady()'); | ||
onShellReady(); | ||
onAllReady(); | ||
}, | ||
onShellError: onError, | ||
onError | ||
}); | ||
let promiseResolved = false; | ||
const { pipeForUser, injectToStream, streamEnd } = await createPipeWrapper(pipeOriginal, { | ||
onReactBug(err) { | ||
debug('react bug'); | ||
didError = true; | ||
firstErr !== null && firstErr !== void 0 ? firstErr : (firstErr = err); | ||
reactBug = err; | ||
// Only log if it wasn't used as rejection for `await renderToStream()` | ||
if (reactBug !== firstErr || promiseResolved) { | ||
console.error(reactBug); | ||
} | ||
} | ||
}); | ||
await shellReady; | ||
if (didError) | ||
throw firstErr; | ||
if (disable) | ||
await allReady; | ||
if (didError) | ||
throw firstErr; | ||
promiseResolved = true; | ||
return { | ||
pipe: pipeForUser, | ||
readable: null, | ||
streamEnd: wrapStreamEnd(streamEnd, didError), | ||
injectToStream | ||
}; | ||
function renderToNodeStream_set(renderToNodeStream) { | ||
globalObject.renderToNodeStream = renderToNodeStream; | ||
} | ||
async function renderToWebStream(element, disable, options) { | ||
var _a; | ||
debug('creating Web Stream Pipe'); | ||
let didError = false; | ||
let firstErr = null; | ||
let reactBug = null; | ||
const onError = (err) => { | ||
didError = true; | ||
firstErr = firstErr || err; | ||
afterReactBugCatch(() => { | ||
var _a; | ||
// Is not a React internal error (i.e. a React bug) | ||
if (err !== reactBug) { | ||
(_a = options.onBoundaryError) === null || _a === void 0 ? void 0 : _a.call(options, err); | ||
} | ||
}); | ||
}; | ||
const renderToReadableStream = (_a = options.renderToReadableStream) !== null && _a !== void 0 ? _a : | ||
// We directly use import() because it needs to be bundled for Cloudflare Workers | ||
(await import('react-dom/server.browser')).renderToReadableStream; | ||
assertReactImport(renderToReadableStream, 'renderToReadableStream'); | ||
const readableOriginal = await renderToReadableStream(element, { onError }); | ||
const { allReady } = readableOriginal; | ||
let promiseResolved = false; | ||
// Upon React internal errors (i.e. React bugs), React rejects `allReady`. | ||
// React doesn't reject `allReady` upon boundary errors. | ||
allReady.catch((err) => { | ||
debug('react bug'); | ||
didError = true; | ||
firstErr = firstErr || err; | ||
reactBug = err; | ||
// Only log if it wasn't used as rejection for `await renderToStream()` | ||
if (reactBug !== firstErr || promiseResolved) { | ||
console.error(reactBug); | ||
} | ||
}); | ||
if (didError) | ||
throw firstErr; | ||
if (disable) | ||
await allReady; | ||
if (didError) | ||
throw firstErr; | ||
const { readableForUser, streamEnd, injectToStream } = createReadableWrapper(readableOriginal); | ||
promiseResolved = true; | ||
return { | ||
readable: readableForUser, | ||
pipe: null, | ||
streamEnd: wrapStreamEnd(streamEnd, didError), | ||
injectToStream | ||
}; | ||
} | ||
// Needed for the hacky solution to workaround https://github.com/facebook/react/issues/24536 | ||
function afterReactBugCatch(fn) { | ||
setTimeout(() => { | ||
fn(); | ||
}, 0); | ||
} | ||
function wrapStreamEnd(streamEnd, didError) { | ||
return (streamEnd | ||
// Needed because of the `afterReactBugCatch()` hack above, otherwise `onBoundaryError` triggers after `streamEnd` resolved | ||
.then(() => new Promise((r) => setTimeout(r, 0))) | ||
.then(() => !didError)); | ||
} | ||
// To debug wrong peer dependency loading: | ||
@@ -198,5 +67,1 @@ // - https://stackoverflow.com/questions/21056748/seriously-debugging-node-js-cannot-find-module-xyz-abcd | ||
} | ||
function assertReactImport(fn, fnName) { | ||
assert(typeof fn === 'function'); | ||
assertUsage(fn, `Couldn't import ${fnName}() from 'react-dom'`); | ||
} |
export { createPipeWrapper }; | ||
import { createDebugger } from '../utils'; | ||
import { createBuffer } from './createBuffer'; | ||
import { loadNodeStreamModule } from './loadNodeStreamModule'; | ||
const debug = createDebugger('react-streaming:createPipeWrapper'); | ||
import { Writable } from 'stream'; | ||
async function createPipeWrapper(pipeFromReact, { onReactBug }) { | ||
const { Writable } = await loadNodeStreamModule(); | ||
const { pipeForUser, streamEnd } = createPipeForUser(); | ||
@@ -9,0 +8,0 @@ const streamOperations = { |
export { projectInfo }; | ||
import { getGlobalObject } from './getGlobalObject'; | ||
const PROJECT_VERSION = '0.3.19'; | ||
const PROJECT_VERSION = '0.3.20'; | ||
const projectInfo = { | ||
@@ -5,0 +5,0 @@ projectName: 'react-streaming', |
{ | ||
"name": "react-streaming", | ||
"description": "React 18 Streaming. Full-fledged & Easy.", | ||
"version": "0.3.19", | ||
"main": "./dist/cjs/server/hooks.js", | ||
"version": "0.3.20", | ||
"peerDependencies": { | ||
@@ -15,2 +14,19 @@ "react": ">=18", | ||
}, | ||
"main": "./dist/cjs/server/hooks.js", | ||
"exports": { | ||
".": { | ||
"node": "./dist/cjs/server/hooks.js", | ||
"worker": "./dist/esm/server/hooks.js", | ||
"deno": "./dist/esm/server/hooks.js", | ||
"browser": "./dist/esm/client/hooks.js", | ||
"types": "./dist/cjs/server/hooks.d.ts" | ||
}, | ||
"./server": { | ||
"node": "./dist/cjs/server/index.node.js", | ||
"worker": "./dist/esm/server/index.js", | ||
"deno": "./dist/esm/server/index.js", | ||
"browser": "./dist/esm/server/client-poison-pill.js", | ||
"types": "./dist/cjs/server/index.d.ts" | ||
} | ||
}, | ||
"scripts": { | ||
@@ -33,18 +49,2 @@ "// === Test ===": "", | ||
}, | ||
"exports": { | ||
".": { | ||
"node": "./dist/cjs/server/hooks.js", | ||
"worker": "./dist/esm/server/hooks.js", | ||
"deno": "./dist/esm/server/hooks.js", | ||
"browser": "./dist/esm/client/hooks.js", | ||
"types": "./dist/cjs/server/hooks.d.ts" | ||
}, | ||
"./server": { | ||
"node": "./dist/cjs/server/index.js", | ||
"worker": "./dist/esm/server/index.js", | ||
"deno": "./dist/esm/server/index.js", | ||
"browser": "./dist/esm/server/client-poison-pill.js", | ||
"types": "./dist/cjs/server/index.d.ts" | ||
} | ||
}, | ||
"devDependencies": { | ||
@@ -51,0 +51,0 @@ "@brillout/part-regex": "^0.1.2", |
125175
145
2747