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

runfig

v0.1.1

Published

Down with YAML and TOML! Generate your config files from code, the way nature intended

Downloads

55

Readme

runfig

Down with YAML and TOML! Generate your config files from code, the way nature intended.

Contents

Overview

Like all developers, I've developed some strong opinions:

  1. JSON is better for config files than "human-readable" formats like YAML and TOML;
  2. JavaScript is better than JSON, because you can use code;
  3. TypeScript is better than JavaScript, because you can type-check it.

I made Runfig so that I can use TypeScript instead of TOML/YAML.

For example, see pnpm-workspace.yaml in this monorepo. PNPM allows wildcards in this file, but the Turbo monorepo tool requires all the packages to be listed explicitly, so I added a script to scan for packages automatically: pnpm-workspace.yaml.ts. Executing runfig in the root directory regenerates the workspace file.

Why use Runfig rather than hand-rolling a script to generate configs?

  • All the boilerplate file access is handled for you;
  • JSON, TOML and YAML supported out of the box;
  • --unfig flag for easy bootstrapping.

Installation & usage

Install runfig via your package manager of choice, e.g.:

pnpm install -g runfig

Executing runfig will then:

  • Scan for config files with an additional .js or .ts extension
  • Load each file and extract its default export
  • Convert each export to the appropriate format (JSON, YAML or TOML)
  • Write a corresponding output file for each input

For example, if Runfig sees a wrangler.toml.ts script it will use that to generate wrangler.toml.

Execute runfig -u to run in the opposite direction, e.g. use wrangler.toml to generate wrangler.toml.ts. This is useful to bootstrap a Runfig setup from existing config files.

Tutorial

Consider Cloudflare's sample wrangler.toml configuration:

# Top-level configuration
name = "my-worker"
main = "src/index.js"
compatibility_date = "2022-07-12"

workers_dev = false
route = { pattern = "example.org/*", zone_name = "example.org" }

kv_namespaces = [
  { binding = "<MY_NAMESPACE>", id = "<KV_ID>" }
]

[env.staging]
name = "my-worker-staging"
route = { pattern = "staging.example.org/*", zone_name = "example.org" }

kv_namespaces = [
  { binding = "<MY_NAMESPACE>", id = "<STAGING_KV_ID>" }
]

I'm not keen on TOML as the syntax is overly subtle; for example, [env.staging] is doing a lot of work. It's "readable" but also error prone.

List existing configs

Runfig understands TOML, so it should be able to parse this config. Let's check:

% runfig --unfig --list
wrangler.toml.ts

The % prompt here indicates the command to type, runfig --unfig --list. (I'm assuming that runfig has been installed globally.)

The --unfig flag says to run in reverse (bootstrap .ts scripts from configs). The --list flag prints the files that would be generated. (All flags can be abbreviated, so we could type just runfig -u -l.)

In this example, the output will consist of one file: wrangler.toml.ts.

Print bootstrapped config script

Before generating that file, let's use --print to see what it will look like:

% runfig --unfig --print
// Auto-generated from wrangler.toml

export default {
  name: 'my-worker',
  main: 'src/index.js',
  compatibility_date: '2022-07-12',
  workers_dev: false,
  route: {
    pattern: 'example.org/*',
    zone_name: 'example.org',
  },
  kv_namespaces: [
    {
      binding: '<MY_NAMESPACE>',
      id: '<KV_ID>',
    },
  ],
  env: {
    staging: {
      name: 'my-worker-staging',
      route: {
        pattern: 'staging.example.org/*',
        zone_name: 'example.org',
      },
      kv_namespaces: [
        {
          binding: '<MY_NAMESPACE>',
          id: '<STAGING_KV_ID>',
        },
      ],
    },
  },
}

Looks good — let's go ahead and generate that file:

% runfig --unfig
wrangler.toml → wrangler.toml.ts

Runfig logs a line to stderr showing what was generated, and from where.

Generate configs from scripts

Now we have a .ts script whose default export is a JSON object. We can modify this script, refactor it using imports and shared constants, and so on. The default behavior of Runfig is to regenerate configs, so we don't need any flags:

% runfig
wrangler.toml.ts → wrangler.toml

You can alternatively use wrangler.toml.js if you prefer JavaScript to TypeScript (although the --unfig flag always generates .ts files right now).

Suggested workflows

There are various ways to fit Runfig into a development workflow, including:

  • Manual: use Runfig ad-hoc when working with TOML and YAML files;
  • Fully automated: store the .ts files in source control, and generate YAML/TOML configs as part of your build;
  • Semi-automated: store both files in source control, and use a precommit hook or similar to keep the configs in sync.

An advantage of the semi-automated approach is that you can code review the config changes as they occur. I use this approach in this monorepo for pnpm-workspace.yaml, where it also solves a chicken-and-egg problem: the file needs to be checked in for Turbo to work, but I use Turbo to regenerate the file!

Reference

Command-line options

  • --indir, -i: set input directory (default is current directory)
  • --outdir, -o: set output directory (default is curent directory)
  • --recursive, -r: include subdirectories (default is false)
  • --unfig, -u: run in reverse to bootstrap inputs
  • --print, -p: print generated output to stdout, don't write files
  • --list, -l: list output filenames to stdout, don't write files

By default, all matching files in the input directory will be processed. However, if there are positional arguments, these will be interpreted as individual files to be processed (relative to --indir).

If the --recursive flag is used, subdirectory paths are preserved in the output.

Supported formats & file extensions

| JavaScript | TypeScript | → | Output | | ---------- | ---------- | --- | ------- | | .json.js | .json.ts | | .json | | .toml.js | .toml.ts | | .toml | | .yaml.js | .yaml.ts | | .yaml | | .yml.js | .yml.ts | | .yml |

Notes & caveats

I've tried to make this tool reasonably robust and flexible, but fundamentally Runfig is at a "works well enough for me" quality level, hence the 0.x version number. If you're interested in using it, please let me know!

Config scripts are currently executed using tsx.

Possible future enhancements

  • Add --bun and --deno flags for different TS engines
  • Avoid stomping on existing files; add --force flag to override
  • Generate multiple configs from a single script
  • Preserve comments in --unfig output
  • TS types for configs — could we generate these from JSON schemas?