Create a JavaScript library using Typescript, Jest and Rollup
Recently I was working on a personal JS library Serina. It is a open source library, I wanted to use Typescript (TS) to ensure better stability due to it is static typed. It should make the code base more readable and therefore easier to contribution.
There are many ways to build projects, and Rollup is just one of them. I decided to go with Rollup because it is much lighter than something like Webpack, but it is still very well supported in terms of plugins. Most importantly, I've used it in other projects and I'm lazy :P All jokes aside, I have to say Rollup is my go to build tool right now.
In this blog, I have broken down each part of the project into different sections, so it is easily to find alternative options if prefered. E.g. rather than using Typescript, you can simple skip it.
Setup core library
Like all JavaScript projects, first create project folder and within the project folder run npm init
to initialise it.
Create a src
folder and then create an empty file inside it. It should now look something like this:
yourFolder
|-- package.json
|-- src
|-- myLib.ts
Inside the myLib.ts
file is where the library logic will live, but let's do something simple for now. In the follow example, our library will simple take in a string then return it back. It is pretty useless, but for the purpose of this blog I'm going to keep it like this so it is easier to follow.
export default myLib = (text: string): string {
return text;
}
Typescript
Install Typescript npm package by running npm i typescript
(or use npm install
).
It is always a good idea to configure the compiler options of Typescript to fit your coding pattern, and pick up on any potential issues with the code when it compiles. Create a file called tsconfig.json
at root folder level with the following config:
{
"version": "2.5.0",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": false,
"diagnostics": true,
"target": "ES5",
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"removeComments": true,
"noLib": false,
"moduleResolution": "node",
"sourceMap": true,
"typeRoots": [
"/.node_modules/@types"
]
},
"include": [
"src/**/*"
],
"exclude": [
"src/**/*.test.ts"
]
}
The folder should now look like this:
yourFolder
|-- package.json
|-- tsconfig.json
|-- src
|-- myLib.ts
Code linting
It is always important to have linting setup for any project to ensure code standards and catch silly errors when coding. Since I'm using Typescript, therefore TSlint
is my choice here, but feel free to use ESlint
if you wish to use pure JavaScript.
Let's install tslint and the extra eslint rules by running npm i tslint tslint-eslint-rules
.
Now create tslint.json
file in the root project directory.
yourFolder
|-- package.json
|-- tsconfig.json
|-- tslint.json
|-- src
|-- myLib.ts
Inside the file, paste in something like the following. This is my prefered setup, but feel free to tweak it however you see fit.
{
"extends": [
"tslint:recommended",
"tslint-eslint-rules"
],
"defaultSeverity": "warning",
"jsRules": {},
"rules": {
"curly": [true, "ignore-same-line"],
"interface-name": [true, "never-prefix"],
"max-line-length": [true, 180],
"object-literal-sort-keys": false,
"trailing-comma": [true, {"multiline": "never", "singleline": "never"}],
"arrow-parens": false,
"member-access": [true, "no-public"],
"eofline": false,
"whitespace": [false, "check-module"],
"no-angle-bracket-type-assertion": false,
"ordered-imports": false,
"quotemark": [true, "single", "avoid-escape"],
"object-curly-spacing":[true, "always"]
},
"rulesDirectory": []
}
For more information on the above rules, refer to the official documentation.
Inside package.json
I would recommend adding 2 npm scripts related to linting. npm run lint
will check for all the TS files within src
folder for mistakes, and npm run lint:fix
will fix all the mistakes it can automatically (e.g. it is nice not having to go in different files and remove a whole bunch of white spaces).
"lint": "tslint -c tslint.json \"src/**/*.ts\"",
"lint:fix": "tslint --fix -c tslint.json \"src/**/*.ts\""
Setup Test
In order to get test set up, we will need to install Jest and the related packages by running npm i jest ts-jest @types/jest
.
Then add the following Jest config inside package.json
. This will be used to pick up test files and etc.
{
...
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!shiva/)"
],
"globals": {
"ts-jest": {
"tsConfigFile": "tsconfig.test.json"
},
"google": {}
},
"collectCoverageFrom": [
"src/**/*.ts",
"!src/**/*.schema.ts",
"!src/schema/*.ts"
],
"testMatch": [
"<rootDir>/src/**/?(*.)test.ts",
"<rootDir>/tests/**/?(*.)test.ts"
],
"coverageThreshold": {
"global": {
"branches": 0,
"functions": 0,
"lines": 0,
"statements": 0
}
},
"moduleFileExtensions": [
"ts",
"js"
],
"transform": {
"\\.(ts)$": "<rootDir>/node_modules/ts-jest/preprocessor.js",
"^.+\\.js$": "babel-jest"
}
}
...
}
Setup Build
To set up Rollup, first install Rollup and TS plugin by running npm i rollup rollup-plugin-typescript2
.
Now create a rollup.config.js
file in the root project directory. Inside this file create a simple rollup config like the following:
import typescript from 'rollup-plugin-typescript2';
export default {
input: 'src/myLib.ts',
output: [{
name: 'MyLib',
file: 'dist/myLib.js',
format: 'umd'
}],
plugins: [
typescript()
]
}
The project structure should now look like this:
yourFolder
|-- package.json
|-- rollup.config.js
|-- tsconfig.json
|-- tslint.json
|-- src
|-- myLib.ts
Now, again inside package.json
and add in some npm scripts for building the project. npm run build
will call other npm scripts and then at the end compile the TS into JS and place it in the dist folder.
"build": "npm run lint && npm run test && npm run clean && rollup --config",
"clean": "rm -rf ./dist && mkdir dist"
Icing on the cake
Since it is going to be an open source library, it is also worth considering doing the following to give people more confidence in the project:
- Add a
README.md
file - Add a
LICENSE
file so people know how they can use your code - Set up CI/CD, and then place a badge in the README to show the code bade status (e.g. passing, failing)
- Consider adding an
.editorconfig
file
That's it!
Hope this was relatively straight forward to follow, I tried to cover all the aspects in this blog and answer the common questions. Please let me know in the comments if there's anything I missed or hit me up on Twitter!