clo4 / run-fig

Build fast (as in, the fastest) command-line apps with JS/TS
https://doc.deno.land/https://denopkg.com/SeparateRecords/run-fig/mod.ts
MIT License
3 stars 0 forks source link

Parser generator #4

Open clo4 opened 1 year ago

clo4 commented 1 year ago

Fig specs are essentially a grammar definition as JSON. The parser isn't optimal for every CLI because it has to be general enough to run them all, but a parser generator can create the optimal parser for each CLI.

I want this to be seamless. It should feel like no effort at all to set it up for your project. Not sure exactly how this will look right now

clo4 commented 1 year ago

This is the current idea that I have... I don't love it. Too complicated.

.
|- deno.json      // defines the build task
|- main.ts        // defines actions for commands, imports _parser.js
|- cli
|  |- spec.ts     // the spec definition file, purely declarative
|  |- _parser.js  // generated parser
clo4 commented 1 year ago

The main problem I have with this right now is that it separates the business logic from the CLI itself. This is probably okay but the possible DX is just worse imo. The generated CLI either needs to be imported from the main file then have you hook into it so you can tell it what to run for each command, or have some kind of way to invoke an external function itself which feels too magical.

// cli_def.ts
import compile from "...";

compile("./cli_gen.ts", {
  name: "test",
  action: true,
  subcommands: [
    { name: "another", action: true },
  ],
});
// main.ts
import CLI from "./cli_gen.ts";

CLI.run({
  test: () => {},
  another: () => {},
});

I don't love this

What if instead main can also lazily define the CLI, so when called with a special command it'll compile instead of running

Keeps the definition and everything in one file, but introduces a bootstrapping problem

// main.ts
import CLI from "./cli_gen.ts";

CLI.spec("./cli_gen.ts", () => ({
  name: "something",
  subcommands: [
    { name: "wow" },
    { name: "non_idiomatic_name", run: "nonIdiomaticName" },
  ],
}));

CLI.run({
  something: () => {},
  wow: () => {},
  nonIdiomaticName: () => {},
});

This is maybe cleaner but it could get really bad for larger specs