Publish `experimental_renderToHTML` (#30690)
Sebastian Silbermann committed
Aug 15, 2024 at 08:50 UTC
cea13feb3449317d8570ba17603747497037d264
6 files changed
+56
-30
packages/react-markup/README.md
+5
-1
@@ -11,7 +11,7 @@ npm install react react-markup
11
## Usage
12
13
```js
14
-import { renderToHTML } from 'react-markup';
14
+import { experimental_renderToHTML as renderToHTML } from 'react-markup';
15
import EmailTemplate from './my-email-template-component.js'
16
17
async function action(email, name) {
@@ -30,3 +30,7 @@ Note that this is an async function that needs to be awaited - unlike the legacy
30
### `react-markup`
31
32
See https://react.dev/reference/react-markup
33
+
34
+## Thanks
35
+
36
+The React team thanks [Nikolai Mavrenkov](https://www.koluch.ru/) for donating the `react-markup` package name.
packages/react-markup/package.json
+1
-2
@@ -1,8 +1,7 @@
1
{
2
"name": "react-markup",
3
"version": "19.0.0",
4
- "private": true,
5
- "description": "React package generating embedded HTML markup such as e-mails using Server Components.",
4
+ "description": "React package generating embedded markup such as e-mails with support for Server Components.",
5
"main": "index.js",
6
"repository": {
7
"type": "git",
packages/react-markup/src/ReactMarkupClient.js
+1
-1
@@ -31,7 +31,7 @@ type MarkupOptions = {
31
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
32
};
33
34
-export function renderToHTML(
34
+export function experimental_renderToHTML(
35
children: ReactNodeList,
36
options?: MarkupOptions,
37
): Promise<string> {
packages/react-markup/src/ReactMarkupServer.js
+1
-1
@@ -75,7 +75,7 @@ function noServerCallOrFormAction() {
75
);
76
}
77
78
-export function renderToHTML(
78
+export function experimental_renderToHTML(
79
children: ReactMarkupNodeList,
80
options?: MarkupOptions,
81
): Promise<string> {
packages/react-markup/src/__tests__/ReactMarkupClient-test.js
+18
-12
@@ -43,7 +43,7 @@ if (!__EXPERIMENTAL__) {
43
return <div>hello world</div>;
44
}
45
46
- const html = await ReactMarkup.renderToHTML(<Component />);
46
+ const html = await ReactMarkup.experimental_renderToHTML(<Component />);
47
expect(html).toBe('<div>hello world</div>');
48
});
49
@@ -52,14 +52,14 @@ if (!__EXPERIMENTAL__) {
52
return <div>{'hello '.repeat(200)}world</div>;
53
}
54
55
- const html = await ReactMarkup.renderToHTML(
55
+ const html = await ReactMarkup.experimental_renderToHTML(
56
React.createElement(Component),
57
);
58
expect(html).toBe('<div>' + ('hello '.repeat(200) + 'world') + '</div>');
59
});
60
61
it('should prefix html tags with a doctype', async () => {
62
- const html = await ReactMarkup.renderToHTML(
62
+ const html = await ReactMarkup.experimental_renderToHTML(
63
<html>
64
<body>hello</body>
65
</html>,
@@ -76,8 +76,10 @@ if (!__EXPERIMENTAL__) {
76
}
77
78
await expect(async () => {
79
- await ReactMarkup.renderToHTML(<Component />);
80
- }).rejects.toThrow();
79
+ await ReactMarkup.experimental_renderToHTML(<Component />);
80
+ }).rejects.toThrow(
81
+ 'Cannot use state or effect Hooks in renderToHTML because this component will never be hydrated.',
82
+ );
83
});
84
85
it('should error on refs passed to host components', async () => {
@@ -87,8 +89,10 @@ if (!__EXPERIMENTAL__) {
89
}
90
91
await expect(async () => {
90
- await ReactMarkup.renderToHTML(<Component />);
91
- }).rejects.toThrow();
92
+ await ReactMarkup.experimental_renderToHTML(<Component />);
93
+ }).rejects.toThrow(
94
+ 'Cannot pass ref in renderToHTML because they will never be hydrated.',
95
+ );
96
});
97
98
it('should error on callbacks passed to event handlers', async () => {
@@ -100,8 +104,10 @@ if (!__EXPERIMENTAL__) {
104
}
105
106
await expect(async () => {
103
- await ReactMarkup.renderToHTML(<Component />);
104
- }).rejects.toThrow();
107
+ await ReactMarkup.experimental_renderToHTML(<Component />);
108
+ }).rejects.toThrow(
109
+ 'Cannot pass event handlers (onClick) in renderToHTML because the HTML will never be hydrated so they can never get called.',
110
+ );
111
});
112
113
it('supports the useId Hook', async () => {
@@ -142,7 +148,7 @@ if (!__EXPERIMENTAL__) {
148
);
149
}
150
145
- const html = await ReactMarkup.renderToHTML(<Component />);
151
+ const html = await ReactMarkup.experimental_renderToHTML(<Component />);
152
const container = document.createElement('div');
153
container.innerHTML = html;
154
@@ -176,7 +182,7 @@ if (!__EXPERIMENTAL__) {
182
);
183
}
184
179
- const html = await ReactMarkup.renderToHTML(<Component />);
185
+ const html = await ReactMarkup.experimental_renderToHTML(<Component />);
186
expect(html).toBe('<div>01</div>');
187
});
188
@@ -199,7 +205,7 @@ if (!__EXPERIMENTAL__) {
205
}
206
207
await expect(async () => {
202
- await ReactMarkup.renderToHTML(
208
+ await ReactMarkup.experimental_renderToHTML(
209
<div>
210
<Foo />
211
</div>,
packages/react-markup/src/__tests__/ReactMarkupServer-test.js
+30
-13
@@ -64,7 +64,7 @@ if (!__EXPERIMENTAL__) {
64
return React.createElement('div', null, 'hello world');
65
}
66
67
- const html = await ReactMarkup.renderToHTML(
67
+ const html = await ReactMarkup.experimental_renderToHTML(
68
React.createElement(Component),
69
);
70
expect(html).toBe('<div>hello world</div>');
@@ -76,15 +76,14 @@ if (!__EXPERIMENTAL__) {
76
return React.createElement('div', null, 'hello '.repeat(200) + 'world');
77
}
78
79
- const html = await ReactMarkup.renderToHTML(
79
+ const html = await ReactMarkup.experimental_renderToHTML(
80
React.createElement(Component),
81
);
82
expect(html).toBe('<div>' + ('hello '.repeat(200) + 'world') + '</div>');
83
});
84
85
it('should prefix html tags with a doctype', async () => {
86
- const html = await ReactMarkup.renderToHTML(
87
- // We can't use JSX because that's client-JSX in our tests.
86
+ const html = await ReactMarkup.experimental_renderToHTML(
87
React.createElement(
88
'html',
89
null,
@@ -104,8 +103,10 @@ if (!__EXPERIMENTAL__) {
103
}
104
105
await expect(async () => {
107
- await ReactMarkup.renderToHTML(React.createElement(Component));
108
- }).rejects.toThrow();
106
+ await ReactMarkup.experimental_renderToHTML(
107
+ React.createElement(Component),
108
+ );
109
+ }).rejects.toThrow('React.useState is not a function');
110
});
111
112
it('should error on refs passed to host components', async () => {
@@ -116,8 +117,12 @@ if (!__EXPERIMENTAL__) {
117
}
118
119
await expect(async () => {
119
- await ReactMarkup.renderToHTML(React.createElement(Component));
120
- }).rejects.toThrow();
120
+ await ReactMarkup.experimental_renderToHTML(
121
+ React.createElement(Component),
122
+ );
123
+ }).rejects.toThrow(
124
+ 'Refs cannot be used in Server Components, nor passed to Client Components.',
125
+ );
126
});
127
128
it('should error on callbacks passed to event handlers', async () => {
@@ -130,8 +135,20 @@ if (!__EXPERIMENTAL__) {
135
}
136
137
await expect(async () => {
133
- await ReactMarkup.renderToHTML(React.createElement(Component));
134
- }).rejects.toThrow();
138
+ await ReactMarkup.experimental_renderToHTML(
139
+ React.createElement(Component),
140
+ );
141
+ }).rejects.toThrowError(
142
+ __DEV__
143
+ ? `Event handlers cannot be passed to Client Component props.\n` +
144
+ ' <div onClick={function onClick}>\n' +
145
+ ' ^^^^^^^^^^^^^^^^^^\n' +
146
+ 'If you need interactivity, consider converting part of this to a Client Component.'
147
+ : `Event handlers cannot be passed to Client Component props.\n` +
148
+ ' {onClick: function onClick}\n' +
149
+ ' ^^^^^^^^^^^^^^^^\n' +
150
+ 'If you need interactivity, consider converting part of this to a Client Component.',
151
+ );
152
});
153
154
it('supports the useId Hook', async () => {
@@ -173,7 +190,7 @@ if (!__EXPERIMENTAL__) {
190
);
191
}
192
176
- const html = await ReactMarkup.renderToHTML(
193
+ const html = await ReactMarkup.experimental_renderToHTML(
194
React.createElement(Component),
195
);
196
const container = document.createElement('div');
@@ -204,7 +221,7 @@ if (!__EXPERIMENTAL__) {
221
return React.createElement('div', null, a, b);
222
}
223
207
- const html = await ReactMarkup.renderToHTML(
224
+ const html = await ReactMarkup.experimental_renderToHTML(
225
React.createElement(Component),
226
);
227
expect(html).toBe('<div>00</div>');
@@ -225,7 +242,7 @@ if (!__EXPERIMENTAL__) {
242
}
243
244
await expect(async () => {
228
- await ReactMarkup.renderToHTML(
245
+ await ReactMarkup.experimental_renderToHTML(
246
React.createElement('div', null, React.createElement(Foo)),
247
{
248
onError(error, errorInfo) {