The minimal task runner for Node.js
Support this project by starring and sharing it. Follow me to see what other cool projects I'm working on.
npm i tasuku
タスク (Tasuku) is a minimal task runner for Node.js. You can use it to label any task/function so that its loading, success, and error states are rendered in the terminal.
For example, here's a simple script that copies a file from path A to B.
import task from 'tasuku'
import { copyFile } from 'fs/promises'
task('Copying file from path A to B', async ({ setTitle }) => {
await copyFile('/path/A', '/path/B');
setTitle('Successfully copied file from path A to B!')
})
Running the script will look like this in the terminal:
Call task(taskTitle, taskFunction)
to start a task and display it in a task list in the terminal.
import task from 'tasuku'
task('Task 1', async () => {
await someAsyncTask()
})
task('Task 2', async () => {
await someAsyncTask()
})
task('Task 3', async () => {
await someAsyncTask()
})
You can call task()
from anywhere. There are no requirements. It is designed to be as unopinionated as possible not to interfere with your code.
The tasks will be displayed in the terminal in a consolidated list.
You can change the title of the task by calling setTitle()
.
import task from 'tasuku'
task('Task 1', async () => {
await someAsyncTask()
})
...
someOtherCode()
...
task('Task 2', async ({ setTitle }) => {
await someAsyncTask()
setTitle('Task 2 complete')
})
The return value of a task will be stored in the output .result
property.
If using TypeScript, the type of .result
will be inferred from the task function.
const myTask = await task('Task 2', async () => {
await someAsyncTask()
return 'Success'
})
console.log(myTask.result) // 'Success'
Tasks can be nested indefinitely. Nested tasks will be stacked hierarchically in the task list.
await task('Do task', async ({ task }) => {
await someAsyncTask()
await task('Do another task', async ({ task }) => {
await someAsyncTask()
await task('And another', async () => {
await someAsyncTask()
})
})
})
Call .clear()
on the returned task API to collapse the nested task.
await task('Do task', async ({ task }) => {
await someAsyncTask()
const nestedTask = await task('Do another task', async ({ task }) => {
await someAsyncTask()
})
nestedTask.clear()
})
Tasks can be grouped with task.group()
. Pass in a function that returns an array of tasks to run them sequentially.
This is useful for displaying a queue of tasks that have yet to run.
const groupedTasks = await task.group(task => [
task('Task 1', async () => {
await someAsyncTask()
return 'one'
}),
task('Waiting for Task 1', async ({ setTitle }) => {
setTitle('Task 2 running...')
await someAsyncTask()
setTitle('Task 2 complete')
return 'two'
}),
...
])
console.log(groupedTasks.results) // ['one', 'two']
You can run tasks in parallel by passing in { concurrency: n }
as the second argument in task.group()
.
const api = await task.group(task => [
task(
'Task 1',
async () => await someAsyncTask(),
),
task(
'Task 2',
async () => await someAsyncTask(),
),
...
], {
concurrency: 2 // Number of tasks to run at a time
})
api.clear() // Clear output
Alternatively, you can also use the native Promise.all()
if you prefer. The advantage of using task.group()
is that you can limit concurrency, displays queued tasks as pending, and it returns an API to easily clear the results.
// No API
await Promise.all([
task(
'Task 1',
async () => await someAsyncTask(),
),
task(
'Task 2',
async () => await someAsyncTask(),
),
...
])
Returns a Promise that resolves with object:
{
// The result from taskFunction
result: any,
// Invoke to clear the results from the terminal
clear: () => void,
}
Type: string
Required: true
The name of the task displayed.
Type:
({
task: taskFunction,
setTitle: (title: string) => void,
setStatus: (status: string) => void,
setOutput: (error: string | { message: string }) => void,
setWarning: (warning: Error | string) => void,
setError: (error: Error | string) => void,
}) => Promise<any>
Required: true
The task function. The return value will be stored in the .result
property of the task()
output object.
A task function to use for nesting.
Call with a string to change the task title.
Call with a string to set the status of the task. See image below.
Call with a string to set the output of the task. See image below.
Call with a string or Error instance to put the task in a warning state.
Call with a string or Error instance to put the task in an error state. Tasks automatically go into an error state when it catches an error in the task.
Returns a Promise that resolves with object:
{
// The results from the taskFunctions
results: any[],
// Invoke to clear the results from the terminal
clear: () => void,
}
Type: (task) => Task[]
Required: true
A function that returns all the tasks you want to group in an array.
Directly passed into p-map
.
Type: number
(Integer)
Default: 1
Number of tasks to run at a time.
Type: boolean
Default: true
When set to false
, instead of stopping when a task fails, it will wait for all the tasks to finish and then reject with an aggregated error containing all the errors from the rejected promises.
Tasuku or タスク is the phonetic Japanese pronounciation of the word "task".
For writing scripts or CLI tools. Tasuku is a great way to convey the state of the tasks that are running in your script without being imposing about the way you write your code.
Major shoutout to listr + listr2 for being the motivation and visual inspiration for Tasuku, and for being my go-to task runner for a long time. I made Tasuku because I eventually found that they were too structured and declarative for my needs.
Big thanks to ink for doing all the heavy lifting for rendering interfaces in the terminal. Implementing a dynamic task list that doesn't interfere with console.logs()
wouldn't have been so easy without it.
task
functions violate ESLint's no-shadow?Yes, but it should be fine as you don't need access to other task
functions aside from the immediate one.
Put task
in the allow list:
"no-shadow": ["error", { "allow": ["task"] }]
"@typescript-eslint/no-shadow": ["error", { "allow": ["task"] }]
美国人詹姆士和日本的Tasuku Honjo因开创性的治疗癌症方法获得2018年诺贝尔医学奖。 诺贝尔委员会说,这两个人的研究等同于“一个对癌症战斗的里程碑”,他们的研究利用免疫系统攻击癌细胞。他们的方法,叫做免疫监测点理论,已经“使癌症治疗发生革命,并且根本性的改变了我们认为如何治疗癌症的方法“。 詹姆士,德克萨斯州大学安德森癌症中心的一位教授,研究一种蛋白质,这种蛋白质的功能是免疫系统的制动器