Skip to content

Commit 717975d

Browse files
committed
feat: Pass the children attribute as-is to the react component
Related to #43
1 parent b78918f commit 717975d

File tree

4 files changed

+71
-32
lines changed

4 files changed

+71
-32
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"type": "git",
1111
"url": "https://github.com/bfanger/svelte-preprocess-react.git"
1212
},
13-
"version": "2.0.1",
13+
"version": "2.0.2",
1414
"license": "MIT",
1515
"type": "module",
1616
"scripts": {

src/lib/preprocessReact.js

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -253,37 +253,49 @@ function replaceReactTags(node, content, filename, components = {}) {
253253
}
254254
}
255255
});
256-
if (node.children && node.children.length > 0) {
257-
const isTextContent =
258-
node.children.filter(
259-
(/** @type {any} */ child) =>
260-
["Text", "MustacheTag"].includes(child.type) === false,
261-
).length === 0;
262-
/** @type {string[]} */
263-
const escaped = [];
264-
if (isTextContent) {
265-
// Convert text & expressions into a children prop.
266-
escaped.push('"');
267-
node.children.forEach((/** @type {any} */ child) => {
268-
if (child.type === "Text") {
269-
escaped.push(
270-
child.data.replace(/"/g, `{'"'}`).replace(/\n/g, `{'\\n'}`),
271-
);
272-
} else if (child.type === "MustacheTag") {
273-
const expression = content.original.slice(child.start, child.end);
274-
escaped.push(expression);
275-
} else {
276-
throw new Error(`Unexpected node type:${child.type}`);
277-
}
278-
});
279-
escaped.push('"');
280-
// slot was converted to children prop
281-
content.appendRight(
282-
node.children[0].start - 1,
283-
` react$children=${escaped.join("")} /`,
284-
);
285-
content.remove(node.children[0].start, node.end);
286-
return components;
256+
if (node.children) {
257+
if (node.children.length === 0) {
258+
const childrenProp =
259+
Array.isArray(node.attributes) &&
260+
node.attributes.find(
261+
(/** @type {any} */ attr) => attr.name === "children",
262+
);
263+
if (childrenProp) {
264+
// If children are passed as attribute, pass the value as-is to the react component.
265+
content.appendLeft(childrenProp.start, "react$"); // renames "children" to "react$children"
266+
}
267+
} else {
268+
const isTextContent =
269+
node.children.filter(
270+
(/** @type {any} */ child) =>
271+
["Text", "MustacheTag"].includes(child.type) === false,
272+
).length === 0;
273+
/** @type {string[]} */
274+
const escaped = [];
275+
if (isTextContent) {
276+
// Convert text & expressions into a children prop.
277+
escaped.push('"');
278+
node.children.forEach((/** @type {any} */ child) => {
279+
if (child.type === "Text") {
280+
escaped.push(
281+
child.data.replace(/"/g, `{'"'}`).replace(/\n/g, `{'\\n'}`),
282+
);
283+
} else if (child.type === "MustacheTag") {
284+
const expression = content.original.slice(child.start, child.end);
285+
escaped.push(expression);
286+
} else {
287+
throw new Error(`Unexpected node type:${child.type}`);
288+
}
289+
});
290+
escaped.push('"');
291+
// slot was converted to children prop
292+
content.appendRight(
293+
node.children[0].start - 1,
294+
` react$children=${escaped.join("")} /`,
295+
);
296+
content.remove(node.children[0].start, node.end);
297+
return components;
298+
}
287299
}
288300
}
289301
}

src/routes/render-prop/+page.svelte

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script lang="ts">
2+
import { createElement } from "react";
3+
import Search from "./Search";
4+
5+
let query = $state("123");
6+
const react = sveltify({ Search });
7+
</script>
8+
9+
<input type="search" bind:value={query} />
10+
11+
<react.Search
12+
{query}
13+
children={(results) =>
14+
results.map((result, i) => createElement("div", { key: i }, result))}
15+
/>

src/routes/render-prop/Search.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
type Props = {
2+
query: string;
3+
children: (results: string[]) => React.ReactNode;
4+
};
5+
export default function Search({ query, children }: Props) {
6+
// The number of results is determined by the length of the query string.
7+
const results: string[] = new Array(query.length)
8+
.fill(null)
9+
.map((_, i) => `result ${i + 1} with ${query}`);
10+
11+
return children(results);
12+
}

0 commit comments

Comments
 (0)