-
Notifications
You must be signed in to change notification settings - Fork 46.4k
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
[Flight] Source Map Server Actions to their Server Location #30741
[Flight] Source Map Server Actions to their Server Location #30741
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
09ec4fe
to
751b9b6
Compare
Another thing to note is that We should probably 1) expose those on the hooks inspection 2) do something clever with the dispatcher function so that it can point to whatever it wraps. E.g. it could itself be an eval like this or we could instrument it with something that at least React DevTools knows about and maybe you can expand in Chrome DevTools. |
…ences This ensures that it looks nicer when printed in debug tools and so that you can inspect the function and see the server code location. This assumes that the compiler generates source map locations that map the location of the registerServerReference/createServerReference call to the original function location or at least where the export happened.
751b9b6
to
b56c1d5
Compare
Follow up to #30741. This is just for the reference Webpack implementation. If there is a source map associated with a Node ESM loader, we generate new source map entries for every `registerServerReference` call. To avoid messing too much with it, this doesn't rewrite the original mappings. It just reads them while finding each of the exports in the original mappings. We need to read all since whatever we append at the end is relative. Then we just generate new appended entries at the end. For the location I picked the location of the local name identifier. Since that's the name of the function and that gives us a source map name index. It means it jumps to the name rather than the beginning of the function declaration. It could be made more clever like finding a local function definition if it is reexported. We could also point to the line/column of the function declaration rather than the identifier but point to the name index of the identifier name. Now jumping to definition works in the fixture. <img width="574" alt="Screenshot 2024-08-20 at 2 49 07 PM" src="https://github.com/user-attachments/assets/7710f0e6-2cee-4aad-8d4c-ae985f8289eb"> Unfortunately this technique doesn't seem to work in Firefox nor Safari. They don't apply the source map for jumping to the definition.
Follow up to #30741. We need to keep the location of the action when it's bound so we can still jump to it.
**breaking change for canary users: Bumps peer dependency of React from `19.0.0-rc-1eaccd82-20240816` to `19.0.0-rc-eb3ad065-20240822`** No changes required in Next.js it seems. [diff facebook/react@1eaccd82...eb3ad065](facebook/react@1eaccd8...eb3ad06) <details> <summary>React upstream changes</summary> - facebook/react#30761 - facebook/react#30779 - facebook/react#30775 - facebook/react#30770 - facebook/react#30756 - facebook/react#30755 - facebook/react#30768 - facebook/react#30760 - facebook/react#30732 - facebook/react#30757 - facebook/react#30750 - facebook/react#30751 - facebook/react#30753 - facebook/react#30740 - facebook/react#30748 - facebook/react#30746 - facebook/react#30747 - facebook/react#30731 - facebook/react#30725 - facebook/react#30741 - facebook/react#30730 - facebook/react#30726 - facebook/react#30717 - facebook/react#30729 - facebook/react#30721 - facebook/react#30720 - facebook/react#30705 </details>
This uses a similar technique to what we use to generate fake stack frames for server components. This generates an eval:ed wrapper function around the Server Reference proxy we create on the client. This wrapper function gets the original
name
of the action on the server and I also add a source map iffindSourceMapURL
is defined that points back to the source of the server function.For
"use server"
on the server, there's no new API. It just uses the callsite ofregisterServerReference()
on the Server. We can infer the function name from the actual function on the server and we already have thefindSourceMapURL
on the client receiving it.For
"use server"
imported from the client, there's two new options added tocreateServerReference()
(in addition to the optionalencodeFormAction
). These are only used in DEV mode. ThefindSourceMapURL
option is the same one added in #29708. We need to pass this these references aren't created in the context of any specific request but globally. The other weird thing about this case is that this is actually a case where the compiled environment is the client so any source maps are the same as for the client layer, so the environment name here is just"Client"
.The key is that we use the location of the
registerServerReference()
/createServerReference()
call as the location of the function. A compiler can either emit those at the same locations as the original functions or use source maps to have those segments refer to the original location of the function (or in the case of a re-export the original location of the re-export is also a fine approximate). The compiled output must call these directly without a wrapper function because the wrapper adds a stack frame. I decided against complicated and fragile dev-only options to skip n number of frames that would just end up in prod code. The implementation just skips one frame - our own. Otherwise it'll just point all source mapping to the wrapper.We don't have a
"use server"
imported from the client implementation in the reference implementation/fixture so it's a bit tricky to test that. In the case of CJS on the server, we just use a runtime instead of compiler so it's tricky to source map those appropriately. We can implement it for ESM on the server which is the main thing we're testing in the fixture. It's easier in a real implementation where all the compilation is just one pass. It's a little tricky since we have to parse and append to other source maps but I'd like to do that as a follow up. Or maybe that's just an exercise for the reader.You can right click an action and click "Go to Definition".
For now they simply don't point to the right place but you can still jump to the right file in the fixture:
In Firefox/Safari given that the location doesn't exist in the source map yet, the browser refuses to open the file. Where as Chrome does nearest (last) line.