Monorepo setup with Lerna, TypeScript, Babel 7 and other — Part 2

Serhii Havrylenko
7 min readOct 28, 2018

--

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 packages
  • conventionalCommits - 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.

--

--