Monorepo setup with Lerna, TypeScript, Babel 7 and other — Part 2
Series
- Basic monorepo setup with Lerna, Babel and TypeScript
- Tslint, Stylelint, Jest, Storybook and Conventional Commit in the monorepo
- First package development and publishing
- Multiple packages and magic in TypeScript monorepo configuration
Tslint, Stylelint, Jest, Storybook and Conventional Commit in the monorepo
In the first part of the article we configured basic setup of the monorepo with Typescript and Babel for building packages. Let’s proceed with Tslint, Jest, Storybook and others.
Static code analyser
Having typescript for types checking could protect us from creating plenty of mistakes and save a lot of time for writing basic unit tests related to incorrect input data. However, it won’t protect us from writing over-complicated, unreadable, or even hacky code. Even more, it won’t protect us from using incorrect or unsupported CSS rules, would it be CSS, SCSS or CSS-in-JS.
Tslint
The easiest solution for automating such checks is using tslint
for static code analyzing.
Let’s install it as devDependency in the root of our monorepo:
and create simple config file for it tslint.json
:
and new script in package.json for linting *.ts
files:
Now static code analyzer could be run with simple yarn lint:ts
command.
Stylelint
Let’s use stylelint
for improving our CSS styles quality and readability. It could be easily used for analyzing CSS-in-JS as well as with simple CSS or SCSS files.
Installation and configuration process as simple as with tslint
:
Create simple config file for it - .stylelintrc
:
and new script in package.json for linting *.ts
files:
Now static code analyzer could be run with simple yarn lint:css
command.
All together
For now we have lint:ts
for checking typescript code quality and lint:css
for CSS, however, they are still separated commands and it would be uncomfortable to run them separately all the time. Let's group them in one unified lint
command and run it with npm-run-al
:
and new script in the root package.json
:
Note: run-p -c
allows to run all lint:*
commands even if one of them failed. It's useful in case of separated static code analyzer steps, as after one run we have output from tslint
and stylelint
, instead of only first failed.
Code formatting
For now we have typescript
for static types checking, tslint
and stylelint
for static code analyzing. Still, we could write unreadable or not well formatted code. We could avoid all issues relate to code formatting with prettier
. It will automatically format our code according to predefined standards. Moreover, it will fix some issues reported by tslint
.
As usual, installation and configuration is very simple:
Next is needed is .prettierrc
:
and integration with tslint
, as both of the tools have common rules (like tabWidth, trailingComma, etc). Next lines should be added to the tslint.json
to make it work with prettier:
Important note is quotemark
rule. Because of jsx
usage we have to override default recommended rules which requires to have single quotemark everywhere.
Last but not least, let’s add script to the root package.json
for automatic code formatting based on defined above rules:
Testing
For now we have TypeScript for checking types, tslint
and stylelint
for static code quality analyzing. Last, but not least part is testing. Let's use jest
as test runner and test assertion tool. It has the best support for react
, including snapshot testing, extensive mocking library, build-in coverage reporting and ability to run tests in different processes. ts-jest
should be used for running typescript
code. Installation is also quite simple:
jest
could be configured in two ways:
yarn jest --init
and answer for all questions- manually create config file with minimum required configuration
Here is basic setup from jest.config.js
in the root of the monorepo
Notes:
ts-jest
could use babel config or pure typescript compiler. In our case we have babel configured, so it could be used with just 1 line in config:
coverageThreshold
is protecting us from writing not enough tests and having poor test coverage.
As soon as we have basic config, it’s time to install enzyme
(and all related libraries) which is extending support for react
:
Add setupTestFrameworkScriptFile: '<rootDir>jest/setupTests.ts'
into jest.config.js
file and proper setup for enzyme
into jest/setupTests.ts
:
Now jest
is able to render react
components, however, it will serialize snapshots as pure objects and we want to have it serialized as HTML markup. We could achieve it with proper serializer:
and snapshotSerializers: ['enzyme-to-json/serializer']
in jest.config.js.
We are almost there, we are able to run tests and create proper snapshots. Nevertheless, we still have issues with styled-components
- on each change in styles, styled-components
is creating different class name. Based on it we'll have plenty of false negative tests fails just because of class name is changed. Let's fix it with proper tool
import 'jest-styled-components'
should be added to the jest/setupTests.ts
.
Just to sum up, jest.config.js
:
That’s it, jest
is configured and could be run. Let's add test
to the root package.json
scripts:
Storybook
We already able to write code in monorepo with typescript
, analyze it with tslint
and stylelint
, test it with jest
. However, we still cannot see how our components will look like and we cannot even debug it properly.
There are plenty of ways how to present react component. Let’s go with most famous one — storybook
. It allows to present separate components and/or group of them as well as testing it in real browsers, and having documentation close to it.
Installation
Latest stable version of storybook@3.4.10
works with webpack@3
, babel@^6
and typescript@^2.7
. As we have latest @babel@^7
and typescript@^3
it's better to use next
version of storybook which has the same set of dependencies as our monorepo, even if it's bleeding edge version.
If you do it for the first time, you should have @storybook@cli
installed globally on your machine and init:
It will automatically detect project type (react), installs all required packages and create basic configuration folder.
Typescript
Still, as we use typescript, we have to install typings for those packages, loader for wepback and needed peerDependencies:
Storybook inits application as it is javascript based, as we have typescript everywhere, we have to change path resolution for stories in storybook/config.js
file:
Last but not least part is webpack.config.js
inside storybook
folder, just create it with next content:
and associated configuration part in tsconfig.json
for awesome-typescript-loader
:
Configuration part is ready, we could start storybook with yarn storybook
command.
Tslint
As soon as we add storybook
to our devDependencies and run yarn lint:ts
we will get an error from tslint
:
The reason is obvious, we use package which is installed as devDependency in our source code (in the story file). Unfortunately there is no options like override for specific path or files. It could be done by splitting lint-ts
command into two separated for production code (which will be shipped as packages) and for test code (storybook, tests, etc).
Let’s create config for tslint
for production phase, called tslint.prod.json
:
Another config for the test phase called tslint.test.json
:
"no-implicit-dependencies": false
into tslint.json
to disable this rule by default. This one is need to fix issues with IDEs as by default they use tslint.json
for all files, whether it test or production code.
Last, but not least, scripts in the root package.json
have to be adjusted:
Addons
Storybook allows to pass any props to the react component without rebuilding stories, just through UI interface. To do it, we have to add one more addons — @storybook/addon-knobs
NOTE: moment has to be installed because of wrong peerDependencies management on storybook
and react-datetime
level.
Next step is to add import '@storybook/addon-knobs/register';
to the storybook/addons.js
and modify storybook/config.js
to have global decorator for each story:
Now knobs
could be used in the stories.
Final build
As we already have build command as well as code formatting and static code analyzing tools in place, it’s time to use them all together in the build process:
As soon as we run yarn build
command, yarn
automatically will run tsc
for type checks, tslint
for code quality analyzing and test
on each of packages in the monorepo.
If they succeed, build
will proceed and prepare all packages for publishing.
Committing your work
Let’s use conventional commit for committing our work for having consistent commit messages across the monorepo and as a benefit, proper version creation per package basing on conventional-commit approach.
Let’s install required packages:
cz-lerna-changelog should be installed with version ^2.0.0
as it supports latest lerna
Configuration is quite simple, just add next line to the root package.json
file:
and simple alias for commit command:
Now it’s time to test it, just change something in one of the packages, stage changes with git and run yarn commit
:
Package publishing
As described earlier, lerna
is used for publishing packages. It could be configured quite easily with next lines in the lerna.json
:
The config is self descriptive, however, the most important parts are:
registry
- specifies where do we want to publish our packagesconventionalCommits
- allows us to use conventional commits for determining new versions
All other options could be found on the official lerna
documentation.
Now we could add simple alias to the our scripts for having release
command there:
That’s it. The key part is configured and ready to be used. Time to test it with real packages and we’ll do it in the next story HERE.