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

Cannot import commonjs module from server component #110

Closed
tom-sherman opened this issue Jul 28, 2023 · 16 comments · Fixed by #445
Closed

Cannot import commonjs module from server component #110

tom-sherman opened this issue Jul 28, 2023 · 16 comments · Fixed by #445
Labels
enhancement New feature or request

Comments

@tom-sherman
Copy link

When trying to import a CJS module from a server component I get an error in dev & production

Error when evaluating SSR module /src/components/App.tsx: failed to import "/src/components/foo.cjs"
|- ReferenceError: require is not defined
    at eval (/Users/tom.sherman/code/waku-example/src/components/foo.cjs:3:31)
    at instantiateModule (file:///Users/tom.sherman/code/waku-example/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-3b8eb186.js:55920:15)

Cannot render RSC ReferenceError: require is not defined
    at eval (/Users/tom.sherman/code/waku-example/src/components/foo.cjs:3:31)
    at instantiateModule (file:///Users/tom.sherman/code/waku-example/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-3b8eb186.js:55920:15)

Minimal reproduction: tom-sherman/waku-example@77cf350

@dai-shi
Copy link
Owner

dai-shi commented Jul 28, 2023

Hmm, I thought it works, but it doesn't. (btw, .cts nor .mts doesn't work and we need to deal with it.) The error is caused with Vite server file loading.
It's either a) there's a misconfiguration for Vite, b) some Vite plugins do something wrong, c) Vite doesn't support it (unlikely?) or d) there's a misunderstanding of mine.

@dai-shi dai-shi added the bug Something isn't working label Jul 28, 2023
@dai-shi dai-shi mentioned this issue Jul 28, 2023
87 tasks
@tom-sherman
Copy link
Author

tom-sherman commented Jul 28, 2023

btw, .cts nor .mts doesn't work and we need to deal with it

Fair point, this was the easiest way to trigger the error for me but I also was able to trigger the error by importing a CJS-only package from node_modules

I think these days it's pretty unlikely for packages to be not have any ESM support at all, but this is pretty critical for the electron use case (electron is CJS only).

@dai-shi
Copy link
Owner

dai-shi commented Aug 2, 2023

@PerfectPan @nksaraf
Do you happen to know if ssrLoadModule supports .cjs ? (More precisely, import .cjs file from an ESM file loaded with ssrLoadModule.)
Or, if we are missing something.

@nksaraf
Copy link

nksaraf commented Aug 2, 2023

No I think within your project you shouldn't really have cjs.. that way vite is ESM only.. it just knows what to do about cjs files in node_modules. But also I could be wrong to pinging @patak-dev

@tom-sherman
Copy link
Author

tom-sherman commented Aug 2, 2023

Note, although the example uses CJS inside my project, this error also appears for CJS inside node_modules.

IMO both of these behaviours are strange, in a Node.js ESM environment you are able to import a CJS module without issue.

I wondered if there was a way to mark the CJS module as external so that it would be simply imported by the server module at runtime, but this didn't seem to work.

@dai-shi
Copy link
Owner

dai-shi commented Aug 2, 2023

in a Node.js ESM environment you are able to import a CJS module without issue.

Yeah.

@PerfectPan
Copy link
Contributor

To be honst, I am a newbie to vite. I just start learning vite from waku lol.

I tried to read the source about ssrLoadModule part and I found the basic rule is that if your code is not external based on https://vitejs.dev/config/ssr-options.html#ssr-external , it cannot use commonjs, otherwise it will based on the node runtime behavior.

When ssrLoadModule being called, vite will transform your code first and wrap your code into a AsyncFunction.

Transform step

Basically it will resolve all the import an export statement and rewrite to a load function, you can see the code here https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrTransform.ts#L126.

