From 786e14999f8d9c60008179cfc91273acac5a7d59 Mon Sep 17 00:00:00 2001 From: John Gee Date: Sat, 18 Jun 2022 10:08:54 +1200 Subject: [PATCH 1/4] docs: add example uses of tokens --- examples/limit-long-syntax.js | 30 ++++++++++++++++++++++++ examples/negate.js | 41 +++++++++++++++++++++++++++++++++ examples/no-repeated-options.js | 29 +++++++++++++++++++++++ examples/ordered-options.mjs | 35 ++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 examples/limit-long-syntax.js create mode 100644 examples/negate.js create mode 100644 examples/no-repeated-options.js create mode 100644 examples/ordered-options.mjs diff --git a/examples/limit-long-syntax.js b/examples/limit-long-syntax.js new file mode 100644 index 0000000..b06f70d --- /dev/null +++ b/examples/limit-long-syntax.js @@ -0,0 +1,30 @@ +'use strict'; + +// How might I require long options with values use '='? +// So allow `--foo=bar`, and not allow `--foo bar`. + +// 1. const { parseArgs } = require('node:util'); // from node +// 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package +const { parseArgs } = require('..'); // in repo + +const options = { + file: { short: 'f', type: 'string' }, + log: { type: 'string' }, +}; + +const { values, tokens } = parseArgs({ options, tokens: true }); + +const badToken = tokens.find((token) => token.kind === 'option' && + token.value != null && + token.rawName.startsWith('--') && + !token.inlineValue +); +if (badToken) { + throw new Error(`Option value for '${badToken.rawName}' must be inline, like '${badToken.rawName}=VALUE'`); +} + +console.log(values); + +// Try the following: +// node limit-long-syntax.js -f FILE --log=LOG +// node limit-long-syntax.js --file FILE diff --git a/examples/negate.js b/examples/negate.js new file mode 100644 index 0000000..4619653 --- /dev/null +++ b/examples/negate.js @@ -0,0 +1,41 @@ +'use strict'; + +// How might I add my own support for --no-foo? + +// 1. const { parseArgs } = require('node:util'); // from node +// 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package +const { parseArgs } = require('..'); // in repo + +const options = { + ['color']: { type: 'string' }, + ['no-color']: { type: 'boolean' }, + ['logfile']: { type: 'string' }, + ['no-logfile']: { type: 'boolean' }, +}; +const { values, tokens } = parseArgs({ options, tokens: true }); + +// Reprocess the option tokens and overwrite the returned values. +tokens + .filter((token) => token.kind === 'option') + .forEach((token) => { + if (token.name.startsWith('no-')) { + // Store foo:false for --no-foo + const positiveName = token.name.slice(3); + values[positiveName] = false; + delete values[token.name]; + } else { + // Resave value so last one wins if both --foo and --no-foo. + values[token.name] = token.value ?? true; + } + }); + +const color = values.color; +const logfile = values.logfile ?? 'default.log'; + +console.log({ logfile, color }); + +// Try the following: +// node negate.js +// node negate.js --logfile=test.log --color=red +// node negate.js --no-logfile --no-color +// node negate.js --no-logfile --logfile=test.log --color=red --no-color diff --git a/examples/no-repeated-options.js b/examples/no-repeated-options.js new file mode 100644 index 0000000..51dcdd1 --- /dev/null +++ b/examples/no-repeated-options.js @@ -0,0 +1,29 @@ +'use strict'; + +// How might I throw if an option is repeated? + +// 1. const { parseArgs } = require('node:util'); // from node +// 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package +const { parseArgs } = require('..'); // in repo + +const options = { + ding: { type: 'boolean', short: 'd' }, + beep: { type: 'boolean', short: 'b' } +}; +const { values, tokens } = parseArgs({ options, tokens: true }); + +const seenBefore = new Set(); +tokens.forEach((token) => { + if (token.kind !== 'option') return; + if (seenBefore.has(token.name)) { + throw new Error(`option '${token.name}' used multiple times`); + } + seenBefore.add(token.name); +}); + +console.log(values); + +// Try the following: +// node no-repeated-options --ding --beep +// node no-repeated-options --beep -b +// node no-repeated-options -ddd diff --git a/examples/ordered-options.mjs b/examples/ordered-options.mjs new file mode 100644 index 0000000..093e56c --- /dev/null +++ b/examples/ordered-options.mjs @@ -0,0 +1,35 @@ +// Now might I enforce that two flags are specified in a specific order? + +import { parseArgs } from '../index.js'; + +function findTokenIndex(tokens, target) { + return tokens.findIndex((token) => token.kind === 'option' && + token.name === target + ); +} + +const experimentalName = 'enable-experimental-options'; +const unstableName = 'some-unstable-option'; + +const options = { + [experimentalName]: { type: 'boolean' }, + [unstableName]: { type: 'boolean' }, +}; + +const { values, tokens } = parseArgs({ options, tokens: true }); + +const experimentalIndex = findTokenIndex(tokens, experimentalName); +const unstableIndex = findTokenIndex(tokens, unstableName); +if (unstableIndex !== -1 && + ((experimentalIndex === -1) || (unstableIndex < experimentalIndex))) { + throw new Error(`'--${experimentalName}' must be specified before '--${unstableName}'`); +} + +console.log(values); + +/* eslint-disable max-len */ +// Try the following: +// node ordered-options.mjs +// node ordered-options.mjs --some-unstable-option +// node ordered-options.mjs --some-unstable-option --enable-experimental-options +// node ordered-options.mjs --enable-experimental-options --some-unstable-option From 0da08c83be1f75c09d59a96c5481105516eed899 Mon Sep 17 00:00:00 2001 From: John Gee Date: Tue, 5 Jul 2022 10:09:51 +1200 Subject: [PATCH 2/4] Lint and feedback --- examples/negate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/negate.js b/examples/negate.js index 4619653..358af28 100644 --- a/examples/negate.js +++ b/examples/negate.js @@ -7,10 +7,10 @@ const { parseArgs } = require('..'); // in repo const options = { - ['color']: { type: 'string' }, - ['no-color']: { type: 'boolean' }, - ['logfile']: { type: 'string' }, - ['no-logfile']: { type: 'boolean' }, + 'color': { type: 'string' }, + 'no-color': { type: 'boolean' }, + 'logfile': { type: 'string' }, + 'no-logfile': { type: 'boolean' }, }; const { values, tokens } = parseArgs({ options, tokens: true }); From 8d9844d73f07649718b5c813ee0b902ac12f30bd Mon Sep 17 00:00:00 2001 From: John Gee Date: Thu, 21 Jul 2022 20:30:31 +1200 Subject: [PATCH 3/4] Leave negate.js alone --- examples/negate.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/negate.js b/examples/negate.js index b749169..b663469 100644 --- a/examples/negate.js +++ b/examples/negate.js @@ -1,5 +1,7 @@ 'use strict'; +// This example is used in the documentation. + // How might I add my own support for --no-foo? // 1. const { parseArgs } = require('node:util'); // from node @@ -7,7 +9,7 @@ const { parseArgs } = require('..'); // in repo const options = { - 'color': { type: 'string' }, + 'color': { type: 'boolean' }, 'no-color': { type: 'boolean' }, 'logfile': { type: 'string' }, 'no-logfile': { type: 'boolean' }, @@ -35,6 +37,7 @@ const logfile = values.logfile ?? 'default.log'; console.log({ logfile, color }); // Try the following: -// node negate.js --logfile=test.log --color=red -// node negate.js --no-logfile --no-color -// node negate.js --no-logfile --logfile=test.log --color=red --no-color +// node negate.js +// node negate.js --no-logfile --no-color +// negate.js --logfile=test.log --color +// node negate.js --no-logfile --logfile=test.log --color --no-color From d98c9a789bc76a6a52a87cb73219b49848795773 Mon Sep 17 00:00:00 2001 From: John Gee Date: Tue, 20 Sep 2022 19:08:52 +1200 Subject: [PATCH 4/4] Clarify the examples that are more about doing something interesting with tokens, rather than the best way to achieve the goal. --- examples/limit-long-syntax.js | 7 ++++++- examples/no-repeated-options.js | 4 +++- examples/ordered-options.mjs | 8 +++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/limit-long-syntax.js b/examples/limit-long-syntax.js index b06f70d..943e643 100644 --- a/examples/limit-long-syntax.js +++ b/examples/limit-long-syntax.js @@ -1,7 +1,12 @@ 'use strict'; -// How might I require long options with values use '='? +// This is an example of using tokens to add a custom behaviour. +// +// Require the use of `=` for long options and values by blocking +// the use of space separated values. // So allow `--foo=bar`, and not allow `--foo bar`. +// +// Note: this is not a common behaviour, most CLIs allow both forms. // 1. const { parseArgs } = require('node:util'); // from node // 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package diff --git a/examples/no-repeated-options.js b/examples/no-repeated-options.js index 51dcdd1..0c32468 100644 --- a/examples/no-repeated-options.js +++ b/examples/no-repeated-options.js @@ -1,6 +1,8 @@ 'use strict'; -// How might I throw if an option is repeated? +// This is an example of using tokens to add a custom behaviour. +// +// Throw an error if an option is used more than once. // 1. const { parseArgs } = require('node:util'); // from node // 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package diff --git a/examples/ordered-options.mjs b/examples/ordered-options.mjs index 093e56c..8ab7367 100644 --- a/examples/ordered-options.mjs +++ b/examples/ordered-options.mjs @@ -1,4 +1,10 @@ -// Now might I enforce that two flags are specified in a specific order? +// This is an example of using tokens to add a custom behaviour. +// +// This adds a option order check so that --some-unstable-option +// may only be used after --enable-experimental-options +// +// Note: this is not a common behaviour, the order of different options +// does not usually matter. import { parseArgs } from '../index.js';