How to develop a local package and use in a vite app

This might be a niche topic, but recently I had to link and develop a shared library that is not part of the same repo as the main app codebase. And there is no monorepo involved either.

On the web the resources to do this are scattered and don’t cover hot reloading.

The problem

if you ever came across such a scenario, in such cases the usual workflow is develop the package in isolation, build and release. Then go to the app and update the version number so you can bring the changes in.

BUT, wouldn’t it be cool to be able to develop the shared package side by side with live reloading and reflect the changes in the main app, make sure everything is working as expected and THEN release ?

In this article, we are gonna do just that.

Setup

The setup for the purposes of this tutorial is the following:

  1. Package boilerplate generated using tsdx
  2. Basic vite react app with typescript
  3. Using yarn

Shared package

Let’s call it @awesome/package (do that by changing your package.json name).

The changes needed here are very few. The main thing to know is that Vite uses ESM and needs to know exactly where to look for the root file so lets help it

in package.json

"exports": { ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/shared.esm.js" }, "require": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, },
Enter fullscreen mode
Exit fullscreen mode

So what we are saying above is the following.

If you are an ESM consumer (import) use the these types and this is the entry point.

If you are a commonJS consumer (require) use these types and this is the entry point.

Now vite knows what to look for, great!

Vite setup

in vite.config.ts

import {defineConfig, ViteDevServer} from 'vite'
import react from '@vitejs/plugin-react' // https://vitejs.dev/config/
export default defineConfig({ plugins: [react()], optimizeDeps: { exclude: ["@awesome/shared"] },
})
Enter fullscreen mode
Exit fullscreen mode

By default, Vite likes to optimize deps but in our case since its an ESM lib we want to skip that.

Great, now vite knows about our package.

Yarn setup

Now we need to do two things.

Install the package as a local dependency.

In shared package

The first step is to make sure yarn knows where to look for the package.

yarn link
Enter fullscreen mode
Exit fullscreen mode

In main app

yarn add link:./../your-project // here adjust to be the relative path to your project. In case you use pnpm or npm you need to adjust this accordingly - most probably use file: in front.
Enter fullscreen mode
Exit fullscreen mode

If you already released and use the package with a specific version. e.g in package.json you already have the package as a dependency like so – "@awesome/shared": "0.0.1" then do the following:

yarn link @awesome/shared
Enter fullscreen mode
Exit fullscreen mode

At this point, we have made sure vite is aware of the package, and our package manager, in this case yarn – has made the necessary linking.

Development

In shared package do a change that is meaningful and can be easily observed in the vite app.

Afterwards,

In shared package

yarn start // if you used tsdx to scaffold or else use the equivalent command with your setup
Enter fullscreen mode
Exit fullscreen mode

In vite app

yarn dev
Enter fullscreen mode
Exit fullscreen mode

You will notice that the shared package change you made is reflected in your vite app.

However, you will notice that making a second change to the package it is not being reflected to the vite app.

Thats because, Vite by design does not watch changes in node_modules and because does caching of the dependencies under node_modules/.vite directory.

The suggestion is to run the following command

yarn dev --force // underneath its vite dev --force
Enter fullscreen mode
Exit fullscreen mode

This is too manual for our liking, isn’t it?

So, let’s see how to fix that.

Adding a plugin to watch specific packages for changes

We need to add a plugin in order to watch for our packages, so here is the final vite.config.ts

import {defineConfig, ViteDevServer} from 'vite'
import react from '@vitejs/plugin-react' // This plugin will watch node_modules for changes and trigger a reload.
// Works only for build --watch mode.
// https://github.com/vitejs/vite/issues/8619
export function pluginWatchNodeModules(modules: string[]) { // Merge module into pipe separated string for RegExp() below. const pattern = `/node_modules\\/(?!${modules.join('|')}).*/`; return { name: 'watch-node-modules', configureServer: (server: ViteDevServer): void => { server.watcher.options = { ...server.watcher.options, ignored: [ new RegExp(pattern), '**/.git/**', ] } } }
} // https://vitejs.dev/config/
export default defineConfig({ plugins: [react(), pluginWatchNodeModules(['@awesome/shared'])], optimizeDeps: { exclude: ["@awesome/shared"] },
})
Enter fullscreen mode
Exit fullscreen mode

Start the vite app again and do changes to the shared package. They should be reflected to vite app almost instantly.

Bonus

Now that you are able to develop and test your package, you need to unlink the local package and use the version you used when releasing the package with the changes

yarn unlink @awesome/shared 
Enter fullscreen mode
Exit fullscreen mode

Now update package.json (in vite app) with the latest release version and run yarn to install it.

Conclusion

You have learned how you can easily make changes to a shared package and they are then reflected to your vite app. Now you can be sure the changes work before releasing the shared package.

I hope you found this useful and if you want to keep in touch you can always find me on Twitter.


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top