Skip to content

Replaced minimist with mri for lighter CLI args parsing#110

Closed
outslept wants to merge 2 commits into
11ty:mainfrom
outslept:slpt/deps
Closed

Replaced minimist with mri for lighter CLI args parsing#110
outslept wants to merge 2 commits into
11ty:mainfrom
outslept:slpt/deps

Conversation

@outslept
Copy link
Copy Markdown

Note

Keeping this as a draft in case of possible discussions

To ensure compatibility, I created a small script comparing both libraries across various CLI argument scenarios
See below for the test cases used and the output.


Test script used:

compare.js
const minimist = require('minimist');
const mri = require('mri');

const options = {
  string: ['dir', 'input', 'port', 'stringArg', 'foo-bar'],
  boolean: ['version', 'help', 'domdiff', 'boolArg', 'boolArg2', 'flag'],
  alias: {
    input: ['dir', 'i'],
    dir: ['input', 'd'],
    version: 'v',
    help: 'h',
    'foo-bar': ['foobar', 'fb']
  },
  default: {
    dir: 'defaultDir',
    port: '3000',
    domdiff: false,
    version: false,
    help: false,
    boolArg: false,
    boolArg2: true,
    stringArg: 'defaultString',
    flag: true,
    'foo-bar': 'baz'
  }
};

const tests = [
  // Basic string/boolean/number flags
  ['--dir=src', '--input=test', '--port', '8080', '--version', '--help', '--domdiff'],
  // Boolean/string values and overrides
  ['--boolArg', '--boolArg2=false', '--stringArg=hello'],
  // Aliases and short flags
  ['-d', 'aliased', '-i', 'double', '-v', '-h'],
  // Repeated (array) values
  ['--dir=src', '--dir=dist', '--input=foo'],
  // Positional and mixed args
  ['file1', 'file2', '--dir=src', '--port=5000'],
  // Boolean flags with/without value
  ['--boolArg', '--boolArg2', '--flag=false'],
  ['--boolArg=false', '--boolArg2=true', '--flag'],
  // Numeric values
  ['--port=8080', '--port', '9090'],
  // Unknown flags
  ['--unknown', '--dir=src'],
  // Combined short flags
  ['-abc'],
  // Dashed flags
  ['--foo-bar=123', '--foobar=456', '--fb=789'],
  // Defaults (no args)
  [],
  // "--" parsing stop scenario
  ['--dir=src', '--', '--notAFlag', 'positional'],
  // String/boolean mix and edge-cases
  ['--version', '--version=false', '--help=hello'],
];

console.log('=== minimist ===');
for (const args of tests) {
  try {
    const result = minimist(args, {
      ...options,
      unknown: (arg) => {
        console.log(`[minimist unknown] ${arg}`);
        return false;
      }
    });
    console.log(`args: ${JSON.stringify(args)} =>`);
    console.dir(result, { depth: null });
    console.log();
  } catch (e) {
    console.error('minimist error:', e.message);
  }
}

console.log('=== mri ===');
for (const args of tests) {
  try {
    const result = mri(args, {
      ...options,
      unknown: (arg) => {
        console.log(`[mri unknown] ${arg}`);
        throw new Error(`[mri unknown] ${arg}`);
      }
    });
    console.log(`args: ${JSON.stringify(args)} =>`);
    console.dir(result, { depth: null });
    console.log();
  } catch (e) {
    console.error('mri error:', e.message);
  }
}

Test output:

Console output
~ node .\compare.js

