As everyone knows, the main goal of the monorepo is to have multiple packages inside one repository for easily solving dependencies between them and simple release process.
Let's create second package which is dependant on our @taxi/input
and check how our tools works with it.
Just create second package in the same way as the first one. In our example it would be @taxi/login-form
with next content:
https://gist.github.com/85189fa9406b12e9fae2b7500158768e
Now we could add @taxi/input
to the list of dependencies. This could be achieved easily with next command:
https://gist.github.com/a95ffc62d80d6477fc1f26c008a31790
As soon as we have it, it's time to run tsc
and check what typescript
thinks about our code:
https://gist.github.com/1ecbc7e3db9072baa9e86cd04d5dbe78
It happens because in the package.json
file for @taxi/input
package we have "main": "dist/index.js"
which is pointing to the build version of the package and we have not run build
command. So, let's build our packages and check tsc
again:
https://gist.github.com/0536fd076f9987d86f02d4c9ebfc0d80
Now typescript
could find our package, however it doesn't know anything about typings for that module. It happens because we built our package with babel
and it cannot create declaration files.
Let's configure tsc
to generate types declarations for our modules. As a first step, we have to move login-form
package to the different folder (we would need to have only compilable with tsc
packages in the packages
directory).
We would need:
tsconfig.build.json
in the root of monorepo with:
https://gist.github.com/88dd8fea231faa8d3ade6a0251556c2b
Where we specify that we need to emit declarations only and exclude test, stories and dist folder.
Next we would need tsconfig.build.json
inside the each of our packages with:
https://gist.github.com/bd5ed5b3876133dfb9b4c39f89bc16aa
Here we would specify from where we would like to take files for generating declarations and where we would like to put them.
Now we would need script inside the root of monorepo for generating declarations:
https://gist.github.com/f384448b08a9da562032d50d3523dec4
Time to test it:
https://gist.github.com/72cda0f027146499c1d08931ca1b5890
As the result we should have *.d.ts
files generated in the dist folder:
https://gist.github.com/1009a031f2b261fc861443d9d2545323
Now we could check that @taxi/login-form
works as well, just return it back to the packages
directory and check that tsc
works now.
https://gist.github.com/d356795f290aeb215a8fe0d4345c98d3
As soon as we generate declarations and run tslint
we would have errors like:
https://gist.github.com/b4b3eee09d65eefe24cbaa6adec36b24
It happens because tslint
tries to validate *d.ts
files as well. Let's add the to the ignore. With latest version of tslint
we could do it in configuration file, so:
tslint.prod.json
https://gist.github.com/e1512fba53fae4ab934f949bcfa7f3ec
tslint.test.json
https://gist.github.com/b387660be45c363349799f91820174e5
Now if we run yarn lint:ts
we won't have any errors related to .d.ts
files.
We are able to generate declarations for all packages, run storybook
and tests
, however, as soon as we change @taxi/input
we would not see any changes in the storybook
for @taxi/login-form
because we still would use previously built version. To see these changes we would need to rebuild packages one more time. The same applies to the jest
runs.
This approach looks not so cool if each change in one of the packages would require constant rebuild. Lucky we have better option how to fix this issues. More details in the next paragraphs.
Firstly, let's remove all dist
folders inside our packages to have clean code only. On this stage tsc
should still fail. Now we could extend tsconfig.json
with:
https://gist.github.com/d505a22773d3bff621a642c7b33c89aa
"@taxi/*": ["packages/*/src"]
tells to tsc
where to search for @taxi/
packages. In our case we would like to search them in packages/*/src
folders as there we have our source code.
Now we could run tsc
and it would finish successfully without any errors like we had earlier.
As storybook
is using webpack
with awesome-typescript-loader
and babel
integration, as soon as we start storybook we would get next errors:
https://gist.github.com/a8bde07545ccf89962b584a8ab75d127
It happens even when tsc
could find those modules. The reason of this is simple: tsc
checks files, babel
transforms them and tries to execute, and it doesn't know anything about @taxi/input
as main
still points to the dist
folder.
However, we could override it with aliases for webpack
. It could be done in two ways:
- Manual one, in this case we'll have to define each package which we would like to have in
webpack.config.resolve.alias
. This approach is simple, but it introduces potential issues in the future when someone could forget about aliases or introduces wrong one. - Automated one with next info in the
storybook/webpack.config.js
https://gist.github.com/5a45a374110520f5665808a714715736
This code is rather simple and self explanatory. The most important part is in reducer for building aliases:
https://gist.github.com/ca581122ab8f446986a5e26e4e2af5c0
With this reducer we would have automatically generated list of our packages from packages
directory.
Now we could start storybook and check that it works, moreover, if we change @taxi/input
and check stories for @taxi/login-form
they would have changes immediately without even building packages.
We have tsc
and storybook
working, however, if we run jest
it would fail with:
https://gist.github.com/873ed9333e12dedd9a0be5941ab390d8
It happens because jest
doesn't tolerate tsconfig.json
, webpack
aliases or even babel-webpack-aliases
. However, this problem could be solved with simple line in jest.config.js
:
https://gist.github.com/cf0e3e55377ee9259a22ce23deb21850
'@taxi/(.+)$': '<rootDir>packages/$1/src
will tell jest where to find source code for @taxi/
packages and again it points to the src
instead of dist
.
Now we could run yarn test
and it will work as expected without even building packages.
The goal of the article was to configure monorepo with lerna
, typescript
, babel
, tslint
, stylelint
, jest
and semantic-release
and it was achieved. We do have monorepo with all those tools in place and it's ready for real usage. Packages could be created, developed, tested, presented and published easily with one commands in the root of monorepo.
For those who doesn't want to use babel
, but pure typescript
, changes would be extremely simple - just drop babel.config.js
, change awesome-typescript-loader
for using tsconfig.json
instead of babel
, ts-jest
to use it as well and build
command from babel
to tsc
(just replace emitDeclarationOnly
to false
in tsconfig.build.json
) and that's it, typescript
could be used without babel
.