Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7806ec2fe1 | ||
|
|
de58cca00c | ||
|
|
d8946cb6b8 | ||
|
|
bd4289b116 | ||
|
|
61c95d04a8 | ||
|
|
ac60e73588 | ||
|
|
6bc6b1c747 | ||
|
|
bbed0b0ee6 | ||
|
|
f3e3f6fbac | ||
|
|
154f4b15e2 |
4
.github/workflows/runtime_build_and_test.yml
vendored
4
.github/workflows/runtime_build_and_test.yml
vendored
@@ -3,6 +3,10 @@ name: (Runtime) Build and Test
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
# To get CI for backport releases.
|
||||
# This will duplicate CI for releases from main which is acceptable
|
||||
- "v*"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- compiler/**
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
//
|
||||
// The @latest channel uses the version as-is, e.g.:
|
||||
//
|
||||
// 19.0.0
|
||||
// 19.0.4
|
||||
//
|
||||
// The @canary channel appends additional information, with the scheme
|
||||
// <version>-<label>-<commit_sha>, e.g.:
|
||||
//
|
||||
// 19.0.0-canary-a1c2d3e4
|
||||
// 19.0.4-canary-a1c2d3e4
|
||||
//
|
||||
// The @experimental channel doesn't include a version, only a date and a sha, e.g.:
|
||||
//
|
||||
// 0.0.0-experimental-241c4467e-20200129
|
||||
|
||||
const ReactVersion = '19.0.0';
|
||||
const ReactVersion = '19.0.4';
|
||||
|
||||
// The label used by the @canary channel. Represents the upcoming release's
|
||||
// stability. Most of the time, this will be "canary", but we may temporarily
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/rollup/build-all-release-channels.js",
|
||||
"build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE",
|
||||
"build-for-devtools": "cross-env yarn build react/index,react/jsx,react/compiler-runtime,react-dom/index,react-dom/client,react-dom/unstable_testing,react-dom/test-utils,react-is,react-debug-tools,scheduler,react-test-renderer,react-refresh,react-art --type=NODE --release-channel=experimental",
|
||||
"build-for-devtools-dev": "yarn build-for-devtools --type=NODE_DEV",
|
||||
"build-for-devtools-prod": "yarn build-for-devtools --type=NODE_PROD",
|
||||
"build-for-flight-dev": "cross-env RELEASE_CHANNEL=experimental node ./scripts/rollup/build.js react/index,react/jsx,react.react-server,react-dom/index,react-dom/client,react-dom/server,react-dom.react-server,react-dom-server.node,react-dom-server-legacy.node,scheduler,react-server-dom-webpack/ --type=NODE_DEV,ESM_PROD,NODE_ES2015 && mv ./build/node_modules ./build/oss-experimental",
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1 || ^24.0.0 || ^25.1.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-test-renderer": "^19.0.0"
|
||||
"react": "^19.0.4",
|
||||
"react-test-renderer": "^19.0.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-art",
|
||||
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,7 +27,7 @@
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
54
packages/react-client/src/ReactFlightClient.js
vendored
54
packages/react-client/src/ReactFlightClient.js
vendored
@@ -79,6 +79,8 @@ import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||||
|
||||
import {getOwnerStackByComponentInfoInDev} from 'shared/ReactComponentInfoStack';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
import {injectInternals} from './ReactFlightClientDevToolsHook';
|
||||
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
@@ -135,6 +137,8 @@ const RESOLVED_MODULE = 'resolved_module';
|
||||
const INITIALIZED = 'fulfilled';
|
||||
const ERRORED = 'rejected';
|
||||
|
||||
const __PROTO__ = '__proto__';
|
||||
|
||||
type PendingChunk<T> = {
|
||||
status: 'pending',
|
||||
value: null | Array<(T) => mixed>,
|
||||
@@ -924,10 +928,21 @@ function waitForReference<T>(
|
||||
return;
|
||||
}
|
||||
}
|
||||
value = value[path[i]];
|
||||
const name = path[i];
|
||||
if (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
hasOwnProperty.call(value, name)
|
||||
) {
|
||||
value = value[name];
|
||||
} else {
|
||||
throw new Error('Invalid reference.');
|
||||
}
|
||||
}
|
||||
const mappedValue = map(response, value, parentObject, key);
|
||||
parentObject[key] = mappedValue;
|
||||
if (key !== __PROTO__) {
|
||||
parentObject[key] = mappedValue;
|
||||
}
|
||||
|
||||
// If this is the root object for a model reference, where `handler.value`
|
||||
// is a stale `null`, the resolved value can be used directly.
|
||||
@@ -1093,7 +1108,9 @@ function loadServerReference<A: Iterable<any>, T>(
|
||||
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
|
||||
}
|
||||
|
||||
parentObject[key] = resolvedValue;
|
||||
if (key !== __PROTO__) {
|
||||
parentObject[key] = resolvedValue;
|
||||
}
|
||||
|
||||
// If this is the root object for a model reference, where `handler.value`
|
||||
// is a stale `null`, the resolved value can be used directly.
|
||||
@@ -1372,7 +1389,7 @@ function parseModelString(
|
||||
// Symbol
|
||||
return Symbol.for(value.slice(2));
|
||||
}
|
||||
case 'F': {
|
||||
case 'h': {
|
||||
// Server Reference
|
||||
const ref = value.slice(2);
|
||||
return getOutlinedModel(
|
||||
@@ -1499,18 +1516,20 @@ function parseModelString(
|
||||
// In DEV mode we encode omitted objects in logs as a getter that throws
|
||||
// so that when you try to access it on the client, you know why that
|
||||
// happened.
|
||||
Object.defineProperty(parentObject, key, {
|
||||
get: function () {
|
||||
// TODO: We should ideally throw here to indicate a difference.
|
||||
return (
|
||||
'This object has been omitted by React in the console log ' +
|
||||
'to avoid sending too much data from the server. Try logging smaller ' +
|
||||
'or more specific objects.'
|
||||
);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
});
|
||||
if (key !== __PROTO__) {
|
||||
Object.defineProperty(parentObject, key, {
|
||||
get: function () {
|
||||
// TODO: We should ideally throw here to indicate a difference.
|
||||
return (
|
||||
'This object has been omitted by React in the console log ' +
|
||||
'to avoid sending too much data from the server. Try logging smaller ' +
|
||||
'or more specific objects.'
|
||||
);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Fallthrough
|
||||
@@ -3144,6 +3163,9 @@ function parseModel<T>(response: Response, json: UninitializedModel): T {
|
||||
function createFromJSONCallback(response: Response) {
|
||||
// $FlowFixMe[missing-this-annot]
|
||||
return function (key: string, value: JSONValue) {
|
||||
if (key === __PROTO__) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
// We can't use .bind here because we need the "this" value.
|
||||
return parseModelString(response, this, key, value);
|
||||
|
||||
@@ -98,6 +98,8 @@ export type ReactServerValue =
|
||||
|
||||
type ReactServerObject = {+[key: string]: ReactServerValue};
|
||||
|
||||
const __PROTO__ = '__proto__';
|
||||
|
||||
function serializeByValueID(id: number): string {
|
||||
return '$' + id.toString(16);
|
||||
}
|
||||
@@ -107,7 +109,7 @@ function serializePromiseID(id: number): string {
|
||||
}
|
||||
|
||||
function serializeServerReferenceID(id: number): string {
|
||||
return '$F' + id.toString(16);
|
||||
return '$h' + id.toString(16);
|
||||
}
|
||||
|
||||
function serializeTemporaryReferenceMarker(): string {
|
||||
@@ -115,7 +117,6 @@ function serializeTemporaryReferenceMarker(): string {
|
||||
}
|
||||
|
||||
function serializeFormDataReference(id: number): string {
|
||||
// Why K? F is "Function". D is "Date". What else?
|
||||
return '$K' + id.toString(16);
|
||||
}
|
||||
|
||||
@@ -365,6 +366,15 @@ export function processReply(
|
||||
): ReactJSONValue {
|
||||
const parent = this;
|
||||
|
||||
if (__DEV__) {
|
||||
if (key === __PROTO__) {
|
||||
console.error(
|
||||
'Expected not to serialize an object with own property `__proto__`. When parsed this property will be omitted.%s',
|
||||
describeObjectForErrorMessage(parent, key),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that `parent[key]` wasn't JSONified before `value` was passed to us
|
||||
if (__DEV__) {
|
||||
// $FlowFixMe[incompatible-use]
|
||||
@@ -477,8 +487,22 @@ export function processReply(
|
||||
}
|
||||
}
|
||||
|
||||
const existingReference = writtenObjects.get(value);
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
if (typeof value.then === 'function') {
|
||||
if (existingReference !== undefined) {
|
||||
if (modelRoot === value) {
|
||||
// This is the ID we're currently emitting so we need to write it
|
||||
// once but if we discover it again, we refer to it by id.
|
||||
modelRoot = null;
|
||||
} else {
|
||||
// We've already emitted this as an outlined object, so we can
|
||||
// just refer to that by its existing ID.
|
||||
return existingReference;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume that any object with a .then property is a "Thenable" type,
|
||||
// or a Promise type. Either of which can be represented by a Promise.
|
||||
if (formData === null) {
|
||||
@@ -487,11 +511,19 @@ export function processReply(
|
||||
}
|
||||
pendingParts++;
|
||||
const promiseId = nextPartId++;
|
||||
const promiseReference = serializePromiseID(promiseId);
|
||||
writtenObjects.set(value, promiseReference);
|
||||
const thenable: Thenable<any> = (value: any);
|
||||
thenable.then(
|
||||
partValue => {
|
||||
try {
|
||||
const partJSON = serializeModel(partValue, promiseId);
|
||||
const previousReference = writtenObjects.get(partValue);
|
||||
let partJSON;
|
||||
if (previousReference !== undefined) {
|
||||
partJSON = JSON.stringify(previousReference);
|
||||
} else {
|
||||
partJSON = serializeModel(partValue, promiseId);
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
|
||||
const data: FormData = formData;
|
||||
data.append(formFieldPrefix + promiseId, partJSON);
|
||||
@@ -507,10 +539,9 @@ export function processReply(
|
||||
// that throws on the server instead.
|
||||
reject,
|
||||
);
|
||||
return serializePromiseID(promiseId);
|
||||
return promiseReference;
|
||||
}
|
||||
|
||||
const existingReference = writtenObjects.get(value);
|
||||
if (existingReference !== undefined) {
|
||||
if (modelRoot === value) {
|
||||
// This is the ID we're currently emitting so we need to write it
|
||||
@@ -770,6 +801,10 @@ export function processReply(
|
||||
if (typeof value === 'function') {
|
||||
const metaData = knownServerReferences.get(value);
|
||||
if (metaData !== undefined) {
|
||||
const existingReference = writtenObjects.get(value);
|
||||
if (existingReference !== undefined) {
|
||||
return existingReference;
|
||||
}
|
||||
const metaDataJSON = JSON.stringify(metaData, resolveToJSON);
|
||||
if (formData === null) {
|
||||
// Upgrade to use FormData to allow us to stream this value.
|
||||
@@ -778,7 +813,10 @@ export function processReply(
|
||||
// The reference to this function came from the same client so we can pass it back.
|
||||
const refId = nextPartId++;
|
||||
formData.set(formFieldPrefix + refId, metaDataJSON);
|
||||
return serializeServerReferenceID(refId);
|
||||
const serverReferenceId = serializeServerReferenceID(refId);
|
||||
// Store the server reference ID for deduplication.
|
||||
writtenObjects.set(value, serverReferenceId);
|
||||
return serverReferenceId;
|
||||
}
|
||||
if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
|
||||
// TODO: If the property name contains a colon, we don't dedupe. Escape instead.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-dom-bindings",
|
||||
"description": "React implementation details for react-dom.",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -18,6 +18,6 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -20,7 +20,7 @@
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-is",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"description": "Brand checking of React Elements.",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-markup",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"description": "React package generating embedded markup such as e-mails with support for Server Components.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"homepage": "https://react.dev/",
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": "^0.23.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-esm",
|
||||
"description": "React Server Components bindings for DOM using ESM. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -54,8 +54,8 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.0.4",
|
||||
"react-dom": "^19.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-loose": "^8.3.0",
|
||||
|
||||
@@ -88,6 +88,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -111,12 +117,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -206,12 +206,17 @@ function prerenderToNodeStream(
|
||||
function decodeReplyFromBusboy<T>(
|
||||
busboyStream: Busboy,
|
||||
moduleBasePath: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
const response = createResponse(
|
||||
moduleBasePath,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
undefined,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
let pendingFiles = 0;
|
||||
const queuedFields: Array<string> = [];
|
||||
@@ -222,16 +227,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -239,14 +251,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -266,7 +282,10 @@ function decodeReplyFromBusboy<T>(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
moduleBasePath: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -278,6 +297,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-turbopack",
|
||||
"description": "React Server Components bindings for DOM using Turbopack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -79,8 +79,8 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react": "^19.0.4",
|
||||
"react-dom": "^19.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-loose": "^8.3.0",
|
||||
|
||||
@@ -102,6 +102,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -125,12 +131,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientCo
|
||||
|
||||
import {loadChunk} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -228,5 +230,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -165,7 +165,10 @@ function prerender(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
turbopackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -177,6 +180,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -165,7 +165,10 @@ function prerender(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
turbopackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -177,6 +180,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -208,12 +208,17 @@ function prerenderToNodeStream(
|
||||
function decodeReplyFromBusboy<T>(
|
||||
busboyStream: Busboy,
|
||||
turbopackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
const response = createResponse(
|
||||
turbopackMap,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
undefined,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
let pendingFiles = 0;
|
||||
const queuedFields: Array<string> = [];
|
||||
@@ -224,16 +229,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -241,14 +253,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -268,7 +284,10 @@ function decodeReplyFromBusboy<T>(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
turbopackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -280,6 +299,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-server-dom-webpack",
|
||||
"description": "React Server Components bindings for DOM using Webpack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -100,8 +100,8 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react": "^19.0.4",
|
||||
"react-dom": "^19.0.4",
|
||||
"webpack": "^5.59.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -103,6 +103,12 @@ function bind(this: ServerReference<any>): any {
|
||||
return newFn;
|
||||
}
|
||||
|
||||
const serverReferenceToString = {
|
||||
value: () => 'function () { [omitted code] }',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: string,
|
||||
@@ -126,12 +132,14 @@ export function registerServerReference<T: Function>(
|
||||
configurable: true,
|
||||
},
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
}
|
||||
: {
|
||||
$$typeof,
|
||||
$$id,
|
||||
$$bound,
|
||||
bind: {value: bind, configurable: true},
|
||||
toString: serverReferenceToString,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
} from '../shared/ReactFlightImportMetadata';
|
||||
import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReference<any>,
|
||||
@@ -158,5 +160,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.default;
|
||||
}
|
||||
return moduleExports[metadata.name];
|
||||
if (hasOwnProperty.call(moduleExports, metadata.name)) {
|
||||
return moduleExports[metadata.name];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import {prepareDestinationWithChunks} from 'react-client/src/ReactFlightClientCo
|
||||
|
||||
import {loadChunk} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import hasOwnProperty from 'shared/hasOwnProperty';
|
||||
|
||||
export type ServerConsumerModuleMap = null | {
|
||||
[clientId: string]: {
|
||||
[clientExportName: string]: ClientReferenceManifestEntry,
|
||||
@@ -249,5 +251,8 @@ export function requireModule<T>(metadata: ClientReference<T>): T {
|
||||
// default property of this if it was an ESM interop module.
|
||||
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
||||
}
|
||||
return moduleExports[metadata[NAME]];
|
||||
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
|
||||
return moduleExports[metadata[NAME]];
|
||||
}
|
||||
return (undefined: any);
|
||||
}
|
||||
|
||||
@@ -165,7 +165,10 @@ function prerender(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -177,6 +180,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -165,7 +165,10 @@ function prerender(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -177,6 +180,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -208,12 +208,17 @@ function prerenderToNodeStream(
|
||||
function decodeReplyFromBusboy<T>(
|
||||
busboyStream: Busboy,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
const response = createResponse(
|
||||
webpackMap,
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
undefined,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
let pendingFiles = 0;
|
||||
const queuedFields: Array<string> = [];
|
||||
@@ -224,16 +229,23 @@ function decodeReplyFromBusboy<T>(
|
||||
// we queue any fields we receive until the previous file is done.
|
||||
queuedFields.push(name, value);
|
||||
} else {
|
||||
resolveField(response, name, value);
|
||||
try {
|
||||
resolveField(response, name, value);
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
busboyStream.on('file', (name, value, {filename, encoding, mimeType}) => {
|
||||
if (encoding.toLowerCase() === 'base64') {
|
||||
throw new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
busboyStream.destroy(
|
||||
new Error(
|
||||
"React doesn't accept base64 encoded file uploads because we don't expect " +
|
||||
"form data passed from a browser to ever encode data that way. If that's " +
|
||||
'the wrong assumption, we can easily fix it.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
pendingFiles++;
|
||||
const file = resolveFileInfo(response, name, filename, mimeType);
|
||||
@@ -241,14 +253,18 @@ function decodeReplyFromBusboy<T>(
|
||||
resolveFileChunk(response, file, chunk);
|
||||
});
|
||||
value.on('end', () => {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
try {
|
||||
resolveFileComplete(response, name, file);
|
||||
pendingFiles--;
|
||||
if (pendingFiles === 0) {
|
||||
// Release any queued fields
|
||||
for (let i = 0; i < queuedFields.length; i += 2) {
|
||||
resolveField(response, queuedFields[i], queuedFields[i + 1]);
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
}
|
||||
queuedFields.length = 0;
|
||||
} catch (error) {
|
||||
busboyStream.destroy(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -268,7 +284,10 @@ function decodeReplyFromBusboy<T>(
|
||||
function decodeReply<T>(
|
||||
body: string | FormData,
|
||||
webpackMap: ServerManifest,
|
||||
options?: {temporaryReferences?: TemporaryReferenceSet},
|
||||
options?: {
|
||||
temporaryReferences?: TemporaryReferenceSet,
|
||||
arraySizeLimit?: number,
|
||||
},
|
||||
): Thenable<T> {
|
||||
if (typeof body === 'string') {
|
||||
const form = new FormData();
|
||||
@@ -280,6 +299,7 @@ function decodeReply<T>(
|
||||
'',
|
||||
options ? options.temporaryReferences : undefined,
|
||||
body,
|
||||
options ? options.arraySizeLimit : undefined,
|
||||
);
|
||||
const root = getRoot<T>(response);
|
||||
close(response);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Thenable, ReactFormState} from 'shared/ReactTypes';
|
||||
import type {ReactFormState} from 'shared/ReactTypes';
|
||||
|
||||
import type {
|
||||
ServerManifest,
|
||||
@@ -20,26 +20,48 @@ import {
|
||||
requireModule,
|
||||
} from 'react-client/src/ReactFlightClientConfig';
|
||||
|
||||
import {createResponse, close, getRoot} from './ReactFlightReplyServer';
|
||||
import {
|
||||
createResponse,
|
||||
close,
|
||||
getRoot,
|
||||
MAX_BOUND_ARGS,
|
||||
} from './ReactFlightReplyServer';
|
||||
|
||||
type ServerReferenceId = any;
|
||||
|
||||
function bindArgs(fn: any, args: any) {
|
||||
if (args.length > MAX_BOUND_ARGS) {
|
||||
throw new Error(
|
||||
'Server Function has too many bound arguments. Received ' +
|
||||
args.length +
|
||||
' but the limit is ' +
|
||||
MAX_BOUND_ARGS +
|
||||
'.',
|
||||
);
|
||||
}
|
||||
|
||||
return fn.bind.apply(fn, [null].concat(args));
|
||||
}
|
||||
|
||||
function loadServerReference<T>(
|
||||
bundlerConfig: ServerManifest,
|
||||
id: ServerReferenceId,
|
||||
bound: null | Thenable<Array<any>>,
|
||||
metaData: {
|
||||
id: string,
|
||||
bound: null | Promise<Array<any>>,
|
||||
},
|
||||
): Promise<T> {
|
||||
const id: ServerReferenceId = metaData.id;
|
||||
if (typeof id !== 'string') {
|
||||
return (null: any);
|
||||
}
|
||||
const serverReference: ServerReference<T> =
|
||||
resolveServerReference<$FlowFixMe>(bundlerConfig, id);
|
||||
// We expect most servers to not really need this because you'd just have all
|
||||
// the relevant modules already loaded but it allows for lazy loading of code
|
||||
// if needed.
|
||||
const preloadPromise = preloadModule(serverReference);
|
||||
if (bound) {
|
||||
const bound = metaData.bound;
|
||||
if (bound instanceof Promise) {
|
||||
return Promise.all([(bound: any), preloadPromise]).then(
|
||||
([args]: Array<any>) => bindArgs(requireModule(serverReference), args),
|
||||
);
|
||||
@@ -57,6 +79,7 @@ function decodeBoundActionMetaData(
|
||||
body: FormData,
|
||||
serverManifest: ServerManifest,
|
||||
formFieldPrefix: string,
|
||||
arraySizeLimit: void | number,
|
||||
): {id: ServerReferenceId, bound: null | Promise<Array<any>>} {
|
||||
// The data for this reference is encoded in multiple fields under this prefix.
|
||||
const actionResponse = createResponse(
|
||||
@@ -64,6 +87,7 @@ function decodeBoundActionMetaData(
|
||||
formFieldPrefix,
|
||||
undefined,
|
||||
body,
|
||||
arraySizeLimit,
|
||||
);
|
||||
close(actionResponse);
|
||||
const refPromise = getRoot<{
|
||||
@@ -89,6 +113,7 @@ export function decodeAction<T>(
|
||||
const formData = new FormData();
|
||||
|
||||
let action: Promise<(formData: FormData) => T> | null = null;
|
||||
const seenActions = new Set<string>();
|
||||
|
||||
// $FlowFixMe[prop-missing]
|
||||
body.forEach((value: string | File, key: string) => {
|
||||
@@ -97,21 +122,36 @@ export function decodeAction<T>(
|
||||
formData.append(key, value);
|
||||
return;
|
||||
}
|
||||
// Later actions may override earlier actions if a button is used to override the default
|
||||
// form action.
|
||||
// Later actions may override earlier actions if a button is used to
|
||||
// override the default form action. However, we don't expect the same
|
||||
// action ref field to be sent multiple times in legitimate form data.
|
||||
if (key.startsWith('$ACTION_REF_')) {
|
||||
if (seenActions.has(key)) {
|
||||
return;
|
||||
}
|
||||
seenActions.add(key);
|
||||
const formFieldPrefix = '$ACTION_' + key.slice(12) + ':';
|
||||
const metaData = decodeBoundActionMetaData(
|
||||
body,
|
||||
serverManifest,
|
||||
formFieldPrefix,
|
||||
);
|
||||
action = loadServerReference(serverManifest, metaData.id, metaData.bound);
|
||||
action = loadServerReference(serverManifest, metaData);
|
||||
return;
|
||||
}
|
||||
// A simple action with no bound arguments may appear twice in the form data
|
||||
// if a button specifies the same action as the default form action. We only
|
||||
// load the first one, as they're guaranteed to be identical.
|
||||
if (key.startsWith('$ACTION_ID_')) {
|
||||
if (seenActions.has(key)) {
|
||||
return;
|
||||
}
|
||||
seenActions.add(key);
|
||||
const id = key.slice(11);
|
||||
action = loadServerReference(serverManifest, id, null);
|
||||
action = loadServerReference(serverManifest, {
|
||||
id,
|
||||
bound: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
1329
packages/react-server/src/ReactFlightReplyServer.js
vendored
1329
packages/react-server/src/ReactFlightReplyServer.js
vendored
File diff suppressed because it is too large
Load Diff
15
packages/react-server/src/ReactFlightServer.js
vendored
15
packages/react-server/src/ReactFlightServer.js
vendored
@@ -354,6 +354,8 @@ type Task = {
|
||||
|
||||
interface Reference {}
|
||||
|
||||
const __PROTO__ = '__proto__';
|
||||
|
||||
const OPENING = 10;
|
||||
const OPEN = 11;
|
||||
const ABORTING = 12;
|
||||
@@ -1936,7 +1938,7 @@ function serializePromiseID(id: number): string {
|
||||
}
|
||||
|
||||
function serializeServerReferenceID(id: number): string {
|
||||
return '$F' + id.toString(16);
|
||||
return '$h' + id.toString(16);
|
||||
}
|
||||
|
||||
function serializeSymbolReference(name: string): string {
|
||||
@@ -2446,6 +2448,17 @@ function renderModelDestructive(
|
||||
// Set the currently rendering model
|
||||
task.model = value;
|
||||
|
||||
if (__DEV__) {
|
||||
if (parentPropertyName === __PROTO__) {
|
||||
callWithDebugContextInDEV(request, task, () => {
|
||||
console.error(
|
||||
'Expected not to serialize an object with own property `__proto__`. When parsed this property will be omitted.%s',
|
||||
describeObjectForErrorMessage(parent, parentPropertyName),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Special Symbol, that's very common.
|
||||
if (value === REACT_ELEMENT_TYPE) {
|
||||
return '$';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-test-renderer",
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"description": "React package for snapshot testing.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -23,7 +23,7 @@
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
"react": "^19.0.4"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
"version": "19.0.0",
|
||||
"version": "19.0.4",
|
||||
"homepage": "https://react.dev/",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
// TODO: This module is used both by the release scripts and to expose a version
|
||||
// at runtime. We should instead inject the version number as part of the build
|
||||
// process, and use the ReactVersions.js module as the single source of truth.
|
||||
export default '19.0.0';
|
||||
export default '19.0.1';
|
||||
|
||||
@@ -526,5 +526,20 @@
|
||||
"538": "Cannot use state or effect Hooks in renderToHTML because this component will never be hydrated.",
|
||||
"539": "Binary RSC chunks cannot be encoded as strings. This is a bug in the wiring of the React streams.",
|
||||
"540": "String chunks need to be passed in their original shape. Not split into smaller string chunks. This is a bug in the wiring of the React streams.",
|
||||
"541": "Compared context values must be arrays"
|
||||
"541": "Compared context values must be arrays",
|
||||
"567": "Already initialized stream.",
|
||||
"568": "Already initialized typed array.",
|
||||
"569": "Cannot have cyclic thenables.",
|
||||
"570": "Invalid reference.",
|
||||
"571": "Maximum array nesting exceeded. Large nested arrays can be dangerous. Try adding intermediate objects.",
|
||||
"572": "Already initialized Map.",
|
||||
"573": "Already initialized Set.",
|
||||
"574": "Invalid forward reference.",
|
||||
"575": "Invalid Map initializer.",
|
||||
"576": "Invalid Set initializer.",
|
||||
"577": "Invalid Iterator initializer.",
|
||||
"578": "Already initialized Iterator.",
|
||||
"579": "Invalid data for bytes stream.",
|
||||
"580": "Server Function has too many bound arguments. Received %s but the limit is %s.",
|
||||
"581": "BigInt is too large. Received %s digits but the limit is %s."
|
||||
}
|
||||
|
||||
@@ -124,25 +124,34 @@ async function main() {
|
||||
throw new Error(`Unknown release channel ${argv.releaseChannel}`);
|
||||
}
|
||||
} else {
|
||||
// Running locally, no concurrency. Move each channel's build artifacts into
|
||||
// a temporary directory so that they don't conflict.
|
||||
buildForChannel('stable', '', '');
|
||||
const stableDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', stableDir);
|
||||
processStable(stableDir);
|
||||
buildForChannel('experimental', '', '');
|
||||
const experimentalDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', experimentalDir);
|
||||
processExperimental(experimentalDir);
|
||||
const releaseChannel = argv.releaseChannel;
|
||||
if (releaseChannel === 'stable') {
|
||||
buildForChannel('stable', '', '');
|
||||
processStable('./build');
|
||||
} else if (releaseChannel === 'experimental') {
|
||||
buildForChannel('experimental', '', '');
|
||||
processExperimental('./build');
|
||||
} else {
|
||||
// Running locally, no concurrency. Move each channel's build artifacts into
|
||||
// a temporary directory so that they don't conflict.
|
||||
buildForChannel('stable', '', '');
|
||||
const stableDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', stableDir);
|
||||
processStable(stableDir);
|
||||
buildForChannel('experimental', '', '');
|
||||
const experimentalDir = tmp.dirSync().name;
|
||||
crossDeviceRenameSync('./build', experimentalDir);
|
||||
processExperimental(experimentalDir);
|
||||
|
||||
// Then merge the experimental folder into the stable one. processExperimental
|
||||
// will have already removed conflicting files.
|
||||
//
|
||||
// In CI, merging is handled by the GitHub Download Artifacts plugin.
|
||||
mergeDirsSync(experimentalDir + '/', stableDir + '/');
|
||||
// Then merge the experimental folder into the stable one. processExperimental
|
||||
// will have already removed conflicting files.
|
||||
//
|
||||
// In CI, merging is handled by the GitHub Download Artifacts plugin.
|
||||
mergeDirsSync(experimentalDir + '/', stableDir + '/');
|
||||
|
||||
// Now restore the combined directory back to its original name
|
||||
crossDeviceRenameSync(stableDir, './build');
|
||||
// Now restore the combined directory back to its original name
|
||||
crossDeviceRenameSync(stableDir, './build');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user