From 717975d7ccc956315c9af2594cc5e5f23691c249 Mon Sep 17 00:00:00 2001 From: Bob Fanger Date: Thu, 31 Oct 2024 20:38:36 +0100 Subject: [PATCH] feat: Pass the children attribute as-is to the react component Related to #43 --- package.json | 2 +- src/lib/preprocessReact.js | 74 +++++++++++++++++------------ src/routes/render-prop/+page.svelte | 15 ++++++ src/routes/render-prop/Search.tsx | 12 +++++ 4 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 src/routes/render-prop/+page.svelte create mode 100644 src/routes/render-prop/Search.tsx diff --git a/package.json b/package.json index a604163..8152ed4 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/bfanger/svelte-preprocess-react.git" }, - "version": "2.0.1", + "version": "2.0.2", "license": "MIT", "type": "module", "scripts": { diff --git a/src/lib/preprocessReact.js b/src/lib/preprocessReact.js index f0c97d4..5c8e9a1 100644 --- a/src/lib/preprocessReact.js +++ b/src/lib/preprocessReact.js @@ -253,37 +253,49 @@ function replaceReactTags(node, content, filename, components = {}) { } } }); - if (node.children && node.children.length > 0) { - const isTextContent = - node.children.filter( - (/** @type {any} */ child) => - ["Text", "MustacheTag"].includes(child.type) === false, - ).length === 0; - /** @type {string[]} */ - const escaped = []; - if (isTextContent) { - // Convert text & expressions into a children prop. - escaped.push('"'); - node.children.forEach((/** @type {any} */ child) => { - if (child.type === "Text") { - escaped.push( - child.data.replace(/"/g, `{'"'}`).replace(/\n/g, `{'\\n'}`), - ); - } else if (child.type === "MustacheTag") { - const expression = content.original.slice(child.start, child.end); - escaped.push(expression); - } else { - throw new Error(`Unexpected node type:${child.type}`); - } - }); - escaped.push('"'); - // slot was converted to children prop - content.appendRight( - node.children[0].start - 1, - ` react$children=${escaped.join("")} /`, - ); - content.remove(node.children[0].start, node.end); - return components; + if (node.children) { + if (node.children.length === 0) { + const childrenProp = + Array.isArray(node.attributes) && + node.attributes.find( + (/** @type {any} */ attr) => attr.name === "children", + ); + if (childrenProp) { + // If children are passed as attribute, pass the value as-is to the react component. + content.appendLeft(childrenProp.start, "react$"); // renames "children" to "react$children" + } + } else { + const isTextContent = + node.children.filter( + (/** @type {any} */ child) => + ["Text", "MustacheTag"].includes(child.type) === false, + ).length === 0; + /** @type {string[]} */ + const escaped = []; + if (isTextContent) { + // Convert text & expressions into a children prop. + escaped.push('"'); + node.children.forEach((/** @type {any} */ child) => { + if (child.type === "Text") { + escaped.push( + child.data.replace(/"/g, `{'"'}`).replace(/\n/g, `{'\\n'}`), + ); + } else if (child.type === "MustacheTag") { + const expression = content.original.slice(child.start, child.end); + escaped.push(expression); + } else { + throw new Error(`Unexpected node type:${child.type}`); + } + }); + escaped.push('"'); + // slot was converted to children prop + content.appendRight( + node.children[0].start - 1, + ` react$children=${escaped.join("")} /`, + ); + content.remove(node.children[0].start, node.end); + return components; + } } } } diff --git a/src/routes/render-prop/+page.svelte b/src/routes/render-prop/+page.svelte new file mode 100644 index 0000000..f1ce9b0 --- /dev/null +++ b/src/routes/render-prop/+page.svelte @@ -0,0 +1,15 @@ + + + + + + results.map((result, i) => createElement("div", { key: i }, result))} +/> diff --git a/src/routes/render-prop/Search.tsx b/src/routes/render-prop/Search.tsx new file mode 100644 index 0000000..44884ad --- /dev/null +++ b/src/routes/render-prop/Search.tsx @@ -0,0 +1,12 @@ +type Props = { + query: string; + children: (results: string[]) => React.ReactNode; +}; +export default function Search({ query, children }: Props) { + // The number of results is determined by the length of the query string. + const results: string[] = new Array(query.length) + .fill(null) + .map((_, i) => `result ${i + 1} with ${query}`); + + return children(results); +}