Skip to content

Commit 78f365f

Browse files
committed
add parent and children for useId
1 parent 2d5ca74 commit 78f365f

File tree

5 files changed

+89
-59
lines changed

5 files changed

+89
-59
lines changed

.changeset/young-cougars-protect.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': minor
3+
---
4+
5+
add \_children and \_parent to the vnode

package-lock.json

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

src/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const SKIP_EFFECTS = '__s';
77

88
// VNode properties
99
export const COMPONENT = '__c';
10+
export const PARENT = '__';
11+
export const CHILDREN = '__k';
1012

1113
// Component properties
1214
export const VNODE = '__v';

src/index.js

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
NEXT_STATE,
1919
RENDER,
2020
SKIP_EFFECTS,
21-
VNODE
21+
VNODE,
22+
PARENT,
23+
CHILDREN
2224
} from './constants';
2325

2426
/** @typedef {import('preact').VNode} VNode */
@@ -72,7 +74,7 @@ function renderToString(vnode, context, opts) {
7274
) {
7375
res = _renderToStringPretty(vnode, context, opts);
7476
} else {
75-
res = _renderToString(vnode, context, false, undefined);
77+
res = _renderToString(vnode, context, false, undefined, vnode);
7678
}
7779

7880
// options._commit, we don't schedule any effects in this library right now,
@@ -181,7 +183,7 @@ const isArray = Array.isArray;
181183
const assign = Object.assign;
182184

