npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

kubik

v0.8.0

Published

> ⚠️ **Warning:** Kubik is currently in pre-1.0.0 release. Expect potential changes and experimental features that may not be fully stable yet.

Downloads

572

Readme

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, designed specifically to build typescript monorepos. Kubik uses .js/.mjs scripts to define tasks.

Quick Start

A template script build.mjs to build typescript with esbuild and lint its types with tsc:

#!/usr/bin/env npx kubik

import path from 'path';
import esbuild from 'esbuild';
import fs from 'fs';
import { Task } from 'kubik';

const { __dirname, $ } = Task.init(import.meta, {
  name: 'build & lint',
  watch: [ './src' ],
});

const outDir = path.join(__dirname, 'lib');
const srcDir = path.join(__dirname, 'src');
const typesDir = path.join(__dirname, 'types');
await fs.promises.rm(outDir, { recursive: true }).catch(e => void e);
await fs.promises.rm(typesDir, { recursive: true }).catch(e => void e);

const { errors } = await esbuild.build({
  color: true,
  entryPoints: [
    path.join(srcDir, '**/*.ts'),
  ],
  outdir: outDir,
  format: 'esm',
  platform: 'node',
  target: ['node22'],
  sourcemap: false,
  bundle: false,
  minify: false,
});

if (!errors.length)
  await $`tsc --pretty -p .`;

Commands:

  • Build: ./build.mjs or npx kubik ./build.mjs
  • Watch mode: ./build.mjs -w or npx kubik -w ./build.mjs
  • Debug (run without Kubik): node build.mjs
  • Run sequential build: ./build.mjs -j 1 or npx kubik -j 1 ./build.mjs

Getting Started

Let's say you have a simple build script:

// build-main.mjs

console.log('Building main library...');
await new Promise(x => setTimeout(x, 1000));
console.log('Done.')

And you want to run this script after building some dependency, which has its own script:

// build-third-party.mjs
console.log('Copied third-party!');

You can use Kubik to declare dependencies in both scripts, using Task.init method at the very beginning of your script:

// build-main.mjs
import { Task } from 'kubik';

Task.init(import.meta, {
  deps: ['./build-third-party.mjs'], // these are relative to script folder
});

console.log('Building main library...');
await new Promise(x => setTimeout(x, 1000));
console.log('Done.')
// build-third-party.mjs
import { Task } from 'kubik';

Task.init(import.meta);

console.log('Copied third-party!');

Now to build dependencies, you can simply execute the first script with Kubik:

npx kubik ./build-main.mjs

Tasks vs 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);

// setInterval will keep node.js process from exiting.
setInterval(() => console.log(Date.now()), 150);
// This is how Kubik will know that this task is "done".
Task.done();

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.mjs

In 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 Tab and Shift-Tab
  • To scroll logs of the focused pane, use arrows, j, k, Ctrl-U, Ctrl-D, gg and Shift-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.json
  • package-lock.json
  • tsconfig.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 ignore option 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.

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