-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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
fix(ssr): fix crash when a pnpm/Yarn workspace depends on a CJS package #9763
Conversation
I think this is currently correctly enforced, that if a dep can be resolved from the project root (regardless of npm/pnpm), it will be externalized. Those that can't be resolved will be no-externalized. That also means there's a difference in the bundle output depending on npm/pnpm is used, but it shouldn't be an issue when executing the bundle code. It seems like the core issue here is with linked deps being no externalized by default, since you've switch the I think the correct solution here is to externalize the problematic CJS deps, which you've also found at #9710 (comment). Another easier way is to externalize the linked package, though note that you'll lost HMR when editing the package. There has also been discussion to always externalize depending on linked or not, but that could be until Vite 4. I've added docs regarding this behaviour at https://main.vitejs.dev/guide/ssr.html#ssr-externals, though it's not updated in vitejs.dev yet. |
Hmm, it seems strange to me that it would be expected behavior to crash SSR in the common scenario of a pnpm or Yarn monorepo with CJS dependencies and default configuration (see https://github.com/rtsao/vite-bug-repro-ssr-pnpm and https://github.com/rtsao/vite-bug-repro-ssr-pnp). IMHO there shouldn’t need to be configuration to prevent a crash in this case. CJS packages are the overwhelming majority of npm dependencies, especially in the realm of server-side dependencies. Manually adding and maintaining dozens (if not hundreds) of exceptions for every CJS dependency seems like an unreasonable burden. |
I don't think that's ok. |
561f7de
to
85d25ed
Compare
85d25ed
to
d0be74c
Compare
2867a32
to
1b94406
Compare
I've rebased the PR. I did have to add these vitepress devDependencies to the root to get Netlify docs build to work: vuejs/vitepress#849 (comment) because vitepress relies on node_modules hoisting to work correctly, which pnpm/Yarn don't allow. This previously worked because Vite was ignoring import importer context when resolving dependencies. |
a12c08d
to
ef93e1c
Compare
Adding this PR to the Vite 5 milestone so we merge or come to a conclusion of what should be done instead. Thanks for your patience here @rtsao |
Coming back to this, I think I misunderstood the initial problem before, and this fix sounds good to me. Thanks for setting up the repros to explain the problem. Given this breaks the VitePress setup, I'm inclined to merge this in Vite 5 too. |
Actually looking deeper into this, I think there's still a way to make this non-breaking.
I don't think it's VitePress to blame here, we're applying a logic that would break all projects that noExternalizes a workspace dep ( This works great in dev for my app ( So what I think still needs to be fixed is that in build, we should still keep the old behaviour. |
Okay, I think I understand. I was confused because these errors happen during In summary:
I see few possible solutions:
I think the appropriate modification to the externalization logic would be as follows: Alternatively:
|
Before this PR, the externalization logic is that any dep that can be resolved from But it's not great for dev as you've shown in the issue. In dev, the ideal experience is that it checks from the The reason for this reverse behaviour is how Vite and Rollup externalization works in general. You can So the easiest fix is to only apply this fix in dev, and keep build as is. Or in other words, pass the |
Just my two cents, it doesn't break just VitePress. It breaks every second project on the ecosystem CI. And I'm assuming the effect will be more considering on the ecosystem CI VP is passing (as VP has no tests where things are isolated). IMO this should not be merged without a major version bump. |
I've rebased and updated this fix to be a non-breaking change as discussed earlier (i.e. skip application of this change during build). |
@bluwy should we move this PR from Vite 5 to 4.4 now? |
Yeah I think we can merge this in a minor now as it's non breaking. |
/ecosystem-ci run (expected to fail: |
📝 Ran ecosystem CI: Open
|
It seems VitePress is still breaking with the PR 🤔 |
It's probably not related to this, but instead to #13482 . I'll update the types there. |
Should be fixed now. |
/ecosystem-ci run vitepress |
@rtsao It fails for nuxt, any update on this ? We have 5+ projects ( we will have +10 by the end of the year ) and we built a monorepo to share logic between those projects and we have 12 packages in the monorepo and everyone of them has it's own dependencies. To fix this issue we currently install the external dependencies from the monorepo packages to the projects directly to get it work but it's very hard to keep track of dependencies versions and with every new installed dependency in the monorepo packages we have to install it to all the projects.. do you plan to work on the nuxt issue or do we raise an issue on their repo ? If you guide us how to fix it, that will be highly appreciated! |
Description
Fixes #9710
Any pnpm/Yarn workspace that depends on any CommonJS package from npm will crash SSR.
Minimal reproductions:
The externalizer logic unconditionally attempts to resolve encountered dependencies from the project root. This works fine with hoisted
node_modules
installations, but does not work with more strict installation schemes where only explicit dependencies are only resolvable (such as pnpm or Yarn PnP). In this case, the externalizer is unable to resolve the sub-dependency from the root, and as a result it is not externalized for SSR.For example, suppose we have the following dependency graph:
app
->dep-a
->dep-b
With npm,
dep-b
is resolvable fromapp
(the root) because it is hoisted:However, pnpm and PnP ensure strictness where
dep-b
cannot be resolved fromapp
(and must be resolved fromdep-a
):In this case,
dep-b
fails to be externalized for SSR (resulting in errors ifdep-b
is CJS)This PR fixes the issue by passing along the importer and resolving from the importer, thereby allowing it to be resolved correctly.
Additional context
The externalization logic is memoized so resolution for a given package name only happens once. However, now resolution depends on two parameters: the package name and the importing path.
But taking into consideration the importing path would largely defeat the point of the cache as misses would be frequent.
I'm not super familiar with the intended logic of externalization, but I suppose a given package is OK to externalize as long as it was resolvable at some point. That being said, I think there probably needs to be more thinking about edge cases here, but I would need some help understanding the original context of this code.
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).