Taskmachine: a pseudo-make made with javascript
Tags: javascript, sideprojects
Published: 2020-04-13 16:18:50

---
title: Taskmachine: a pseudo-make made with javascript
published: true
description: Showing a utility script I made
tags: #javascript #sideprojects
---

It turns out that making a task runner is not that difficult. Well... making a good task runner might be difficult but building something that just runs functions is not.

Just for fun I took a look inside the code of [@ygor/task](https://github.com/shannonmoeller/ygor/tree/master/packages/tasks) and saw how simple it was. Decided to make myself a version of that, one that was designed not to live inside javascript project. So I could have something like this.

```sh
task do-stuff
# stuff happens
```

That's it. The thing lives here: [taskmachine](https://github.com/VonHeikemen/taskmachine).

## Why?

`make` and a million other tools do this. Then why? I was bored. Also, a `Makefile` gets kinda weird as soon as you start doing some bash-kung-fu inside of it. If I'm going to do some kung-fu fighting might as well do it in javascript, at least I could understand it a little bit better and without google help.

## Show me the money

I mentioned that this tool wasn't created to be in a javascript project. This is actually better as lonely script in an alias.

```sh
alias task='node /path/to/taskmachine.js ./make.js'
```

Now you can create a `make.js` file.

```js
// make.js
const log = (name) => (...args) => console.log(name, args[0]);

module.exports = function(tasks) {
  tasks
    .add('test', 'Run some tests',log('test'))
    .add('cover', log('cover'));
}
```

You call one of the tasks.

```
task test
```

And you should have.

```
[15:32:33] Starting 'test' ...
test { _: [] }
[15:32:33] Finished 'test' (10ms)
```

If you want to add some arguments, you're more than welcome.

```sh
task test --foo 1234 hi --foo 5678 there -- --foo 2468
```
```
[15:33:56] Starting 'test' ...
test { _: [ 'hi', 'there', '--foo', '2468' ], foo: '5678' }
[15:33:56] Finished 'test' (9ms)
```

See that `foo` outside the array? Is a property of `args[0]`. You get the flags from the cli as a plain object. The `_` gets the positional arguments and the `--` stops the argument parsing, so everything after that is included as is.

If you omit the task name it will try to run a task named `default`. If it can't find the `default` task then it shows a list of available tasks.

```sh
Available tasks:
* test: Run some tests
* cover
```

If you do have a `default` task but you still want that list, use the `addlist` method.

```diff
const log = (name) => (...args) => console.log(name, args[0]);

 module.exports = function(tasks) {
   tasks
     .add('test', 'Run some tests',log('test')) 
     .add('cover', log('cover'))
+    .add('default', log('default'))
+    .addlist('list');
 }
```

Calling `task list` gets you.

```
[22:30:58] Starting 'list' ...

Available tasks:
* test: Run some tests
* cover
* default
* list: List available tasks

[22:30:58] Finished 'list' (5ms)
```

If you don't want the timestamps use the `--quiet/-q` flag.

One cool trick this thing can do is creating `subtasks`. Yes, tasks within tasks.

```js
// make.js

function childA1() { console.log('hi from a1'); }
function childA2() { console.log('hi from a2'); }

function parentA(cli, { tasks }) {
  // Subtasks
  return tasks()
    .add('1', childA1)
    .add('2', childA2);
}

function childB1() { console.log('hi from b1'); }
function childB2() { console.log('hi from b2'); }

function parentB(cli, { tasks }) {
    // Subtasks
    return tasks()
        .add('1', childB1)
        .add('2', childB2);
}

module.exports = function(tasks) {
  tasks
    .add('a', parentA)
    .add('b', parentB);
}
```

Call `task b 1 --quiet`.

```
hi from b1
```

At some point you would want to call external commands, so I added [execa.command](https://github.com/sindresorhus/execa#execacommandcommand-options). You can access it from the second argument of the function as `sh`.

```js
async function build(args, { sh }) {
  await sh('serious-command --important --not-really');
}

module.exports = function(tasks) {
  tasks
    .add('build', build)
}
```

`sh` doesn't use a shell so don't do any kung-fu in it. It's just there to call other commands.

## Should people use this?

No. This is a toy project. Don't do anything serious with this. If you really want something like this, use this one: [just](https://microsoft.github.io/just/).