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

Fix configuring resolve conditions for Vite v6 #12644

Merged
merged 11 commits into from
Jan 6, 2025

Conversation

silvenon
Copy link

@silvenon silvenon commented Dec 26, 2024

Configuring resolve.conditions/resolve.externalConditions in Vite v6 has changed: https://vite.dev/guide/migration.html#default-value-for-resolve-conditions

The configured conditions are no longer added to the default values, they now replace the defaults. This was causing problems with resolving packages like @prisma/client during SSR.

This fix adds the missing default values if the default values exist, and they only exist in Vite v6, otherwise it preserves the previous behavior in order to (hopefully) maintain the same behavior with Vite v5.

Fixes #12610.

Copy link

changeset-bot bot commented Dec 26, 2024

🦋 Changeset detected

Latest commit: e7fa7e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@react-router/dev Patch
@react-router/fs-routes Patch
@react-router/remix-routes-option-adapter Patch
create-react-router Patch
react-router Patch
react-router-dom Patch
@react-router/architect Patch
@react-router/cloudflare Patch
@react-router/express Patch
@react-router/node Patch
@react-router/serve Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Dec 26, 2024

Hi @silvenon,

Welcome, and thank you for contributing to React Router!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at [email protected].

Thanks!

- The Remix team

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Dec 26, 2024

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

@silvenon silvenon force-pushed the vite-v6-resolve-conditions branch 2 times, most recently from f8fa75d to 488778a Compare December 26, 2024 17:27
@silvenon silvenon changed the title Make resolve.conditions work with Vite v6 Fix configuring resolve.conditions for Vite v6 Dec 26, 2024
@silvenon silvenon changed the title Fix configuring resolve.conditions for Vite v6 Fix configuring resolve conditions for Vite v6 Dec 26, 2024
Copy link

@tigerabrodi tigerabrodi left a comment

Choose a reason for hiding this comment

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

I'm wondering if we should add tests?

This is critical code handling module resolution, important part how deps get loaded in the application

subtle bugs ruin it for many and cause hours of debugging 🤷

we should cover this with tests imo

packages/react-router-dev/vite/plugin.ts Show resolved Hide resolved
Comment on lines 741 to 752
let viteClientConditions: string[] =
viteUserConfig.resolve?.conditions ??
"defaultClientConditions" in vite
? Array.from(vite.defaultClientConditions)
: [];
let viteServerConditions: string[] =
viteUserConfig.ssr?.resolve?.conditions ??
"defaultServerConditions" in vite
? Array.from(vite.defaultServerConditions)
: [];

Choose a reason for hiding this comment

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

We could make this more type safe by specifying the literal types accepted here.

type DevProdCondition = 'development' | 'production';
type BaseCondition = 'module' | DevProdCondition;

type ClientCondition = BaseCondition | 'browser';
type ServerCondition = BaseCondition | 'node' | 'workerd' | 'worker';

You could then use this as ClientCondition[] or ServerCondition[].

Reading the vite docs and seeing what we have here, I think this should be fine, also helps make sure we don't mistype anything.

Copy link
Author

@silvenon silvenon Jan 4, 2025

Choose a reason for hiding this comment

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

I don't see the benefit of coercing unknown strings types to known strings types. What would I be doing this for?

@tigerabrodi
Copy link

tigerabrodi commented Jan 4, 2025

i think we should add a test here @silvenon

if rr team had a test for this config in the first place, it would've been caught before shipping to production (i think, not sure if this is on vite or rr team here to be fair 😅 ), im not entirely sure what the conventions here are etc.

i was playing around with code quickly, but the test case below should've caught the issue

i imagine it looking like:

import { describe, it, expect, beforeEach } from 'vitest';

type DevProdCondition = 'development' | 'production';
type BaseCondition = 'module' | DevProdCondition;
type ServerCondition = BaseCondition | 'node' | 'workerd' | 'worker';

interface ViteConfig {
 ssr?: {
   resolve?: {
     conditions?: string[];
   }
 }
}

interface ConditionsConfig {
 vite: {
   defaultServerConditions?: string[];
 };
 userConfig?: ViteConfig;
 command: 'build' | 'serve';
}

