Skip to content

Commit 5925622

Browse files
committed
merge master
1 parent a47080d commit 5925622

File tree

12 files changed

+169
-49
lines changed

12 files changed

+169
-49
lines changed

.changeset/famous-days-invite.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/honest-kangaroos-mix.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# preact-render-to-string
22

3+
## 5.2.6
4+
5+
### Patch Changes
6+
7+
- [#257](https://github.com/preactjs/preact-render-to-string/pull/257) [`8b944b2`](https://github.com/preactjs/preact-render-to-string/commit/8b944b28be64d470a947f999153c9b64b078f7a8) Thanks [@marvinhagemeister](https://github.com/marvinhagemeister)! - Fix `preact/debug` incorrectly throwing errors on text children
8+
9+
## 5.2.5
10+
11+
### Patch Changes
12+
13+
- [#246](https://github.com/preactjs/preact-render-to-string/pull/246) [`ad35c4c`](https://github.com/preactjs/preact-render-to-string/commit/ad35c4c931db37837761038d33ae71fa31ebc9e3) Thanks [@developit](https://github.com/developit) and [@marvinhagemeister](https://github.com/marvinhagemeister)! - Fix object and function children being rendered as `undefined`
14+
15+
* [#248](https://github.com/preactjs/preact-render-to-string/pull/248) [`aa12b3c`](https://github.com/preactjs/preact-render-to-string/commit/aa12b3c61528813c7a3978410d1d551afbdb08ba) Thanks [@marvinhagemeister](https://github.com/marvinhagemeister)! - Fix vnode masks not matching with core due to top level component Fragments
16+
17+
## 5.2.4
18+
19+
### Patch Changes
20+
21+
- [#242](https://github.com/preactjs/preact-render-to-string/pull/242) [`bd5e5eb`](https://github.com/preactjs/preact-render-to-string/commit/bd5e5eb1c97355d81710c17a10208b1cb3b439a0) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - correctly unmount vnodes
22+
23+
* [#237](https://github.com/preactjs/preact-render-to-string/pull/237) [`dec7a7a`](https://github.com/preactjs/preact-render-to-string/commit/dec7a7a575149187942adb92f644c302db4b0599) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - add parent and children for useId
24+
25+
## 5.2.3
26+
27+
### Patch Changes
28+
29+
- [#232](https://github.com/preactjs/preact-render-to-string/pull/232) [`2d5ca74`](https://github.com/preactjs/preact-render-to-string/commit/2d5ca74646f2f9f2e9ddeb20ed9c3fc47171c264) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Performance enhancements
30+
31+
* [#238](https://github.com/preactjs/preact-render-to-string/pull/238) [`7cdf4d6`](https://github.com/preactjs/preact-render-to-string/commit/7cdf4d67abba622124902e53e016affbbebc647e) Thanks [@developit](https://github.com/developit)! - Fix the order of invocation for the "before diff" (`__b`) and "diffed" [options hooks](https://preactjs.com/guide/v10/options/).
32+
333
## 5.2.2
434

535
### Patch Changes

README.md

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
[![NPM](http://img.shields.io/npm/v/preact-render-to-string.svg)](https://www.npmjs.com/package/preact-render-to-string)
44
[![Build status](https://github.com/preactjs/preact-render-to-string/actions/workflows/ci.yml/badge.svg)](https://github.com/preactjs/preact-render-to-string/actions/workflows/ci.yml)
55

6-
Render JSX and [Preact] components to an HTML string.
6+
Render JSX and [Preact](https://github.com/preactjs/preact) components to an HTML string.
77

88
Works in Node & the browser, making it useful for universal/isomorphic rendering.
99

1010
\>\> **[Cute Fox-Related Demo](http://codepen.io/developit/pen/dYZqjE?editors=001)** _(@ CodePen)_ <<
1111

12-
1312
---
1413

15-
1614
### Render JSX/VDOM to HTML
1715

1816
```js
19-
import render from 'preact-render-to-string';
17+
import { render } from 'preact-render-to-string';
2018
import { h } from 'preact';
2119
/** @jsx h */
2220

@@ -27,24 +25,23 @@ console.log(html);
2725
// <div class="foo">content</div>
2826
```
2927

30-
3128
### Render Preact Components to HTML
3229

3330
```js
34-
import render from 'preact-render-to-string';
31+
import { render } from 'preact-render-to-string';
3532
import { h, Component } from 'preact';
3633
/** @jsx h */
3734

3835
// Classical components work
3936
class Fox extends Component {
4037
render({ name }) {
41-
return <span class="fox">{ name }</span>;
38+
return <span class="fox">{name}</span>;
4239
}
4340
}
4441

4542
// ... and so do pure functional components:
4643
const Box = ({ type, children }) => (
47-
<div class={`box box-${type}`}>{ children }</div>
44+
<div class={`box box-${type}`}>{children}</div>
4845
);
4946

5047
let html = render(
@@ -57,22 +54,20 @@ console.log(html);
5754
// <div class="box box-open"><span class="fox">Finn</span></div>
5855
```
5956

60-
6157
---
6258

63-
6459
### Render JSX / Preact / Whatever via Express!
6560

6661
```js
6762
import express from 'express';
6863
import { h } from 'preact';
69-
import render from 'preact-render-to-string';
64+
import { render } from 'preact-render-to-string';
7065
/** @jsx h */
7166

7267
// silly example component:
7368
const Fox = ({ name }) => (
7469
<div class="fox">
75-
<h5>{ name }</h5>
70+
<h5>{name}</h5>
7671
<p>This page is all about {name}.</p>
7772
</div>
7873
);
@@ -89,14 +84,8 @@ app.get('/:fox', (req, res) => {
8984
});
9085
```
9186

92-
9387
---
9488

95-
9689
### License
9790

98-
[MIT]
99-
100-
101-
[Preact]: https://github.com/developit/preact
102-
[MIT]: http://choosealicense.com/licenses/mit/
91+
[MIT](http://choosealicense.com/licenses/mit/)

package-lock.json

Lines changed: 14 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "preact-render-to-string",
33
"amdName": "preactRenderToString",
4-
"version": "5.2.2",
4+
"version": "5.2.6",
55
"description": "Render JSX to an HTML string, with support for Preact components.",
66
"main": "dist/index.js",
77
"umd:main": "dist/index.js",
@@ -31,9 +31,10 @@
3131
"transpile": "microbundle src/index.js -f es,umd --target web --external preact",
3232
"transpile:jsx": "microbundle src/jsx.js -o dist/jsx.js --target web --external preact && microbundle dist/jsx.js -o dist/jsx.js -f cjs --external preact",
3333
"copy-typescript-definition": "copyfiles -f src/*.d.ts dist",
34-
"test": "eslint src test && tsc && npm run test:mocha && npm run bench",
35-
"test:mocha": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/**/[!compat]*.test.js && npm run test:mocha:compat",
36-
"test:mocha:compat": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/compat.test.js",
34+
"test": "eslint src test && tsc && npm run test:mocha && npm run test:mocha:compat && npm run test:mocha:debug && npm run bench",
35+
"test:mocha": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/**/[!compat][!debug]*.test.js",
36+
"test:mocha:compat": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/compat.test.js 'test/compat-*.test.js'",
37+
"test:mocha:debug": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/debug.test.js 'test/debug-*.test.js'",
3738
"format": "prettier src/**/*.{d.ts,js} test/**/*.js --write",
3839
"prepublishOnly": "npm run build",
3940
"release": "npm run build && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
@@ -120,7 +121,7 @@
120121
"lint-staged": "^10.5.3",
121122
"microbundle": "^0.15.1",
122123
"mocha": "^8.2.1",
123-
"preact": "^10.5.7",
124+
"preact": "^10.11.1",
124125
"prettier": "^2.2.1",
125126
"sinon": "^9.2.2",
126127
"sinon-chai": "^3.5.0",

src/index.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ function markAsDirty() {
6464

6565
/**
6666
* @param {VNode} vnode
67-
* @param {any} context
67+
* @param {Record<string, unknown>} context
6868
*/
6969
function renderClassComponent(vnode, context) {
70-
let type = vnode.type;
70+
let type = /** @type {import("preact").ComponentClass<typeof vnode.props>} */ (vnode.type);
7171

7272
let c = new type(vnode.props, context);
7373

@@ -123,6 +123,7 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
123123

124124
// Text VNodes: escape as HTML
125125
if (typeof vnode !== 'object') {
126+
if (typeof vnode === 'function') return '';
126127
return encodeEntities(vnode + '');
127128
}
128129

@@ -131,13 +132,28 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
131132
let rendered = '';
132133
parent[CHILDREN] = vnode;
133134
for (let i = 0; i < vnode.length; i++) {
135+
let child = vnode[i];
136+
if (child == null || typeof child === 'boolean') continue;
137+
134138
rendered =
135139
rendered +
136-
_renderToString(vnode[i], context, isSvgMode, selectValue, parent);
140+
_renderToString(child, context, isSvgMode, selectValue, parent);
141+
142+
if (
143+
typeof child === 'string' ||
144+
typeof child === 'number' ||
145+
typeof child === 'bigint'
146+
) {
147+
// @ts-ignore manually constructing a Text vnode
148+
vnode[i] = h(null, null, child);
149+
}
137150
}
138151
return rendered;
139152
}
140153

154+
// VNodes have {constructor:undefined} to prevent JSON injection:
155+
if (vnode.constructor !== undefined) return '';
156+
141157
vnode[PARENT] = parent;
142158
if (beforeDiff) beforeDiff(vnode);
143159

@@ -198,6 +214,12 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
198214
}
199215
}
200216

217+
// When a component returns a Fragment node we flatten it in core, so we
218+
// need to mirror that logic here too
219+
let isTopLevelFragment =
220+
rendered != null && rendered.type === Fragment && rendered.key == null;
221+
rendered = isTopLevelFragment ? rendered.props.children : rendered;
222+
201223
// Recurse into children before invoking the after-diff hook
202224
const str = _renderToString(
203225
rendered,
@@ -208,6 +230,9 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
208230
);
209231
if (afterDiff) afterDiff(vnode);
210232
vnode[PARENT] = undefined;
233+
234+
if (options.unmount) options.unmount(vnode);
235+
211236
return str;
212237
}
213238

src/pretty.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function _renderToStringPretty(
6565

6666
// #text nodes
6767
if (typeof vnode !== 'object') {
68+
if (typeof vnode === 'function') return '';
6869
return encodeEntities(vnode + '');
6970
}
7071

@@ -91,6 +92,9 @@ function _renderToStringPretty(
9192

9293
if (options[DIFF]) options[DIFF](vnode);
9394

95+
// VNodes have {constructor:undefined} to prevent JSON injection:
96+
if (vnode.constructor !== undefined) return '';
97+
9498
let nodeName = vnode.type,
9599
props = vnode.props,
96100
isComponent = false;

test/debug.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'preact/debug';
2+
import render from '../src';
3+
import { h } from 'preact';
4+
import { expect } from 'chai';
5+
6+
describe('debug', () => {
7+
it('should not throw "Objects are not valid as a child" error', () => {
8+
expect(() => render(<p>{'foo'}</p>)).not.to.throw();
9+
expect(() => render(<p>{2}</p>)).not.to.throw();
10+
expect(() => render(<p>{true}</p>)).not.to.throw();
11+
expect(() => render(<p>{false}</p>)).not.to.throw();
12+
expect(() => render(<p>{null}</p>)).not.to.throw();
13+
expect(() => render(<p>{undefined}</p>)).not.to.throw();
14+
});
15+
16+
it('should not throw "Objects are not valid as a child" error #2', () => {
17+
function Str() {
18+
return ['foo'];
19+
}
20+
expect(() => render(<Str />)).not.to.throw();
21+
});
22+
23+
it('should not throw "Objects are not valid as a child" error #3', () => {
24+
expect(() => render(<p>{'foo'}bar</p>)).not.to.throw();
25+
});
26+
});

test/jsx.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,12 @@ describe('jsx', () => {
163163
<meta charset="utf-8" />
164164
`);
165165
});
166+
167+
it('should prevent JSON injection', () => {
168+
expect(renderJsx(<div>{{ hello: 'world' }}</div>)).to.equal('<div></div>');
169+
});
170+
171+
it('should not render function children', () => {
172+
expect(renderJsx(<div>{() => {}}</div>)).to.equal('<div></div>');
173+
});
166174
});

0 commit comments

Comments
 (0)