In which we understand how Bilt monorepos are structured, and how to structure the NPM packages in it.
A monorepo in Bilt is structured in a simple manner:
- NPM packages in any directory structure. Putting all
packages in a
packages
directory, in a flat way, is a common way to structure them, but any structure is OK, including a hierarchical one—bilt deals with them all. - Each package is a regular publishable NPM package, with it's own
package.json
, includingname
,version
, and usually a set ofscripts
likebuild
andtest
that are used to build and test the package before publishing. - Each package should be publishable to an NPM registry. This is important because packages consume other packages through the regular mechanism of NPM dependencies.
- If a package needs to generate other artifacts in other registries (such as a docker image
for microservice packages), use
postpublish
. The recommendation is to publish it with the same version number as the package's. - The only mechanism for code sharing should be through NPM dependencies. Packages should never directly consume another package's source code (i.e. by importing it directly).
Each package needs to be able to be built separately. While build steps are configurable (see Configuring the build), the default build steps work pretty nicely for most projects:
npm install
ensures all dependencies are installednpm update
updates all the dependencies. This is especially important in Bilt moonorepos, as it updates the dependencies to the other packages in the monorepo. Withoutnpm update
, packages will have outdated dependencies on the other packages in a monorepo.- Increment version: to update the version of the package so it can be published. See this for more information.
npm audit fix
. Because we're security conscious! (See Snyk for a more powerful alternative.)npm run build
: build the source code. For example transpile the code, bundle it, or build a docker image. This runs only if abuild
script exists in thepackage.json
.npm test
: because we have tests, right? 😉 Will skip if notest
script existsnpm publish
: publishes the package
A package.json
that works nicely with the build steps above looks like this:
{
"name": "@some-scope/a-microservice-in-typescript",
"version": "1.0.10",
// ...
"scripts": {
// building the code: transpiling and building the docker image
"build": "tsc && npm run build:docker",
// testing whatever needs to be tested
"test": "npm run test:eslint && npm run test:mocha",
// publish the docker image when publishing the package
"postpublish": "npm run publish:docker",
// sub-scripts used by the above main scripts
"test:mocha": "mocha ...",
"test:eslint": "eslint 'test/**/*.?s' 'test/**/*.?s'",
// building a docker image with the same version as the package
"build:docker": "docker build -t some-scope/a-microservice:${npm_package_version}",
// ensuring it gets published to the docker registry along with the package
"publish:docker": "docker push some-scope/a-microservice:${npm_package_version}"
},
"dependencies": {
"some-scope/another-package-used-by-this-one": "^2.4.3",
//...
},
"devDependencies": {
"some-scope/a-build-tool-used-by-this-one": "^1.7.2s",
// ...
}
}