function getServerConditions({
 vite,
 userConfig,
 command
}: ConditionsConfig): string[] {
 // Check user config first
 if (userConfig?.ssr?.resolve?.conditions !== undefined) {
   return userConfig.ssr.resolve.conditions;
 }

 // Get Vite defaults or empty array if old Vite version
 const defaultConditions = "defaultServerConditions" in vite
   ? Array.from(vite.defaultServerConditions)
   : [];

 // Add development in dev mode
 return command === 'build' 
   ? defaultConditions
   : ['development', ...defaultConditions];
}

describe('Server Conditions', () => {
 let mockVite: ConditionsConfig['vite'];
 let mockUserConfig: ViteConfig;
 
 beforeEach(() => {
   // Reset mocks before each test
   mockVite = {
     defaultServerConditions: ['module', 'node']
   };
   mockUserConfig = {};
 });

// a case like this would've caught the issue ensuring conditions isn't empty
 it('should use Vite default server conditions in build mode', () => {
   const conditions = getServerConditions({
     vite: mockVite,
     userConfig: mockUserConfig,
     command: 'build'
   });
   
   expect(conditions).toEqual(['module', 'node']);
 });
});

PS.

im just a community contributor

rr team are the ones calling the shots ❤️ 🫡

@silvenon
Copy link
Author

silvenon commented Jan 4, 2025

i think we should add a test here

I'd like to add a test as well, but I'd like to wait for some team feedback first, I don't really know how to test this, but thanks for your suggestion.

TBH I don't know why the "development" condition is even being forced at all. I thought that Vite by default resolves to "development" condition during development mode and to "production" condition during build both in Vite v5 and v6.

@silvenon
Copy link
Author

silvenon commented Jan 5, 2025

@markdalgleish done. Do you know what the reason is for adding the "development" condition in the first place? Is it for virtual modules?

@markdalgleish
Copy link
Member

@silvenon It was added in this PR: #12269 — but this change was later rolled back. Looks like the condition was accidentally left in, but as you pointed out, it might not have been needed in the first place. We could look at removing this in a separate PR.

@silvenon silvenon changed the base branch from main to dev January 5, 2025 23:46
contributors.yml Outdated Show resolved Hide resolved
contributors.yml Outdated Show resolved Hide resolved
// https://vite.dev/guide/migration.html#default-value-for-resolve-conditions
let viteClientConditions: string[] =
"defaultClientConditions" in vite
? Array.from(vite.defaultClientConditions)
Copy link
Member

Choose a reason for hiding this comment

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

Why is Array.from used here?

Copy link
Author

@silvenon silvenon Jan 5, 2025

Choose a reason for hiding this comment

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

To satisfy TypeScript, I can also do [...vite.defaultClientConditions].

vite.defaultClientConditions is a readonly array.

Copy link
Member

Choose a reason for hiding this comment

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

Could do ...vite.defaultClientConditions ?? [] (and similar in all other cases) to inline it?

Copy link
Author

Choose a reason for hiding this comment

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

I've done spreading instead, shorter.

Copy link
Author

@silvenon silvenon Jan 6, 2025

Choose a reason for hiding this comment

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

Could do ...vite.defaultClientConditions ?? [] (and similar in all other cases) to inline it?

Ah, sorry, I didn't see this. I pushed the change now, let me know if this is what you meant.

The default value for resolve.conditions in Vite v6 has changed:

https://vite.dev/guide/migration.html#default-value-for-resolve-conditions

This means that configuring them no longer adds the values to default
values, it replaces them. This was causing problems with resolving
packages like @prisma/client during SSR.

This change maintains compatibility with Vite v5.
This is new behavior that didn't exist before, so if this should be done
it should be done separately.
@silvenon silvenon force-pushed the vite-v6-resolve-conditions branch from 00a7624 to 80b5f15 Compare January 6, 2025 00:07
@silvenon
Copy link
Author

silvenon commented Jan 6, 2025

I don't like rebasing, but if there's one circumstance where it's appropriate, it's when I literally based my branch off the wrong one. Here, everything clean now.

Copy link
Member

@markdalgleish markdalgleish left a comment

Choose a reason for hiding this comment

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

I've just pushed a couple of minor tweaks to finish this off.

Thanks for your work on this 🙏

@markdalgleish markdalgleish merged commit aeb2e17 into remix-run:dev Jan 6, 2025
1 check passed
@silvenon
Copy link
Author

silvenon commented Jan 6, 2025

You're welcome, I'm very glad this got merged, thanks for your reviews!

@silvenon silvenon deleted the vite-v6-resolve-conditions branch January 6, 2025 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fails to resolve @prisma/client with Vite v6
3 participants