@parcel/rsc 3.0.0-alpha.0 → 3.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcel/rsc",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.0-alpha.2",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -14,7 +14,10 @@
14
14
  "url": "https://github.com/parcel-bundler/parcel.git"
15
15
  },
16
16
  "sideEffects": false,
17
- "files": ["lib", "src"],
17
+ "files": [
18
+ "lib",
19
+ "src"
20
+ ],
18
21
  "exports": {
19
22
  ".": {
20
23
  "types": "./src/ssg.d.ts"
@@ -53,4 +56,4 @@
53
56
  "react": "^19.1.0",
54
57
  "react-dom": "^19.1.0"
55
58
  }
56
- }
59
+ }
package/src/client.tsx CHANGED
@@ -1,6 +1,12 @@
1
1
  /* @jsxRuntime automatic */
2
2
  import {ReactNode, startTransition, useInsertionEffect} from 'react';
3
- import {createFromReadableStream, createFromFetch, encodeReply, setServerCallback as setReactServerCallback, createTemporaryReferenceSet} from 'react-server-dom-parcel/client';
3
+ import {
4
+ createFromReadableStream,
5
+ createFromFetch,
6
+ encodeReply,
7
+ setServerCallback as setReactServerCallback,
8
+ createTemporaryReferenceSet,
9
+ } from 'react-server-dom-parcel/client';
4
10
  import {rscStream} from 'rsc-html-stream/client';
5
11
  import {hydrateRoot, HydrationOptions, Root} from 'react-dom/client';
6
12
 