183185
/** The default export is an alias of `render()`. */
184-
function _renderToString(vnode, context, isSvgMode, selectValue) {
186+
function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
185187
if (vnode == null || vnode === true || vnode === false || vnode === '') {
186188
return '';
187189
}
@@ -193,9 +195,15 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
193195

194196
if (isArray(vnode)) {
195197
let rendered = '';
198+
parent[CHILDREN] = [];
196199
for (let i = 0; i < vnode.length; i++) {
200+
if (typeof vnode[i] === 'object' && parent) {
201+
vnode[i][PARENT] = parent;
202+
parent[CHILDREN].push(vnode[i]);
203+
}
197204
rendered =
198-
rendered + _renderToString(vnode[i], context, isSvgMode, selectValue);
205+
rendered +
206+
_renderToString(vnode[i], context, isSvgMode, selectValue, parent);
199207
}
200208
return rendered;
201209
}
@@ -206,12 +214,14 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
206214

207215
// components
208216
if (isComponent) {
217+
vnode[PARENT] = parent;
209218
if (nodeName === Fragment) {
210219
return _renderToString(
211220
vnode.props.children,
212221
context,
213222
isSvgMode,
214-
selectValue
223+
selectValue,
224+
vnode
215225
);
216226
}
217227

@@ -231,7 +241,8 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
231241

232242
if (options[DIFFED]) options[DIFFED](vnode);
233243

234-
return _renderToString(rendered, context, isSvgMode, selectValue);
244+
vnode[CHILDREN] = Array.isArray(rendered) ? rendered : [rendered];
245+
return _renderToString(rendered, context, isSvgMode, selectValue, vnode);
235246
}
236247

237248
// render JSX to HTML
@@ -246,36 +257,6 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
246257
for (let name in props) {
247258
let v = props[name];
248259

249-
// switch (name) {
250-
// case 'className':
251-
// if ('class' in props) continue;
252-
// name = 'class';
253-
// break;
254-
// case 'htmlFor':
255-
// if ('for' in props) continue;
256-
// name = 'for';
257-
// break;
258-
// case 'defaultValue':
259-
// name = 'value';
260-
// break;
261-
// case 'defaultChecked':
262-
// name = 'checked';
263-
// break;
264-
// case 'defaultSelected':
265-
// name = 'selected';
266-
// break;
267-
// case 'key':
268-
// case 'ref':
269-
// case '__self':
270-
// case '__source':
271-
// case 'children':
272-
// continue;
273-
// default:
274-
// if (isSvgMode && XLINK.test(name)) {
275-
// name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
276-
// }
277-
// }
278-
279260
if (
280261
name === 'key' ||
281262
name === 'ref' ||
@@ -335,11 +316,6 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
335316
let pieces = '';
336317
let hasChildren = false;
337318

338-
// let children = isArray(propChildren)
339-
// ? propChildren
340-
// : propChildren != null
341-
// ? [propChildren]
342-
// : undefined;
343319
if (html) {
344320
// return s + html + '</' + nodeName + '>';
345321
// s = s + html;
@@ -350,13 +326,24 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
350326
pieces = pieces + encodeEntities(children);
351327
hasChildren = true;
352328
} else if (isArray(children)) {
329+
vnode[CHILDREN] = [];
330+
353331
for (let i = 0; i < children.length; i++) {
354332
let child = children[i];
355-
333+
vnode[CHILDREN].push(child);
356334
if (child != null && child !== false) {
335+
if (typeof child === 'object') {
336+
child[PARENT] = vnode;
337+
}
357338
let childSvgMode =
358339
nodeName === 'svg' || (nodeName !== 'foreignObject' && isSvgMode);
359-
let ret = _renderToString(child, context, childSvgMode, selectValue);
340+
let ret = _renderToString(
341+
child,
342+
context,
343+
childSvgMode,
344+
selectValue,
345+
vnode
346+
);
360347

361348
// Skip if we received an empty string
362349
if (ret) {
@@ -367,9 +354,19 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
367354
}
368355
}
369356
} else if (children != null && children !== false && children !== true) {
357+
vnode[CHILDREN] = [children];
358+
if (typeof children === 'object') {
359+
children[PARENT] = vnode;
360+
}
370361
let childSvgMode =
371362
nodeName === 'svg' || (nodeName !== 'foreignObject' && isSvgMode);
372-
let ret = _renderToString(children, context, childSvgMode, selectValue);
363+
let ret = _renderToString(
364+
children,
365+
context,
366+
childSvgMode,
367+
selectValue,
368+
vnode
369+
);
373370

374371
// Skip if we received an empty string
375372
if (ret) {

test/render.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,45 @@ describe('render', () => {
1919
expect(rendered).to.equal(expected);
2020
});
2121

22+
describe('setting vnode properties', () => {
23+
it('sets the parent and children attributes for normal nodes', () => {
24+
const paragraph = <p>hi</p>;
25+
const root = <div>{paragraph}</div>;
26+
const rendered = render(root);
27+
28+
expect(rendered).to.equal(`<div><p>hi</p></div>`);
29+
expect(root.__k).to.deep.equal([paragraph]);
30+
expect(paragraph.__).to.deep.equal(root);
31+
});
32+
33+
it('sets the parent and children attributes for functions', () => {
34+
const Root = () => <p>hi</p>;
35+
const root = <Root />;
36+
const wrapper = <div>{root}</div>;
37+
const rendered = render(wrapper);
38+
39+
expect(rendered).to.equal(`<div><p>hi</p></div>`);
40+
expect(wrapper.__k).to.deep.equal([root]);
41+
expect(root.__).to.deep.equal(wrapper);
42+
});
43+
44+
it('sets the parent and children attributes for nested functions', () => {
45+
const Root = (props) => props.children;
46+
const Child = () => (
47+
<div>
48+
<p>hi</p>
49+
</div>
50+
);
51+
const child = <Child />;
52+
const root = <Root>{child}</Root>;
53+
const rendered = render(root);
54+
55+
expect(rendered).to.equal(`<div><p>hi</p></div>`);
56+
expect(root.__k).to.deep.equal([child]);
57+
expect(child.__).to.deep.equal(root);
58+
});
59+
});
60+
2261
describe('whitespace', () => {
2362
it('should omit whitespace between elements', () => {
2463
let children = [];

0 commit comments

Comments
 (0)