Now that you have a configuration module, we can integrate it downstream into a project. But first, go ahead and delete all the old config files and dependencies in each project (if they exist), as all that logic should now be housed in your configuration module.
Once you have a clean slate, install your configuration module, and BOOM, it's as easy as that. No more development dependency hell, just a single dependency.
With that being said, create a
.config/beemo.ts file (or
.yaml) in your project
root with a
module property that matches the name of your configuration module, or another
third-party module (if you don't want to manage your own provider).
string) - Name of your configuration module.
boolean) - Remove generated config files after execution. Defaults to
boolean) - Create configuration files in parallel. Defaults to
number) - Number of builds to run in parallel. Defaults to the number of CPUs.
boolean) - Prioritize workspace builds based on dependency graph. Defaults to
buffer | pipe | stream | none) - Override the output strategy of all drivers. Is not defined by default.
string | object) - Drivers to enable for the consumer.
string | object) - Scripts to enable for the consumer.
object) - Custom settings specific to your project that can easily be referenced.
Periods denote nested objects.
Driver dependencies may have been installed in your configuration module, but that does not make
them available to the current project, as not all drivers will always be necessary. To enable
drivers per project, a
drivers property must be defined.
This property accepts an array of strings/tuples or objects, with the names of each driver you want to enable. For example, if we want to use Babel, ESLint, and Jest, we would have the following.
Furthermore, drivers can be configured with options by using an object. If a driver does not require
options, either pass an empty object, or a boolean
Options can also be set through the bootstrap and event system.
string) - Arguments to always pass when executing the driver binary.
create | copy | reference | template | native | none) - Type of strategy to use when generating a config file. Default is different per driver.
string) - Other drivers that are required for this driver to run.
object) - Environment variables to pass when executing the driver binary with execa.
boolean) - Controls whether or not glob patterns in args are automatically expanded before being passed to the driver binary. Defaults to
buffer | pipe | stream | none) - Type of strategy to use when displaying driver command line output. Default is
string) - File path to a template function for generating custom config files and paths. Is required when
Now for the fun part, executing the driver! It's as simple as
yarn beemo <driver> (or
npx beemo <driver>). Once entered, this will initialize Beemo's pipeline, generate a configuration
file, execute the underlying driver binary, handle stdout and stderr output, cleanup after itself,
and lastly, leave a beautiful message in your console.
All arguments passed to Beemo are passed to the driver's underlying binary.
That being said, consistently remembering the correct commands and arguments to pass to
npx is tedious. So why not use scripts? Feel free to steal the following.
The following options are available when executing a driver.
number) - Number of builds to run in parallel. Defaults to the amount of CPUs.
bool) - Prioritize workspace builds based on dependency graph.
string) - Execute the command in each workspace defined by the pattern/value. Pass
*to run in all workspaces.
If the underlying driver supports file watching, most commonly through a CLI option like
--watch, Beemo will attempt to capture and pipe this output to your terminal.
A script within your configuration module can be executed using
yarn beemo run-script <name> (or
npx beemo run-script <name>). The name of the script should be passed in kebab-case.
All arguments passed to Beemo are passed to the script's
Executing a driver will dynamically create a configuration file at runtime. If you'd like to create
the config manually outside of executing a driver, you can use the
yarn beemo create-config (or
npx beemo create-config).
When no arguments are passed, it will create a config file for all enabled drivers (found in the
drivers setting). Otherwise, a config file will be created for each driver name passed as an
If a driver has a dependency on another driver, it will create a config file for the dependency as well.
Your configuration module may now house and provide all configurations, but that doesn't mean it's
applicable to all consuming projects. To accomodate this, Beemo supports overriding of driver
config on a project-by-project basis through a local
Some dev tools support
package.jsonoverrides like this, but it's preferred to use the Beemo approach for interoperability.
Beemo provides sane defaults for all official drivers and attempts to standardize the configuration process as much as possible. However, it's not perfect, and may not work for all consumers. To mitigate this problem, each driver supports a template based strategy, in which a custom template function can be used to handle the config generation (custom merging, etc), and the destination file path.
To use templates, set the driver
configStrategy option to "template", and the
template option to
a file path for the template function (relative to the
The template is merely a function that receives a list of config objects from multiple sources, and must return a single config object (or string), and an optional destination path. It also receives an options object with helpful information about the current process.
To demonstrate the power of templates, let's write a custom template that generates a YAML configuration file for ESLint.
The list of available options are:
string) - Name of the configuration module.
Path | null) - Path to the driver's config file in the configuration module. For example,
nullif not found.
Context) - Current pipeline context.
Driver) - Current instance for the driver being processed.
Path) - Path to the driver's default config file destination. For example,
.eslintrc.jsin the root.
string) - Name of the driver being processed. For example,
Path | null) - Path to the driver's config file in the current project. For example,
nullif not found.
Path) - Path to the template file (itself).
Tool) - Current tool instance.