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

jumpgen

v0.2.0

Published

Easy, transparent ”watch mode” for filesystem access (powered by Chokidar)

Downloads

262

Readme

jumpgen

Easy, transparent ”watch mode” for filesystem access (powered by Chokidar)

The problem: You're writing a script that uses the filesystem as one of its inputs. You want to watch files for changes, so you can rerun your script when they change.

The solution: Use jumpgen.

Now, your script can use filesystem APIs without worrying about how to watch files for changes, leaving you to focus on the logic of your generator.

  • Your script will rerun automatically when files it relies on get added, changed, or deleted.
  • Glob patterns are also watched for you.
  • Jumpgen determines if a path needs to be watched recursively, only its children, or just the path itself. This means your generator will only rerun when it needs to, reducing unnecessary work.
  • Incremental updates are easier than ever, thanks to Jumpgen's task API. (coming soon)
  • If you have a long-running script, it can be automatically aborted if a file changes during its execution.
  • If your script relies on third-party code that accesses the filesystem, you can tell Jumpgen to watch those files too, optionally blaming other files when it detects a change.
  • If your script reads from a configuration file, you can tell Jumpgen to “hard reset” the generator when that file changes. This is useful for far-reaching changes that might invalidate your entire script's output.
  • It uses the chokidar@4, picomatch, tinyglobby, and fdir npm packages under the hood for file watching and globbing.

API Reference

See the API Reference for the full documentation.

Use cases

What this library isn't for

  • Jumpgen isn't focused on ”project scaffolding”, like plop or yeoman. But you can absolutely use it to build your own scaffolding tools.
  • Jumpgen isn't focused on basic file-watching tasks. If you're looking for a simple way to watch files for changes, use chokidar directly.
  • Jumpgen isn't focused on basic script re-running. If you're looking to rerun a script when files change, use something like watchlist instead.

So what is it for?

Jumpgen saves the day when your script has complex, configurable logic and/or could benefit from incremental updates. For example, I'm using it to build a type-safe RPC library, alien-rpc, which is highly configurable and greatly benefits from knowing when certain files change.

Installation

pnpm add jumpgen

Usage

Define your generator with a name and a function that receives a Context object with helper functions for reading, scanning, and writing files. Your generator should avoid using node:fs APIs directly, or else file-watching will break.

import { jumpgen } from 'jumpgen'

export default jumpgen(
  'my-generator',
  async ({ read, scan, dedent, write }) => {
    // Find files to use as source modules. If a file matching your globs
    // is later added or removed, your generator will be rerun (if watch
    // mode is enabled).
    const sourceModulePaths = scan(['src/**/*.ts', '!**/*.test.ts'], {
      absolute: true,
    })

    // When you read a file, and you later change or delete it, your generator
    // will be rerun (if watch mode is enabled).
    const contents = sourceModulePaths.map(p => read(p))

    // When you write a file, your generator emits a "write" event. This
    // is useful for logging, which helps you understand what's happening.
    contents.forEach((content, i) => {
      const outPath = sourceModulePaths[i].replace(/\.ts$/, '.js')
      write(outPath, transform(content))
    })

    // Use the "dedent" function to remove excess indentation from your
    // template literals.
    write(
      'foo.ts',
      dedent`
        export const foo = true
      `
    )
  }
)

To run your generator, simply import and call it.

import myGenerator from './my-generator.js'

// This example uses the default options.
const runner = myGenerator({
  // All file operations are relative to this path.
  root: process.cwd(),

  // Watch mode must be explicitly enabled.
  watch: false,

  // You may provide your own EventEmitter, which is mainly useful for
  // consolidating events across multiple generators. Whether or not you
  // provide one, you can listen for events on the `runner.events` property.
  events: undefined,
})

// The generator runs immediately. To wait for it to finish, you can
// await it or call its "then" method.
await runner
// or
runner.then(() => {
  console.log('done')
})

// If the generator is asynchronous and respects the abort signal it's given,
// you can stop it early with the "stop" method. This also disables file watching.
await runner.stop()

// Listen to events from the runner.
runner.events.on('start', generatorName => {
  console.log(generatorName, 'started')
})
runner.events.on('write', (file, generatorName) => {
  console.log(generatorName, 'wrote', file)
})
runner.events.on('finish', (result, generatorName) => {
  console.log(generatorName, 'finished with', result)
})
runner.events.on('error', (error, generatorName) => {
  console.error(generatorName, 'errored with', error)
})

Composing generators

The compose function lets you combine multiple generators into a single generator that runs them all in parallel.

import { compose } from 'jumpgen'

// The returned generator has the same API as the generators you pass to it,
// except it resolves with an array containing the results of all the generators.
const myGenerator = compose(generatorA, generatorB)

Testing

See the testing guide for information on how to test your generators.

License

MIT