Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support React 19 #10942

Merged
merged 4 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/short-phones-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/react": patch
---

Updates package to support React 19 beta
4 changes: 2 additions & 2 deletions packages/integrations/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
"peerDependencies": {
"@types/react": "^17.0.50 || ^18.0.21",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add the @types/react preview package as well? They are listed as npm:types-react@beta and npm:types-react-dom@beta. Not sure if the npm: prefix works with peer deps though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to semver this, is it:

"@types/react": "^17.0.50 || ^18.0.21 || npm:types-react@beta"

I admit this is outside of my wheelhouse

Copy link
Contributor

@bholmesdev bholmesdev May 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that looks correct. I tested locally to confirm whether npm will raise an exception resolving peer deps, and it unfortunately does. pnpm works fine since it only warns for mismatched peer deps. I think we need to add this for everything to install correctly.

"@types/react-dom": "^17.0.17 || ^18.0.6",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
"react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta",
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta"
},
"engines": {
"node": "^18.17.1 || ^20.3.0 || >=21.0.0"
Expand Down
67 changes: 48 additions & 19 deletions packages/integrations/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,44 @@ export type ReactIntegrationOptions = Pick<

const FAST_REFRESH_PREAMBLE = react.preambleCode;

function getRenderer() {
const versionsConfig = {
17: {
server: '@astrojs/react/server-v17.js',
client: '@astrojs/react/client-v17.js',
externals: ['react-dom/server.js', 'react-dom/client.js'],
},
18: {
server: '@astrojs/react/server.js',
client: '@astrojs/react/client.js',
externals: ['react-dom/server', 'react-dom/client']
},
19: {
server: '@astrojs/react/server.js',
client: '@astrojs/react/client.js',
externals: ['react-dom/server', 'react-dom/client']
}
};

type SupportedReactVersion = keyof (typeof versionsConfig);
type ReactVersionConfig = (typeof versionsConfig)[SupportedReactVersion];

function getReactMajorVersion(): number {
const matches = /\d+\./.exec(ReactVersion);
if(!matches) {
return NaN;
}
return Number(matches[0]);
}

function isUnsupportedVersion(majorVersion: number) {
return majorVersion < 17 || majorVersion > 19 || Number.isNaN(majorVersion);
}

function getRenderer(reactConfig: ReactVersionConfig) {
return {
name: '@astrojs/react',
clientEntrypoint: ReactVersion.startsWith('18.')
? '@astrojs/react/client.js'
: '@astrojs/react/client-v17.js',
serverEntrypoint: ReactVersion.startsWith('18.')
? '@astrojs/react/server.js'
: '@astrojs/react/server-v17.js',
clientEntrypoint: reactConfig.client,
serverEntrypoint: reactConfig.server,
};
}

Expand Down Expand Up @@ -51,32 +80,26 @@ function getViteConfiguration({
exclude,
babel,
experimentalReactChildren,
}: ReactIntegrationOptions = {}) {
}: ReactIntegrationOptions = {}, reactConfig: ReactVersionConfig) {
return {
optimizeDeps: {
include: [
ReactVersion.startsWith('18.')
? '@astrojs/react/client.js'
: '@astrojs/react/client-v17.js',
reactConfig.client,
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime',
'react-dom',
],
exclude: [
ReactVersion.startsWith('18.')
? '@astrojs/react/server.js'
: '@astrojs/react/server-v17.js',
reactConfig.server,
],
},
plugins: [react({ include, exclude, babel }), optionsPlugin(!!experimentalReactChildren)],
resolve: {
dedupe: ['react', 'react-dom', 'react-dom/server'],
},
ssr: {
external: ReactVersion.startsWith('18.')
? ['react-dom/server', 'react-dom/client']
: ['react-dom/server.js', 'react-dom/client.js'],
external: reactConfig.externals,
noExternal: [
// These are all needed to get mui to work.
'@mui/material',
Expand All @@ -95,13 +118,19 @@ export default function ({
babel,
experimentalReactChildren,
}: ReactIntegrationOptions = {}): AstroIntegration {
const majorVersion = getReactMajorVersion();
if(isUnsupportedVersion(majorVersion)) {
throw new Error(`Unsupported React version: ${majorVersion}.`);
}
const versionConfig = versionsConfig[majorVersion as SupportedReactVersion];

return {
name: '@astrojs/react',
hooks: {
'astro:config:setup': ({ command, addRenderer, updateConfig, injectScript }) => {
addRenderer(getRenderer());
addRenderer(getRenderer(versionConfig));
updateConfig({
vite: getViteConfiguration({ include, exclude, babel, experimentalReactChildren }),
vite: getViteConfiguration({ include, exclude, babel, experimentalReactChildren }, versionConfig),
});
if (command === 'dev') {
const preamble = FAST_REFRESH_PREAMBLE.replace(`__BASE__`, '/');
Expand Down
Loading