=== minimist ===
args: ["--dir=src","--input=test","--port","8080","--version","--help","--domdiff"] =>
{
  _: [],
  version: true,
  v: true,
  help: true,
  h: true,
  domdiff: true,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: [ 'src', 'test' ],
  input: [ 'src', 'test' ],
  d: [ 'src', 'test' ],
  port: '8080',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg","--boolArg2=false","--stringArg=hello"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: true,
  boolArg2: false,
  flag: true,
  stringArg: 'hello',
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["-d","aliased","-i","double","-v","-h"] =>
{
  _: [],
  version: true,
  v: true,
  help: true,
  h: true,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  d: 'aliased',
  dir: [ 'aliased', 'double' ],
  input: [ 'aliased', 'double' ],
  i: 'double',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--dir=src","--dir=dist","--input=foo"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: [ 'src', 'dist', 'foo' ],
  input: [ 'src', 'dist', 'foo' ],
  d: [ 'src', 'dist', 'foo' ],
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

[minimist unknown] file1
[minimist unknown] file2
args: ["file1","file2","--dir=src","--port=5000"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'src',
  input: 'src',
  d: 'src',
  port: '5000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg","--boolArg2","--flag=false"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: true,
  boolArg2: true,
  flag: false,
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg=false","--boolArg2=true","--flag"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--port=8080","--port","9090"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  port: [ '8080', '9090' ],
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

[minimist unknown] --unknown
args: ["--unknown","--dir=src"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'src',
  input: 'src',
  d: 'src',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

[minimist unknown] -abc
[minimist unknown] -abc
[minimist unknown] -abc
args: ["-abc"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--foo-bar=123","--foobar=456","--fb=789"] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  'foo-bar': [ '123', '456', '789' ],
  foobar: [ '123', '456', '789' ],
  fb: [ '123', '456', '789' ],
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString'
}

args: [] =>
{
  _: [],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--dir=src","--","--notAFlag","positional"] =>
{
  _: [ '--notAFlag', 'positional' ],
  version: false,
  v: false,
  help: false,
  h: false,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'src',
  input: 'src',
  d: 'src',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

args: ["--version","--version=false","--help=hello"] =>
{
  _: [],
  version: false,
  v: false,
  help: true,
  h: true,
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'defaultDir',
  input: 'defaultDir',
  d: 'defaultDir',
  port: '3000',
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  foobar: 'baz',
  fb: 'baz'
}

=== mri ===
args: ["--dir=src","--input=test","--port","8080","--version","--help","--domdiff"] =>
{
  _: [],
  dir: 'src',
  input: 'src',
  port: '8080',
  version: true,
  help: true,
  domdiff: true,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  i: 'src',
  v: true,
  h: true,
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg","--boolArg2=false","--stringArg=hello"] =>
{
  _: [],
  boolArg: true,
  boolArg2: false,
  stringArg: 'hello',
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  flag: true,
  'foo-bar': 'baz',
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

[mri unknown] -d
mri error: [mri unknown] -d
args: ["--dir=src","--dir=dist","--input=foo"] =>
{
  _: [],
  dir: [ 'src', 'dist' ],
  input: [ 'src', 'dist' ],
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  i: [ 'src', 'dist' ],
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

args: ["file1","file2","--dir=src","--port=5000"] =>
{
  _: [ 'file1', 'file2' ],
  dir: 'src',
  port: '5000',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  input: 'src',
  i: 'src',
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg","--boolArg2","--flag=false"] =>
{
  _: [],
  boolArg: true,
  boolArg2: true,
  flag: false,
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

args: ["--boolArg=false","--boolArg2=true","--flag"] =>
{
  _: [],
  boolArg: false,
  boolArg2: true,
  flag: true,
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  stringArg: 'defaultString',
  'foo-bar': 'baz',
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

args: ["--port=8080","--port","9090"] =>
{
  _: [],
  port: [ '8080', '9090' ],
  dir: 'defaultDir',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false,
  foobar: 'baz',
  fb: 'baz'
}

[mri unknown] --unknown
mri error: [mri unknown] --unknown
[mri unknown] -a
mri error: [mri unknown] -a
args: ["--foo-bar=123","--foobar=456","--fb=789"] =>
{
  _: [],
  'foo-bar': '123',
  foobar: '123',
  fb: '123',
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false
}

args: [] =>
{
  _: [],
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  input: 'defaultDir',
  i: 'defaultDir',
  v: false,
  h: false
}

args: ["--dir=src","--","--notAFlag","positional"] =>
{
  _: [ '--notAFlag', 'positional' ],
  dir: 'src',
  port: '3000',
  domdiff: false,
  version: false,
  help: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  input: 'src',
  i: 'src',
  v: false,
  h: false
}

args: ["--version","--version=false","--help=hello"] =>
{
  _: [ 'hello' ],
  version: [ true, false ],
  help: true,
  dir: 'defaultDir',
  port: '3000',
  domdiff: false,
  boolArg: false,
  boolArg2: true,
  stringArg: 'defaultString',
  flag: true,
  'foo-bar': 'baz',
  v: [ true, false ],
  h: true,
  input: 'defaultDir',
  i: 'defaultDir'
}

Comment thread cmd.js Outdated
@outslept
Copy link
Copy Markdown
Author

Close in favor of #123

@outslept outslept closed this Jul 27, 2025
@outslept outslept deleted the slpt/deps branch July 27, 2025 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants