diff options
-rw-r--r-- | .changeset/yellow-snakes-jam.md | 21 | ||||
-rw-r--r-- | packages/astro/test/fixtures/react-component/src/components/WithChildren.jsx | 5 | ||||
-rw-r--r-- | packages/integrations/react/README.md | 40 | ||||
-rw-r--r-- | packages/integrations/react/package.json | 8 | ||||
-rw-r--r-- | packages/integrations/react/server.js | 6 | ||||
-rw-r--r-- | packages/integrations/react/src/index.ts | 36 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/astro.config.mjs (renamed from packages/astro/test/fixtures/react-component/astro.config.mjs) | 6 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/package.json (renamed from packages/astro/test/fixtures/react-component/package.json) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/ArrowFunction.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/ArrowFunction.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/CloneElement.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/CloneElement.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/ForgotImport.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/ForgotImport.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/GetSearch.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/GetSearch.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/Goodbye.vue (renamed from packages/astro/test/fixtures/react-component/src/components/Goodbye.vue) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/Hello.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/Hello.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/LazyComponent.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/LazyComponent.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/PragmaComment.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/PragmaComment.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx (renamed from packages/astro/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/PropsSpread.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/PropsSpread.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/Pure.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/Pure.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/Research.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/Research.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/Suspense.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/Suspense.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/ThrowsAnError.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/ThrowsAnError.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/TypeScriptComponent.tsx (renamed from packages/astro/test/fixtures/react-component/src/components/TypeScriptComponent.tsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/WithChildren.jsx | 10 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/components/WithId.jsx (renamed from packages/astro/test/fixtures/react-component/src/components/WithId.jsx) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/pages/children.astro | 14 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/pages/error-rendering.astro (renamed from packages/astro/test/fixtures/react-component/src/pages/error-rendering.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/pages/index.astro (renamed from packages/astro/test/fixtures/react-component/src/pages/index.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/pages/pragma-comment.astro (renamed from packages/astro/test/fixtures/react-component/src/pages/pragma-comment.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/pages/suspense.astro (renamed from packages/astro/test/fixtures/react-component/src/pages/suspense.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/skipped-pages/forgot-import.astro (renamed from packages/astro/test/fixtures/react-component/src/skipped-pages/forgot-import.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/fixtures/react-component/src/skipped-pages/window.astro (renamed from packages/astro/test/fixtures/react-component/src/skipped-pages/window.astro) | 0 | ||||
-rw-r--r-- | packages/integrations/react/test/react-component.test.js (renamed from packages/astro/test/react-component.test.js) | 14 | ||||
-rw-r--r-- | packages/integrations/react/vnode-children.js | 38 | ||||
-rw-r--r-- | pnpm-lock.yaml | 58 |
36 files changed, 218 insertions, 38 deletions
diff --git a/.changeset/yellow-snakes-jam.md b/.changeset/yellow-snakes-jam.md new file mode 100644 index 000000000..87c4d1da7 --- /dev/null +++ b/.changeset/yellow-snakes-jam.md @@ -0,0 +1,21 @@ +--- +'@astrojs/react': minor +--- + +Optionally parse React slots as React children. + +This adds a new configuration option for the React integration `experimentalReactChildren`: + +```js +export default { + integrations: [ + react({ + experimentalReactChildren: true, + }) + ] +} +``` + +With this enabled, children passed to React from Astro components via the default slot are parsed as React components. + +This enables better compatibility with certain React components which manipulate their children. diff --git a/packages/astro/test/fixtures/react-component/src/components/WithChildren.jsx b/packages/astro/test/fixtures/react-component/src/components/WithChildren.jsx deleted file mode 100644 index cdcb0e0a6..000000000 --- a/packages/astro/test/fixtures/react-component/src/components/WithChildren.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export default function ({ children }) { - return <div className="with-children">{children}</div>; -} diff --git a/packages/integrations/react/README.md b/packages/integrations/react/README.md index 48c45881f..7504490e0 100644 --- a/packages/integrations/react/README.md +++ b/packages/integrations/react/README.md @@ -61,6 +61,46 @@ To use your first React component in Astro, head to our [UI framework documentat - 💧 client-side hydration options, and - 🤝 opportunities to mix and nest frameworks together +## Options + +### Children parsing + +Children passed into a React component from an Astro component are parsed as plain strings, not React nodes. + +For example, the `<ReactComponent />` below will only receive a single child element: + +```astro +--- +import ReactComponent from './ReactComponent'; +--- + +<ReactComponent> + <div>one</div> + <div>two</div> +</ReactComponent> +``` + +If you are using a library that *expects* more than one child element element to be passed, for example so that it can slot certain elements in different places, you might find this to be a blocker. + +You can set the experimental flag `experimentalReactChildren` to tell Astro to always pass children to React as React vnodes. There is some runtime cost to this, but it can help with compatibility. + +You can enable this option in the configuration for the React integration: + +```js +// astro.config.mjs +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; + +export default defineConfig({ + // ... + integrations: [ + react({ + experimentalReactChildren: true, + }) + ], +}); +``` + ## Troubleshooting For help, check out the `#support` channel on [Discord](https://astro.build/chat). Our friendly Support Squad members are here to help! diff --git a/packages/integrations/react/package.json b/packages/integrations/react/package.json index 31527a9e4..e238e21b9 100644 --- a/packages/integrations/react/package.json +++ b/packages/integrations/react/package.json @@ -45,7 +45,8 @@ }, "dependencies": { "@babel/core": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.5" + "@babel/plugin-transform-react-jsx": "^7.22.5", + "ultrahtml": "^1.2.0" }, "devDependencies": { "@types/react": "^17.0.62", @@ -53,7 +54,10 @@ "astro": "workspace:*", "astro-scripts": "workspace:*", "react": "^18.1.0", - "react-dom": "^18.1.0" + "react-dom": "^18.1.0", + "chai": "^4.3.7", + "cheerio": "1.0.0-rc.12", + "vite": "^4.4.6" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21", diff --git a/packages/integrations/react/server.js b/packages/integrations/react/server.js index 8c02c4b26..3f0d93e97 100644 --- a/packages/integrations/react/server.js +++ b/packages/integrations/react/server.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom/server'; import StaticHtml from './static-html.js'; import { incrementId } from './context.js'; +import opts from 'astro:react:opts'; const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); const reactTypeof = Symbol.for('react.element'); @@ -85,7 +86,10 @@ async function renderToStaticMarkup(Component, props, { default: children, ...sl ...slots, }; const newChildren = children ?? props.children; - if (newChildren != null) { + if (children && opts.experimentalReactChildren) { + const convert = await import('./vnode-children.js').then(mod => mod.default); + newProps.children = convert(children); + } else if (newChildren != null) { newProps.children = React.createElement(StaticHtml, { hydrate: needsHydration(metadata), value: newChildren, diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts index d7906fe4a..556062282 100644 --- a/packages/integrations/react/src/index.ts +++ b/packages/integrations/react/src/index.ts @@ -1,5 +1,6 @@ import type { AstroIntegration } from 'astro'; import { version as ReactVersion } from 'react-dom'; +import type * as vite from 'vite'; function getRenderer() { return { @@ -36,7 +37,29 @@ function getRenderer() { }; } -function getViteConfiguration() { +function optionsPlugin(experimentalReactChildren: boolean): vite.Plugin { + const virtualModule = 'astro:react:opts'; + const virtualModuleId = '\0' + virtualModule; + return { + name: '@astrojs/react:opts', + resolveId(id) { + if(id === virtualModule) { + return virtualModuleId; + } + }, + load(id) { + if(id === virtualModuleId) { + return { + code: `export default { + experimentalReactChildren: ${JSON.stringify(experimentalReactChildren)} + }` + }; + } + } + }; +} + +function getViteConfiguration(experimentalReactChildren: boolean) { return { optimizeDeps: { include: [ @@ -70,16 +93,23 @@ function getViteConfiguration() { 'use-immer', ], }, + plugins: [ + optionsPlugin(experimentalReactChildren) + ] }; } -export default function (): AstroIntegration { +export type ReactIntegrationOptions = { + experimentalReactChildren: boolean; +} + +export default function ({ experimentalReactChildren }: ReactIntegrationOptions = { experimentalReactChildren: false }): AstroIntegration { return { name: '@astrojs/react', hooks: { 'astro:config:setup': ({ addRenderer, updateConfig }) => { addRenderer(getRenderer()); - updateConfig({ vite: getViteConfiguration() }); + updateConfig({ vite: getViteConfiguration(experimentalReactChildren) }); }, }, }; diff --git a/packages/astro/test/fixtures/react-component/astro.config.mjs b/packages/integrations/react/test/fixtures/react-component/astro.config.mjs index 53d0bd03b..cd54d60f8 100644 --- a/packages/astro/test/fixtures/react-component/astro.config.mjs +++ b/packages/integrations/react/test/fixtures/react-component/astro.config.mjs @@ -4,5 +4,7 @@ import vue from '@astrojs/vue'; // https://astro.build/config export default defineConfig({ - integrations: [react(), vue()], -});
\ No newline at end of file + integrations: [react({ + experimentalReactChildren: true, + }), vue()], +}); diff --git a/packages/astro/test/fixtures/react-component/package.json b/packages/integrations/react/test/fixtures/react-component/package.json index cf7b2b057..cf7b2b057 100644 --- a/packages/astro/test/fixtures/react-component/package.json +++ b/packages/integrations/react/test/fixtures/react-component/package.json diff --git a/packages/astro/test/fixtures/react-component/src/components/ArrowFunction.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/ArrowFunction.jsx index 16fac5bb6..16fac5bb6 100644 --- a/packages/astro/test/fixtures/react-component/src/components/ArrowFunction.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/ArrowFunction.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/CloneElement.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/CloneElement.jsx index 809ac4aa4..809ac4aa4 100644 --- a/packages/astro/test/fixtures/react-component/src/components/CloneElement.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/CloneElement.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/ForgotImport.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/ForgotImport.jsx index 9ee27faca..9ee27faca 100644 --- a/packages/astro/test/fixtures/react-component/src/components/ForgotImport.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/ForgotImport.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/GetSearch.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/GetSearch.jsx index d3fee2f9a..d3fee2f9a 100644 --- a/packages/astro/test/fixtures/react-component/src/components/GetSearch.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/GetSearch.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/Goodbye.vue b/packages/integrations/react/test/fixtures/react-component/src/components/Goodbye.vue index 430dfdb71..430dfdb71 100644 --- a/packages/astro/test/fixtures/react-component/src/components/Goodbye.vue +++ b/packages/integrations/react/test/fixtures/react-component/src/components/Goodbye.vue diff --git a/packages/astro/test/fixtures/react-component/src/components/Hello.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/Hello.jsx index 4c241162d..4c241162d 100644 --- a/packages/astro/test/fixtures/react-component/src/components/Hello.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/Hello.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx index d6ff21dc3..d6ff21dc3 100644 --- a/packages/astro/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/ImportsThrowsAnError.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/LazyComponent.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/LazyComponent.jsx index b43aa36be..b43aa36be 100644 --- a/packages/astro/test/fixtures/react-component/src/components/LazyComponent.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/LazyComponent.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/PragmaComment.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/PragmaComment.jsx index d8ea77810..d8ea77810 100644 --- a/packages/astro/test/fixtures/react-component/src/components/PragmaComment.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/PragmaComment.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx b/packages/integrations/react/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx index 9f2256fbf..9f2256fbf 100644 --- a/packages/astro/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/PragmaCommentTypeScript.tsx diff --git a/packages/astro/test/fixtures/react-component/src/components/PropsSpread.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/PropsSpread.jsx index 044c2a019..044c2a019 100644 --- a/packages/astro/test/fixtures/react-component/src/components/PropsSpread.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/PropsSpread.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/Pure.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/Pure.jsx index 6fae8613b..6fae8613b 100644 --- a/packages/astro/test/fixtures/react-component/src/components/Pure.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/Pure.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/Research.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/Research.jsx index 9ab83e5f3..9ab83e5f3 100644 --- a/packages/astro/test/fixtures/react-component/src/components/Research.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/Research.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/Suspense.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/Suspense.jsx index 87dc82625..87dc82625 100644 --- a/packages/astro/test/fixtures/react-component/src/components/Suspense.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/Suspense.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/ThrowsAnError.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/ThrowsAnError.jsx index cf970e38c..cf970e38c 100644 --- a/packages/astro/test/fixtures/react-component/src/components/ThrowsAnError.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/ThrowsAnError.jsx diff --git a/packages/astro/test/fixtures/react-component/src/components/TypeScriptComponent.tsx b/packages/integrations/react/test/fixtures/react-component/src/components/TypeScriptComponent.tsx index bde96da84..bde96da84 100644 --- a/packages/astro/test/fixtures/react-component/src/components/TypeScriptComponent.tsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/TypeScriptComponent.tsx diff --git a/packages/integrations/react/test/fixtures/react-component/src/components/WithChildren.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/WithChildren.jsx new file mode 100644 index 000000000..500c0c694 --- /dev/null +++ b/packages/integrations/react/test/fixtures/react-component/src/components/WithChildren.jsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function ({ children }) { + return ( + <div> + <div className="with-children">{children}</div> + <div className="with-children-count">{children.length}</div> + </div> + ); +} diff --git a/packages/astro/test/fixtures/react-component/src/components/WithId.jsx b/packages/integrations/react/test/fixtures/react-component/src/components/WithId.jsx index 0abe91c72..0abe91c72 100644 --- a/packages/astro/test/fixtures/react-component/src/components/WithId.jsx +++ b/packages/integrations/react/test/fixtures/react-component/src/components/WithId.jsx diff --git a/packages/integrations/react/test/fixtures/react-component/src/pages/children.astro b/packages/integrations/react/test/fixtures/react-component/src/pages/children.astro new file mode 100644 index 000000000..59595c266 --- /dev/null +++ b/packages/integrations/react/test/fixtures/react-component/src/pages/children.astro @@ -0,0 +1,14 @@ +--- +import WithChildren from '../components/WithChildren'; +--- + +<html> + <head> + <!-- Head Stuff --> + </head> + <body> + <WithChildren> + <div>child 1</div><div>child 2</div> + </WithChildren> + </body> +</html> diff --git a/packages/astro/test/fixtures/react-component/src/pages/error-rendering.astro b/packages/integrations/react/test/fixtures/react-component/src/pages/error-rendering.astro index 6984a6da5..6984a6da5 100644 --- a/packages/astro/test/fixtures/react-component/src/pages/error-rendering.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/pages/error-rendering.astro diff --git a/packages/astro/test/fixtures/react-component/src/pages/index.astro b/packages/integrations/react/test/fixtures/react-component/src/pages/index.astro index 3afd8233f..3afd8233f 100644 --- a/packages/astro/test/fixtures/react-component/src/pages/index.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/pages/index.astro diff --git a/packages/astro/test/fixtures/react-component/src/pages/pragma-comment.astro b/packages/integrations/react/test/fixtures/react-component/src/pages/pragma-comment.astro index b3ddba639..b3ddba639 100644 --- a/packages/astro/test/fixtures/react-component/src/pages/pragma-comment.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/pages/pragma-comment.astro diff --git a/packages/astro/test/fixtures/react-component/src/pages/suspense.astro b/packages/integrations/react/test/fixtures/react-component/src/pages/suspense.astro index 5a9d15644..5a9d15644 100644 --- a/packages/astro/test/fixtures/react-component/src/pages/suspense.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/pages/suspense.astro diff --git a/packages/astro/test/fixtures/react-component/src/skipped-pages/forgot-import.astro b/packages/integrations/react/test/fixtures/react-component/src/skipped-pages/forgot-import.astro index de5d319d9..de5d319d9 100644 --- a/packages/astro/test/fixtures/react-component/src/skipped-pages/forgot-import.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/skipped-pages/forgot-import.astro diff --git a/packages/astro/test/fixtures/react-component/src/skipped-pages/window.astro b/packages/integrations/react/test/fixtures/react-component/src/skipped-pages/window.astro index e780f3c44..e780f3c44 100644 --- a/packages/astro/test/fixtures/react-component/src/skipped-pages/window.astro +++ b/packages/integrations/react/test/fixtures/react-component/src/skipped-pages/window.astro diff --git a/packages/astro/test/react-component.test.js b/packages/integrations/react/test/react-component.test.js index a6bb8cfae..da7fa018a 100644 --- a/packages/astro/test/react-component.test.js +++ b/packages/integrations/react/test/react-component.test.js @@ -1,13 +1,13 @@ import { expect } from 'chai'; import { load as cheerioLoad } from 'cheerio'; -import { isWindows, loadFixture } from './test-utils.js'; +import { isWindows, loadFixture } from '../../../astro/test/test-utils.js'; let fixture; describe('React Components', () => { before(async () => { fixture = await loadFixture({ - root: './fixtures/react-component/', + root: new URL('./fixtures/react-component/', import.meta.url), }); }); @@ -51,7 +51,7 @@ describe('React Components', () => { // test 10: Should properly render children passed as props const islandsWithChildren = $('.with-children'); expect(islandsWithChildren).to.have.lengthOf(2); - expect($(islandsWithChildren[0]).html()).to.equal($(islandsWithChildren[1]).html()); + expect($(islandsWithChildren[0]).html()).to.equal($(islandsWithChildren[1]).find('astro-slot').html()); // test 11: Should generate unique React.useId per island const islandsWithId = $('.react-use-id'); @@ -99,12 +99,18 @@ describe('React Components', () => { const $ = cheerioLoad(html); expect($('#cloned').text()).to.equal('Cloned With Props'); }); + + it('Children are parsed as React components, can be manipulated', async () => { + const html = await fixture.readFile('/children/index.html'); + const $ = cheerioLoad(html); + expect($(".with-children-count").text()).to.equal('2'); + }) }); if (isWindows) return; describe('dev', () => { - /** @type {import('./test-utils').Fixture} */ + /** @type {import('../../../astro/test/test-utils.js').Fixture} */ let devServer; before(async () => { diff --git a/packages/integrations/react/vnode-children.js b/packages/integrations/react/vnode-children.js new file mode 100644 index 000000000..0c9d5e08d --- /dev/null +++ b/packages/integrations/react/vnode-children.js @@ -0,0 +1,38 @@ +import { parse, walkSync, DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE } from 'ultrahtml' +import { createElement, Fragment } from 'react'; + +export default function convert(children) { + const nodeMap = new WeakMap(); + let doc = parse(children.toString().trim()); + let root = createElement(Fragment, { children: [] }); + + walkSync(doc, (node, parent, index) => { + let newNode = {}; + if (node.type === DOCUMENT_NODE) { + nodeMap.set(node, root); + } else if (node.type === ELEMENT_NODE) { + const { class: className, ...props } = node.attributes; + newNode = createElement(node.name, { ...props, className, children: [] }); + nodeMap.set(node, newNode); + if (parent) { + const newParent = nodeMap.get(parent); + newParent.props.children[index] = newNode; + + } + } else if (node.type === TEXT_NODE) { + newNode = node.value.trim(); + if (newNode.trim()) { + if (parent) { + const newParent = nodeMap.get(parent); + if (parent.children.length === 1) { + newParent.props.children[0] = newNode; + } else { + newParent.props.children[index] = newNode; + } + } + } + } + }); + + return root.props.children; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8eaf40cbe..d30fa57a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3000,27 +3000,6 @@ importers: specifier: ^1.7.6 version: 1.7.6 - packages/astro/test/fixtures/react-component: - dependencies: - '@astrojs/react': - specifier: workspace:* - version: link:../../../../integrations/react - '@astrojs/vue': - specifier: workspace:* - version: link:../../../../integrations/vue - astro: - specifier: workspace:* - version: link:../../.. - react: - specifier: ^18.1.0 - version: 18.2.0 - react-dom: - specifier: ^18.1.0 - version: 18.2.0(react@18.2.0) - vue: - specifier: ^3.3.4 - version: 3.3.4 - packages/astro/test/fixtures/react-jsx-export: dependencies: react: @@ -4787,6 +4766,9 @@ importers: '@babel/plugin-transform-react-jsx': specifier: ^7.22.5 version: 7.22.5(@babel/core@7.22.5) + ultrahtml: + specifier: ^1.2.0 + version: 1.2.0 devDependencies: '@types/react': specifier: ^17.0.62 @@ -4800,12 +4782,42 @@ importers: astro-scripts: specifier: workspace:* version: link:../../../scripts + chai: + specifier: ^4.3.7 + version: 4.3.7 + cheerio: + specifier: 1.0.0-rc.12 + version: 1.0.0-rc.12 + react: + specifier: ^18.1.0 + version: 18.2.0 + react-dom: + specifier: ^18.1.0 + version: 18.2.0(react@18.2.0) + vite: + specifier: ^4.4.6 + version: 4.4.6(@types/node@18.16.18)(sass@1.63.4) + + packages/integrations/react/test/fixtures/react-component: + dependencies: + '@astrojs/react': + specifier: workspace:* + version: link:../../.. + '@astrojs/vue': + specifier: workspace:* + version: link:../../../../vue + astro: + specifier: workspace:* + version: link:../../../../../astro react: specifier: ^18.1.0 version: 18.2.0 react-dom: specifier: ^18.1.0 version: 18.2.0(react@18.2.0) + vue: + specifier: ^3.3.4 + version: 3.3.4 packages/integrations/sitemap: dependencies: @@ -17374,6 +17386,10 @@ packages: resolution: {integrity: sha512-P24ulZdT9UKyQuKA1IApdAZ+F9lwruGvmKb4pG3+sMvR3CjN0pjawPnxuSABHQFB+XqnB35TVXzJPOBYjCv6Kw==} dev: false + /ultrahtml@1.2.0: + resolution: {integrity: sha512-vxZM2yNvajRmCj/SknRYGNXk2tqiy6kRNvZjJLaleG3zJbSh/aNkOqD1/CVzypw8tyHyhpzYuwQgMMhUB4ZVNQ==} + dev: false + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: |