0.1.1 • Published 2 months ago

runfig v0.1.1

Weekly downloads
-
License
0BSD
Repository
github
Last release
2 months ago

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

JavaScriptTypeScriptOutput
.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?
0.1.1

2 months ago

0.0.11

2 months ago

0.1.0

2 months ago

0.0.10

2 months ago

0.0.9

2 months ago

0.0.8

2 months ago

0.0.7

2 months ago

0.0.6

2 months ago

0.0.5

2 months ago

0.0.4

3 months ago

0.0.3

3 months ago

0.0.2

3 months ago

0.0.1

3 months ago