kubik v0.9.3
Kubik
⚠️ Warning: Kubik is currently in pre-1.0.0 release. Expect potential changes and experimental features that may not be fully stable yet.
Kubik is a simple task runner for node.js with first-class TypeScript support.
Kubik tasks are defined by TypeScript / Node.js scripts, with dependencies to other tasks. Kubik supports running tasks with different parallelization modes and has a built-in watch mode.
- Quick Start
- Getting Started
- Tasks vs Services
- Typescript Support
- Watch Mode
- Parallelization
- Environment Files
- Shebang
- API
- Debugging
Quick Start
Any build.(m)js script can be converted to a task by importing Task and running Task.init in the
very beginning of the script:
import { Task } from 'kubik';
Task.init(import.meta);
/* ... build script ... */The Task.init method accepts configuration (dependencies & watch mode configuration), see
API section.
Use the following commands to run tasks:
- Build:
npx kubik ./build.mjs - Watch mode:
npx kubik -w ./build.mjs - Debug (run without Kubik):
node build.mjsortsx build.mjs - Run sequential build:
npx kubik -j 1 ./build.mjs
A real-life example is available here.
Task dependencies
Kubik allows defining dependencies between tasks using deps option in the Task.init method:
// build-main.mjs
import { Task } from 'kubik';
Task.init(import.meta, {
deps: ['./other-task.mjs'], // these are relative to script folder
});
// ... run some tasksTo run tasks with their dependencies to completion, run:
npx kubik ./build-main.mjsMultiple roots
In a complicated projects, it might be necessary to build a project from multiple entry points. In this case, you can pass multiple entry points to Kubik:
npx kubik ./build-main.mjs ./build-other.mjsIn this case, if both build-main.mjs and build-other.mjs depend on shared.mjs task, then
the task will be executed only once.
Running services
By default, task is considered successful if its process completes with 0 exit code, and unsuccessful if it fails with non-zero code.
However, certain tasks require a running process; for example, launching development server.
In this case, you can use Task.done() to notify Kubik that the task completed and it's dependants
can start executing:
import { Task } from 'kubik';
Task.init(import.meta);
// ...launch HTTP server...
// Report the task as complete.
Task.done();TypeScript support
Kubik supports running tasks defined in a .ts / .mts files using tsx. To use typescript, simply install tsx along side with kubik,
and use .ts/.tsx extension to write your scripts:
Install
tsx:npm i --save-dev tsxWrite your scripts in a
.tsor.mtsfiles:// hello.ts import { Task } from 'kubik'; Task.init(import.meta); const foo: String = 'Hello, typescript!'; console.log(foo);Run your tasks as usual:
npx kubik ./hello.ts
Watch Mode
Kubik supports watch mode where it listens for changes on the file system and reruns tasks and their dependencies.
To run watch mode, use -w or --watch flag:
npx kubik -w ./build.mjsIn watch mode, Kubik launches a terminal app that shows progress, duration and logs from all the tasks:
There are a few shortcuts available to navigate inside the watch mode app:
- To cycle focus through panels, use
TabandShift-Tab - To scroll logs of the focused pane, use arrows,
j,k,Ctrl-U,Ctrl-D,ggandShift-G. - You can also use mouse to scroll logs
By default, Kubik watches for changes in files commonly involved in build tasks, such as:
package.jsonpackage-lock.jsontsconfig.json
However, you can customize files and directories to watch and to ignore during Task initialization:
import { Task } from 'kubik';
Task.init(import.meta, {
deps: ['./build-third-party.mjs'],
watch: ['./src'], // these are relative to script folder
ignore: ['./src/generated'], // these are relative to script folder too
});NOTE: Be careful with watch mode: if the build procedure changes some of the watched files, then Kubik will re-run the build one time, causing "infinite" builds. You'll observe this with tasks never completing. Use
ignoreoption to mitigate this behavior.
Parallelization
Kubik supports -j, --jobs <number> flag to customize number of parallel jobs. By default, Kubik allows an unlimited number of parallel jobs.
Environment Files
Kubik supports -e, --env-file <env file> flag to load environment variables from a file.
npx kubik -e .env ./build.mjsThis will load all the environment variables from .env file, and pass them to all scripts.
Shebang
You can use kubik shebang in scripts, like this:
#!/usr/bin/env npx kubik
import { Task } from 'kubik';
Task.init(import.meta, {
watch: ['./src'],
ignore: ['./src/generated'],
});API
The Task.init function prepares the build environment, offering utilities like $ for shell commands (powered by execa), __dirname, and __filename based on the current script's context.
The whole API boils down to the following:
#!/usr/bin/env npx kubik
import { Task } from 'kubik';
import fs from 'fs';
const {
$, // execa shell runner, that uses __dirname as CWD
__dirname, // **this** script directory absolute path
__filename, // **this** script file absolute path
} = Task.init(import.meta, {
name: 'my library',
watch: ['./src'], // all the paths are resolved relative to this script
ignore: ['./src/generated'], // relative to this script
deps: ['../third-party/build.mjs'], // relative to this script
});
console.log(Task.isWatchMode()); // wether the script is being run under watch mode.
// Use $ to run commands, e.g. typescript.
// Note that $ uses __dirname as CWD.
await $`tsc --pretty -p .`;
// If node.js process does not exit (i.e. it runs a server),
// then we can notify Kubik explicitly that the task is done.
Task.done(); Debugging
You can run build scripts as regular node.js scripts; in this case, these are executed directly by node.js, with no Kubik in the way.
node ./build-main.mjs