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

feat: Add plugin for React router 7 framework mode #931

Merged
merged 9 commits into from
Feb 10, 2025

Conversation

lasseklovstad
Copy link
Contributor

Hi, I was hoping to add support for React Router 7 framework mode 😄 I have tried my best to implement a plugin based on the docs. In React Router 7 the routes.ts file defines all the routes so I map over every route entry and their children, I think this is the correct way to do it. This plugin should also handle only js files aswell.

If you use react router as library mode (only having react-router as dependency) the plugin should not be enabled. So I only enabled it when @react-router/dev is present.

I was a bit unsure what to include in packages/knip/fixtures/plugins/react-router/** directory. I asumed that I only include mock files to be used when running the tests, so I omited the vite config for instance.

@webpro
Copy link
Member

webpro commented Feb 4, 2025

Hi, I was hoping to add support for React Router 7 framework mode 😄 I have tried my best to implement a plugin based on the docs. In React Router 7 the routes.ts file defines all the routes so I map over every route entry and their children, I think this is the correct way to do it. This plugin should also handle only js files aswell.

Thanks! Good stuff indeed 👍

If you use react router as library mode (only having react-router as dependency) the plugin should not be enabled. So I only enabled it when @react-router/dev is present.

Do you think we might get in trouble if library mode will be supported here? Can we distinguish that easily, or would we need separate plugins, if at all necessary?

I was a bit unsure what to include in packages/knip/fixtures/plugins/react-router/** directory. I asumed that I only include mock files to be used when running the tests, so I omited the vite config for instance.

This looks fine. Maybe you know of a public project we could run this on for some additional smoke testing?

if (existsSync(routesPathTs)) {
routeConfig = await load(routesPathTs);
} else if (existsSync(routesPathJs)) {
routeConfig = await load(routesPathJs);
Copy link
Member

Choose a reason for hiding this comment

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

maybe good idea to check if routeConfig is actually set later on?

Copy link
Contributor Author

@lasseklovstad lasseklovstad Feb 4, 2025

Choose a reason for hiding this comment

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

I initialize routeConfig as an empty array above. So if no routes file is defined it is ignored.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, gotcha. Good!

file: "routes/layout.tsx",
children: [{ file: "routes/another-route.tsx" }],
},
];
Copy link
Member

Choose a reason for hiding this comment

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

seeing things in the docs like route("/", "./home.tsx") - do we cater for that as well? or how's that returned when loading app/routes.ts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

route("/", "./home.tsx") returns a simple object containing { file: "./home.tsx", path: "/" }. Since we are joining the path like this: join(appDir, route.file) and the routes.ts file is defined in appDir this is catered for.

@lasseklovstad
Copy link
Contributor Author

If you use react router as library mode (only having react-router as dependency) the plugin should not be enabled. So I only enabled it when @react-router/dev is present.

Do you think we might get in trouble if library mode will be supported here? Can we distinguish that easily, or would we need separate plugins, if at all necessary?

Do you mean if library mode wil be supported by @react-router/dev or if framwork mode will be supported by react-router package? In the @react-router/dev package they describe it as Dev tools and CLI for React Router that enables framework features through bundler integration like server rendering, code splitting, HMR, etc.. So for now its safe to say that your using react router as framwork mode if you have that package installed.

I was a bit unsure what to include in packages/knip/fixtures/plugins/react-router/** directory. I asumed that I only include mock files to be used when running the tests, so I omited the vite config for instance.

This looks fine. Maybe you know of a public project we could run this on for some additional smoke testing?

Yes. We can test it on The epic stack which uses remix-flat-routes package. They also use remixRoutesOptionAdapter which is a function that eases migration from remix to react router 7.

I have a public project which uses react router flat routes (@react-router/fs-routes) package: https://github.com/lasseklovstad/gata
And I have a private repository which just uses the utility functions from react router index, layout, prefix and so on...

I can do some smoke testing tonight.

@webpro
Copy link
Member

webpro commented Feb 4, 2025

If you use react router as library mode (only having react-router as dependency) the plugin should not be enabled. So I only enabled it when @react-router/dev is present.

Do you think we might get in trouble if library mode will be supported here? Can we distinguish that easily, or would we need separate plugins, if at all necessary?

Do you mean if library mode wil be supported by @react-router/dev or if framwork mode will be supported by react-router package? In the @react-router/dev package they describe it as Dev tools and CLI for React Router that enables framework features through bundler integration like server rendering, code splitting, HMR, etc.. So for now its safe to say that your using react router as framwork mode if you have that package installed.

What I meant to ask was: is it possible we might need another plugin for react-router? Ideally we can keep just one plugin, unless it becomes too messy e.g. for having to support multiple modes.

@lasseklovstad
Copy link
Contributor Author

If you use react router as library mode (only having react-router as dependency) the plugin should not be enabled. So I only enabled it when @react-router/dev is present.

Do you think we might get in trouble if library mode will be supported here? Can we distinguish that easily, or would we need separate plugins, if at all necessary?

Do you mean if library mode wil be supported by @react-router/dev or if framwork mode will be supported by react-router package? In the @react-router/dev package they describe it as Dev tools and CLI for React Router that enables framework features through bundler integration like server rendering, code splitting, HMR, etc.. So for now its safe to say that your using react router as framwork mode if you have that package installed.

What I meant to ask was: is it possible we might need another plugin for react-router? Ideally we can keep just one plugin, unless it becomes too messy e.g. for having to support multiple modes.

No I don't think it will be any more modes we need to support in the future. The library mode for react router doesn't require a knip plugin and is already supported by how knip works today.


const isEnabled: IsPluginEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);

const config: string[] = ['react-router.config.{js,ts}', ...vite.config];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added vite.config aswell because the react-router.config is optional.

Copy link
Member

Choose a reason for hiding this comment

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

All config paths are loaded and if there's resolveConfig its executed with the default exported value of those config files. Also they're automatically added as entry files (so their imports are registered as dependencies like any other source file).

Since this plugin doesn't have resolveConfig I think 'react-router.config.{js,ts}' should be moved to the entry array (guess I've missed this before), and looks like there's no value in adding vite.config.

Just saying, the config array items do not influence whether the plugin is enabled or not.

Copy link
Member

Choose a reason for hiding this comment

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

Any chance you could move config to entry and remove vite.config - I'd guess that gives the same results, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moving react-router.config.ts to entry and removing vite doesn't seem to enable the plugin. When console logging inside resolveEntryPaths nothing happends. Not sure whats happening.

Copy link
Member

Choose a reason for hiding this comment

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

Ah sorry I looked over the fact that localConfig is an argument to resolveEntryPaths and thus a config item is required. Took the liberty to clean up and merge :)

@lasseklovstad
Copy link
Contributor Author

I did some smoketesting on epic-stack and my personal projects and it seemed to work fine. Found some edge cases in the process which I fixed. One thing to mention is that react router generates types with the command react-router typegen if these types are not generated knip will complain about Unresolved imports. Is this something we should handle?

@lasseklovstad lasseklovstad requested a review from webpro February 4, 2025 20:05
@webpro
Copy link
Member

webpro commented Feb 5, 2025

I did some smoketesting on epic-stack and my personal projects and it seemed to work fine. Found some edge cases in the process which I fixed.

Thanks a lot!

One thing to mention is that react router generates types with the command react-router typegen if these types are not generated knip will complain about Unresolved imports. Is this something we should handle?

Good question, tbh I'm not sure. If it's recommended to generate those I guess not. Maybe if used in JavaScript-only context that would be annoying, and we could add an entry item.

Copy link

pkg-pr-new bot commented Feb 5, 2025

Open in Stackblitz

npm i https://pkg.pr.new/knip@931

commit: 3ead7b9

@webpro webpro merged commit 012fd5b into webpro-nl:main Feb 10, 2025
@webpro
Copy link
Member

webpro commented Feb 11, 2025

🚀 This pull request is included in v5.44.0. See Release 5.44.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants