├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ ├── ci.yaml │ └── release-please.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── test └── index.spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{*.md,*.snap}] 12 | trim_trailing_whitespace = false 13 | 14 | [package.json] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint-config-moxy/es6", 5 | "eslint-config-moxy/addons/node", 6 | "eslint-config-moxy/addons/jest" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | types: [ assigned, opened, synchronize, reopened, labeled ] 7 | name: ci 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node: [10, 12, 14] 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: ${{ matrix.node }} 19 | - run: node --version 20 | - run: npm install --engines-strict 21 | - run: npm test 22 | windows: 23 | runs-on: windows-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: 14 29 | - run: npm install 30 | - run: npm test 31 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | name: release-please 6 | jobs: 7 | release-please: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: GoogleCloudPlatform/release-please-action@v2.4.2 11 | id: release 12 | with: 13 | token: ${{ secrets.GITHUB_TOKEN }} 14 | release-type: node 15 | package-name: yargs-unparser 16 | # The logic below handles the npm publication: 17 | - uses: actions/checkout@v2 18 | # these if statements ensure that a publication only occurs when 19 | # a new release is created: 20 | if: ${{ steps.release.outputs.release_created }} 21 | - uses: actions/setup-node@v1 22 | with: 23 | node-version: 12 24 | registry-url: 'https://external-dot-oss-automation.appspot.com' 25 | if: ${{ steps.release.outputs.release_created }} 26 | - run: npm ci 27 | if: ${{ steps.release.outputs.release_created }} 28 | - run: npm publish 29 | env: 30 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 31 | if: ${{ steps.release.outputs.release_created }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.* 3 | coverage/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [2.0.0](https://www.github.com/yargs/yargs-unparser/compare/v1.6.4...v2.0.0) (2020-10-02) 6 | 7 | 8 | ### ⚠ BREAKING CHANGES 9 | 10 | * upgrade deps drop Node 6/8 (#71) 11 | 12 | ### Code Refactoring 13 | 14 | * upgrade deps drop Node 6/8 ([#71](https://www.github.com/yargs/yargs-unparser/issues/71)) ([c686882](https://www.github.com/yargs/yargs-unparser/commit/c686882f5ad554be44169e3745e741cb4ec898d0)) 15 | 16 | ### [1.6.4](https://www.github.com/yargs/yargs-unparser/compare/v1.6.3...v1.6.4) (2020-10-01) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * **security:** upgraded flat to version ^5.0.2 ([9bd7c67](https://www.github.com/yargs/yargs-unparser/commit/9bd7c672e12417319c5d4de79070d9c7cd5107f2)) 22 | 23 | ### [1.6.3](https://www.github.com/yargs/yargs-unparser/compare/v1.6.2...v1.6.3) (2020-06-17) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * test automatic publish ([209a487](https://www.github.com/yargs/yargs-unparser/commit/209a4870af799f4b200c2a89d7b7e50c9fd5fd1f)) 29 | 30 | ### [1.6.2](https://www.github.com/yargs/yargs-unparser/compare/v1.6.1...v1.6.2) (2020-06-17) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * **readme:** marketing was injected dubiously into README ([#60](https://www.github.com/yargs/yargs-unparser/issues/60)) ([1167667](https://www.github.com/yargs/yargs-unparser/commit/1167667886fcb103c747e3c9855f353ee0e41c03)) 36 | 37 | ### [1.6.1](https://www.github.com/yargs/yargs-unparser/compare/v1.6.0...v1.6.1) (2020-06-17) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * **deps:** downgrade yargs, such that we continue supporting Node 6 ([#57](https://www.github.com/yargs/yargs-unparser/issues/57)) ([f69406c](https://www.github.com/yargs/yargs-unparser/commit/f69406c34bead63011590f7b51a24a6f311c1a48)) 43 | 44 | ## 1.6.0 (2019-07-30) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **security:** update deps addressing recent audit vulnerabilities ([#40](https://github.com/yargs/yargs-unparser/issues/40)) ([2e74f1b](https://github.com/yargs/yargs-unparser/commit/2e74f1b)) 50 | * address bug with camelCased flattened keys ([#32](https://github.com/yargs/yargs-unparser/issues/32)) ([981533a](https://github.com/yargs/yargs-unparser/commit/981533a)) 51 | * **deps:** updated the lodash version to fix vulnerability ([#39](https://github.com/yargs/yargs-unparser/issues/39)) ([7375966](https://github.com/yargs/yargs-unparser/commit/7375966)) 52 | * **package:** update yargs to version 10.0.3 ([f1eb4cb](https://github.com/yargs/yargs-unparser/commit/f1eb4cb)) 53 | * **package:** update yargs to version 11.0.0 ([6aa7c91](https://github.com/yargs/yargs-unparser/commit/6aa7c91)) 54 | 55 | 56 | ### Features 57 | 58 | * add interoperation with minimist ([ba477f5](https://github.com/yargs/yargs-unparser/commit/ba477f5)) 59 | 60 | 61 | # 1.5.0 (2018-11-30) 62 | 63 | 64 | ### Bug Fixes 65 | 66 | * **package:** update yargs to version 10.0.3 ([f1eb4cb](https://github.com/yargs/yargs-unparser/commit/f1eb4cb)) 67 | * **package:** update yargs to version 11.0.0 ([6aa7c91](https://github.com/yargs/yargs-unparser/commit/6aa7c91)) 68 | 69 | 70 | ### Features 71 | 72 | * add interoperation with minimist ([ba477f5](https://github.com/yargs/yargs-unparser/commit/ba477f5)) 73 | 74 | 75 | 76 | 77 | # [1.4.0](https://github.com/moxystudio/yargs-unparser/compare/v1.3.0...v1.4.0) (2017-12-30) 78 | 79 | 80 | ### Features 81 | 82 | * add interoperation with minimist ([ba477f5](https://github.com/moxystudio/yargs-unparser/commit/ba477f5)) 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Made With MOXY Lda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yargs-unparser 2 | 3 | [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] 4 | 5 | [npm-url]:https://npmjs.org/package/yargs-unparser 6 | [npm-image]:http://img.shields.io/npm/v/yargs-unparser.svg 7 | [downloads-image]:http://img.shields.io/npm/dm/yargs-unparser.svg 8 | 9 | Converts back a `yargs` argv object to its original array form. 10 | 11 | Probably the unparser word doesn't even exist, but it sounds nice and goes well with [yargs-parser](https://github.com/yargs/yargs-parser). 12 | 13 | The code originally lived in [MOXY](https://github.com/moxystudio)'s GitHub but was later moved here for discoverability. 14 | 15 | 16 | ## Installation 17 | 18 | `$ npm install yargs-unparser` 19 | 20 | 21 | ## Usage 22 | 23 | ```js 24 | const parse = require('yargs-parser'); 25 | const unparse = require('yargs-unparser'); 26 | 27 | const argv = parse(['--no-boolean', '--number', '4', '--string', 'foo'], { 28 | boolean: ['boolean'], 29 | number: ['number'], 30 | string: ['string'], 31 | }); 32 | // { boolean: false, number: 4, string: 'foo', _: [] } 33 | 34 | const unparsedArgv = unparse(argv); 35 | // ['--no-boolean', '--number', '4', '--string', 'foo']; 36 | ``` 37 | 38 | The second argument of `unparse` accepts an options object: 39 | 40 | - `alias`: The [aliases](https://github.com/yargs/yargs-parser#requireyargs-parserargs-opts) so that duplicate options aren't generated 41 | - `default`: The [default](https://github.com/yargs/yargs-parser#requireyargs-parserargs-opts) values so that the options with default values are omitted 42 | - `command`: The [command](https://github.com/yargs/yargs/blob/master/docs/advanced.md#commands) first argument so that command names and positional arguments are handled correctly 43 | 44 | ### Example with `command` options 45 | 46 | ```js 47 | const yargs = require('yargs'); 48 | const unparse = require('yargs-unparser'); 49 | 50 | const argv = yargs 51 | .command('my-command ', 'My awesome command', (yargs) => 52 | yargs 53 | .option('boolean', { type: 'boolean' }) 54 | .option('number', { type: 'number' }) 55 | .option('string', { type: 'string' }) 56 | ) 57 | .parse(['my-command', 'hello', '--no-boolean', '--number', '4', '--string', 'foo']); 58 | // { positional: 'hello', boolean: false, number: 4, string: 'foo', _: ['my-command'] } 59 | 60 | const unparsedArgv = unparse(argv, { 61 | command: 'my-command ', 62 | }); 63 | // ['my-command', 'hello', '--no-boolean', '--number', '4', '--string', 'foo']; 64 | ``` 65 | 66 | ### Caveats 67 | 68 | The returned array can be parsed again by `yargs-parser` using the default configuration. If you used custom configuration that you want `yargs-unparser` to be aware, please fill an [issue](https://github.com/yargs/yargs-unparser/issues). 69 | 70 | If you `coerce` in weird ways, things might not work correctly. 71 | 72 | 73 | ## Tests 74 | 75 | `$ npm test` 76 | `$ npm test -- --watch` during development 77 | 78 | ## Supported Node.js Versions 79 | 80 | Libraries in this ecosystem make a best effort to track 81 | [Node.js' release schedule](https://nodejs.org/en/about/releases/). Here's [a 82 | post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a). 83 | 84 | ## License 85 | 86 | [MIT License](http://opensource.org/licenses/MIT) 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const flatten = require('flat'); 4 | const camelcase = require('camelcase'); 5 | const decamelize = require('decamelize'); 6 | const isPlainObj = require('is-plain-obj'); 7 | 8 | function isAlias(key, alias) { 9 | // TODO Switch to Object.values one Node.js 6 is dropped 10 | return Object.keys(alias).some((id) => [].concat(alias[id]).indexOf(key) !== -1); 11 | } 12 | 13 | function hasDefaultValue(key, value, defaults) { 14 | return value === defaults[key]; 15 | } 16 | 17 | function isCamelCased(key, argv) { 18 | return /[A-Z]/.test(key) && camelcase(key) === key && // Is it camel case? 19 | argv[decamelize(key, '-')] != null; // Is the standard version defined? 20 | } 21 | 22 | function keyToFlag(key) { 23 | return key.length === 1 ? `-${key}` : `--${key}`; 24 | } 25 | 26 | function parseCommand(cmd) { 27 | const extraSpacesStrippedCommand = cmd.replace(/\s{2,}/g, ' '); 28 | const splitCommand = extraSpacesStrippedCommand.split(/\s+(?![^[]*]|[^<]*>)/); 29 | const bregex = /\.*[\][<>]/g; 30 | const firstCommand = splitCommand.shift(); 31 | 32 | if (!firstCommand) { throw new Error(`No command found in: ${cmd}`); } 33 | const parsedCommand = { 34 | cmd: firstCommand.replace(bregex, ''), 35 | demanded: [], 36 | optional: [], 37 | }; 38 | 39 | splitCommand.forEach((cmd, i) => { 40 | let variadic = false; 41 | 42 | cmd = cmd.replace(/\s/g, ''); 43 | if (/\.+[\]>]/.test(cmd) && i === splitCommand.length - 1) { variadic = true; } 44 | if (/^\[/.test(cmd)) { 45 | parsedCommand.optional.push({ 46 | cmd: cmd.replace(bregex, '').split('|'), 47 | variadic, 48 | }); 49 | } else { 50 | parsedCommand.demanded.push({ 51 | cmd: cmd.replace(bregex, '').split('|'), 52 | variadic, 53 | }); 54 | } 55 | }); 56 | 57 | return parsedCommand; 58 | } 59 | 60 | function unparseOption(key, value, unparsed) { 61 | if (typeof value === 'string') { 62 | unparsed.push(keyToFlag(key), value); 63 | } else if (value === true) { 64 | unparsed.push(keyToFlag(key)); 65 | } else if (value === false) { 66 | unparsed.push(`--no-${key}`); 67 | } else if (Array.isArray(value)) { 68 | value.forEach((item) => unparseOption(key, item, unparsed)); 69 | } else if (isPlainObj(value)) { 70 | const flattened = flatten(value, { safe: true }); 71 | 72 | for (const flattenedKey in flattened) { 73 | if (!isCamelCased(flattenedKey, flattened)) { 74 | unparseOption(`${key}.${flattenedKey}`, flattened[flattenedKey], unparsed); 75 | } 76 | } 77 | // Fallback case (numbers and other types) 78 | } else if (value != null) { 79 | unparsed.push(keyToFlag(key), `${value}`); 80 | } 81 | } 82 | 83 | function unparsePositional(argv, options, unparsed) { 84 | const knownPositional = []; 85 | 86 | // Unparse command if set, collecting all known positional arguments 87 | // e.g.: build 88 | if (options.command) { 89 | const { 0: cmd, index } = options.command.match(/[^<[]*/); 90 | const { demanded, optional } = parseCommand(`foo ${options.command.substr(index + cmd.length)}`); 91 | 92 | // Push command (can be a deep command) 93 | unparsed.push(...cmd.trim().split(/\s+/)); 94 | 95 | // Push positional arguments 96 | [...demanded, ...optional].forEach(({ cmd: cmds, variadic }) => { 97 | knownPositional.push(...cmds); 98 | 99 | const cmd = cmds[0]; 100 | const args = (variadic ? argv[cmd] || [] : [argv[cmd]]) 101 | .filter((arg) => arg != null) 102 | .map((arg) => `${arg}`); 103 | 104 | unparsed.push(...args); 105 | }); 106 | } 107 | 108 | // Unparse unkown positional arguments 109 | argv._ && unparsed.push(...argv._.slice(knownPositional.length)); 110 | 111 | return knownPositional; 112 | } 113 | 114 | function unparseOptions(argv, options, knownPositional, unparsed) { 115 | for (const key of Object.keys(argv)) { 116 | const value = argv[key]; 117 | 118 | if ( 119 | // Remove positional arguments 120 | knownPositional.includes(key) || 121 | // Remove special _, -- and $0 122 | ['_', '--', '$0'].includes(key) || 123 | // Remove aliases 124 | isAlias(key, options.alias) || 125 | // Remove default values 126 | hasDefaultValue(key, value, options.default) || 127 | // Remove camel-cased 128 | isCamelCased(key, argv) 129 | ) { 130 | continue; 131 | } 132 | 133 | unparseOption(key, argv[key], unparsed); 134 | } 135 | } 136 | 137 | function unparseEndOfOptions(argv, options, unparsed) { 138 | // Unparse ending (--) arguments if set 139 | argv['--'] && unparsed.push('--', ...argv['--']); 140 | } 141 | 142 | // ------------------------------------------------------------ 143 | 144 | function unparser(argv, options) { 145 | options = Object.assign({ 146 | alias: {}, 147 | default: {}, 148 | command: null, 149 | }, options); 150 | 151 | const unparsed = []; 152 | 153 | // Unparse known & unknown positional arguments (foo [rest...]) 154 | // All known positional will be returned so that they are not added as flags 155 | const knownPositional = unparsePositional(argv, options, unparsed); 156 | 157 | // Unparse option arguments (--foo hello --bar hi) 158 | unparseOptions(argv, options, knownPositional, unparsed); 159 | 160 | // Unparse "end-of-options" arguments (stuff after " -- ") 161 | unparseEndOfOptions(argv, options, unparsed); 162 | 163 | return unparsed; 164 | } 165 | 166 | module.exports = unparser; 167 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yargs-unparser", 3 | "description": "Converts back a yargs argv object to its original array form", 4 | "version": "2.0.0", 5 | "keywords": [ 6 | "yargs", 7 | "unparse", 8 | "expand", 9 | "inverse", 10 | "argv" 11 | ], 12 | "author": "André Cruz ", 13 | "engines": { 14 | "node": ">=10" 15 | }, 16 | "homepage": "https://github.com/yargs/yargs-unparser", 17 | "repository": "yargs/yargs-unparser", 18 | "license": "MIT", 19 | "main": "index.js", 20 | "files": [], 21 | "scripts": { 22 | "lint": "eslint .", 23 | "fix": "eslint . --fix", 24 | "test": "jest --env node --coverage", 25 | "prerelease": "npm t && npm run lint", 26 | "precommit": "lint-staged" 27 | }, 28 | "lint-staged": { 29 | "*.js": [ 30 | "eslint --fix", 31 | "git add" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "eslint": "^6.1.0", 36 | "eslint-config-moxy": "^7.1.0", 37 | "husky": "^3.0.1", 38 | "jest": "^24.9.0", 39 | "lint-staged": "^9.5.0", 40 | "minimist": "^1.2.5", 41 | "yargs-parser": "^18.1.3" 42 | }, 43 | "dependencies": { 44 | "camelcase": "^6.0.0", 45 | "decamelize": "^4.0.0", 46 | "flat": "^5.0.2", 47 | "is-plain-obj": "^2.1.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const yargs = require('yargs/yargs'); 4 | const parse = require('yargs-parser'); 5 | const minimist = require('minimist'); 6 | const unparse = require('../'); 7 | 8 | it('should unparse options whose values are primitives', () => { 9 | const argv = parse(['--no-boolean1', '--boolean2', '--number', '4', '--string', 'foo'], { 10 | number: 'number', 11 | string: 'string', 12 | boolean: ['boolean1', 'boolean2'], 13 | }); 14 | 15 | expect(unparse(argv)).toEqual(['--no-boolean1', '--boolean2', '--number', '4', '--string', 'foo']); 16 | }); 17 | 18 | it('should unparse options whose values are arrays', () => { 19 | const argv = parse(['--array1', '1', '2', '--array2', '3', '--array2', '4'], { 20 | array: ['array1', 'array2'], 21 | }); 22 | 23 | expect(unparse(argv)).toEqual(['--array1', '1', '--array1', '2', '--array2', '3', '--array2', '4']); 24 | }); 25 | 26 | it('should unparse options whose values are objects', () => { 27 | const argv = parse(['--foo.x', 'x', '--foo.y', 'y', '--foo.w', '1', '2', '--foo.z', '3', '--foo.z', '4'], { 28 | array: ['foo.w', 'foo.z'], 29 | }); 30 | 31 | expect(unparse(argv)).toEqual(['--foo.x', 'x', '--foo.y', 'y', '--foo.w', '1', '--foo.w', '2', '--foo.z', '3', '--foo.z', '4']); 32 | }); 33 | 34 | it('should unparse options whose values are not primitives, arrays or objects', () => { 35 | class Baz { 36 | toString() { 37 | return 'baz'; 38 | } 39 | } 40 | 41 | const date = new Date(1502708276274); 42 | 43 | expect(unparse({ 44 | foo: date, 45 | bar: /bar/, 46 | baz: new Baz(), 47 | })).toEqual(['--foo', date.toString(), '--bar', '/bar/', '--baz', 'baz']); 48 | }); 49 | 50 | it('should unparse options whose values are empty strings correctly', () => { 51 | let argv = parse(['--string', ''], { 52 | string: 'string', 53 | }); 54 | 55 | expect(unparse(argv)).toEqual(['--string', '']); 56 | 57 | argv = parse(['--string', '']); 58 | 59 | expect(unparse(argv)).toEqual(['--string', '']); 60 | }); 61 | 62 | it('should only add a dash for short option names', () => { 63 | const argv = parse(['--n', '4', '--s', 'foo'], { 64 | number: 'n', 65 | string: 's', 66 | }); 67 | 68 | expect(unparse(argv)).toEqual(['-n', '4', '-s', 'foo']); 69 | }); 70 | 71 | it('should not duplicate keys that are camel-cased', () => { 72 | const argv = parse(['--some-string', 'foo']); 73 | 74 | expect(unparse(argv)).toEqual(['--some-string', 'foo']); 75 | }); 76 | 77 | it('should keep camel-cased keys if standard ones were not found on argv', () => { 78 | const argv = parse(['--someNumber', '1']); 79 | 80 | expect(unparse(argv)).toEqual(['--someNumber', '1']); 81 | }); 82 | 83 | it('should not duplicate nested keys that are camel-cased', () => { 84 | const argv = parse(['--paths.some-string', 'foo']); 85 | 86 | expect(unparse(argv)).toEqual(['--paths.some-string', 'foo']); 87 | }); 88 | 89 | it('should keep nested camel-cased keys if standard ones were not found on argv', () => { 90 | const argv = parse(['--paths.someNumber', '1']); 91 | 92 | expect(unparse(argv)).toEqual(['--paths.someNumber', '1']); 93 | }); 94 | 95 | it('should ignore nullish option values', () => { 96 | const argv = parse(['node', 'cli.js'], { 97 | number: 'n', 98 | string: 's', 99 | }); 100 | 101 | expect(unparse(argv)).toEqual(['node', 'cli.js']); 102 | }); 103 | 104 | it('should add unknown positional arguments at the start', () => { 105 | const argv = parse(['foo', '--string', 'bar']); 106 | 107 | expect(unparse(argv)).toEqual(['foo', '--string', 'bar']); 108 | }); 109 | 110 | it('should add arguments after -- at the end', () => { 111 | const argv = parse(['--string', 'foo', '--', '--number', '1'], { 112 | configuration: { 113 | 'populate--': true, 114 | }, 115 | }); 116 | 117 | expect(unparse(argv)).toEqual(['--string', 'foo', '--', '--number', '1']); 118 | }); 119 | 120 | it('should ignore $0', () => { 121 | const argv = parse(['foo', '--string', 'bar']); 122 | 123 | argv.$0 = 'foo.js'; 124 | 125 | expect(unparse(argv)).toEqual(['foo', '--string', 'bar']); 126 | }); 127 | 128 | describe('options', () => { 129 | it('should ignore aliases specified via `options.aliases`', () => { 130 | const alias = { 131 | string: 's', // Single alias 132 | number: ['n', 'no'], // Multiple aliases 133 | substr: 'substring', 134 | }; 135 | const argv = parse(['--string', 'foo', '--number', '1', '--substr', 'a'], { alias }); 136 | 137 | expect(unparse(argv, { alias })).toEqual([ 138 | '--string', 'foo', 139 | '--number', '1', 140 | '--substr', 'a', 141 | ]); 142 | }); 143 | 144 | it('should filter flags with default values via `options.defaults`', () => { 145 | const defaults = { foo: false }; 146 | const argv = parse([], { default: defaults }); 147 | 148 | expect(unparse(argv, { default: defaults })).toEqual([]); 149 | }); 150 | 151 | it('should handle known positional arguments specified via `options.command`', () => { 152 | const command = 'build [second] [rest..]'; 153 | const argv = yargs() 154 | .help(false) 155 | .version(false) 156 | .command(command, '', (yargs) => yargs, () => {}) 157 | .parse(['build', 'foo', 'foz', 'bar', 'baz', '--string', 'hello']); 158 | 159 | expect(unparse(argv, { 160 | command, 161 | })).toEqual(['build', 'foo', 'foz', 'bar', 'baz', '--string', 'hello']); 162 | }); 163 | 164 | it('should handle optional known positional arguments specified via `options.command`', () => { 165 | const command = 'build [first] [rest..]'; 166 | const argv = yargs() 167 | .help(false) 168 | .version(false) 169 | .command(command, '', (yargs) => yargs, () => {}) 170 | .parse(['build', '--string', 'hello']); 171 | 172 | expect(unparse(argv, { 173 | command, 174 | })).toEqual(['build', '--string', 'hello']); 175 | }); 176 | 177 | it('should handle unknown positional arguments when `options.command` is set', () => { 178 | const command = 'build '; 179 | const argv = yargs() 180 | .help(false) 181 | .version(false) 182 | .command(command, '', (yargs) => yargs, () => {}) 183 | .parse(['build', 'foo', 'foz', '--string', 'hello']); 184 | 185 | expect(unparse(argv, { 186 | command, 187 | })).toEqual(['build', 'foo', 'foz', '--string', 'hello']); 188 | }); 189 | 190 | it('should handle positional arguments with alias specified in `options.command`', () => { 191 | const command = 'build '; 192 | const argv = yargs() 193 | .help(false) 194 | .version(false) 195 | .command(command, '', (yargs) => yargs, () => {}) 196 | .parse(['build', 'foo', '--string', 'hello']); 197 | 198 | expect(unparse(argv, { 199 | command, 200 | })).toEqual(['build', 'foo', '--string', 'hello']); 201 | }); 202 | }); 203 | 204 | describe('interoperation with other libraries', () => { 205 | it('should have basic integration with minimist', () => { 206 | const argv = parse(['--no-cache', '--optimize', '--host', '0.0.0.0', '--collect', 'x', 'y'], { 207 | boolean: ['cache', 'optimize'], 208 | string: 'host', 209 | array: 'collect', 210 | }); 211 | 212 | const argvArray = unparse(argv); 213 | 214 | expect(minimist(argvArray)).toMatchObject({ 215 | cache: false, 216 | optimize: true, 217 | host: '0.0.0.0', 218 | collect: ['x', 'y'], 219 | }); 220 | }); 221 | }); 222 | --------------------------------------------------------------------------------