@@ -11,7 +17,7 @@ export function setServerCallback(cb: CallServerCallback): void {
11
17
 
12
18
  // Stream in initial RSC payload embedded in the HTML.
13
19
  let initialRSCPayload: Promise<ReactNode>;
14
- function RSCRoot({value, cb}: {value?: ReactNode, cb?: () => void}) {
20
+ function RSCRoot({value, cb}: {value?: ReactNode; cb?: () => void}) {
15
21
  initialRSCPayload ??= createFromReadableStream<ReactNode>(rscStream);
16
22
  useInsertionEffect(() => {
17
23
  cb?.();
@@ -20,11 +26,13 @@ function RSCRoot({value, cb}: {value?: ReactNode, cb?: () => void}) {
20
26
  }
21
27
 
22
28
  export interface HydrateOptions extends HydrationOptions {
23
- callServer?: CallServerCallback,
24
- onHmrReload?: () => void
29
+ callServer?: CallServerCallback;
30
+ onHmrReload?: () => void;
25
31
  }
26
32
 
27
- export function hydrate(options?: HydrateOptions): (value: ReactNode, cb?: () => void) => void {
33
+ export function hydrate(
34
+ options?: HydrateOptions,
35
+ ): (value: ReactNode, cb?: () => void) => void {
28
36
  if (options?.callServer) {
29
37
  setServerCallback(options.callServer);
30
38
  }
@@ -43,16 +51,27 @@ export function hydrate(options?: HydrateOptions): (value: ReactNode, cb?: () =>
43
51
 
44
52
  return (value: ReactNode, cb?: () => void) => {
45
53
  startTransition(() => {
46
- root?.render(<RSCRoot value={value} cb={() => {cb?.(); cb = undefined}} />);
54
+ root?.render(
55
+ <RSCRoot
56
+ value={value}
57
+ cb={() => {
58
+ cb?.();
59
+ cb = undefined;
60
+ }}
61
+ />,
62
+ );
47
63
  });
48
64
  };
49
65
  }
50
66
 
51
67
  export interface RSCRequestInit extends Omit<RequestInit, 'body'> {
52
- body?: any
68
+ body?: any;
53
69
  }
54
70
 
55
- export async function fetchRSC<T>(url: string | URL | Request, options?: RSCRequestInit): Promise<T> {
71
+ export async function fetchRSC<T>(
72
+ url: string | URL | Request,
73
+ options?: RSCRequestInit,
74
+ ): Promise<T> {
56
75
  const temporaryReferences = createTemporaryReferenceSet();
57
76
  const response = fetch(url, {
58
77
  ...options,
@@ -60,9 +79,13 @@ export async function fetchRSC<T>(url: string | URL | Request, options?: RSCRequ
60
79
  Accept: 'text/x-component',
61
80
  ...options?.headers,
62
81
  },
63
- body: options && 'body' in options
64
- ? await encodeReply(options.body, {temporaryReferences, signal: options?.signal})
65
- : undefined,
82
+ body:
83
+ options && 'body' in options
84
+ ? await encodeReply(options.body, {
85
+ temporaryReferences,
86
+ signal: options?.signal,
87
+ })
88
+ : undefined,
66
89
  });
67
90
 
68
91
  return createFromFetch<T>(response, {temporaryReferences});
package/src/node.tsx CHANGED
@@ -1,17 +1,31 @@
1
1
  /* @jsxRuntime automatic */
2
- import type { IncomingMessage, ServerResponse } from 'http';
3
- import type { ReadableStream as NodeReadableStream } from 'stream/web';
4
- import type { ReactNode } from 'react';
2
+ import type {IncomingMessage, ServerResponse} from 'http';
3
+ import type {ReadableStream as NodeReadableStream} from 'stream/web';
4
+ import type {ReactNode} from 'react';
5
5
 
6
6
  import {Readable} from 'stream';
7
- import {renderToReadableStream, createTemporaryReferenceSet} from 'react-server-dom-parcel/server.edge';
8
- import {RSCToHTMLOptions, RSCOptions, renderHTML as renderHTMLBase, callAction as callActionBase, renderDevError} from './server';
7
+ import {
8
+ renderToReadableStream,
9
+ createTemporaryReferenceSet,
10
+ } from 'react-server-dom-parcel/server.edge';
11
+ import {
12
+ RSCToHTMLOptions,
13
+ RSCOptions,
14
+ renderHTML as renderHTMLBase,
15
+ callAction as callActionBase,
16
+ renderDevError,
17
+ } from './server';
9
18
 
10
19
  export function renderRSC(root: any, options?: RSCOptions): Readable {
11
- return Readable.fromWeb(renderToReadableStream(root, options) as NodeReadableStream);
20
+ return Readable.fromWeb(
21
+ renderToReadableStream(root, options) as NodeReadableStream,
22
+ );
12
23
  }
13
24
 
14
- export async function renderHTML(root: any, options?: RSCToHTMLOptions): Promise<Readable> {
25
+ export async function renderHTML(
26
+ root: any,
27
+ options?: RSCToHTMLOptions,
28
+ ): Promise<Readable> {
15
29
  let htmlStream = await renderHTMLBase(root, options);
16
30
  return Readable.fromWeb(htmlStream as NodeReadableStream);
17
31
  }
@@ -19,15 +33,22 @@ export async function renderHTML(root: any, options?: RSCToHTMLOptions): Promise
19
33
  const temporaryReferencesSymbol = Symbol.for('temporaryReferences');
20
34
 
21
35
  export interface RenderRequestOptions extends RSCToHTMLOptions {
22
- renderError?: (err: Error) => ReactNode
36
+ renderError?: (err: Error) => ReactNode;
23
37
  }
24
38
 
25
- export async function renderRequest(request: IncomingMessage, response: ServerResponse, root: any, options?: RenderRequestOptions): Promise<void> {
39
+ export async function renderRequest(
40
+ request: IncomingMessage,
41
+ response: ServerResponse,
42
+ root: any,
43
+ options?: RenderRequestOptions,
44
+ ): Promise<void> {
26
45
  options = {
27
46
  ...options,
28
- temporaryReferences: options?.temporaryReferences ?? (request as any)[temporaryReferencesSymbol]
47
+ temporaryReferences:
48
+ options?.temporaryReferences ??
49
+ (request as any)[temporaryReferencesSymbol],
29
50
  };
30
-
51
+
31
52
  if (request.headers.accept?.includes('text/html')) {
32
53
  try {
33
54
  let html = await renderHTML(root, options);
@@ -38,7 +59,7 @@ export async function renderRequest(request: IncomingMessage, response: ServerRe
38
59
  let error = err instanceof Error ? err : new Error(String(err));
39
60
  let renderedError;
40
61
  try {
41
- renderedError = options?.renderError
62
+ renderedError = options?.renderError
42
63
  ? await renderHTMLBase(options.renderError(error), options)
43
64
  : await renderDevError(error, options);
44
65
  } catch {
@@ -56,7 +77,10 @@ export async function renderRequest(request: IncomingMessage, response: ServerRe
56
77
  }
57
78
  }
58
79
 
59
- export async function callAction(request: IncomingMessage, id: string | null | undefined): Promise<{result: any}> {
80
+ export async function callAction(
81
+ request: IncomingMessage,
82
+ id: string | null | undefined,
83
+ ): Promise<{result: any}> {
60
84
  (request as any)[temporaryReferencesSymbol] ??= createTemporaryReferenceSet();
61
85
 
62
86
  let req = new Request('http://localhost' + request.url, {
@@ -64,9 +88,11 @@ export async function callAction(request: IncomingMessage, id: string | null | u
64
88
  headers: request.headers as any,
65
89
  body: Readable.toWeb(request) as ReadableStream,
66
90
  // @ts-ignore
67
- duplex: 'half'
91
+ duplex: 'half',
68
92
  });
69
93
 
70
- (req as any)[temporaryReferencesSymbol] = (request as any)[temporaryReferencesSymbol];
94
+ (req as any)[temporaryReferencesSymbol] = (request as any)[
95
+ temporaryReferencesSymbol
96
+ ];
71
97
  return callActionBase(req, id);
72
98
  }
@@ -1,12 +1,10 @@
1
- /* eslint-env browser */
2
- /* eslint-disable react/display-name */
3
-
4
1
  'use parcel-rsc-runtime';
5
2
 
6
3
  import {createElement, Fragment} from 'react';
7
4
 
8
5
  export function stylesheetResource(href) {
9
6
  return createElement('link', {
7
+ key: href,
10
8
  rel: 'stylesheet',
11
9
  href,
12
10
  precedence: 'default',
@@ -14,16 +12,16 @@ export function stylesheetResource(href) {
14
12
  }
15
13
 
16
14
  export function modulePreloadResource(href) {
17
- return createElement('link', {rel: 'modulepreload', href});
15
+ return createElement('link', {key: href, rel: 'modulepreload', href});
18
16
  }
19
17
 
20
18
  export function scriptPreloadResource(href) {
21
- return createElement('link', {rel: 'preload', as: 'script', href});
19
+ return createElement('link', {key: href, rel: 'preload', as: 'script', href});
22
20
  }
23
21
 
24
22
  export function wrapClientReferenceWithResources(Reference, resources) {
25
23
  return props =>
26
- createElement(Fragment, null, ...resources, createElement(Reference, props));
24
+ createElement(Fragment, null, resources, createElement(Reference, props));
27
25
  }
28
26
 
29
27
  export function createResourcesProxy(
@@ -93,7 +91,7 @@ function createResourcesValueProxy(value, resources, bootstrapScript) {
93
91
  apply(target, thisArg, args) {
94
92
  let result = Reflect.apply(target, thisArg, args);
95
93
  return isReactComponent
96
- ? createElement(Fragment, null, ...resources, result)
94
+ ? createElement(Fragment, null, resources, result)
97
95
  : result;
98
96
  },
99
97
  });
package/src/server.tsx CHANGED
@@ -1,24 +1,34 @@
1
1
  /* @jsxRuntime automatic */
2
- import type { ErrorInfo } from 'react-dom/client';
2
+ import type {ErrorInfo} from 'react-dom/client';
3
3
 
4
4
  // Server dependencies.
5
- import {renderToReadableStream, loadServerAction, decodeReply, decodeAction, createTemporaryReferenceSet} from 'react-server-dom-parcel/server.edge';
5
+ import {
6
+ renderToReadableStream,
7
+ loadServerAction,
8
+ decodeReply,
9
+ decodeAction,
10
+ createTemporaryReferenceSet,
11
+ } from 'react-server-dom-parcel/server.edge';
6
12
  import {injectRSCPayload} from 'rsc-html-stream/server';
7
13
 
8
14
  // Client dependencies, used for SSR.
9
15
  // These must run in the same environment as client components (e.g. same instance of React).
10
- import {createFromReadableStream} from 'react-server-dom-parcel/client.edge' with {env: 'react-client'};
11
- import {renderToReadableStream as renderHTMLToReadableStream} from 'react-dom/server.edge' with {env: 'react-client'};
16
+ import {createFromReadableStream} from 'react-server-dom-parcel/client.edge' with {
17
+ env: 'react-client',
18
+ };
19
+ import {renderToReadableStream as renderHTMLToReadableStream} from 'react-dom/server.edge' with {
20
+ env: 'react-client',
21
+ };
12
22
  import {ComponentType, ReactNode} from 'react' with {env: 'react-client'};
13
23
 
14
24
  export interface RSCOptions {
15
25
  // environmentName?: string | (() => string),
16
26
  // filterStackFrame?: (url: string, functionName: string) => boolean,
17
- identifierPrefix?: string,
18
- signal?: AbortSignal,
19
- temporaryReferences?: any,
20
- onError?: (error: unknown) => void,
21
- onPostpone?: (reason: string) => void,
27
+ identifierPrefix?: string;
28
+ signal?: AbortSignal;
29
+ temporaryReferences?: any;
30
+ onError?: (error: unknown) => void;
31
+ onPostpone?: (reason: string) => void;
22
32
  }
23
33
 
24
34
  export function renderRSC(root: any, options?: RSCOptions): ReadableStream {
@@ -26,17 +36,20 @@ export function renderRSC(root: any, options?: RSCOptions): ReadableStream {
26
36
  }
27
37
 
28
38
  export interface RSCToHTMLOptions {
29
- component?: ComponentType,
39
+ component?: ComponentType;
30
40
  identifierPrefix?: string;
31
41
  namespaceURI?: string;
32
42
  nonce?: string;
33
43
  progressiveChunkSize?: number;
34
44
  signal?: AbortSignal;
35
- temporaryReferences?: any,
45
+ temporaryReferences?: any;
36
46
  onError?: (error: unknown, errorInfo?: ErrorInfo) => string | void;
37
47
  }
38
48
 
39
- export async function renderHTML(root: any, options?: RSCToHTMLOptions): Promise<ReadableStream> {
49
+ export async function renderHTML(
50
+ root: any,
51
+ options?: RSCToHTMLOptions,
52
+ ): Promise<ReadableStream> {
40
53
  let rscStream = renderToReadableStream(root, options);
41
54
 
42
55
  // Use client react to render the RSC payload to HTML.
@@ -57,16 +70,22 @@ export async function renderHTML(root: any, options?: RSCToHTMLOptions): Promise
57
70
  }
58
71
 
59
72
  export interface RenderRequestOptions extends RSCToHTMLOptions {
60
- headers?: HeadersInit,
61
- renderError?: (err: Error) => ReactNode
73
+ headers?: HeadersInit;
74
+ renderError?: (err: Error) => ReactNode;
62
75
  }
63
76
 
64
- const temporaryReferencesSymbol = Symbol.for('temporaryReferences')
77
+ const temporaryReferencesSymbol = Symbol.for('temporaryReferences');
65
78
 
66
- export async function renderRequest(request: Request, root: any, options?: RenderRequestOptions): Promise<Response> {
79
+ export async function renderRequest(
80
+ request: Request,
81
+ root: any,
82
+ options?: RenderRequestOptions,
83
+ ): Promise<Response> {
67
84
  options = {
68
85
  ...options,
69
- temporaryReferences: options?.temporaryReferences ?? (request as any)[temporaryReferencesSymbol]
86
+ temporaryReferences:
87
+ options?.temporaryReferences ??
88
+ (request as any)[temporaryReferencesSymbol],
70
89
  };
71
90
 
72
91
  if (request.headers.get('Accept')?.includes('text/html')) {
@@ -75,14 +94,14 @@ export async function renderRequest(request: Request, root: any, options?: Rende
75
94
  return new Response(html, {
76
95
  headers: {
77
96
  ...options?.headers,
78
- 'Content-Type': 'text/html'
79
- }
97
+ 'Content-Type': 'text/html',
98
+ },
80
99
  });
81
100
  } catch (err) {
82
101
  let error = err instanceof Error ? err : new Error(String(err));
83
102
  let res;
84
103
  try {
85
- res = options?.renderError
104
+ res = options?.renderError
86
105
  ? await renderHTML(options.renderError(error), options)
87
106
  : await renderDevError(error, options);
88
107
  } catch {
@@ -92,8 +111,8 @@ export async function renderRequest(request: Request, root: any, options?: Rende
92
111
  status: 500,
93
112
  headers: {
94
113
  ...options?.headers,
95
- 'Content-Type': 'text/html'
96
- }
114
+ 'Content-Type': 'text/html',
115
+ },
97
116
  });
98
117
  }
99
118
  } else {
@@ -101,22 +120,27 @@ export async function renderRequest(request: Request, root: any, options?: Rende
101
120
  return new Response(rscStream, {
102
121
  headers: {
103
122
  ...options?.headers,
104
- 'Content-Type': 'text/x-component'
105
- }
123
+ 'Content-Type': 'text/x-component',
124
+ },
106
125
  });
107
126
  }
108
127
  }
109
128
 
110
- export async function callAction(request: Request, id: string | null | undefined): Promise<{result: any}> {
129
+ export async function callAction(
130
+ request: Request,
131
+ id: string | null | undefined,
132
+ ): Promise<{result: any}> {
111
133
  (request as any)[temporaryReferencesSymbol] ??= createTemporaryReferenceSet();
112
134
 
113
135
  if (id) {
114
136
  let action = await loadServerAction(id);
115
- let body = request.headers.get('content-type')?.includes('multipart/form-data')
137
+ let body = request.headers
138
+ .get('content-type')
139
+ ?.includes('multipart/form-data')
116
140
  ? await request.formData()
117
141
  : await request.text();
118
142
  let args = await decodeReply<any[]>(body, {
119
- temporaryReferences: (request as any)[temporaryReferencesSymbol]
143
+ temporaryReferences: (request as any)[temporaryReferencesSymbol],
120
144
  });
121
145
 
122
146
  let result = action.apply(null, args);
@@ -137,7 +161,10 @@ export async function callAction(request: Request, id: string | null | undefined
137
161
  }
138
162
  }
139
163
 
140
- export async function renderDevError(error: Error, options: RSCToHTMLOptions): Promise<ReadableStream | string> {
164
+ export async function renderDevError(
165
+ error: Error,
166
+ options: RSCToHTMLOptions,
167
+ ): Promise<ReadableStream | string> {
141
168
  if (process.env.NODE_ENV !== 'production') {
142
169
  let content = (
143
170
  <html>
@@ -163,8 +190,8 @@ export async function renderDevError(error: Error, options: RSCToHTMLOptions): P
163
190
  return await renderHTML(content, {
164
191
  ...options,
165
192
  component: {
166
- bootstrapScript
167
- } as any
193
+ bootstrapScript,
194
+ } as any,
168
195
  });
169
196
  } else {
170
197
  return '<h1>Something went wrong!</h1>';