A JavaScript bundler for node.js monorepo-codebased applications.
This tool comes to fill a gap for node.js developpers who :
Monopack aims to build a static deterministic deliverable bundle from your application's entrypoint main.js.
It will build:
It can be installed globally and locally
Using yarn
yarn global add monopack
Or npm
npm install -g monopack
You can then use it with
$ monopack
Using yarn
yarn add -D monopack
Or npm
npm install --save-dev monopack
You can then use it on your project
With yarn
$ yarn run monopack
With npm
./node_modules/.bin/monopack
monopack <command>
Commands:
monopack build main Builds an application
monopack run main Runs an application
monopack debug main Runs an application in debug mode (Node >= v8 only)
Options:
--help Show help [boolean]
--version Show version number [boolean]
--watch, -w Enable watch mode [boolean] [default: false]
--out-dir, -d Output directory (default into a temp dir)
[string]
--no-packages-installation, -n Do not install packages after build [boolean]
--install-packages, -i Install packages after build [boolean]
--with-extra-module, -m Adds an extra module to the dependencies.
It can be useful for dynamically required
dependencies that monopack cannot detect
(e.g.: an sql driver).
It expects the package name without the
version. (e.g: 'mysql' not 'mysql@2.16.0).
It can be use multiple times "monopack build
main.js -m mysql -m postgresql" in order to
provide multiple dependencies.
Make sure to install it in the same package as
the main file, otherwise another version might
be picked up. [string]
--debug-host-port [host:]port setting to pass to node --inspect
option.
It must be used with the debug command.
[string]
--debug-break Break at start of main script.
This option is required when you want to debug
something that gets immediately executed when
starting.
It triggers the --inspect-brk node option.
It must be used with the debug command.
[boolean]
By default monopack will use babel 7 to compile your code into js code that node.js 6.14.4 understands. It supports flow and stage-2 features.
const baseBabelConfig = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: '6.14.4',
},
},
],
'@babel/preset-flow',
],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-throw-expressions',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-syntax-import-meta',
['@babel/plugin-proposal-class-properties', { loose: false }],
'@babel/plugin-proposal-json-strings',
],
};
Monopack will use webpack 4 to produce the bundle. The bundle is produced in 'development' mode in order not to obfuscate the code. Source maps are included.
You can include an optional configuration file : monopack.config.js
This configuration file can be used to :
You can use multiple configuration files, have a top-level one that the top of the monorepo and have a more specific one per package. More specific entries will override the ones from the root. If you would like to combine then, you have to require the parent configuration file and implement the merge operation on your own.
The config file can export the following entries :
For example :
module.exports.monorepoRootPath = '../..';
module.exports.outputDirectory = './build';
module.exports.babelConfigModifier = defaultBabelConfiguration => {
return babelConfiguration(defaultBabelConfiguration);
};
module.exports.webpackConfigModifier = defaultWebpackConfig => {
return webPackConfiguration(defaultWebpackConfig);
};
module.exports.installPackagesAfterBuild = true;
module.exports.extraModules = ['mysql', 'postgresql'];
module.exports.modifyPackageJson = pkg => ({ ...pkg, private: false });
module.exports.afterBuild = async buildDirectory => {
return triggerActionAfterBuild(buildDirectory);
};
I encourage you not to modify the webpack configuration, as I intend to keep in track with latest webpack versions, having a custom webpack configuration will be difficult to update on your side. If you have specific needs that require a customization please open an issue
Monopack will collect:
All the dependencies are collected, a package.json with the collected dependencies will be compiled.
Your project's yarn.lock will be copied if it exists. If you are using multiple yarn.lock files, only the top-most one will be copied.
The dependencies collection is deterministic only if you have a single yarn.lock file. In order to achieve that you can :
As far as I know such a tool does not exist so far.
Many developpers (me included) tend to consider that the only viable way of performing continous integration is to use a monorepo.
I personally find it very practical :
Unfortunately, this is not currently a very popular trend amongst many Node.JS developpers and micro-services developpers.
The classical tooling in Node.JS is designed for delivering open-source libraries and not organizing for mono-repos :
When deploying a node.js application (either within a container, or within a PAAS or on physical/virtual machine), you would like to rely on NPM/Yarn to install the application dependencies and rely on your "start" script to execute your application.
But how to bundle your application from your mono-repo sources to a static deliverable package ?
That's the purpose of monopack !
You can usedversions and make LernaJS manage versions :
You could bring your whole monorepo in your application release :
If you are compiling your javascript code (using flow, typescript, or unsupported ES features: you still have to compile your code :
You could use babel-node to execute your main.js file. Babel-node will compile it on the fly.
This works, but it's not optimal, it requires a lot more memory in order to compile, startup time is increased, and as stated on babel-node : "You should not be using babel-node in production"
You could use Webpack to bundle your main.js entrypoint and bundle all your used dependencies like you would do with web bundles.
Theorically this will work, however in practice it doesn't work, as some node.js modules are not designed to work like this :
You could use Webpack to bundle your main.js entrypoint and rely on Node externals to avoid packaging the node_modules in webpack. Actually that's what Backpack does.
This will generate a single main.js file that includes only the sources that are imported. This is what monopack does, but you will still have to install all your monorepo dependencies.
See CONTRIBUTING