Skip to content

Commit 5ac0c30

Browse files
Merge pull request #257 from preactjs/invalid-child-debug
2 parents 1aaf2fe + 8b944b2 commit 5ac0c30

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

.changeset/brown-boats-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'preact-render-to-string': patch
3+
---
4+
5+
Fix `preact/debug` incorrectly throwing errors on text children

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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 test:mocha:compat && npm run bench",
35-
"test:mocha": "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",
3636
"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"

src/index.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ function renderClassComponent(vnode, context) {
165165
return c.render(c.props, c.state, c.context);
166166
}
167167

168+
/**
169+
* @param {any} vnode
170+
* @returns {VNode}
171+
*/
172+
function normalizeVNode(vnode) {
173+
if (vnode == null || typeof vnode == 'boolean') {
174+
return null;
175+
} else if (
176+
typeof vnode == 'string' ||
177+
typeof vnode == 'number' ||
178+
typeof vnode == 'bigint'
179+
) {
180+
return h(null, null, vnode);
181+
}
182+
return vnode;
183+
}
184+
168185
/**
169186
* @param {string} name
170187
* @param {boolean} isSvgMode
@@ -237,6 +254,8 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
237254
rendered =
238255
rendered +
239256
_renderToString(vnode[i], context, isSvgMode, selectValue, parent);
257+
258+
vnode[i] = normalizeVNode(vnode[i]);
240259
}
241260
return rendered;
242261
}
@@ -372,6 +391,8 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
372391
vnode[CHILDREN] = children;
373392
for (let i = 0; i < children.length; i++) {
374393
let child = children[i];
394+
children[i] = normalizeVNode(child);
395+
375396
if (child != null && child !== false) {
376397
let childSvgMode =
377398
type === 'svg' || (type !== 'foreignObject' && isSvgMode);
@@ -391,7 +412,7 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
391412
}
392413
}
393414
} else if (children != null && children !== false && children !== true) {
394-
vnode[CHILDREN] = [children];
415+
vnode[CHILDREN] = [normalizeVNode(children)];
395416
let childSvgMode =
396417
type === 'svg' || (type !== 'foreignObject' && isSvgMode);
397418
let ret = _renderToString(

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+
});

0 commit comments

Comments
 (0)