Tool Kit provides a development workflow for tooling that's common to most Customer Products projects. But almost every project has unique requirements that the default set of plugins and configuration in Tool Kit doesn't cover. For these use cases, there are various ways you can extend Tool Kit.
You don't have to use Tool Kit for every tooling use case in your project. However, using Tool Kit for your custom tooling makes it more likely that it will be shareable between multiple projects and teams and potentially adopted into Tool Kit itself.
In your repo's .toolkitrc.yml
, you can define new commands to run tasks:
commands:
build:e2e: Webpack
This can be run with npx dotcom-tool-kit build:e2e
.
A task's options can be set on a per-command basis, allowing the same task to be used for multiple use cases:
commands:
build:local:
Webpack:
configFile: webpack.config.js
build:e2e:
Webpack:
configFile: webpack.e2e.config.js
Tool Kit hooks manage files like package.json
and .circleci/config.yml
to run commands from your existing tooling like npm scripts and CircleCI jobs. Hooks can be configured in your .toolkitrc.yml
to integrate your new commands:
options:
hooks:
- PackageJson:
scripts:
e2e: build:e2e
- CircleCIConfig:
jobs:
- name: build-e2e
command: build:e2e
workflows:
- name: 'tool-kit'
jobs:
- name: build-e2e
requires:
- setup
Adding this configuration and running npx dotcom-tool-kit --install
will install the new npm script and CircleCI job into your repo, alongside the existing configuration from your Tool Kit plugins.
If your app requires some tooling that's not provided by a first-party Tool Kit plugin, you can write a custom plugin for that feature, which works seamlessly together with the core Tool Kit plugins.
A custom plugin can be written for a single repo or distributed as an npm package to be consumed by multiple repos owned by your team. The custom plugins themselves will be maintained and supported by your team, not Platforms, although we can .
If there's wide demand for a particular custom plugin (for example, if it starts being used across multiple teams), we will consider adopting that plugin into Tool Kit. Writing a custom plugin (rather than implementing the tooling another way) will make it much more likely for us to be able to add the feature to Tool Kit.
We recommend creating a tool-kit
folder at the root of your repository to contain your custom plugins, and folders inside that for each plugin. Each plugin folder must contain at least a .toolkitrc.yml
file.
The .toolkitrc.yml
is the manifest that tells the Tool Kit plugin loader what your plugin needs to load. It must contain a version
field (Tool Kit will currently only load a version 2
plugin), and for custom plugins will probably need a tasks
field telling Tool Kit to load your custom tasks.
Let's say you're creating a plugin to run Rollup. Your folder structure should look like this:
└ tool-kit
└ rollup
├ .toolkitrc.yml
└ tasks
└ rollup.js
The .toolkitrc.yml
would then contain:
version: 2
tasks:
Rollup: ./tasks/rollup.js
This plugin can then be included in your top-level .toolkitrc.yml
by referencing it as a relative path:
plugins:
- './tool-kit/rollup'
Create a subclass of the Task
class from @dotcom-tool-kit/base
, implement the run
method, and export it as the default export of the task module.
You'll need to install @dotcom-tool-kit/base
and the tooling you're implementing as devDependencies
of your repo (e.g. npm install --save-dev @dotcom-tool-kit/base rollup
).
Your tool-kit/rollup/index.js
might look like this:
const { Task } = require('@dotcom-tool-kit/base')
const rollup = require('rollup')
const loadConfigFile = require('rollup/dist/loadConfigFile')
const path = require('path')
class Rollup extends Task {
async run() {
const config = path.join(process.cwd(), 'rollup.config.js')
const { options, warnings } = await loadConfigFile(config)
// print any config warnings to the console
warnings.flush()
for (const optionsEntry of options) {
const bundle = await rollup.rollup(optionsEntry)
await Promise.all(optionsEntry.output.map(bundle.write))
}
}
}
module.exports = Rollup
Then, in the plugin's .toolkitrc.yml
, you can provide the default commands this task will run on. It's preferable to do this in the plugin .toolkitrc.yml
instead of your top-level .toolkitrc.yml
so your plugin is self-contained and can be more easily moved into its own repo or Tool Kit itself, if it's something that can be shared between multiple repos/teams.
version: 2
tasks:
Rollup: ./tasks/rollup.js
commands:
'build:local': Rollup
'build:ci': Rollup
'build:remote': Rollup