This is an example of a monorepo of JavaScript packages managed with a single
Makefile
. These packages can be written in ES6+ JavaScript and may contain
accompanying CSS and LESS files.
I wrote up an accompanying blog post that explains my reasoning for using Make over lerna or other build frameworks.
Tech used:
Simply run: make
NOTE: entr
is required for make watch
to work.
(We could probably make the executable [and its options] a variable so we can use other watcher programs like watchman)
make
to pull down all dependencies and initialize everything.make watch
to rebuild build artifacts when source files change.make check
to run all linting and testing checks.make VERSION=patch release-<package-name>
to tag and publish a new patch version of a package.
make
to pull down all dependencies and initialize everything.make test-watch
to being the test runner which will rerun on any changes to tests or source files.make check
to run all checks on all packages.
For when you are developing on a package while integrating it with a project outside the monorepo.
make
to pull down all dependencies and initialize everything.make watch
to rebuild build artifacts when source files change.make link-<package-name>
to create an npm link to the package being worked on.- In your external project
npm link <package-name>
Now your external project and your monorepo package are hooked up and you can work on integrating and developing.
For situations when you want to test integrating a package as a dependency of another package.
make
to pull down all dependencies and initialize everything.make packages
to build initial build artifacts.- For the two packages, run
make link-<package-name>
to create an npm link. cd
into each package's root directory and runnpm link <other-package-name>
// @todo(mjf)make watch
to rebuild build artifacts when source files change.
make packages
For all packages, transpile all JavaScript and copy .less
files to the
package's lib/
directory.
make <package-name>
Run babel on all JavaScript files and copy .less
and .css
files to the
package's lib/
directory.
ex: make example
make watch
NOTE: entr
is required for make watch
to work.
This runs make <package-name>
when any source for that package changes.
Useful for local development. Can be paired with make link-<package-name>
to
integrate a package in a project and develop at the same time.
make test
Runs the Jest test runner on all the packages at once. Generally, this is what you would use when running all the unit tests.
make test-packages
Runs a separate test runner for each package.
make test-watch
will start Jest's file watcher mode.
Runs the Jest test runner on a single package.
make test-<package-name>
ex: make test-example
make lint
Runs standard on all the files for each package.
make lint-<package-name>
Runs standard on all the files for a single package.
ex: make lint-example
make lesslint
Runs lesshint on all the LESS files for each package.
make lesslint-<package-name>
Runs lesshint on all the LESS files for a single package.
ex: make lesslint-example
"Check" is analogous to running all the "checks" needed to ensure everything for the package is in proper order. Right now this includes ensuring the build step works, unit tests pass, and JS and LESS linting pass.
make check
Runs check on all the packages at once. Generally, this is what you want to use to check all the packages. Useful for CI.
make check-packages
Runs a separate check for each package.
make check-<package-name>
make clean
Deletes all the build artifacts and removes the node_modules
directory for
the root and each package.
make clean-node_modules
Deletes the root node_modules
.
make clean-packages
Deletes build artifacts and node_modules
for all packages.
make clean-<package-name>
Deletes build artifacts and node_modules
for a single package.
npm link is useful when working on two or
more packages simultaneously. Link works by creating a symbolic link to
package A from package B's node_modules
so any changes to package A are
immediately reflected in package B. Without link one would need to
continuously publish a package in order to test its integration with another.
Releases involve running all checks against a package, properly incrementing
the version in package.json
, creating a git tag
with the proper version and package metadata, and finally publishing the
package on the npm registry.
An tag follows the specification: <package-name>@<version>
Example: example@v1.0.1
Properly tag, create, and publish a pre-release as defined by the semver specification
on prereleases. The package publish on npm
will be tagged with beta
.
make VERSION=<major|minor|patch|prerelease> prerelease-<package-name>
ex: make VERSION=<patch> prerelease-example
Properly tag, create, and publish a release. The package publish on npm
will be tagged with latest
.
make VERSION=<major|minor|patch> release-<package-name>
ex: make VERSION=<major> release-example
Generally the convention for our make
commands is:
make [ARGUMENT=VALUES] <command-name>[-packages|<package-name>]
What does that mean exactly?
For each command, there is a whole project version, a for each package do this version, and a single package version. For example,
make lint
is a whole project command and runs lint on all the packages at
once.
make lint-packages
is a for each package command and runs lint for each
package (compared to all at once).
make lint-<package-name>
is run only for <package-name>
.
It is usually easier to run the whole project version of commands, and make
does try to be intelligent about not doing extra work. Sometimes it is handy
in a pinch to be able to run other versions of commands.
The commands that follow this convention are:
check
clean
install
lesslint
link
lint
test
Obviously for commands where it doesn't make sense like make watch
or make test-watch
this convention is not followed.