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

tache

v1.0.0-beta.5

Published

<h1 align="center"> <img alt="tâche" src="extra/logo.svg" width="240"> </h1>

Downloads

1

Readme

what & why

tâche is a tool for running functions in a javascript module from the command line. it's also a collection of libraries to let you write build scripts as async functions. it's meant as a modern, javascript-based alternative to tools like make.

how

basic task

here's how to write a module to use with tâche:

hello-world.js

exports.helloWorld = function() {
	console.log('hello world')
}

and to run it from the command line, using npx (so you don't have to have tâche installed globally to run tasks):

npx tache hello-world.js helloWorld

which outputs:

 start   │ helloWorld({})
hello world
 done    ✓ helloWorld

shebang

in a UNIX-like shell, tâche can be run using a shebang at the start of your file and marking your file as executable:

#!/usr/bin/env npx tache

exports.helloWorld = function() {
	console.log('hello world')
}
chmod +x hello-world.js

which can then be run using:

./hello-world.js helloWorld

logging

tâche includes command-line logging to make it easier to debug which tasks are running, and what they're doing. you can also import this logger to output your own logs in the same style:

const { log } = require('tache')

exports.helloWorld = function() {
	log.log('hello world')
}

the task now outputs:

 start   │ helloWorld({})
         │ hello world
 done    ✓ helloWorld

the logger includes a few different styles. see @tache/logger for what's available

command line argument parsing

each whitespace-separated argument passed to tâche on the command line is a separate task. the part before the first colon in the argument (or the whole argument if there's no colon) is the name of the task to run, then the rest of the argument after the first colon is parsed as HJSON (a format similar to JSON but with a more lightweight syntax; in practice, this means you can omit most {} curly braces and "" double quotes), and passed as the single argument to the task function. if the task argument needs whitespace, surround the whole thing with double quotes, which are parsed by your shell as a single argument.

adding an argument to the helloWorld task, with a default value, to show this (note that the task logging outputs the arguments the task was called with, to help debugging):

const { log } = require('tache')

exports.helloWorld = function({ message = 'world' }) {
	log.log(`hello ${message}`)
}
⟩ ./tachefile.js helloWorld

 start   │ helloWorld({})
         │ hello world
 done    ✓ helloWorld

⟩ ./tachefile.js helloWorld:message:folks

 start   │ helloWorld({ message: 'folks' })
         │ hello folks
 done    ✓ helloWorld

⟩ ./tachefile.js "helloWorld:message:'stuff and things'" helloWorld:message:again

 start   │ helloWorld({ message: 'stuff and things' })
         │ hello stuff and things
 done    ✓ helloWorld

 start   │ helloWorld({ message: 'again' })
         │ hello again
 done    ✓ helloWorld

async tasks

if your task function returns a Promise, e.g. if the function is an async function, tâche will wait for the task to complete before running the next task, and log timing if it took more than 20 milliseconds:

const { log } = require('tache')
const delay = require('delay') // https://www.npmjs.com/package/delay

exports.helloWorld = async function() {
	log.log('one')
	await delay(500)
	log.log('two')
}

running this task twice from the command line will give:

 start   │ helloWorld({})
         │ one
         │ two
 done    ✓ helloWorld (506ms)

 start   │ helloWorld({})
         │ one
         │ two
 done    ✓ helloWorld (504ms)

error handling

when a task throws an error, or returns a promise that rejects, tâche will output the stack trace, and the name of the task that originally threw the error. it will also exit to the command line with a non-zero exit code to indicate failure.

exports.helloWorld = function() {
	throw new Error('something went wrong')
}
 start   │ helloWorld({})
 failed  ✕ helloWorld
         │ Error (from task helloWorld): something went wrong
         │     at exports.helloWorld (tache/packages/example/tachefile.js:15:8)
         │     at Object.helloWorld (tache/packages/cli/wrap.js:13:24)
         │     at <anonymous>
         │     at process._tickCallback (internal/process/next_tick.js:188:7)
         │     at Function.Module.runMain (module.js:695:11)
         │     at findNodeScript.then.existing (/usr/local/lib/node_modules/npm/node_modules/libnpx/index.js:268:14)
         │     at <anonymous>

task dependencies

tâche doesn't have any kind of abstraction or special handling for task dependencies. because tasks are functions, to run a dependency, call a function, using await if the dependency is async. to run dependencies in parallel, use Promise.all. all functions that are exported are treated as tasks and wrapped with logging and error handling.

const { log } = require('tache')

exports.upperCase = string => string.toUpperCase()

exports.helloWorld = function() {
	const upper = exports.upperCase('hello world')
	log.log(upper)
}
 start   │ helloWorld({})
 start   │ upperCase('hello world')
         │ HELLO WORLD
 done    ✓ upperCase
 done    ✓ helloWorld

bootstrapping & dependencies

some tasks may require dependencies, but also need to be able to run without the dependencies installed, such as if you have a task to actually do the install. tâche provides a load function, which uses npx to install the package. when using load, the package must either export a function, or export an object containing functions. the package isn't installed until a function from it is called, allowing independent tasks to install only the dependencies they need. calling a function from load always returns a promise, even if the package's functions are synchronous, because the installation is asynchronous.

const { load, log } = require('tache')
const upperCase = load('upper-case')

exports.helloWorld = async function() {
	const upper = await upperCase('hello world')
	log.log(upper)
}
 start   │ helloWorld({})
         │ HELLO WORLD
 done    ✓ helloWorld

loading libraries of tasks

loading tasks from npm using load works, but doesn't include the same wrapping for logging and error handling. for tasks, you should use load.tasks instead (since all functions can be run as tasks, here we can wrap upper-case as a task):

const { load, log } = require('tache')
const upperCase = load.tasks('upper-case')

exports.helloWorld = async function() {
	const upper = await upperCase('hello world')
	log.log(upper)
}
 start   │ helloWorld({})
 start   │ upperCase('hello world')
         │ HELLO WORLD
 done    ✓ upperCase
 done    ✓ helloWorld