From dc67c4f64e65bdb69fae2165458897338aa1fafe Mon Sep 17 00:00:00 2001 From: Pedro Cattori Date: Fri, 30 Aug 2024 15:14:31 -0400 Subject: [PATCH] future flag for automatic optimize deps --- .changeset/hot-dogs-applaud.md | 6 +-- docs/guides/dependency-optimization.md | 64 ++++++++++++++++++++++++++ docs/start/future-flags.md | 5 ++ packages/remix-dev/config.ts | 2 + packages/remix-dev/vite/plugin.ts | 14 +++--- 5 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 docs/guides/dependency-optimization.md diff --git a/.changeset/hot-dogs-applaud.md b/.changeset/hot-dogs-applaud.md index a966a76ceb8..7e41800566a 100644 --- a/.changeset/hot-dogs-applaud.md +++ b/.changeset/hot-dogs-applaud.md @@ -2,9 +2,9 @@ "@remix-run/dev": patch --- -Automatically optimize dependencies during development +(unstable) Automatic dependency optimization -Now Remix will tell Vite to find dependencies by crawling imports starting with your route modules. -This should resolve most (if not all) `504 Outdated Dependency` errors that could previously break HMR. +You can now opt-in to automatic dependency optimization during development by using the `future.unstable_optimizeDeps` future flag. +For details, check out the docs at [`Guides` > `Dependency optimization`](https://remix.run/docs/en/main/guides/dependency-optimization). For users who were previously working around this limiation, you no longer need to explicitly add routes to Vite's `optimizeDeps.entries` nor do you need to disable the `remix-dot-server` plugin. diff --git a/docs/guides/dependency-optimization.md b/docs/guides/dependency-optimization.md new file mode 100644 index 00000000000..e949149606d --- /dev/null +++ b/docs/guides/dependency-optimization.md @@ -0,0 +1,64 @@ +--- +title: Dependency optimization +--- + +This feature only affects development. It does not impact production builds. + +# Dependency optimization + +Remix introduced automatic dependency optimization in development behind the `future.unstable_optimizeDeps` [Future Flag][future-flags]. +This allows you to opt-into this behavior which will become the default in the next major version of Remix - a.k.a. React Router v7 ([1][rr-v7], [2][rr-v7-2]). + +In development, Vite aims to [prebundle dependencies](https://vitejs.dev/guide/dep-pre-bundling.html) so that it can efficiently serve up those dependencies on-demand. +To do this, Vite needs to know where to start crawling your app's module graph to look for dependencies. + +Previously, Remix did not inform Vite to start dependency detection at route modules nor at the client entry. +That meant that in development, Vite would encounter new dependencies as you navigated around in your app resulting in `504 Outdated Dependency` errors. +Consequently, the development experience could feel janky at times since those errors could cause HMR to break or link navigations to be aborted. +Navigation could also feel sluggish as processing dependencies interactively could sometimes be slow. + +For more information, see [Vite's Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options#dep-optimization-options). + +## Troubleshooting + +### `Failed to resolve entry for package` + +```txt +✘ [ERROR] Failed to resolve entry for package "". The package may have incorrect main/module/exports specified in its package.json. [plugin vite:dep-pre-bundle] +``` + +This is usually caused by a misconfigured dependency. +You use [publint](https://publint.dev/) to check if the offending package is misconfigured. +To fix the issue, you'll need to use `npm why` or `pnpm why` to determine which of your direct dependencies to add to `optimizeDeps.exclude`. + +For example, let's say your app is running into this error: + +```txt +✘ [ERROR] Failed to resolve entry for package "jimp". The package may have incorrect main/module/exports specified in its package.json. [plugin vite:dep-pre-bundle] +``` + +Sure enough, `publint` reports that the [`jimp` package is misconfigured](https://publint.dev/jimp@0.22.12). +Then, you determine that `jimp` is an indirect dependency being pulled in by your `svg2img` direct dependency: + +```sh +❯ npm why jimp +jimp@0.16.13 +node_modules/jimp + jimp@"^0.16.1" from svg2img@1.0.0-beta.2 + node_modules/svg2img + svg2img@"^1.0.0-beta.2" from the root project +``` + +Finally, you add `svg2img` to `optimizeDeps.exclude`, which should fix the issue: + +```ts filename=vite.config.ts +export default defineConfig({ + optimizeDeps: { + exclude: ["svg2img"], + }, +}); +``` + +[future-flags]: ../guides/api-development-strategy +[rr-v7]: https://remix.run/blog/merging-remix-and-react-router +[rr-v7-2]: https://remix.run/blog/incremental-path-to-react-19 diff --git a/docs/start/future-flags.md b/docs/start/future-flags.md index a262544c0a7..522b65a0820 100644 --- a/docs/start/future-flags.md +++ b/docs/start/future-flags.md @@ -340,6 +340,10 @@ Opt into [Single Fetch][single-fetch] behavior (details will be expanded once th Opt into [Lazy Route Discovery][lazy-route-discovery] behavior (details will be expanded once the flag stabilizes). +## unstable_optimizeDeps + +Opt into automatic [dependency optimization][dependency-optimization] during development. + [development-strategy]: ../guides/api-development-strategy [fetcherpersist-rfc]: https://github.com/remix-run/remix/discussions/7698 [relativesplatpath-changelog]: https://github.com/remix-run/remix/blob/main/CHANGELOG.md#futurev3_relativesplatpath @@ -357,3 +361,4 @@ Opt into [Lazy Route Discovery][lazy-route-discovery] behavior (details will be [vite-url-imports]: https://vitejs.dev/guide/assets.html#explicit-url-imports [mdx]: https://mdxjs.com [mdx-rollup-plugin]: https://mdxjs.com/packages/rollup +[dependency-optimization]: ../guides/dependency-optimization diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts index 8f993d61888..7234202be8f 100644 --- a/packages/remix-dev/config.ts +++ b/packages/remix-dev/config.ts @@ -39,6 +39,7 @@ interface FutureConfig { v3_throwAbortReason: boolean; unstable_singleFetch: boolean; unstable_lazyRouteDiscovery: boolean; + unstable_optimizeDeps: boolean; } type NodeBuiltinsPolyfillOptions = Pick< @@ -611,6 +612,7 @@ export async function resolveConfig( unstable_singleFetch: appConfig.future?.unstable_singleFetch === true, unstable_lazyRouteDiscovery: appConfig.future?.unstable_lazyRouteDiscovery === true, + unstable_optimizeDeps: appConfig.future?.unstable_optimizeDeps === true, }; if (appConfig.future) { diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index ca683f03538..d96d6b547cd 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -1075,12 +1075,14 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { : undefined, }, optimizeDeps: { - entries: [ - ctx.entryClientFilePath, - ...Object.values(ctx.remixConfig.routes).map((route) => - path.join(ctx.remixConfig.appDirectory, route.file) - ), - ], + entries: ctx.remixConfig.future.unstable_optimizeDeps + ? [ + ctx.entryClientFilePath, + ...Object.values(ctx.remixConfig.routes).map((route) => + path.join(ctx.remixConfig.appDirectory, route.file) + ), + ] + : [], include: [ // Pre-bundle React dependencies to avoid React duplicates, // even if React dependencies are not direct dependencies.