Creating a TypeScript Package with Vite
Published at October 22, 2022.
Sometimes, we have some utility functions or some complex stuff that we need to use in multiple projects and we don't want to copy it to each and every project. Or sometimes we just want to contribute to open-source community to both improve ourselves and help others.
There are a lot of different ways of creating a TypeScript package. And here is a simple tutorial to make it happen by using Vite.
We are gonna create a simple utility package with a couple of functions. And it can be used both for frontend and backend.
Note: Here are the package versions used in this tutorial. When a new version is published, some of these may have breaking changes, change of best practices, have new options/usages etc. It is always nice to check the original documentation of the tools we are using every now and then.
For testing with Jest:
For testing with Vitest:
Scaffolding the Project
First of all, we need to create a new Vite project. To accomplish this, we can run the following command:
It will ask us some questions to create our project. We can set them like below:
It will have a folder structure like this:
We can delete index.html
, public
folder and all the files in src
. In the end, it will look this this:
Now, install the dependencies by using:
Implementing the Features
Now, we can implement our utility functions and create a simple folder structure.
The key point here is, exporting all the functions, constants, enums, types etc. by using index.ts
file. We are gonna point to it (actually, the output based on it) in our package.json
in the following steps. So, we can say that, this is the place where we describe all the things we expose to the outside world and let other developers to be able to use this package.
There can be multiple entry points for a package, but this is not a topic of this tutorial. For this, we can check Vite Library Mode documentation.
Note: We could create a single index.ts
file and put all our code in it. But we just created multiple files to see how it affects the structure. The topics like the folder structure and how we export our functions (named or default export etc.) are completely up to us. We can sort out how to structure the project as it grows. There are also some recommended approaches which are worth for checking.
Bundling the Package
To create the distributable package to publish on npm, we need to create a vite.config.ts
at the root of the project first. But before that, we need to install a couple more dependencies to get ready.
Since we are gonna use some Node.js modules like path
, we need no install @types/node
. And to be able to include our type definitions as .d.ts
files to our bundle, we need vite-plugin-dts
.
We need these packages only for local development or testing our package. So we put them in our devDependencies
by --save-dev
or -D
flag. For more information about dependencies
, devDependencies
and peerDependencies
, we can check npm Docs.
And we need to add the entry points of our package to package.json
. We also need to remove private
field from it, if there is one.
Basically, we are pointing out the root of our package and where the types are (by type
field). Even if it's not the full explanation, we can basically think that when someone uses our package by using import ... from 'my-ts-lib'
and if it runs in an environment which supports ECMAScript modules (ESM)
, more modern ESM
version of our code will be used. Otherwise, if someone uses our package by using require('my-ts-lib')
, it will use the CommonJS (CJS)
version.
Now, we are ready to build our package and see the first result. Let's run the build command:
It will create a dist
folder at the root of our project and it will look like this:
It looks fine for now. But we need to add a couple of more stuff before publishing it to npm
.
First of all, we need a README.md
file to inform other developers about how to use this package, showing examples etc. It will be shown on the page of our package on npm
.
We will create the README.md
file at the root of the project. It can have any type of information we want.
We also need to create a LICENSE
file too. We can check licensing a repository docs of GitHub to have a little knowledge about it.
And lastly, we need to add a files
field to package.json
to indicate npm what we want to be in the final package. We just need to point dist
folder here. README.md
, LICENSE
and package.json
will be automatically included. If we come across any problem, we can put them in this array too.
And now, we can run the following command to preview what our package will look like without publishing it:
npm pack command helps us to preview what our package will include and its size when we publish it. --dry-run
is optional here. If we don't use it and just run npm pack
, it will also create a .tgz
file which is what would be deployed on npm. We are just previewing our package without publishing it, yet. We can use this command at anytime to preview our package.
We have dist
folder, README.md
, LICENSE
and package.json
in our package. Just like we want.
Versioning
As we can see, our package version is 0.0.0
now. We might want to update our package version, especially as we add new features, make fixes or refactors. Semantic Versioning is a nice way to follow for this.
We can use following commands to bump our package version:
We can also use alpha
or beta
versions. npm version Docs is a nice place to check out for it.
Setting Up Tests
We may want to test our package to be sure if it's reliable and we're not breaking anything in time. To do that, we need to install some packages to be used for testing.
We can use the good old Jest or Vitest for testing. It's up to you to choose the one you like.
Testing with Jest
First, we need to install the packages required for testing.
And we need to create a jest.config.js
file to configure Jest to test our ts
files.
Lastly, we need to add a test
script to our package.json
.
Now, we can create our test files and see if our package works properly.
Let's run our test and see if we're all good:
🎉🎉🎉
Testing with Vitest
As the first step, we will install Vitest.
Also, even if it's not required for this example, we can configure it in our vite.config.ts
.
We will add test
script to package.json
:
Create our test files to be sure our package works properly:
And we can run the tests to see if everything is fine.
🎉🎉🎉
Linting & Formatting
We may want to lint our code for finding problems and format it to have a well structured project.
To do this, a good way is using ESLint and Prettier.
This tutorial will not be deep dive about how to set these up. Rules, plugins and configs may differ based on the project and team preferences. Like mentioned at the beginning, official docs of these kind of tools are the best places to check out.
But as a couple of advices, a fast way of setting ESLint up is using the following command:
And if we want to use Prettier with it, eslint-plugin-prettier and eslint-config-prettier are worth checking.
Also, Husky and lint-staged are nice tools to have a more more strict and automated flow for linting and formatting.
Publishing the Package
We are nearly there. We just need to add a couple of more fields to inform npm about our package.
And finally, we use npm publish command and publish our package to npm:
Thanks for reading!