From d0eae83cd7bd0ed7ed8d2ceb9fbc706984ce3536 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 26 Apr 2024 18:01:59 -0400 Subject: [PATCH] feat: get react integration 19-ready --- packages/integrations/react/client-v17.js | 22 ------ packages/integrations/react/package.json | 8 +-- packages/integrations/react/server-v17.js | 88 ----------------------- packages/integrations/react/server.js | 2 +- pnpm-lock.yaml | 54 ++++++++++++++ 5 files changed, 59 insertions(+), 115 deletions(-) delete mode 100644 packages/integrations/react/client-v17.js delete mode 100644 packages/integrations/react/server-v17.js diff --git a/packages/integrations/react/client-v17.js b/packages/integrations/react/client-v17.js deleted file mode 100644 index 565c688d40..0000000000 --- a/packages/integrations/react/client-v17.js +++ /dev/null @@ -1,22 +0,0 @@ -import { createElement } from 'react'; -import { hydrate, render, unmountComponentAtNode } from 'react-dom'; -import StaticHtml from './static-html.js'; - -export default (element) => - (Component, props, { default: children, ...slotted }, { client }) => { - for (const [key, value] of Object.entries(slotted)) { - props[key] = createElement(StaticHtml, { value, name: key }); - } - const componentEl = createElement( - Component, - props, - children != null ? createElement(StaticHtml, { value: children }) : children - ); - - const isHydrate = client !== 'only'; - const bootstrap = isHydrate ? hydrate : render; - bootstrap(componentEl, element); - element.addEventListener('astro:unmount', () => unmountComponentAtNode(element), { - once: true, - }); - }; diff --git a/packages/integrations/react/package.json b/packages/integrations/react/package.json index a3213503a3..90d4631a81 100644 --- a/packages/integrations/react/package.json +++ b/packages/integrations/react/package.json @@ -60,10 +60,10 @@ "vite": "^5.2.10" }, "peerDependencies": { - "@types/react": "^17.0.50 || ^18.0.21", - "@types/react-dom": "^17.0.17 || ^18.0.6", - "react": "^17.0.2 || ^18.0.0", - "react-dom": "^17.0.2 || ^18.0.0" + "@types/react": "^18.0.21 || npm:types-react@beta", + "@types/react-dom": "^18.0.6 || npm:types-react@beta", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "engines": { "node": "^18.17.1 || ^20.3.0 || >=21.0.0" diff --git a/packages/integrations/react/server-v17.js b/packages/integrations/react/server-v17.js deleted file mode 100644 index ad0c99622b..0000000000 --- a/packages/integrations/react/server-v17.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/server.js'; -import StaticHtml from './static-html.js'; - -const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); -const reactTypeof = Symbol.for('react.element'); - -function errorIsComingFromPreactComponent(err) { - return ( - err.message && - (err.message.startsWith("Cannot read property '__H'") || - err.message.includes("(reading '__H')")) - ); -} - -function check(Component, props, children) { - // Note: there are packages that do some unholy things to create "components". - // Checking the $$typeof property catches most of these patterns. - if (typeof Component === 'object') { - return Component['$$typeof']?.toString().slice('Symbol('.length).startsWith('react'); - } - if (typeof Component !== 'function') return false; - if (Component.name === 'QwikComponent') return false; - - if (Component.prototype != null && typeof Component.prototype.render === 'function') { - return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component); - } - - let error = null; - let isReactComponent = false; - function Tester(...args) { - try { - const vnode = Component(...args); - if (vnode && vnode['$$typeof'] === reactTypeof) { - isReactComponent = true; - } - } catch (err) { - if (!errorIsComingFromPreactComponent(err)) { - error = err; - } - } - - return React.createElement('div'); - } - - renderToStaticMarkup(Tester, props, children, {}); - - if (error) { - throw error; - } - return isReactComponent; -} - -function renderToStaticMarkup(Component, props, { default: children, ...slotted }, metadata) { - delete props['class']; - const slots = {}; - for (const [key, value] of Object.entries(slotted)) { - const name = slotName(key); - slots[name] = React.createElement(StaticHtml, { value, name }); - } - // Note: create newProps to avoid mutating `props` before they are serialized - const newProps = { - ...props, - ...slots, - }; - const newChildren = children ?? props.children; - if (newChildren != null) { - newProps.children = React.createElement(StaticHtml, { - // Adjust how this is hydrated only when the version of Astro supports `astroStaticSlot` - hydrate: metadata.astroStaticSlot ? !!metadata.hydrate : true, - value: newChildren, - }); - } - const vnode = React.createElement(Component, newProps); - let html; - if (metadata?.hydrate) { - html = ReactDOM.renderToString(vnode); - } else { - html = ReactDOM.renderToStaticMarkup(vnode); - } - return { html }; -} - -export default { - check, - renderToStaticMarkup, - supportsAstroStaticSlot: true, -}; diff --git a/packages/integrations/react/server.js b/packages/integrations/react/server.js index b2e94fbf0b..47280c0f3e 100644 --- a/packages/integrations/react/server.js +++ b/packages/integrations/react/server.js @@ -1,6 +1,6 @@ import opts from 'astro:react:opts'; import React from 'react'; -import ReactDOM from 'react-dom/server'; +import ReactDOM from 'react-dom/server.browser'; import { incrementId } from './context.js'; import StaticHtml from './static-html.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0deccf78f6..ec46d82e40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4847,6 +4847,27 @@ importers: specifier: ^5.2.10 version: 5.2.10(@types/node@18.19.31)(sass@1.75.0) + packages/integrations/react/test/fixtures/react-19: + dependencies: + '@astrojs/react': + specifier: workspace:* + version: link:../../.. + '@types/react': + specifier: npm:types-react@beta + version: /types-react@19.0.0-beta.1 + '@types/react-dom': + specifier: npm:types-react-dom@beta + version: /types-react-dom@19.0.0-beta.1 + astro: + specifier: workspace:* + version: link:../../../../../astro + react: + specifier: 19.0.0-beta-94eed63c49-20240425 + version: 19.0.0-beta-94eed63c49-20240425 + react-dom: + specifier: 19.0.0-beta-94eed63c49-20240425 + version: 19.0.0-beta-94eed63c49-20240425(react@19.0.0-beta-94eed63c49-20240425) + packages/integrations/react/test/fixtures/react-component: dependencies: '@astrojs/react': @@ -14474,6 +14495,18 @@ packages: react: 18.2.0 scheduler: 0.23.0 + /react-dom@19.0.0-beta-94eed63c49-20240425(react@19.0.0-beta-94eed63c49-20240425): + resolution: {integrity: sha512-V0uHW7Xd0u/LDlmFO8sJ9TTNizAESS+pexJNOi3KbOU1taf2gUO5J8YIWis60xcQbh7YBqSklyYIIq3DfiKz3Q==} + peerDependencies: + react: 19.0.0-beta-94eed63c49-20240425 + peerDependenciesMeta: + react: + optional: true + dependencies: + react: 19.0.0-beta-94eed63c49-20240425 + scheduler: 0.25.0-beta-94eed63c49-20240425 + dev: false + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: false @@ -14489,6 +14522,11 @@ packages: dependencies: loose-envify: 1.4.0 + /react@19.0.0-beta-94eed63c49-20240425: + resolution: {integrity: sha512-BPPKh5bZwcpw/Dgfh3A0MoU1GSl2edR2JggCq3QPdghQsrFg1aBuMkul5YB4rpII400RYq9VC5eF5Nm3spx0gA==} + engines: {node: '>=0.10.0'} + dev: false + /read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: @@ -14982,6 +15020,10 @@ packages: dependencies: loose-envify: 1.4.0 + /scheduler@0.25.0-beta-94eed63c49-20240425: + resolution: {integrity: sha512-U8hoOV7uut5E8cMvRSMikEBBqLXfv2BpdxUsG45euaOfnqEgFrCdLLh6ydM+YBJ36+28olXMNmK2PFFzeg4UdQ==} + dev: false + /scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -16061,6 +16103,18 @@ packages: possible-typed-array-names: 1.0.0 dev: true + /types-react-dom@19.0.0-beta.1: + resolution: {integrity: sha512-fXQfv6OQOXxmgNQ7RuhX615Wx4680LqVbrEbxch+ZQ56ZDAZKXptLl3XoHaHU31Yd2xiig/VlXzohCexKXNzBQ==} + dependencies: + '@types/react': 18.2.79 + dev: false + + /types-react@19.0.0-beta.1: + resolution: {integrity: sha512-gQpuPdi+Gu+nIFmhnFLINkGQ4j0eRY7olV3lzncFJy2g5TfnSEFD1xU86u5KgRytFswwZ5pGEPWEizefwPaxTw==} + dependencies: + csstype: 3.1.3 + dev: false + /typesafe-path@0.2.2: resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==}