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

Add expo plugin #879

Merged
merged 2 commits into from
Dec 16, 2024
Merged

Add expo plugin #879

merged 2 commits into from
Dec 16, 2024

Conversation

jjjjonathan
Copy link
Contributor

@jjjjonathan jjjjonathan commented Dec 15, 2024

This PR adds a plugin for Expo.

Features

  • Adds entry files based on whether expo-router (A file-based router akin to NextJS) is used or not.
  • Adds expo, react, and react-native as dependencies; required for all Expo projects.
  • Adds dependencies from plugins listed in Expo config file
  • Adds dependencies that are implied from other config options
  • Adds dependencies from certain packages that only need to be installed to be picked up by Expo. For example, installing expo-dev-client will cause Expo to use the package when building the native app; it does not need to referenced or imported anywhere other than the package.json.

Copy link
Collaborator

@webpro webpro left a comment

Choose a reason for hiding this comment

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

Thanks, Jonathan! Great PR, happy to merge. A few remarks I have.

const resolveEntryPaths: ResolveEntryPaths<ExpoConfig> = async (expoConfig, { manifest }) => {
const config = 'expo' in expoConfig ? expoConfig.expo : expoConfig;

let production: string[] = ['index.{js,jsx,ts,tsx}'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Doesn't hurt, but the root index.* pattern is already a default so a bit redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I'll remove it here

.filter(Boolean) as string[]) ?? [];

const inputs = new Set<Input>([
...['expo', 'react-native', 'react'].map(toProductionDependency),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Adds expo, react, and react-native as dependencies; required for all Expo projects.

Aren't those dependencies referenced in regular ways already? I'd think it isn't necessary to add them explicitly. It's a bit far-fetched, but in theory this might change, and if that's what they are, peer dependencies should picked up properly already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh I didn't realize Knip picked up peer dependencies. Very cool! That takes care of react and react-native, and on second thought expo will almost certainly be in the package.json scripts (if not imported in the project), so I'll remove that as well.


// https://docs.expo.dev/router/installation/#setup-entry-point
if (manifest.main === 'expo-router/entry') {
production = ['app/**/*.{js,jsx,ts,tsx}', 'src/app/**/*.{js,jsx,ts,tsx}'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does this manifest.main value result in all those files becoming entry files, isn't that pattern too broad?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Setting main to expo-router/entry essentially "turns on" expo-router (if the package is installed, of course). It's a file-system based router, so every file in the app directory becomes it own route. (e.g. an app/profile/settings.tsx file will become a /profile/settings URL) This is all done under the hood by expo-router; these route files are not imported elsewhere in the project source code. So I do think the broad pattern is warranted here, you can name a file anything you want, and expo router will pick it up. It's the same concept as the NextJS "pages" router; anything in that directory is an entry file.

'pages/**/*.{js,jsx,ts,tsx}',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Side note, the app directory can live either at the root or under src, and src/app takes precedence. (https://docs.expo.dev/router/reference/src-directory/) Is it worth it to check for the existence of src/app and fall back to app if it doesn't exist, instead of using both patterns at the same time?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Clear, thanks for explaining that. I'd say the check isn't really necessary, assuming that globbing will essentially do the same thing. Unless you can think of a situation where this might go awry, i guess it makes sense to keep this simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, I'll keep it simple

Copy link

@wcastand wcastand Dec 17, 2024

Choose a reason for hiding this comment

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

i also use expo-router but in a index.ts file as my entry and it seems that break that plugin, i can't get it to not put everything as unused. maybe i configure it badly.

one of the reason for using index.ts as entry file even with expo router is for example skia
https://shopify.github.io/react-native-skia/docs/getting-started/web/

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks @wcastand, here as a comment it might get lost, any chance you could give more details so we have something to look into? What's the issue you're seeing and how to reproduce it?

Choose a reason for hiding this comment

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

i changed the way i handle the entry point so seems to be good for now, issue i can see seems to be with custom modules in modules directory being flagged at unused.
other than that hard to say what's an issue on our repo or not so will take a deeper look and if i find anything i will get back to you.

great tool either way thanks :)

inputs.add(toProductionDependency('expo-navigation-bar'));
}

return [...inputs];
Copy link
Collaborator

Choose a reason for hiding this comment

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

That looks like a lot of magic Expo is pulling off, thanks a lot for figuring that all out here. Also appreciate you've added links to docs and used some "heuristics" to best-guess including things like expo-system-ui.

@jjjjonathan jjjjonathan requested a review from webpro December 16, 2024 17:32
Copy link

pkg-pr-new bot commented Dec 16, 2024

Open in Stackblitz

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

commit: 8e4c5dc

@webpro
Copy link
Collaborator

webpro commented Dec 16, 2024

Excellent stuff, Jonathan! Really appreciate this, and I'm sure others will as well.

@webpro webpro merged commit 40f7be9 into webpro-nl:main Dec 16, 2024
23 checks passed
@webpro
Copy link
Collaborator

webpro commented Dec 16, 2024

🚀 This pull request is included in v5.41.0. See Release 5.41.0 for release notes.

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

@jjjjonathan
Copy link
Contributor Author

Thanks for the awesome project, it's tremendously useful! There will be more plugin PRs from me :)

@jerone
Copy link
Contributor

jerone commented Dec 19, 2024

@jjjjonathan Thanks for this plugin! It allowed me to remove a lot of whitelisted dependencies.

I do have a few left, that might still be relevant:

  • babel-preset-expo
  • expo-updates
  • react-dom
  • And if your project uses Jest:
    • babel-jest
    • jest-watch-select-projects
    • jest-watch-typeahead

@jjjjonathan
Copy link
Contributor Author

@jerone Glad you're getting some use out of it! Here are my thoughts/suggestions:

  • expo-updates should already be picked up by this plugin, unless you have updates: { enabled: false } } in your Expo config file
  • react-dom should already be picked up by this plugin, make sure the platforms array includes web in your Expo config file
  • babel-preset-expo and the Jest dependencies should be picked up by the Babel/Jest plugins. I'd knip --debug and make sure those plugins are active

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.

4 participants