example:(more can see the test code https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts#L10)

import foo from 'vue';console.log(foo.bar)
// after
const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\");
console.log(__vite_ssr_import_0__.default.bar)

Wrap step

As you can see, for import statement it will be replaced by __vite_ssr_import, this is injected by the vite by wrapping the code into a async function.

https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrModuleLoader.ts#L216
CleanShot 2023-08-04 at 16 27 31@2x

So the load result is an async function and will be executed by the user side.

It is a little like use the function closure to emulate a import / export environment instead of bundling all the code into a file like webpack. So as you can see, the final code is just a function, and the current situation is esm, node esm does not exist require or module, so it will throw error. A piece of code can prove my point.
CleanShot 2023-08-04 at 17 04 47@2x

However, there is a workaround or by design pattern provided by vite which is https://vitejs.dev/config/ssr-options.html#ssr-external.

When you external the whole third package, the resolve id will be the original instead of the parsed absolute path.

Once vite detect it is external package, vite will use the node loader to handle it. The code is here https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrModuleLoader.ts#L265.

CleanShot 2023-08-04 at 17 16 46@2x

CleanShot 2023-08-04 at 17 10 05@2x

When you try to use import() call in esm, it can handle cjs correctly.

Another workaround I think maybe you can use @rollup/plugin-commonjs to convert cjs to esm which I have not tried yet.

Apparently I hide a whole lot of details like the plugin transformation or the optimize thing, I just read the basic code but I think it is enough to explain this situation.

@PerfectPan
Copy link
Contributor

PerfectPan commented Aug 4, 2023

And for your anothor issue tom-sherman/waku-desktop#2, simply add external can not resolve all the problem.

CleanShot 2023-08-04 at 17 22 08@2x

You still will get a following runtime error:

TypeError: Cannot read properties of undefined (reading 'getVersion')

This is because the whole code executes in a node environment instead of electron. So when you import you will only get the execute path of Electron just like the doc says
https://github.com/electron/electron#programmatic-usage.

Maybe you should encapsulate a remote call for server and electron environment to communicate so that you can use the original electron ability.

@tom-sherman
Copy link
Author

In waku-desktop the "server" is the electron main process, so it should be running in that environment. Like you though I'm learning these things as I go, it's possible I've misunderstood something.

@PerfectPan
Copy link
Contributor

In waku-desktop the "server" is the electron main process, so it should be running in that environment. Like you though I'm learning these things as I go, it's possible I've misunderstood something.

I know what you mean. But waku will create a worker thread to handle the rsc file(https://github.com/dai-shi/waku/blob/main/src/lib/middleware/rsc/worker-api.ts#L12), so user code actually is running in another node environment.

@tom-sherman
Copy link
Author

Hmm, ok - yep that is an issue. I think changing this approach would solve my electron problem then.

I understand that RSC + SSR need to run in two separate processes (or at least react-server-dom-webpack assumes this to be the case) but Waku with SSR turned off shouldn't need to spin up any child processes when SSR is disabled I don't think.

@dai-shi
Copy link
Owner

dai-shi commented Nov 14, 2023

Can anyone try with Waku v0.17.0? I don't expect it's solved, but would the error message be different?

@himself65
Copy link
Contributor

TIP: This is one way to use cjs module:

#362 (review)

@dai-shi
Copy link
Owner

dai-shi commented Jan 15, 2024

Another solution is to externalize the library. I think it's a valid workaround.

if (mode === 'development') {
return {
ssr: {
external: ['next-mdx-remote'],
},
};
}

Please note that this is DEV-only issue now.

@tom-sherman commonjs should work for PRD bundling.

@himself65
Copy link
Contributor

Another solution is to externalize the library. I think it's a valid workaround.

if (mode === 'development') {
return {
ssr: {
external: ['next-mdx-remote'],
},
};
}

Please note that this is DEV-only issue now.

@tom-sherman commonjs should work for PRD bundling.

So, this looks like vite loadSsrModule cannot handle cjs bundle.

Related issue: vitejs/vite#3024

@dai-shi
Copy link
Owner

dai-shi commented Jan 17, 2024

As we have a workaround, I don't consider this a bug, but it would be nice to provide better DX.

@dai-shi dai-shi added enhancement New feature or request and removed bug Something isn't working labels Jan 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants