-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
feat: Add native node esm module exports #8268
Conversation
FWIW we already tried to do this before we launched v6 and ran into some issues with it. I can't remember exactly what they were, but @jacob-ebey and @mcansh spent probably 2 days trying to figure out why esbuild was including 2 copies of the router in Remix. I'd be very cautious about merging something like this. |
i'm almost wondering if that was partly due to having it as a regular dependency in react router, but also having it installed as a dependency in Remix apps 🤔 - because even after reverting the esm changes, we still had duplicate copies floating around. as an aside, i think shipping esm would be a breaking change and thus React Router v7 🤪 |
Yeah, these kind of changes can be very tricky for sure, totally get cautious... but sooner or later I imagine this will need to be solved? |
Slightly updated the exports to be more conservative and target the node environment explicitly. |
Isn't React itself blocking this until they ship ESM? Honestly this doesn't feel like it's very high on the priority list for us given that we already had a bad experience with it and React itself doesn't even ship ESM.
Agree this doesn't feel like a breaking change. Why do you think it's breaking, @mcansh? |
❯ cat foo.mjs
import { createElement } from 'react';
console.log(createElement); As mentioned before, the bad experiences you mentioned do not seem related to this change... EDIT: In my personal project,
|
sorry, y'all are right, i was thinking of just migrating to straight esm without still having commonjs for some reason |
Sorry, I've been avoiding module compatibility for many years, can you help me understand why we need two ESM builds? Can we accomplish this with the package.json config alone? |
Well, you are right, we only really need one build of the esm (the |
I think the better approach here would be to add |
Why don't we try that? Then we can publish an experimental release and see how many things falls apart with it, and if it's a disaster, we can do the double builds. |
Tried this approach! Hoping I didn't miss anything |
contributors file: https://github.com/perrin4869/react-router/blob/main/contributors.yml |
formatted 9fa2b3b528fde633af575bb2c587de4a3f90d9de
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
@ryanflorence I just cleaned up the PR and fixed the tests |
Just wanted to note that |
We did some work a while back to make sure React Router and History both load natively in an ESM module, this works for me today with a native node es module: import { StaticRouter } from "react-router-dom/server.mjs";
import { BrowserRouter } from "react-router-dom";
import { createMemoryHistory } from "history";
console.log(StaticRouter);
console.log(BrowserRouter);
console.log(createMemoryHistory); I think this solves your problem? |
Yeah, those changes do solve my issue! Do note though that |
@perrin4869 Glad it's working for you :) What does your patch package look like today? |
Just using the bare minimum needed to get my project running:
The PR here is a much more complete implementation |
Would be good to see this merged since esm is getting more and more popular nowadays (especially in vite ecosystem) |
Not sure why this has not been merged yet! ? |
@Rutulpatel7077 it says there are conflicts that have to be resolved first |
I've been solving conflicts for many months, but I feel there is no real interest in merging this so I kinda gave up, and also there were changes made in #9017 and others which I didn't look deeply into, but I'd imagine maybe the approach here might need to be changed somewhat... |
@perrin4869 Thank you for the patch-package hint. I ran into a seemingly related issue with React Router v6.4.0, Preact and SSR. React Router was .../react-router-dom-npm-6.4.0-6eb09f3674.patch | 15 +++++++++++++++
.../react-router-npm-6.4.0-b95e890b57.patch | 14 ++++++++++++++
2 files changed, 29 insertions(+)
create mode 100644 .yarn/patches/react-router-dom-npm-6.4.0-6eb09f3674.patch
create mode 100644 .yarn/patches/react-router-npm-6.4.0-b95e890b57.patch
diff --git a/.yarn/patches/react-router-dom-npm-6.4.0-6eb09f3674.patch b/.yarn/patches/react-router-dom-npm-6.4.0-6eb09f3674.patch
new file mode 100644
index 0000000..46299f2
--- /dev/null
+++ b/.yarn/patches/react-router-dom-npm-6.4.0-6eb09f3674.patch
@@ -0,0 +1,15 @@
+diff --git a/package.json b/package.json
+index 9e5daceb8ccb3b31891de4000b0c9d3620dde41a..cd0ec9fa3af104662a4500ac7ad4b8489bfaabf4 100644
+--- a/package.json
++++ b/package.json
+@@ -44,5 +44,10 @@
+ ],
+ "engines": {
+ "node": ">=14"
++ },
++ "type": "module",
++ "exports": {
++ ".": "./dist/index.js",
++ "./server": "./dist/server.mjs"
+ }
+ }
diff --git a/.yarn/patches/react-router-npm-6.4.0-b95e890b57.patch b/.yarn/patches/react-router-npm-6.4.0-b95e890b57.patch
new file mode 100644
index 0000000..c28f772
--- /dev/null
+++ b/.yarn/patches/react-router-npm-6.4.0-b95e890b57.patch
@@ -0,0 +1,14 @@
+diff --git a/package.json b/package.json
+index fba23039473b34557f5b131658fa939f796bfbfa..8cabd79c3b4776bbc38063528417f4f063402637 100644
+--- a/package.json
++++ b/package.json
+@@ -39,5 +39,9 @@
+ ],
+ "engines": {
+ "node": ">=14"
++ },
++ "type": "module",
++ "exports": {
++ ".": "./dist/index.js"
+ }
+ } Here is the full setup: https://github.com/patdx/preact-react-router-ssr-esm If react-router and react-router-dom provided proper Node ESM exports I think this patching could be avoided. |
Hey folks! I caught up with Michael and Ryan on this and we generally think that any addition of That being said, it seems the only real issue here is the ergonomics of: // This requires including the .mjs extension
import { StaticRouter } from "react-router-dom/server.mjs"; Please correct me if I'm mistaken on this and there are other functional issues. Michael/Ryan both seem to recall the only reason import { StaticRouter } from "react-router-dom"; Let us know if there would still be any ESM issues for your apps if we did that instead, and we'll keep |
We're doing some house cleaning to start the new year. We switched to a new Open Development process recently so new features should go through that process. Do you mind creating a new Discussion with the |
Exporting StaticRouter through the main bundle would help 👍
The following simplified example still breaks for me in Node.js and I think it would continue to break even with the proposed StaticRouter change above:
To my understanding it is because something inside react-router/react-router-dom imported a CJS module, which subsequently loaded Preact CJS version (hooks.js), even though the direct script imports Preact ESM version (hooks.mjs). // package.json
{
"type": "module",
"dependencies": {
"preact": "10.13.1",
"preact-render-to-string": "5.2.6",
"react-router": "6.8.2",
"react-router-dom": "6.8.2",
"react": "npm:@preact/[email protected]",
"react-dom": "npm:@preact/[email protected]"
}
} // main.js
import { h } from "preact"; // node_modules/preact/dist/preact.mjs
import renderToString from "preact-render-to-string"; // node_modules/preact-render-to-string/dist/index.mjs
import { Route, Routes } from "react-router"; // node_modules/react-router/dist/main.js
import { StaticRouter } from "react-router-dom/server.mjs"; // node_modules/react-router-dom/server.mjs
function Home() {
return h("div", undefined, "Home");
}
const out = renderToString(
h(
StaticRouter,
{
location: "/home",
},
h(Routes, undefined, h(Route, { path: "/home", element: h(Home) }))
)
);
console.log(out); |
I created a proposal for the first part of your suggestion here: |
I am migrating some of my code to native node ESM modules, but I got stuck due to incompatibilities importing
react-router
in node.js and in client code.Doing SSR, I would have to do something like:
This is because the import will point to the
cjs
build, and it does not support named-exports.The code above in the client side will import the default export for
ReactRouter
, and I have no way to get the named imports from it, likeLink
.This PR fixes the issue by introducing a clone of
index.js
asindex.mjs
and exposes it using theexports
field ofpackage.json
. I do not believe this would cause breaking changes to end users.Thanks!