A simple and lightweight tool to manage your monorepo.
Warning: This automatically updates your local package's package.json
Install
npm i comptroller
Usage
comp <command> [options]
Commands:
--------
help Show this message
update [root-directory] Update all subpackages of package found at [root-directory]
link [root-directory] Create symlink in node_modules for each subpackage found at [root-directory]
version Print Comptroller version
Options:
--------
--prune -p Remove unused dependencies from subpackges' package.json
--self -s If set, only the root package will be updated
How it works
Commands
update
Comptroller's update
command analyzes the packages in a given directory and
identifies static require
calls. Using this, it is able to update each
package's package.json
to include these as dependencies (Comptroller ignores
Node.js builtin modules by default). Comptroller finds these modules in the
project root's package.json
(or a specified file) and adds the defined version
to the subpackage's package.json
. If a module is required but doesn't exist in
the root package.json
a warning is issued. If a module in the root
package.json
has a different version than one used in the subpackage
package.json
then it's version is updated in the subpackage. If a dependency
is listed in the subpackage package.json
but not found in the package source,
a warning is issued. If a require
call is found that references a local
package, Comptroller locates the specified package's package.json
and updates
the issuing package's package.json
dependencies with the version information
of the specified package.
link
Comptroller's link
command creates symlinks in the root node_modules
directory to each child package. These symlinks are stuctured to match the names
provided in each package's package.json replicating the same structure that an
npm install would create. This enables require
calls to local packages without
the need for to specify relative paths or to npm install
or npm link
them.
Options
prune
Comptroller's prune
option takes all those extraneous dependencies found by
the update
command and delivers them to the void.
self
Comptroller's self
option switches from managing subpackage dependencies and
inherits to only managing the top level dependencies.
The nitty gritty
Comptroller's power is it's simplicity and flexibility.
Configuration
Comptroller has a cascading configuration scheme. This means that your root package's configuration will cascade through to its child packages, as long as they don't override the configuration themselves. If all of your packages share the same configuration, you only need to declare your configuration in the root package, but if any of your subpackages deviate from the norm just specify its own custom configuration within it's package.json and it'll be right as rain! The subpackage's configration will seamlessly override the root configuration.
Currently the only place to specify your config is in a package.json, but this is likely to expand to accomodate a variety of workflows.
Inheritance
Inheritance is the tool that saves you from the mundane maintainence of a multitude of package.json files throughout the life of your project. This is like the swiss army knife version of Lerna's 'fixed/locked' mode. While it's perfect for keeping your package versions in sync, it can be used for any field in your package.json (including dependencies, but we'd recommend against that. Comptroller has bigger plans for your dependencies). It also has the added benefit of being able to opt in/out of any field inheritance globally or locally per each subpackage.
Do you wan't to keep all package versions in sync? Simply inherit the version
field. Do all of your packages share the same homepage? Do the same with the
homepage
field. Did one of your packages mature and deserves its own homepage?
Simply stop inheriting the homepage
key for that package and provide it with
its with its own. The possibilities are endless!
Dependency management
Comptroller's dependency management allows you to manage your dependencies at
the top level only. Comptroller will intelligently analyze the invoked
dependencies in your packages' source files and add (and remove, with the prune
option) them from each package.json as needed. This will ensure that all of your
packages' dependencies stay in sync and helps you avoid heavy downloads by
pruning out any unused dependencies hiding in the corner. With Comptroller,
keeping all of your packages' dependencies perfectly managed is only 14
keystrokes away! comp update -p
.
But we know the world isn't perfect (if it was there'd be no use for a tool like this), which is why Comptroller allows you to opt out of dependency management for specific named dependencies. If one of your packages relies on a different version of a specific dependency, exlude that dependency in the package's configuration and Comptroller will look the other way when it sees it.
Updating your packages
Let's say the package.json above belongs to the root package. Now if you have a child package with the below package.json:
And the below index.js:
;;
A call to comp update
will log this in your terminal:
Updating remote package 'need-this' from 0.1.0 to 1.0.0 in package '@my-fancy-package/my-fancy-module'
Adding remote package 'and-this@^1.2.0' to package '@my-fancy-package/my-fancy-module'
DISABLED: Removing package 'dont-need-this' from '@my-fancy-package/my-fancy-module'
Updating field version from "0.4.1" to "0.4.2" in package '@my-fancy-package/my-fancy-module'
Adding field homepage as "https://somewhere.org" to package '@my-fancy-package/my-fancy-module'
Adding field author as "Some Body" to package '@my-fancy-package/my-fancy-module'
And update the package.json like so:
Oops! We have a dependency in our package.json that appears to be unused!
Comptroller won't just go deleting your dependencies williy nilly, it must be
given the right to do so by issuing the command with the --prune
or -p
option.
comp update -p
Removing package 'dont-need-this' from '@my-fancy-package/my-fancy-module'
Whew! That's better 😉. Now we're free to just develop our project and not worry about managing our dependencies and metadata.
Note that this works just as well with sibling package dependencies, it just seemed unweildy to add an example for this here.
Link's Awakening
The link command is like a super simplified version of Lerna's bootstrap
command (step #2). It doesn't take a
very aggressive approach, but it gets the job done well. It's kind of like if
npm link
worked on all subpackages at once and only impacted the project scope
rather than the global scope.
Let's pretend you have a project with three subpackages named
@my-fancy-package/my-fancy-module
, @my-fancy-package/my-other-fancy-module
,
and my-not-so-fancy-module
. A call to comp link
will ensure that the top
level node_modules
includes:
node_modules
├── @my-fancy-package
│ ├── my-fancy-module
│ └── my-other-fancy-module
└── my-not-so-fancy-module
Now you can have no fear that your interdependent packages will function exactly as they will in the wild (and you'll have only burned ~0.017 calories in the process)!