pax_global_header00006660000000000000000000000064141446157120014517gustar00rootroot0000000000000052 comment=217aa62906d0bbd40e06d675b8a23df8d49a5ad4 yargs-parser-yargs-parser-v21.0.0/000077500000000000000000000000001414461571200170215ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/.eslintrc000066400000000000000000000007101414461571200206430ustar00rootroot00000000000000{ "overrides": [ { "files": "*.ts", "parser": "@typescript-eslint/parser", "rules": { "no-unused-vars": "off", "no-useless-constructor": "off", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-useless-constructor": "error" } } ], "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "plugins": [ "@typescript-eslint/eslint-plugin" ] } yargs-parser-yargs-parser-v21.0.0/.github/000077500000000000000000000000001414461571200203615ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/.github/workflows/000077500000000000000000000000001414461571200224165ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/.github/workflows/ci.yaml000066400000000000000000000043121414461571200236750ustar00rootroot00000000000000on: push: branches: - main pull_request: types: [ assigned, opened, synchronize, reopened, labeled ] name: ci jobs: test: runs-on: ubuntu-latest strategy: matrix: node: [12, 14, 16] steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: ${{ matrix.node }} - run: node --version - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm test - run: npm run check windows: runs-on: windows-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 12 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm test coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm test - run: npm run coverage deno: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm run compile - uses: denolib/setup-deno@v2 with: deno-version: v1.x - run: | deno --version deno test --allow-read test/deno/yargs-test.ts browser: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install - run: npm run test:browser typescript: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install - run: npm run test:typescript optimize: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 16 - run: cd test/tscc && npm install && npx @tscc/tscc - run: cd test/tscc && node out.js yargs-parser-yargs-parser-v21.0.0/.github/workflows/release-please.yml000066400000000000000000000032551414461571200260350ustar00rootroot00000000000000on: push: branches: - main name: release-please jobs: release-please: runs-on: ubuntu-latest steps: - uses: google-github-actions/release-please-action@v2 id: release with: token: ${{ secrets.GITHUB_TOKEN }} command: manifest default-branch: main - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 14 - run: npm install env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - run: npm run compile - name: push Deno release run: | git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com git remote add gh-token "https://${{ secrets.GITHUB_TOKEN}}@github.com/yargs/yargs-parser.git" git checkout -b deno git add -f build git commit -a -m 'chore: ${{ steps.release.outputs.tag_name }} release' git push origin +deno git tag -a ${{ steps.release.outputs.tag_name }}-deno -m 'chore: ${{ steps.release.outputs.tag_name }} release' git push origin ${{ steps.release.outputs.tag_name }}-deno if: ${{ steps.release.outputs.releases_created }} - uses: actions/setup-node@v1 with: node-version: 14 registry-url: 'https://external-dot-oss-automation.appspot.com/' if: ${{ steps.release.outputs.releases_created }} - run: npm install if: ${{ steps.release.outputs.releases_created }} - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.NPM_MONOREPO_TOKEN}} if: ${{ steps.release.outputs.releases_created }} yargs-parser-yargs-parser-v21.0.0/.gitignore000066400000000000000000000001611414461571200210070ustar00rootroot00000000000000.idea .nyc_output node_modules .DS_Store package-lock.json ./test/fixtures/package.json coverage build example.* yargs-parser-yargs-parser-v21.0.0/.nycrc000066400000000000000000000002431414461571200201370ustar00rootroot00000000000000{ "exclude": [ "build/test/**", "test/**" ], "reporter": [ "html", "text" ], "lines": 99.5, "branches": "98", "statements": "99.5" } yargs-parser-yargs-parser-v21.0.0/.release-please-manifest.json000066400000000000000000000000161414461571200244620ustar00rootroot00000000000000{".":"21.0.0"}yargs-parser-yargs-parser-v21.0.0/CHANGELOG.md000066400000000000000000000345751414461571200206500ustar00rootroot00000000000000# Changelog 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. ## [21.0.0](https://www.github.com/yargs/yargs-parser/compare/yargs-parser-v20.2.9...yargs-parser-v21.0.0) (2021-11-15) ### ⚠ BREAKING CHANGES * drops support for 10 (#421) ### Bug Fixes * esm json import ([#416](https://www.github.com/yargs/yargs-parser/issues/416)) ([90f970a](https://www.github.com/yargs/yargs-parser/commit/90f970a6482dd4f5b5eb18d38596dd6f02d73edf)) * parser should preserve inner quotes ([#407](https://www.github.com/yargs/yargs-parser/issues/407)) ([ae11f49](https://www.github.com/yargs/yargs-parser/commit/ae11f496a8318ea8885aa25015d429b33713c314)) ### Code Refactoring * drops support for 10 ([#421](https://www.github.com/yargs/yargs-parser/issues/421)) ([3aaf878](https://www.github.com/yargs/yargs-parser/commit/3aaf8784f5c7f2aec6108c1c6a55537fa7e3b5c1)) ### [20.2.9](https://www.github.com/yargs/yargs-parser/compare/yargs-parser-v20.2.8...yargs-parser-v20.2.9) (2021-06-20) ### Bug Fixes * **build:** fixed automated release pipeline ([1fe9135](https://www.github.com/yargs/yargs-parser/commit/1fe9135884790a083615419b2861683e2597dac3)) ### [20.2.8](https://www.github.com/yargs/yargs-parser/compare/yargs-parser-v20.2.7...yargs-parser-v20.2.8) (2021-06-20) ### Bug Fixes * **locale:** Turkish camelize and decamelize issues with toLocaleLowerCase/toLocaleUpperCase ([2617303](https://www.github.com/yargs/yargs-parser/commit/261730383e02448562f737b94bbd1f164aed5143)) * **perf:** address slow parse when using unknown-options-as-args ([#394](https://www.github.com/yargs/yargs-parser/issues/394)) ([441f059](https://www.github.com/yargs/yargs-parser/commit/441f059d585d446551068ad213db79ac91daf83a)) * **string-utils:** detect [0,1] ranged values as numbers ([#388](https://www.github.com/yargs/yargs-parser/issues/388)) ([efcc32c](https://www.github.com/yargs/yargs-parser/commit/efcc32c2d6b09aba31abfa2db9bd947befe5586b)) ### [20.2.7](https://www.github.com/yargs/yargs-parser/compare/v20.2.6...v20.2.7) (2021-03-10) ### Bug Fixes * **deno:** force release for Deno ([6687c97](https://www.github.com/yargs/yargs-parser/commit/6687c972d0f3ca7865a97908dde3080b05f8b026)) ### [20.2.6](https://www.github.com/yargs/yargs-parser/compare/v20.2.5...v20.2.6) (2021-02-22) ### Bug Fixes * **populate--:** -- should always be array ([#354](https://www.github.com/yargs/yargs-parser/issues/354)) ([585ae8f](https://www.github.com/yargs/yargs-parser/commit/585ae8ffad74cc02974f92d788e750137fd65146)) ### [20.2.5](https://www.github.com/yargs/yargs-parser/compare/v20.2.4...v20.2.5) (2021-02-13) ### Bug Fixes * do not lowercase camel cased string ([#348](https://www.github.com/yargs/yargs-parser/issues/348)) ([5f4da1f](https://www.github.com/yargs/yargs-parser/commit/5f4da1f17d9d50542d2aaa206c9806ce3e320335)) ### [20.2.4](https://www.github.com/yargs/yargs-parser/compare/v20.2.3...v20.2.4) (2020-11-09) ### Bug Fixes * **deno:** address import issues in Deno ([#339](https://www.github.com/yargs/yargs-parser/issues/339)) ([3b54e5e](https://www.github.com/yargs/yargs-parser/commit/3b54e5eef6e9a7b7c6eec7c12bab3ba3b8ba8306)) ### [20.2.3](https://www.github.com/yargs/yargs-parser/compare/v20.2.2...v20.2.3) (2020-10-16) ### Bug Fixes * **exports:** node 13.0 and 13.1 require the dotted object form _with_ a string fallback ([#336](https://www.github.com/yargs/yargs-parser/issues/336)) ([3ae7242](https://www.github.com/yargs/yargs-parser/commit/3ae7242040ff876d28dabded60ac226e00150c88)) ### [20.2.2](https://www.github.com/yargs/yargs-parser/compare/v20.2.1...v20.2.2) (2020-10-14) ### Bug Fixes * **exports:** node 13.0-13.6 require a string fallback ([#333](https://www.github.com/yargs/yargs-parser/issues/333)) ([291aeda](https://www.github.com/yargs/yargs-parser/commit/291aeda06b685b7a015d83bdf2558e180b37388d)) ### [20.2.1](https://www.github.com/yargs/yargs-parser/compare/v20.2.0...v20.2.1) (2020-10-01) ### Bug Fixes * **deno:** update types for deno ^1.4.0 ([#330](https://www.github.com/yargs/yargs-parser/issues/330)) ([0ab92e5](https://www.github.com/yargs/yargs-parser/commit/0ab92e50b090f11196334c048c9c92cecaddaf56)) ## [20.2.0](https://www.github.com/yargs/yargs-parser/compare/v20.1.0...v20.2.0) (2020-09-21) ### Features * **string-utils:** export looksLikeNumber helper ([#324](https://www.github.com/yargs/yargs-parser/issues/324)) ([c8580a2](https://www.github.com/yargs/yargs-parser/commit/c8580a2327b55f6342acecb6e72b62963d506750)) ### Bug Fixes * **unknown-options-as-args:** convert positionals that look like numbers ([#326](https://www.github.com/yargs/yargs-parser/issues/326)) ([f85ebb4](https://www.github.com/yargs/yargs-parser/commit/f85ebb4face9d4b0f56147659404cbe0002f3dad)) ## [20.1.0](https://www.github.com/yargs/yargs-parser/compare/v20.0.0...v20.1.0) (2020-09-20) ### Features * adds parse-positional-numbers configuration ([#321](https://www.github.com/yargs/yargs-parser/issues/321)) ([9cec00a](https://www.github.com/yargs/yargs-parser/commit/9cec00a622251292ffb7dce6f78f5353afaa0d4c)) ### Bug Fixes * **build:** update release-please; make labels kick off builds ([#323](https://www.github.com/yargs/yargs-parser/issues/323)) ([09f448b](https://www.github.com/yargs/yargs-parser/commit/09f448b4cd66e25d2872544718df46dab8af062a)) ## [20.0.0](https://www.github.com/yargs/yargs-parser/compare/v19.0.4...v20.0.0) (2020-09-09) ### ⚠ BREAKING CHANGES * do not ship type definitions (#318) ### Bug Fixes * only strip camel case if hyphenated ([#316](https://www.github.com/yargs/yargs-parser/issues/316)) ([95a9e78](https://www.github.com/yargs/yargs-parser/commit/95a9e785127b9bbf2d1db1f1f808ca1fb100e82a)), closes [#315](https://www.github.com/yargs/yargs-parser/issues/315) ### Code Refactoring * do not ship type definitions ([#318](https://www.github.com/yargs/yargs-parser/issues/318)) ([8fbd56f](https://www.github.com/yargs/yargs-parser/commit/8fbd56f1d0b6c44c30fca62708812151ca0ce330)) ### [19.0.4](https://www.github.com/yargs/yargs-parser/compare/v19.0.3...v19.0.4) (2020-08-27) ### Bug Fixes * **build:** fixing publication ([#310](https://www.github.com/yargs/yargs-parser/issues/310)) ([5d3c6c2](https://www.github.com/yargs/yargs-parser/commit/5d3c6c29a9126248ba601920d9cf87c78e161ff5)) ### [19.0.3](https://www.github.com/yargs/yargs-parser/compare/v19.0.2...v19.0.3) (2020-08-27) ### Bug Fixes * **build:** switch to action for publish ([#308](https://www.github.com/yargs/yargs-parser/issues/308)) ([5c2f305](https://www.github.com/yargs/yargs-parser/commit/5c2f30585342bcd8aaf926407c863099d256d174)) ### [19.0.2](https://www.github.com/yargs/yargs-parser/compare/v19.0.1...v19.0.2) (2020-08-27) ### Bug Fixes * **types:** envPrefix should be optional ([#305](https://www.github.com/yargs/yargs-parser/issues/305)) ([ae3f180](https://www.github.com/yargs/yargs-parser/commit/ae3f180e14df2de2fd962145f4518f9aa0e76523)) ### [19.0.1](https://www.github.com/yargs/yargs-parser/compare/v19.0.0...v19.0.1) (2020-08-09) ### Bug Fixes * **build:** push tag created for deno ([2186a14](https://www.github.com/yargs/yargs-parser/commit/2186a14989749887d56189867602e39e6679f8b0)) ## [19.0.0](https://www.github.com/yargs/yargs-parser/compare/v18.1.3...v19.0.0) (2020-08-09) ### ⚠ BREAKING CHANGES * adds support for ESM and Deno (#295) * **ts:** projects using `@types/yargs-parser` may see variations in type definitions. * drops Node 6. begin following Node.js LTS schedule (#278) ### Features * adds support for ESM and Deno ([#295](https://www.github.com/yargs/yargs-parser/issues/295)) ([195bc4a](https://www.github.com/yargs/yargs-parser/commit/195bc4a7f20c2a8f8e33fbb6ba96ef6e9a0120a1)) * expose camelCase and decamelize helpers ([#296](https://www.github.com/yargs/yargs-parser/issues/296)) ([39154ce](https://www.github.com/yargs/yargs-parser/commit/39154ceb5bdcf76b5f59a9219b34cedb79b67f26)) * **deps:** update to latest camelcase/decamelize ([#281](https://www.github.com/yargs/yargs-parser/issues/281)) ([8931ab0](https://www.github.com/yargs/yargs-parser/commit/8931ab08f686cc55286f33a95a83537da2be5516)) ### Bug Fixes * boolean numeric short option ([#294](https://www.github.com/yargs/yargs-parser/issues/294)) ([f600082](https://www.github.com/yargs/yargs-parser/commit/f600082c959e092076caf420bbbc9d7a231e2418)) * raise permission error for Deno if config load fails ([#298](https://www.github.com/yargs/yargs-parser/issues/298)) ([1174e2b](https://www.github.com/yargs/yargs-parser/commit/1174e2b3f0c845a1cd64e14ffc3703e730567a84)) * **deps:** update dependency decamelize to v3 ([#274](https://www.github.com/yargs/yargs-parser/issues/274)) ([4d98698](https://www.github.com/yargs/yargs-parser/commit/4d98698bc6767e84ec54a0842908191739be73b7)) * **types:** switch back to using Partial types ([#293](https://www.github.com/yargs/yargs-parser/issues/293)) ([bdc80ba](https://www.github.com/yargs/yargs-parser/commit/bdc80ba59fa13bc3025ce0a85e8bad9f9da24ea7)) ### Build System * drops Node 6. begin following Node.js LTS schedule ([#278](https://www.github.com/yargs/yargs-parser/issues/278)) ([9014ed7](https://www.github.com/yargs/yargs-parser/commit/9014ed722a32768b96b829e65a31705db5c1458a)) ### Code Refactoring * **ts:** move index.js to TypeScript ([#292](https://www.github.com/yargs/yargs-parser/issues/292)) ([f78d2b9](https://www.github.com/yargs/yargs-parser/commit/f78d2b97567ac4828624406e420b4047c710b789)) ### [18.1.3](https://www.github.com/yargs/yargs-parser/compare/v18.1.2...v18.1.3) (2020-04-16) ### Bug Fixes * **setArg:** options using camel-case and dot-notation populated twice ([#268](https://www.github.com/yargs/yargs-parser/issues/268)) ([f7e15b9](https://www.github.com/yargs/yargs-parser/commit/f7e15b9800900b9856acac1a830a5f35847be73e)) ### [18.1.2](https://www.github.com/yargs/yargs-parser/compare/v18.1.1...v18.1.2) (2020-03-26) ### Bug Fixes * **array, nargs:** support -o=--value and --option=--value format ([#262](https://www.github.com/yargs/yargs-parser/issues/262)) ([41d3f81](https://www.github.com/yargs/yargs-parser/commit/41d3f8139e116706b28de9b0de3433feb08d2f13)) ### [18.1.1](https://www.github.com/yargs/yargs-parser/compare/v18.1.0...v18.1.1) (2020-03-16) ### Bug Fixes * \_\_proto\_\_ will now be replaced with \_\_\_proto\_\_\_ in parse ([#258](https://www.github.com/yargs/yargs-parser/issues/258)), patching a potential prototype pollution vulnerability. This was reported by the Snyk Security Research Team.([63810ca](https://www.github.com/yargs/yargs-parser/commit/63810ca1ae1a24b08293a4d971e70e058c7a41e2)) ## [18.1.0](https://www.github.com/yargs/yargs-parser/compare/v18.0.0...v18.1.0) (2020-03-07) ### Features * introduce single-digit boolean aliases ([#255](https://www.github.com/yargs/yargs-parser/issues/255)) ([9c60265](https://www.github.com/yargs/yargs-parser/commit/9c60265fd7a03cb98e6df3e32c8c5e7508d9f56f)) ## [18.0.0](https://www.github.com/yargs/yargs-parser/compare/v17.1.0...v18.0.0) (2020-03-02) ### ⚠ BREAKING CHANGES * the narg count is now enforced when parsing arrays. ### Features * NaN can now be provided as a value for nargs, indicating "at least" one value is expected for array ([#251](https://www.github.com/yargs/yargs-parser/issues/251)) ([9db4be8](https://www.github.com/yargs/yargs-parser/commit/9db4be81417a2c7097128db34d86fe70ef4af70c)) ## [17.1.0](https://www.github.com/yargs/yargs-parser/compare/v17.0.1...v17.1.0) (2020-03-01) ### Features * introduce greedy-arrays config, for specifying whether arrays consume multiple positionals ([#249](https://www.github.com/yargs/yargs-parser/issues/249)) ([60e880a](https://www.github.com/yargs/yargs-parser/commit/60e880a837046314d89fa4725f923837fd33a9eb)) ### [17.0.1](https://www.github.com/yargs/yargs-parser/compare/v17.0.0...v17.0.1) (2020-02-29) ### Bug Fixes * normalized keys were not enumerable ([#247](https://www.github.com/yargs/yargs-parser/issues/247)) ([57119f9](https://www.github.com/yargs/yargs-parser/commit/57119f9f17cf27499bd95e61c2f72d18314f11ba)) ## [17.0.0](https://www.github.com/yargs/yargs-parser/compare/v16.1.0...v17.0.0) (2020-02-10) ### ⚠ BREAKING CHANGES * this reverts parsing behavior of booleans to that of yargs@14 * objects used during parsing are now created with a null prototype. There may be some scenarios where this change in behavior leaks externally. ### Features * boolean arguments will not be collected into an implicit array ([#236](https://www.github.com/yargs/yargs-parser/issues/236)) ([34c4e19](https://www.github.com/yargs/yargs-parser/commit/34c4e19bae4e7af63e3cb6fa654a97ed476e5eb5)) * introduce nargs-eats-options config option ([#246](https://www.github.com/yargs/yargs-parser/issues/246)) ([d50822a](https://www.github.com/yargs/yargs-parser/commit/d50822ac10e1b05f2e9643671ca131ac251b6732)) ### Bug Fixes * address bugs with "uknown-options-as-args" ([bc023e3](https://www.github.com/yargs/yargs-parser/commit/bc023e3b13e20a118353f9507d1c999bf388a346)) * array should take precedence over nargs, but enforce nargs ([#243](https://www.github.com/yargs/yargs-parser/issues/243)) ([4cbc188](https://www.github.com/yargs/yargs-parser/commit/4cbc188b7abb2249529a19c090338debdad2fe6c)) * support keys that collide with object prototypes ([#234](https://www.github.com/yargs/yargs-parser/issues/234)) ([1587b6d](https://www.github.com/yargs/yargs-parser/commit/1587b6d91db853a9109f1be6b209077993fee4de)) * unknown options terminated with digits now handled by unknown-options-as-args ([#238](https://www.github.com/yargs/yargs-parser/issues/238)) ([d36cdfa](https://www.github.com/yargs/yargs-parser/commit/d36cdfa854254d7c7e0fe1d583818332ac46c2a5)) ## [16.1.0](https://www.github.com/yargs/yargs-parser/compare/v16.0.0...v16.1.0) (2019-11-01) ### ⚠ BREAKING CHANGES * populate error if incompatible narg/count or array/count options are used (#191) ### Features * options that have had their default value used are now tracked ([#211](https://www.github.com/yargs/yargs-parser/issues/211)) ([a525234](https://www.github.com/yargs/yargs-parser/commit/a525234558c847deedd73f8792e0a3b77b26e2c0)) * populate error if incompatible narg/count or array/count options are used ([#191](https://www.github.com/yargs/yargs-parser/issues/191)) ([84a401f](https://www.github.com/yargs/yargs-parser/commit/84a401f0fa3095e0a19661670d1570d0c3b9d3c9)) ### Reverts * revert 16.0.0 CHANGELOG entry ([920320a](https://www.github.com/yargs/yargs-parser/commit/920320ad9861bbfd58eda39221ae211540fc1daf)) yargs-parser-yargs-parser-v21.0.0/LICENSE.txt000066400000000000000000000013331414461571200206440ustar00rootroot00000000000000Copyright (c) 2016, Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. yargs-parser-yargs-parser-v21.0.0/README.md000066400000000000000000000272141414461571200203060ustar00rootroot00000000000000# yargs-parser ![ci](https://github.com/yargs/yargs-parser/workflows/ci/badge.svg) [![NPM version](https://img.shields.io/npm/v/yargs-parser.svg)](https://www.npmjs.com/package/yargs-parser) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) ![nycrc config on GitHub](https://img.shields.io/nycrc/yargs/yargs-parser) The mighty option parser used by [yargs](https://github.com/yargs/yargs). visit the [yargs website](http://yargs.js.org/) for more examples, and thorough usage instructions. ## Example ```sh npm i yargs-parser --save ``` ```js const argv = require('yargs-parser')(process.argv.slice(2)) console.log(argv) ``` ```console $ node example.js --foo=33 --bar hello { _: [], foo: 33, bar: 'hello' } ``` _or parse a string!_ ```js const argv = require('yargs-parser')('--foo=99 --bar=33') console.log(argv) ``` ```console { _: [], foo: 99, bar: 33 } ``` Convert an array of mixed types before passing to `yargs-parser`: ```js const parse = require('yargs-parser') parse(['-f', 11, '--zoom', 55].join(' ')) // <-- array to string parse(['-f', 11, '--zoom', 55].map(String)) // <-- array of strings ``` ## Deno Example As of `v19` `yargs-parser` supports [Deno](https://github.com/denoland/deno): ```typescript import parser from "https://deno.land/x/yargs_parser/deno.ts"; const argv = parser('--foo=99 --bar=9987930', { string: ['bar'] }) console.log(argv) ``` ## ESM Example As of `v19` `yargs-parser` supports ESM (_both in Node.js and in the browser_): **Node.js:** ```js import parser from 'yargs-parser' const argv = parser('--foo=99 --bar=9987930', { string: ['bar'] }) console.log(argv) ``` **Browsers:** ```html ``` ## API ### parser(args, opts={}) Parses command line arguments returning a simple mapping of keys and values. **expects:** * `args`: a string or array of strings representing the options to parse. * `opts`: provide a set of hints indicating how `args` should be parsed: * `opts.alias`: an object representing the set of aliases for a key: `{alias: {foo: ['f']}}`. * `opts.array`: indicate that keys should be parsed as an array: `{array: ['foo', 'bar']}`.
Indicate that keys should be parsed as an array and coerced to booleans / numbers:
`{array: [{ key: 'foo', boolean: true }, {key: 'bar', number: true}]}`. * `opts.boolean`: arguments should be parsed as booleans: `{boolean: ['x', 'y']}`. * `opts.coerce`: provide a custom synchronous function that returns a coerced value from the argument provided (or throws an error). For arrays the function is called only once for the entire array:
`{coerce: {foo: function (arg) {return modifiedArg}}}`. * `opts.config`: indicate a key that represents a path to a configuration file (this file will be loaded and parsed). * `opts.configObjects`: configuration objects to parse, their properties will be set as arguments:
`{configObjects: [{'x': 5, 'y': 33}, {'z': 44}]}`. * `opts.configuration`: provide configuration options to the yargs-parser (see: [configuration](#configuration)). * `opts.count`: indicate a key that should be used as a counter, e.g., `-vvv` = `{v: 3}`. * `opts.default`: provide default values for keys: `{default: {x: 33, y: 'hello world!'}}`. * `opts.envPrefix`: environment variables (`process.env`) with the prefix provided should be parsed. * `opts.narg`: specify that a key requires `n` arguments: `{narg: {x: 2}}`. * `opts.normalize`: `path.normalize()` will be applied to values set to this key. * `opts.number`: keys should be treated as numbers. * `opts.string`: keys should be treated as strings (even if they resemble a number `-x 33`). **returns:** * `obj`: an object representing the parsed value of `args` * `key/value`: key value pairs for each argument and their aliases. * `_`: an array representing the positional arguments. * [optional] `--`: an array with arguments after the end-of-options flag `--`. ### require('yargs-parser').detailed(args, opts={}) Parses a command line string, returning detailed information required by the yargs engine. **expects:** * `args`: a string or array of strings representing options to parse. * `opts`: provide a set of hints indicating how `args`, inputs are identical to `require('yargs-parser')(args, opts={})`. **returns:** * `argv`: an object representing the parsed value of `args` * `key/value`: key value pairs for each argument and their aliases. * `_`: an array representing the positional arguments. * [optional] `--`: an array with arguments after the end-of-options flag `--`. * `error`: populated with an error object if an exception occurred during parsing. * `aliases`: the inferred list of aliases built by combining lists in `opts.alias`. * `newAliases`: any new aliases added via camel-case expansion: * `boolean`: `{ fooBar: true }` * `defaulted`: any new argument created by `opts.default`, no aliases included. * `boolean`: `{ foo: true }` * `configuration`: given by default settings and `opts.configuration`. ### Configuration The yargs-parser applies several automated transformations on the keys provided in `args`. These features can be turned on and off using the `configuration` field of `opts`. ```js var parsed = parser(['--no-dice'], { configuration: { 'boolean-negation': false } }) ``` ### short option groups * default: `true`. * key: `short-option-groups`. Should a group of short-options be treated as boolean flags? ```console $ node example.js -abc { _: [], a: true, b: true, c: true } ``` _if disabled:_ ```console $ node example.js -abc { _: [], abc: true } ``` ### camel-case expansion * default: `true`. * key: `camel-case-expansion`. Should hyphenated arguments be expanded into camel-case aliases? ```console $ node example.js --foo-bar { _: [], 'foo-bar': true, fooBar: true } ``` _if disabled:_ ```console $ node example.js --foo-bar { _: [], 'foo-bar': true } ``` ### dot-notation * default: `true` * key: `dot-notation` Should keys that contain `.` be treated as objects? ```console $ node example.js --foo.bar { _: [], foo: { bar: true } } ``` _if disabled:_ ```console $ node example.js --foo.bar { _: [], "foo.bar": true } ``` ### parse numbers * default: `true` * key: `parse-numbers` Should keys that look like numbers be treated as such? ```console $ node example.js --foo=99.3 { _: [], foo: 99.3 } ``` _if disabled:_ ```console $ node example.js --foo=99.3 { _: [], foo: "99.3" } ``` ### parse positional numbers * default: `true` * key: `parse-positional-numbers` Should positional keys that look like numbers be treated as such. ```console $ node example.js 99.3 { _: [99.3] } ``` _if disabled:_ ```console $ node example.js 99.3 { _: ['99.3'] } ``` ### boolean negation * default: `true` * key: `boolean-negation` Should variables prefixed with `--no` be treated as negations? ```console $ node example.js --no-foo { _: [], foo: false } ``` _if disabled:_ ```console $ node example.js --no-foo { _: [], "no-foo": true } ``` ### combine arrays * default: `false` * key: `combine-arrays` Should arrays be combined when provided by both command line arguments and a configuration file. ### duplicate arguments array * default: `true` * key: `duplicate-arguments-array` Should arguments be coerced into an array when duplicated: ```console $ node example.js -x 1 -x 2 { _: [], x: [1, 2] } ``` _if disabled:_ ```console $ node example.js -x 1 -x 2 { _: [], x: 2 } ``` ### flatten duplicate arrays * default: `true` * key: `flatten-duplicate-arrays` Should array arguments be coerced into a single array when duplicated: ```console $ node example.js -x 1 2 -x 3 4 { _: [], x: [1, 2, 3, 4] } ``` _if disabled:_ ```console $ node example.js -x 1 2 -x 3 4 { _: [], x: [[1, 2], [3, 4]] } ``` ### greedy arrays * default: `true` * key: `greedy-arrays` Should arrays consume more than one positional argument following their flag. ```console $ node example --arr 1 2 { _: [], arr: [1, 2] } ``` _if disabled:_ ```console $ node example --arr 1 2 { _: [2], arr: [1] } ``` **Note: in `v18.0.0` we are considering defaulting greedy arrays to `false`.** ### nargs eats options * default: `false` * key: `nargs-eats-options` Should nargs consume dash options as well as positional arguments. ### negation prefix * default: `no-` * key: `negation-prefix` The prefix to use for negated boolean variables. ```console $ node example.js --no-foo { _: [], foo: false } ``` _if set to `quux`:_ ```console $ node example.js --quuxfoo { _: [], foo: false } ``` ### populate -- * default: `false`. * key: `populate--` Should unparsed flags be stored in `--` or `_`. _If disabled:_ ```console $ node example.js a -b -- x y { _: [ 'a', 'x', 'y' ], b: true } ``` _If enabled:_ ```console $ node example.js a -b -- x y { _: [ 'a' ], '--': [ 'x', 'y' ], b: true } ``` ### set placeholder key * default: `false`. * key: `set-placeholder-key`. Should a placeholder be added for keys not set via the corresponding CLI argument? _If disabled:_ ```console $ node example.js -a 1 -c 2 { _: [], a: 1, c: 2 } ``` _If enabled:_ ```console $ node example.js -a 1 -c 2 { _: [], a: 1, b: undefined, c: 2 } ``` ### halt at non-option * default: `false`. * key: `halt-at-non-option`. Should parsing stop at the first positional argument? This is similar to how e.g. `ssh` parses its command line. _If disabled:_ ```console $ node example.js -a run b -x y { _: [ 'b' ], a: 'run', x: 'y' } ``` _If enabled:_ ```console $ node example.js -a run b -x y { _: [ 'b', '-x', 'y' ], a: 'run' } ``` ### strip aliased * default: `false` * key: `strip-aliased` Should aliases be removed before returning results? _If disabled:_ ```console $ node example.js --test-field 1 { _: [], 'test-field': 1, testField: 1, 'test-alias': 1, testAlias: 1 } ``` _If enabled:_ ```console $ node example.js --test-field 1 { _: [], 'test-field': 1, testField: 1 } ``` ### strip dashed * default: `false` * key: `strip-dashed` Should dashed keys be removed before returning results? This option has no effect if `camel-case-expansion` is disabled. _If disabled:_ ```console $ node example.js --test-field 1 { _: [], 'test-field': 1, testField: 1 } ``` _If enabled:_ ```console $ node example.js --test-field 1 { _: [], testField: 1 } ``` ### unknown options as args * default: `false` * key: `unknown-options-as-args` Should unknown options be treated like regular arguments? An unknown option is one that is not configured in `opts`. _If disabled_ ```console $ node example.js --unknown-option --known-option 2 --string-option --unknown-option2 { _: [], unknownOption: true, knownOption: 2, stringOption: '', unknownOption2: true } ``` _If enabled_ ```console $ node example.js --unknown-option --known-option 2 --string-option --unknown-option2 { _: ['--unknown-option'], knownOption: 2, stringOption: '--unknown-option2' } ``` ## Supported Node.js Versions Libraries in this ecosystem make a best effort to track [Node.js' release schedule](https://nodejs.org/en/about/releases/). Here's [a post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a). ## Special Thanks The yargs project evolves from optimist and minimist. It owes its existence to a lot of James Halliday's hard work. Thanks [substack](https://github.com/substack) **beep** **boop** \o/ ## License ISC yargs-parser-yargs-parser-v21.0.0/browser.js000066400000000000000000000017701414461571200210470ustar00rootroot00000000000000// Main entrypoint for ESM web browser environments. Avoids using Node.js // specific libraries, such as "path". // // TODO: figure out reasonable web equivalents for "resolve", "normalize", etc. import { camelCase, decamelize, looksLikeNumber } from './build/lib/string-utils.js' import { YargsParser } from './build/lib/yargs-parser.js' const parser = new YargsParser({ cwd: () => { return '' }, format: (str, arg) => { return str.replace('%s', arg) }, normalize: (str) => { return str }, resolve: (str) => { return str }, require: () => { throw Error('loading config from files not currently supported in browser') }, env: () => {} }) const yargsParser = function Parser (args, opts) { const result = parser.parse(args.slice(), opts) return result.argv } yargsParser.detailed = function (args, opts) { return parser.parse(args.slice(), opts) } yargsParser.camelCase = camelCase yargsParser.decamelize = decamelize yargsParser.looksLikeNumber = looksLikeNumber export default yargsParser yargs-parser-yargs-parser-v21.0.0/deno.ts000066400000000000000000000024601414461571200203200ustar00rootroot00000000000000/* global Deno */ // Main entrypoint for Deno. // // TODO: find reasonable replacement for require logic. import * as path from 'https://deno.land/std/path/mod.ts' import { camelCase, decamelize, looksLikeNumber } from './build/lib/string-utils.js' import { YargsParser } from './build/lib/yargs-parser.js' import type { Arguments, ArgsInput, Parser, Options, DetailedArguments } from './build/lib/yargs-parser-types.d.ts' const parser = new YargsParser({ cwd: Deno.cwd, env: () => { Deno.env.toObject() }, format: (str: string, arg: string) => { return str.replace('%s', arg) }, normalize: path.posix.normalize, resolve: path.posix.resolve, require: (path: string) => { if (!path.match(/\.json$/)) { throw Error('only .json config files are supported in Deno') } else { return JSON.parse(Deno.readTextFileSync(path)) } } }) const yargsParser: Parser = function Parser (args: ArgsInput, opts?: Partial): Arguments { const result = parser.parse(args.slice(), opts) return result.argv } yargsParser.detailed = function (args: ArgsInput, opts?: Partial): DetailedArguments { return parser.parse(args.slice(), opts) } yargsParser.camelCase = camelCase yargsParser.decamelize = decamelize yargsParser.looksLikeNumber = looksLikeNumber export default yargsParser yargs-parser-yargs-parser-v21.0.0/docs/000077500000000000000000000000001414461571200177515ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/docs/CHANGELOG-full.md000066400000000000000000000461571414461571200225370ustar00rootroot00000000000000## [15.0.0](https://github.com/yargs/yargs-parser/compare/v14.0.0...v15.0.0) (2019-10-07) ### Features * rework `collect-unknown-options` into `unknown-options-as-args`, providing more comprehensive functionality ([ef771ca](https://github.com/yargs/yargs-parser/commit/ef771ca)) ### BREAKING CHANGES * rework `collect-unknown-options` into `unknown-options-as-args`, providing more comprehensive functionality ## [14.0.0](https://github.com/yargs/yargs-parser/compare/v13.1.1...v14.0.0) (2019-09-06) ### Bug Fixes * boolean arrays with default values ([#185](https://github.com/yargs/yargs-parser/issues/185)) ([7d42572](https://github.com/yargs/yargs-parser/commit/7d42572)) * boolean now behaves the same as other array types ([#184](https://github.com/yargs/yargs-parser/issues/184)) ([17ca3bd](https://github.com/yargs/yargs-parser/commit/17ca3bd)) * eatNargs() for 'opt.narg === 0' and boolean typed options ([#188](https://github.com/yargs/yargs-parser/issues/188)) ([c5a1db0](https://github.com/yargs/yargs-parser/commit/c5a1db0)) * maybeCoerceNumber now takes precedence over coerce return value ([#182](https://github.com/yargs/yargs-parser/issues/182)) ([2f26436](https://github.com/yargs/yargs-parser/commit/2f26436)) * take into account aliases when appending arrays from config object ([#199](https://github.com/yargs/yargs-parser/issues/199)) ([f8a2d3f](https://github.com/yargs/yargs-parser/commit/f8a2d3f)) ### Features * add configuration option to "collect-unknown-options" ([#181](https://github.com/yargs/yargs-parser/issues/181)) ([7909cc4](https://github.com/yargs/yargs-parser/commit/7909cc4)) * maybeCoerceNumber() now takes into account arrays ([#187](https://github.com/yargs/yargs-parser/issues/187)) ([31c204b](https://github.com/yargs/yargs-parser/commit/31c204b)) ### BREAKING CHANGES * unless "parse-numbers" is set to "false", arrays of numeric strings are now parsed as numbers, rather than strings. * we have dropped the broken "defaulted" functionality; we would like to revisit adding this in the future. * maybeCoerceNumber now takes precedence over coerce return value (#182) ### [13.1.1](https://www.github.com/yargs/yargs-parser/compare/v13.1.0...v13.1.1) (2019-06-10) ### Bug Fixes * convert values to strings when tokenizing ([#167](https://www.github.com/yargs/yargs-parser/issues/167)) ([57b7883](https://www.github.com/yargs/yargs-parser/commit/57b7883)) * nargs should allow duplicates when duplicate-arguments-array=false ([#164](https://www.github.com/yargs/yargs-parser/issues/164)) ([47ccb0b](https://www.github.com/yargs/yargs-parser/commit/47ccb0b)) * should populate "_" when given config with "short-option-groups" false ([#179](https://www.github.com/yargs/yargs-parser/issues/179)) ([6055974](https://www.github.com/yargs/yargs-parser/commit/6055974)) ## [13.1.0](https://github.com/yargs/yargs-parser/compare/v13.0.0...v13.1.0) (2019-05-05) ### Features * add `strip-aliased` and `strip-dashed` configuration options. ([#172](https://github.com/yargs/yargs-parser/issues/172)) ([a3936aa](https://github.com/yargs/yargs-parser/commit/a3936aa)) * support boolean which do not consume next argument. ([#171](https://github.com/yargs/yargs-parser/issues/171)) ([0ae7fcb](https://github.com/yargs/yargs-parser/commit/0ae7fcb)) # [13.0.0](https://github.com/yargs/yargs-parser/compare/v12.0.0...v13.0.0) (2019-02-02) ### Features * don't coerce number from string with leading '0' or '+' ([#158](https://github.com/yargs/yargs-parser/issues/158)) ([18d0fd5](https://github.com/yargs/yargs-parser/commit/18d0fd5)) ### BREAKING CHANGES * options with leading '+' or '0' now parse as strings # [12.0.0](https://github.com/yargs/yargs-parser/compare/v11.1.1...v12.0.0) (2019-01-29) ### Bug Fixes * better handling of quoted strings ([#153](https://github.com/yargs/yargs-parser/issues/153)) ([2fb71b2](https://github.com/yargs/yargs-parser/commit/2fb71b2)) ### Features * default value is now used if no right-hand value provided for numbers/strings ([#156](https://github.com/yargs/yargs-parser/issues/156)) ([5a7c46a](https://github.com/yargs/yargs-parser/commit/5a7c46a)) ### BREAKING CHANGES * a flag with no right-hand value no longer populates defaulted options with `undefined`. * quotes at beginning and endings of strings are not removed during parsing. ## [11.1.1](https://github.com/yargs/yargs-parser/compare/v11.1.0...v11.1.1) (2018-11-19) ### Bug Fixes * ensure empty string is added into argv._ ([#140](https://github.com/yargs/yargs-parser/issues/140)) ([79cda98](https://github.com/yargs/yargs-parser/commit/79cda98)) ### Reverts * make requiresArg work in conjunction with arrays ([#136](https://github.com/yargs/yargs-parser/issues/136)) ([f4a3063](https://github.com/yargs/yargs-parser/commit/f4a3063)) # [11.1.0](https://github.com/yargs/yargs-parser/compare/v11.0.0...v11.1.0) (2018-11-10) ### Bug Fixes * handling of one char alias ([#139](https://github.com/yargs/yargs-parser/issues/139)) ([ee56e31](https://github.com/yargs/yargs-parser/commit/ee56e31)) ### Features * add halt-at-non-option configuration option ([#130](https://github.com/yargs/yargs-parser/issues/130)) ([a849fce](https://github.com/yargs/yargs-parser/commit/a849fce)) # [11.0.0](https://github.com/yargs/yargs-parser/compare/v10.1.0...v11.0.0) (2018-10-06) ### Bug Fixes * flatten-duplicate-arrays:false for more than 2 arrays ([#128](https://github.com/yargs/yargs-parser/issues/128)) ([2bc395f](https://github.com/yargs/yargs-parser/commit/2bc395f)) * hyphenated flags combined with dot notation broke parsing ([#131](https://github.com/yargs/yargs-parser/issues/131)) ([dc788da](https://github.com/yargs/yargs-parser/commit/dc788da)) * make requiresArg work in conjunction with arrays ([#136](https://github.com/yargs/yargs-parser/issues/136)) ([77ae1d4](https://github.com/yargs/yargs-parser/commit/77ae1d4)) ### Chores * update dependencies ([6dc42a1](https://github.com/yargs/yargs-parser/commit/6dc42a1)) ### Features * also add camelCase array options ([#125](https://github.com/yargs/yargs-parser/issues/125)) ([08c0117](https://github.com/yargs/yargs-parser/commit/08c0117)) * array.type can now be provided, supporting coercion ([#132](https://github.com/yargs/yargs-parser/issues/132)) ([4b8cfce](https://github.com/yargs/yargs-parser/commit/4b8cfce)) ### BREAKING CHANGES * drops Node 4 support * the argv object is now populated differently (correctly) when hyphens and dot notation are used in conjunction. # [10.1.0](https://github.com/yargs/yargs-parser/compare/v10.0.0...v10.1.0) (2018-06-29) ### Features * add `set-placeholder-key` configuration ([#123](https://github.com/yargs/yargs-parser/issues/123)) ([19386ee](https://github.com/yargs/yargs-parser/commit/19386ee)) # [10.0.0](https://github.com/yargs/yargs-parser/compare/v9.0.2...v10.0.0) (2018-04-04) ### Bug Fixes * do not set boolean flags if not defined in `argv` ([#119](https://github.com/yargs/yargs-parser/issues/119)) ([f6e6599](https://github.com/yargs/yargs-parser/commit/f6e6599)) ### BREAKING CHANGES * `boolean` flags defined without a `default` value will now behave like other option type and won't be set in the parsed results when the user doesn't set the corresponding CLI arg. Previous behavior: ```js var parse = require('yargs-parser'); parse('--flag', {boolean: ['flag']}); // => { _: [], flag: true } parse('--no-flag', {boolean: ['flag']}); // => { _: [], flag: false } parse('', {boolean: ['flag']}); // => { _: [], flag: false } ``` New behavior: ```js var parse = require('yargs-parser'); parse('--flag', {boolean: ['flag']}); // => { _: [], flag: true } parse('--no-flag', {boolean: ['flag']}); // => { _: [], flag: false } parse('', {boolean: ['flag']}); // => { _: [] } => flag not set similarly to other option type ``` ## [9.0.2](https://github.com/yargs/yargs-parser/compare/v9.0.1...v9.0.2) (2018-01-20) ### Bug Fixes * nargs was still aggressively consuming too many arguments ([9b28aad](https://github.com/yargs/yargs-parser/commit/9b28aad)) ## [9.0.1](https://github.com/yargs/yargs-parser/compare/v9.0.0...v9.0.1) (2018-01-20) ### Bug Fixes * nargs was consuming too many arguments ([4fef206](https://github.com/yargs/yargs-parser/commit/4fef206)) # [9.0.0](https://github.com/yargs/yargs-parser/compare/v8.1.0...v9.0.0) (2018-01-20) ### Features * narg arguments no longer consume flag arguments ([#114](https://github.com/yargs/yargs-parser/issues/114)) ([60bb9b3](https://github.com/yargs/yargs-parser/commit/60bb9b3)) ### BREAKING CHANGES * arguments of form --foo, -abc, will no longer be consumed by nargs # [8.1.0](https://github.com/yargs/yargs-parser/compare/v8.0.0...v8.1.0) (2017-12-20) ### Bug Fixes * allow null config values ([#108](https://github.com/yargs/yargs-parser/issues/108)) ([d8b14f9](https://github.com/yargs/yargs-parser/commit/d8b14f9)) * ensure consistent parsing of dot-notation arguments ([#102](https://github.com/yargs/yargs-parser/issues/102)) ([c9bd79c](https://github.com/yargs/yargs-parser/commit/c9bd79c)) * implement [@antoniom](https://github.com/antoniom)'s fix for camel-case expansion ([3087e1d](https://github.com/yargs/yargs-parser/commit/3087e1d)) * only run coercion functions once, despite aliases. ([#76](https://github.com/yargs/yargs-parser/issues/76)) ([#103](https://github.com/yargs/yargs-parser/issues/103)) ([507aaef](https://github.com/yargs/yargs-parser/commit/507aaef)) * scientific notation circumvented bounds check ([#110](https://github.com/yargs/yargs-parser/issues/110)) ([3571f57](https://github.com/yargs/yargs-parser/commit/3571f57)) * tokenizer should ignore spaces at the beginning of the argString ([#106](https://github.com/yargs/yargs-parser/issues/106)) ([f34ead9](https://github.com/yargs/yargs-parser/commit/f34ead9)) ### Features * make combining arrays a configurable option ([#111](https://github.com/yargs/yargs-parser/issues/111)) ([c8bf536](https://github.com/yargs/yargs-parser/commit/c8bf536)) * merge array from arguments with array from config ([#83](https://github.com/yargs/yargs-parser/issues/83)) ([806ddd6](https://github.com/yargs/yargs-parser/commit/806ddd6)) # [8.0.0](https://github.com/yargs/yargs-parser/compare/v7.0.0...v8.0.0) (2017-10-05) ### Bug Fixes * Ignore multiple spaces between arguments. ([#100](https://github.com/yargs/yargs-parser/issues/100)) ([d137227](https://github.com/yargs/yargs-parser/commit/d137227)) ### Features * allow configuration of prefix for boolean negation ([#94](https://github.com/yargs/yargs-parser/issues/94)) ([00bde7d](https://github.com/yargs/yargs-parser/commit/00bde7d)) * reworking how numbers are parsed ([#104](https://github.com/yargs/yargs-parser/issues/104)) ([fba00eb](https://github.com/yargs/yargs-parser/commit/fba00eb)) ### BREAKING CHANGES * strings that fail `Number.isSafeInteger()` are no longer coerced into numbers. # [7.0.0](https://github.com/yargs/yargs-parser/compare/v6.0.1...v7.0.0) (2017-05-02) ### Chores * revert populate-- logic ([#91](https://github.com/yargs/yargs-parser/issues/91)) ([6003e6d](https://github.com/yargs/yargs-parser/commit/6003e6d)) ### BREAKING CHANGES * populate-- now defaults to false. ## [6.0.1](https://github.com/yargs/yargs-parser/compare/v6.0.0...v6.0.1) (2017-05-01) ### Bug Fixes * default '--' to undefined when not provided; this is closer to the array API ([#90](https://github.com/yargs/yargs-parser/issues/90)) ([4e739cc](https://github.com/yargs/yargs-parser/commit/4e739cc)) # [6.0.0](https://github.com/yargs/yargs-parser/compare/v4.2.1...v6.0.0) (2017-05-01) ### Bug Fixes * environment variables should take precedence over config file ([#81](https://github.com/yargs/yargs-parser/issues/81)) ([76cee1f](https://github.com/yargs/yargs-parser/commit/76cee1f)) * parsing hints should apply for dot notation keys ([#86](https://github.com/yargs/yargs-parser/issues/86)) ([3e47d62](https://github.com/yargs/yargs-parser/commit/3e47d62)) ### Chores * upgrade to newest version of camelcase ([#87](https://github.com/yargs/yargs-parser/issues/87)) ([f1903aa](https://github.com/yargs/yargs-parser/commit/f1903aa)) ### Features * add -- option which allows arguments after the -- flag to be returned separated from positional arguments ([#84](https://github.com/yargs/yargs-parser/issues/84)) ([2572ca8](https://github.com/yargs/yargs-parser/commit/2572ca8)) * when parsing stops, we now populate "--" by default ([#88](https://github.com/yargs/yargs-parser/issues/88)) ([cd666db](https://github.com/yargs/yargs-parser/commit/cd666db)) ### BREAKING CHANGES * rather than placing arguments in "_", when parsing is stopped via "--"; we now populate an array called "--" by default. * camelcase now requires Node 4+. * environment variables will now override config files (args, env, config-file, config-object) # [5.0.0](https://github.com/yargs/yargs-parser/compare/v4.2.1...v5.0.0) (2017-02-18) ### Bug Fixes * environment variables should take precedence over config file ([#81](https://github.com/yargs/yargs-parser/issues/81)) ([76cee1f](https://github.com/yargs/yargs-parser/commit/76cee1f)) ### BREAKING CHANGES * environment variables will now override config files (args, env, config-file, config-object) ## [4.2.1](https://github.com/yargs/yargs-parser/compare/v4.2.0...v4.2.1) (2017-01-02) ### Bug Fixes * flatten/duplicate regression ([#75](https://github.com/yargs/yargs-parser/issues/75)) ([68d68a0](https://github.com/yargs/yargs-parser/commit/68d68a0)) # [4.2.0](https://github.com/yargs/yargs-parser/compare/v4.1.0...v4.2.0) (2016-12-01) ### Bug Fixes * inner objects in configs had their keys appended to top-level key when dot-notation was disabled ([#72](https://github.com/yargs/yargs-parser/issues/72)) ([0b1b5f9](https://github.com/yargs/yargs-parser/commit/0b1b5f9)) ### Features * allow multiple arrays to be provided, rather than always combining ([#71](https://github.com/yargs/yargs-parser/issues/71)) ([0f0fb2d](https://github.com/yargs/yargs-parser/commit/0f0fb2d)) # [4.1.0](https://github.com/yargs/yargs-parser/compare/v4.0.2...v4.1.0) (2016-11-07) ### Features * apply coercions to default options ([#65](https://github.com/yargs/yargs-parser/issues/65)) ([c79052b](https://github.com/yargs/yargs-parser/commit/c79052b)) * handle dot notation boolean options ([#63](https://github.com/yargs/yargs-parser/issues/63)) ([02c3545](https://github.com/yargs/yargs-parser/commit/02c3545)) ## [4.0.2](https://github.com/yargs/yargs-parser/compare/v4.0.1...v4.0.2) (2016-09-30) ### Bug Fixes * whoops, let's make the assign not change the Object key order ([29d069a](https://github.com/yargs/yargs-parser/commit/29d069a)) ## [4.0.1](https://github.com/yargs/yargs-parser/compare/v4.0.0...v4.0.1) (2016-09-30) ### Bug Fixes * lodash.assign was deprecated ([#59](https://github.com/yargs/yargs-parser/issues/59)) ([5e7eb11](https://github.com/yargs/yargs-parser/commit/5e7eb11)) # [4.0.0](https://github.com/yargs/yargs-parser/compare/v3.2.0...v4.0.0) (2016-09-26) ### Bug Fixes * coerce should be applied to the final objects and arrays created ([#57](https://github.com/yargs/yargs-parser/issues/57)) ([4ca69da](https://github.com/yargs/yargs-parser/commit/4ca69da)) ### BREAKING CHANGES * coerce is no longer applied to individual arguments in an implicit array. # [3.2.0](https://github.com/yargs/yargs-parser/compare/v3.1.0...v3.2.0) (2016-08-13) ### Features * coerce full array instead of each element ([#51](https://github.com/yargs/yargs-parser/issues/51)) ([cc4dc56](https://github.com/yargs/yargs-parser/commit/cc4dc56)) # [3.1.0](https://github.com/yargs/yargs-parser/compare/v3.0.0...v3.1.0) (2016-08-09) ### Bug Fixes * address pkgConf parsing bug outlined in [#37](https://github.com/yargs/yargs-parser/issues/37) ([#45](https://github.com/yargs/yargs-parser/issues/45)) ([be76ee6](https://github.com/yargs/yargs-parser/commit/be76ee6)) * better parsing of negative values ([#44](https://github.com/yargs/yargs-parser/issues/44)) ([2e43692](https://github.com/yargs/yargs-parser/commit/2e43692)) * check aliases when guessing defaults for arguments fixes [#41](https://github.com/yargs/yargs-parser/issues/41) ([#43](https://github.com/yargs/yargs-parser/issues/43)) ([f3e4616](https://github.com/yargs/yargs-parser/commit/f3e4616)) ### Features * added coerce option, for providing specialized argument parsing ([#42](https://github.com/yargs/yargs-parser/issues/42)) ([7b49cd2](https://github.com/yargs/yargs-parser/commit/7b49cd2)) # [3.0.0](https://github.com/yargs/yargs-parser/compare/v2.4.1...v3.0.0) (2016-08-07) ### Bug Fixes * parsing issue with numeric character in group of options ([#19](https://github.com/yargs/yargs-parser/issues/19)) ([f743236](https://github.com/yargs/yargs-parser/commit/f743236)) * upgraded lodash.assign ([5d7fdf4](https://github.com/yargs/yargs-parser/commit/5d7fdf4)) ### BREAKING CHANGES * subtle change to how values are parsed in a group of single-character arguments. * _first released in 3.1.0, better handling of negative values should be considered a breaking change._ ## [2.4.1](https://github.com/yargs/yargs-parser/compare/v2.4.0...v2.4.1) (2016-07-16) ### Bug Fixes * **count:** do not increment a default value ([#39](https://github.com/yargs/yargs-parser/issues/39)) ([b04a189](https://github.com/yargs/yargs-parser/commit/b04a189)) # [2.4.0](https://github.com/yargs/yargs-parser/compare/v2.3.0...v2.4.0) (2016-04-11) ### Features * **environment:** Support nested options in environment variables ([#26](https://github.com/yargs/yargs-parser/issues/26)) thanks [@elas7](https://github.com/elas7) \o/ ([020778b](https://github.com/yargs/yargs-parser/commit/020778b)) # [2.3.0](https://github.com/yargs/yargs-parser/compare/v2.2.0...v2.3.0) (2016-04-09) ### Bug Fixes * **boolean:** fix for boolean options with non boolean defaults (#20) ([2dbe86b](https://github.com/yargs/yargs-parser/commit/2dbe86b)), closes [(#20](https://github.com/(/issues/20) * **package:** remove tests from tarball ([0353c0d](https://github.com/yargs/yargs-parser/commit/0353c0d)) * **parsing:** handle calling short option with an empty string as the next value. ([a867165](https://github.com/yargs/yargs-parser/commit/a867165)) * boolean flag when next value contains the strings 'true' or 'false'. ([69941a6](https://github.com/yargs/yargs-parser/commit/69941a6)) * update dependencies; add standard-version bin for next release (#24) ([822d9d5](https://github.com/yargs/yargs-parser/commit/822d9d5)) ### Features * **configuration:** Allow to pass configuration objects to yargs-parser ([0780900](https://github.com/yargs/yargs-parser/commit/0780900)) * **normalize:** allow normalize to work with arrays ([e0eaa1a](https://github.com/yargs/yargs-parser/commit/e0eaa1a)) yargs-parser-yargs-parser-v21.0.0/lib/000077500000000000000000000000001414461571200175675ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/lib/index.ts000066400000000000000000000044251414461571200212530ustar00rootroot00000000000000/** * @fileoverview Main entrypoint for libraries using yargs-parser in Node.js * CJS and ESM environments. * * @license * Copyright (c) 2016, Contributors * SPDX-License-Identifier: ISC */ import { format } from 'util' import { normalize, resolve } from 'path' import { ArgsInput, Arguments, Parser, Options, DetailedArguments } from './yargs-parser-types.js' import { camelCase, decamelize, looksLikeNumber } from './string-utils.js' import { YargsParser } from './yargs-parser.js' import { readFileSync } from 'fs' // See https://github.com/yargs/yargs-parser#supported-nodejs-versions for our // version support policy. The YARGS_MIN_NODE_VERSION is used for testing only. const minNodeVersion = (process && process.env && process.env.YARGS_MIN_NODE_VERSION) ? Number(process.env.YARGS_MIN_NODE_VERSION) : 12 if (process && process.version) { const major = Number(process.version.match(/v([^.]+)/)![1]) if (major < minNodeVersion) { throw Error(`yargs parser supports a minimum Node.js version of ${minNodeVersion}. Read our version support policy: https://github.com/yargs/yargs-parser#supported-nodejs-versions`) } } // Creates a yargs-parser instance using Node.js standard libraries: const env = process ? process.env as { [key: string]: string } : {} const parser = new YargsParser({ cwd: process.cwd, env: () => { return env }, format, normalize, resolve, // TODO: figure out a way to combine ESM and CJS coverage, such that // we can exercise all the lines below: require: (path: string) => { if (typeof require !== 'undefined') { return require(path) } else if (path.match(/\.json$/)) { // Addresses: https://github.com/yargs/yargs/issues/2040 return JSON.parse(readFileSync(path, 'utf8')) } else { throw Error('only .json config files are supported in ESM') } } }) const yargsParser: Parser = function Parser (args: ArgsInput, opts?: Partial): Arguments { const result = parser.parse(args.slice(), opts) return result.argv } yargsParser.detailed = function (args: ArgsInput, opts?: Partial): DetailedArguments { return parser.parse(args.slice(), opts) } yargsParser.camelCase = camelCase yargsParser.decamelize = decamelize yargsParser.looksLikeNumber = looksLikeNumber export default yargsParser yargs-parser-yargs-parser-v21.0.0/lib/string-utils.ts000066400000000000000000000036011414461571200226030ustar00rootroot00000000000000/** * @license * Copyright (c) 2016, Contributors * SPDX-License-Identifier: ISC */ export function camelCase (str: string): string { // Handle the case where an argument is provided as camel case, e.g., fooBar. // by ensuring that the string isn't already mixed case: const isCamelCase = str !== str.toLowerCase() && str !== str.toUpperCase() if (!isCamelCase) { str = str.toLowerCase() } if (str.indexOf('-') === -1 && str.indexOf('_') === -1) { return str } else { let camelcase = '' let nextChrUpper = false const leadingHyphens = str.match(/^-+/) for (let i = leadingHyphens ? leadingHyphens[0].length : 0; i < str.length; i++) { let chr = str.charAt(i) if (nextChrUpper) { nextChrUpper = false chr = chr.toUpperCase() } if (i !== 0 && (chr === '-' || chr === '_')) { nextChrUpper = true } else if (chr !== '-' && chr !== '_') { camelcase += chr } } return camelcase } } export function decamelize (str: string, joinString?: string): string { const lowercase = str.toLowerCase() joinString = joinString || '-' let notCamelcase = '' for (let i = 0; i < str.length; i++) { const chrLower = lowercase.charAt(i) const chrString = str.charAt(i) if (chrLower !== chrString && i > 0) { notCamelcase += `${joinString}${lowercase.charAt(i)}` } else { notCamelcase += chrString } } return notCamelcase } export function looksLikeNumber (x: null | undefined | number | string): boolean { if (x === null || x === undefined) return false // if loaded from config, may already be a number. if (typeof x === 'number') return true // hexadecimal. if (/^0x[0-9a-f]+$/i.test(x)) return true // don't treat 0123 as a number; as it drops the leading '0'. if (/^0[^.]/.test(x)) return false return /^[-]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x) } yargs-parser-yargs-parser-v21.0.0/lib/tokenize-arg-string.ts000066400000000000000000000020121414461571200240350ustar00rootroot00000000000000/** * @license * Copyright (c) 2016, Contributors * SPDX-License-Identifier: ISC */ // take an un-split argv string and tokenize it. export function tokenizeArgString (argString: string | any[]): string[] { if (Array.isArray(argString)) { return argString.map(e => typeof e !== 'string' ? e + '' : e) } argString = argString.trim() let i = 0 let prevC: string | null = null let c: string | null = null let opening: string | null = null const args: string[] = [] for (let ii = 0; ii < argString.length; ii++) { prevC = c c = argString.charAt(ii) // split on spaces unless we're in quotes. if (c === ' ' && !opening) { if (!(prevC === ' ')) { i++ } continue } // don't split the string if we're in matching // opening or closing single and double quotes. if (c === opening) { opening = null } else if ((c === "'" || c === '"') && !opening) { opening = c } if (!args[i]) args[i] = '' args[i] += c } return args } yargs-parser-yargs-parser-v21.0.0/lib/yargs-parser-types.ts000066400000000000000000000174221414461571200237260ustar00rootroot00000000000000/** * @license * Copyright (c) 2016, Contributors * SPDX-License-Identifier: ISC */ /** * An object whose all properties have the same type, where each key is a string. */ export interface Dictionary { [key: string]: T; } /** * Returns the keys of T. */ export type KeyOf = { [K in keyof T]: string extends K ? never : number extends K ? never : K } extends { [_ in keyof T]: infer U } ? U : never; /** * Returns the type of a Dictionary or array values. */ export type ValueOf = T extends (infer U)[] ? U : T[keyof T]; export type ArgsInput = string | any[]; export type ArgsOutput = (string | number)[]; export interface Arguments { /** Non-option arguments */ _: ArgsOutput; /** Arguments after the end-of-options flag `--` */ '--'?: ArgsOutput; /** All remaining options */ [argName: string]: any; } export interface DetailedArguments { /** An object representing the parsed value of `args` */ argv: Arguments; /** Populated with an error object if an exception occurred during parsing. */ error: Error | null; /** The inferred list of aliases built by combining lists in opts.alias. */ aliases: Dictionary; /** Any new aliases added via camel-case expansion. */ newAliases: Dictionary; /** Any new argument created by opts.default, no aliases included. */ defaulted: Dictionary; /** The configuration loaded from the yargs stanza in package.json. */ configuration: Configuration; } export interface Configuration { /** Should variables prefixed with --no be treated as negations? Default is `true` */ 'boolean-negation': boolean; /** Should hyphenated arguments be expanded into camel-case aliases? Default is `true` */ 'camel-case-expansion': boolean; /** Should arrays be combined when provided by both command line arguments and a configuration file? Default is `false` */ 'combine-arrays': boolean; /** Should keys that contain `.` be treated as objects? Default is `true` */ 'dot-notation': boolean; /** Should arguments be coerced into an array when duplicated? Default is `true` */ 'duplicate-arguments-array': boolean; /** Should array arguments be coerced into a single array when duplicated? Default is `true` */ 'flatten-duplicate-arrays': boolean; /** Should arrays consume more than one positional argument following their flag? Default is `true` */ 'greedy-arrays': boolean; /** Should parsing stop at the first text argument? This is similar to how e.g. ssh parses its command line. Default is `false` */ 'halt-at-non-option': boolean; /** Should nargs consume dash options as well as positional arguments? Default is `false` */ 'nargs-eats-options': boolean; /** The prefix to use for negated boolean variables. Default is `'no-'` */ 'negation-prefix': string; /** Should positional values that look like numbers be parsed? Default is `true` */ 'parse-positional-numbers': boolean; /** Should keys that look like numbers be treated as such? Default is `true` */ 'parse-numbers': boolean; /** Should unparsed flags be stored in -- or _? Default is `false` */ 'populate--': boolean; /** Should a placeholder be added for keys not set via the corresponding CLI argument? Default is `false` */ 'set-placeholder-key': boolean; /** Should a group of short-options be treated as boolean flags? Default is `true` */ 'short-option-groups': boolean; /** Should aliases be removed before returning results? Default is `false` */ 'strip-aliased': boolean; /** Should dashed keys be removed before returning results? This option has no effect if camel-case-expansion is disabled. Default is `false` */ 'strip-dashed': boolean; /** Should unknown options be treated like regular arguments? An unknown option is one that is not configured in opts. Default is `false` */ 'unknown-options-as-args': boolean; } export type ArrayOption = string | { key: string; boolean?: boolean, string?: boolean, number?: boolean, integer?: boolean }; export type CoerceCallback = (arg: any) => any; export type ConfigCallback = (configPath: string) => { [key: string]: any } | Error; export interface Options { /** An object representing the set of aliases for a key: `{ alias: { foo: ['f']} }`. */ alias: Dictionary; /** * Indicate that keys should be parsed as an array: `{ array: ['foo', 'bar'] }`. * Indicate that keys should be parsed as an array and coerced to booleans / numbers: * { array: [ { key: 'foo', boolean: true }, {key: 'bar', number: true} ] }`. */ array: ArrayOption | ArrayOption[]; /** Arguments should be parsed as booleans: `{ boolean: ['x', 'y'] }`. */ boolean: string | string[]; /** Indicate a key that represents a path to a configuration file (this file will be loaded and parsed). */ config: string | string[] | Dictionary; /** configuration objects to parse, their properties will be set as arguments */ configObjects: Dictionary[]; /** Provide configuration options to the yargs-parser. */ configuration: Partial; /** * Provide a custom synchronous function that returns a coerced value from the argument provided (or throws an error), e.g. * `{ coerce: { foo: function (arg) { return modifiedArg } } }`. */ coerce: Dictionary; /** Indicate a key that should be used as a counter, e.g., `-vvv = {v: 3}`. */ count: string | string[]; /** Provide default values for keys: `{ default: { x: 33, y: 'hello world!' } }`. */ default: Dictionary; /** Environment variables (`process.env`) with the prefix provided should be parsed. */ envPrefix?: string; /** Specify that a key requires n arguments: `{ narg: {x: 2} }`. */ narg: Dictionary; /** `path.normalize()` will be applied to values set to this key. */ normalize: string | string[]; /** Keys should be treated as strings (even if they resemble a number `-x 33`). */ string: string | string[]; /** Keys should be treated as numbers. */ number: string | string[]; /** i18n handler, defaults to util.format */ __: (format: any, ...param: any[]) => string; /** alias lookup table defaults */ key: Dictionary; } export interface YargsParserMixin { cwd: Function; format: Function; normalize: Function; require: Function; resolve: Function; env: Function; } export type OptionsDefault = ValueOf, 'default'>>; export interface Parser { (args: ArgsInput, opts?: Partial): Arguments; detailed(args: ArgsInput, opts?: Partial): DetailedArguments; camelCase(str: string): string; decamelize(str: string, joinString?: string): string; looksLikeNumber(x: null | undefined | number | string): boolean; } export type StringFlag = Dictionary; export type BooleanFlag = Dictionary; export type NumberFlag = Dictionary; export type ConfigsFlag = Dictionary; export type CoercionsFlag = Dictionary; export type KeysFlag = string[]; export interface Flags { aliases: StringFlag; arrays: BooleanFlag; bools: BooleanFlag; strings: BooleanFlag; numbers: BooleanFlag; counts: BooleanFlag; normalize: BooleanFlag; configs: ConfigsFlag; nargs: NumberFlag; coercions: CoercionsFlag; keys: KeysFlag; } export type Flag = ValueOf>; export type FlagValue = ValueOf; export type FlagsKey = KeyOf>; export type ArrayFlagsKey = Extract; export enum DefaultValuesForTypeKey { BOOLEAN = 'boolean', STRING = 'string', NUMBER = 'number', ARRAY = 'array', } export interface DefaultValuesForType { [DefaultValuesForTypeKey.BOOLEAN]: boolean; [DefaultValuesForTypeKey.STRING]: string; [DefaultValuesForTypeKey.NUMBER]: undefined; [DefaultValuesForTypeKey.ARRAY]: any[]; } yargs-parser-yargs-parser-v21.0.0/lib/yargs-parser.ts000066400000000000000000001176631414461571200225740ustar00rootroot00000000000000/** * @license * Copyright (c) 2016, Contributors * SPDX-License-Identifier: ISC */ import { tokenizeArgString } from './tokenize-arg-string.js' import type { ArgsInput, Arguments, ArrayFlagsKey, ArrayOption, CoerceCallback, Configuration, DefaultValuesForType, DetailedArguments, Dictionary, Flag, Flags, FlagsKey, StringFlag, BooleanFlag, NumberFlag, ConfigsFlag, CoercionsFlag, Options, OptionsDefault, ValueOf, YargsParserMixin } from './yargs-parser-types.js' import { DefaultValuesForTypeKey } from './yargs-parser-types.js' import { camelCase, decamelize, looksLikeNumber } from './string-utils.js' let mixin: YargsParserMixin export class YargsParser { constructor (_mixin: YargsParserMixin) { mixin = _mixin } parse (argsInput: ArgsInput, options?: Partial): DetailedArguments { const opts: Partial = Object.assign({ alias: undefined, array: undefined, boolean: undefined, config: undefined, configObjects: undefined, configuration: undefined, coerce: undefined, count: undefined, default: undefined, envPrefix: undefined, narg: undefined, normalize: undefined, string: undefined, number: undefined, __: undefined, key: undefined }, options) // allow a string argument to be passed in rather // than an argv array. const args = tokenizeArgString(argsInput) // tokenizeArgString adds extra quotes to args if argsInput is a string // only strip those extra quotes in processValue if argsInput is a string const inputIsString = typeof argsInput === 'string' // aliases might have transitive relationships, normalize this. const aliases = combineAliases(Object.assign(Object.create(null), opts.alias)) const configuration: Configuration = Object.assign({ 'boolean-negation': true, 'camel-case-expansion': true, 'combine-arrays': false, 'dot-notation': true, 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': true, 'greedy-arrays': true, 'halt-at-non-option': false, 'nargs-eats-options': false, 'negation-prefix': 'no-', 'parse-numbers': true, 'parse-positional-numbers': true, 'populate--': false, 'set-placeholder-key': false, 'short-option-groups': true, 'strip-aliased': false, 'strip-dashed': false, 'unknown-options-as-args': false }, opts.configuration) const defaults: OptionsDefault = Object.assign(Object.create(null), opts.default) const configObjects = opts.configObjects || [] const envPrefix = opts.envPrefix const notFlagsOption = configuration['populate--'] const notFlagsArgv: string = notFlagsOption ? '--' : '_' const newAliases: Dictionary = Object.create(null) const defaulted: Dictionary = Object.create(null) // allow a i18n handler to be passed in, default to a fake one (util.format). const __ = opts.__ || mixin.format const flags: Flags = { aliases: Object.create(null), arrays: Object.create(null), bools: Object.create(null), strings: Object.create(null), numbers: Object.create(null), counts: Object.create(null), normalize: Object.create(null), configs: Object.create(null), nargs: Object.create(null), coercions: Object.create(null), keys: [] } const negative = /^-([0-9]+(\.[0-9]+)?|\.[0-9]+)$/ const negatedBoolean = new RegExp('^--' + configuration['negation-prefix'] + '(.+)') ;([] as ArrayOption[]).concat(opts.array || []).filter(Boolean).forEach(function (opt) { const key = typeof opt === 'object' ? opt.key : opt // assign to flags[bools|strings|numbers] const assignment: ArrayFlagsKey | undefined = Object.keys(opt).map(function (key) { const arrayFlagKeys: Record = { boolean: 'bools', string: 'strings', number: 'numbers' } return arrayFlagKeys[key] }).filter(Boolean).pop() // assign key to be coerced if (assignment) { flags[assignment][key] = true } flags.arrays[key] = true flags.keys.push(key) }) ;([] as string[]).concat(opts.boolean || []).filter(Boolean).forEach(function (key) { flags.bools[key] = true flags.keys.push(key) }) ;([] as string[]).concat(opts.string || []).filter(Boolean).forEach(function (key) { flags.strings[key] = true flags.keys.push(key) }) ;([] as string[]).concat(opts.number || []).filter(Boolean).forEach(function (key) { flags.numbers[key] = true flags.keys.push(key) }) ;([] as string[]).concat(opts.count || []).filter(Boolean).forEach(function (key) { flags.counts[key] = true flags.keys.push(key) }) ;([] as string[]).concat(opts.normalize || []).filter(Boolean).forEach(function (key) { flags.normalize[key] = true flags.keys.push(key) }) if (typeof opts.narg === 'object') { Object.entries(opts.narg).forEach(([key, value]) => { if (typeof value === 'number') { flags.nargs[key] = value flags.keys.push(key) } }) } if (typeof opts.coerce === 'object') { Object.entries(opts.coerce).forEach(([key, value]) => { if (typeof value === 'function') { flags.coercions[key] = value flags.keys.push(key) } }) } if (typeof opts.config !== 'undefined') { if (Array.isArray(opts.config) || typeof opts.config === 'string') { ;([] as string[]).concat(opts.config).filter(Boolean).forEach(function (key) { flags.configs[key] = true }) } else if (typeof opts.config === 'object') { Object.entries(opts.config).forEach(([key, value]) => { if (typeof value === 'boolean' || typeof value === 'function') { flags.configs[key] = value } }) } } // create a lookup table that takes into account all // combinations of aliases: {f: ['foo'], foo: ['f']} extendAliases(opts.key, aliases, opts.default, flags.arrays) // apply default values to all aliases. Object.keys(defaults).forEach(function (key) { (flags.aliases[key] || []).forEach(function (alias) { defaults[alias] = defaults[key] }) }) let error: Error | null = null checkConfiguration() let notFlags: string[] = [] const argv: Arguments = Object.assign(Object.create(null), { _: [] }) // TODO(bcoe): for the first pass at removing object prototype we didn't // remove all prototypes from objects returned by this API, we might want // to gradually move towards doing so. const argvReturn: { [argName: string]: any } = {} for (let i = 0; i < args.length; i++) { const arg = args[i] const truncatedArg = arg.replace(/^-{3,}/, '---') let broken: boolean let key: string | undefined let letters: string[] let m: RegExpMatchArray | null let next: string let value: string // any unknown option (except for end-of-options, "--") if (arg !== '--' && isUnknownOptionAsArg(arg)) { pushPositional(arg) // ---, ---=, ----, etc, } else if (truncatedArg.match(/---+(=|$)/)) { // options without key name are invalid. pushPositional(arg) continue // -- separated by = } else if (arg.match(/^--.+=/) || ( !configuration['short-option-groups'] && arg.match(/^-.+=/) )) { // Using [\s\S] instead of . because js doesn't support the // 'dotall' regex modifier. See: // http://stackoverflow.com/a/1068308/13216 m = arg.match(/^--?([^=]+)=([\s\S]*)$/) // arrays format = '--f=a b c' if (m !== null && Array.isArray(m) && m.length >= 3) { if (checkAllAliases(m[1], flags.arrays)) { i = eatArray(i, m[1], args, m[2]) } else if (checkAllAliases(m[1], flags.nargs) !== false) { // nargs format = '--f=monkey washing cat' i = eatNargs(i, m[1], args, m[2]) } else { setArg(m[1], m[2], true) } } } else if (arg.match(negatedBoolean) && configuration['boolean-negation']) { m = arg.match(negatedBoolean) if (m !== null && Array.isArray(m) && m.length >= 2) { key = m[1] setArg(key, checkAllAliases(key, flags.arrays) ? [false] : false) } // -- separated by space. } else if (arg.match(/^--.+/) || ( !configuration['short-option-groups'] && arg.match(/^-[^-]+/) )) { m = arg.match(/^--?(.+)/) if (m !== null && Array.isArray(m) && m.length >= 2) { key = m[1] if (checkAllAliases(key, flags.arrays)) { // array format = '--foo a b c' i = eatArray(i, key, args) } else if (checkAllAliases(key, flags.nargs) !== false) { // nargs format = '--foo a b c' // should be truthy even if: flags.nargs[key] === 0 i = eatNargs(i, key, args) } else { next = args[i + 1] if (next !== undefined && (!next.match(/^-/) || next.match(negative)) && !checkAllAliases(key, flags.bools) && !checkAllAliases(key, flags.counts)) { setArg(key, next) i++ } else if (/^(true|false)$/.test(next)) { setArg(key, next) i++ } else { setArg(key, defaultValue(key)) } } } // dot-notation flag separated by '='. } else if (arg.match(/^-.\..+=/)) { m = arg.match(/^-([^=]+)=([\s\S]*)$/) if (m !== null && Array.isArray(m) && m.length >= 3) { setArg(m[1], m[2]) } // dot-notation flag separated by space. } else if (arg.match(/^-.\..+/) && !arg.match(negative)) { next = args[i + 1] m = arg.match(/^-(.\..+)/) if (m !== null && Array.isArray(m) && m.length >= 2) { key = m[1] if (next !== undefined && !next.match(/^-/) && !checkAllAliases(key, flags.bools) && !checkAllAliases(key, flags.counts)) { setArg(key, next) i++ } else { setArg(key, defaultValue(key)) } } } else if (arg.match(/^-[^-]+/) && !arg.match(negative)) { letters = arg.slice(1, -1).split('') broken = false for (let j = 0; j < letters.length; j++) { next = arg.slice(j + 2) if (letters[j + 1] && letters[j + 1] === '=') { value = arg.slice(j + 3) key = letters[j] if (checkAllAliases(key, flags.arrays)) { // array format = '-f=a b c' i = eatArray(i, key, args, value) } else if (checkAllAliases(key, flags.nargs) !== false) { // nargs format = '-f=monkey washing cat' i = eatNargs(i, key, args, value) } else { setArg(key, value) } broken = true break } if (next === '-') { setArg(letters[j], next) continue } // current letter is an alphabetic character and next value is a number if (/[A-Za-z]/.test(letters[j]) && /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next) && checkAllAliases(next, flags.bools) === false) { setArg(letters[j], next) broken = true break } if (letters[j + 1] && letters[j + 1].match(/\W/)) { setArg(letters[j], next) broken = true break } else { setArg(letters[j], defaultValue(letters[j])) } } key = arg.slice(-1)[0] if (!broken && key !== '-') { if (checkAllAliases(key, flags.arrays)) { // array format = '-f a b c' i = eatArray(i, key, args) } else if (checkAllAliases(key, flags.nargs) !== false) { // nargs format = '-f a b c' // should be truthy even if: flags.nargs[key] === 0 i = eatNargs(i, key, args) } else { next = args[i + 1] if (next !== undefined && (!/^(-|--)[^-]/.test(next) || next.match(negative)) && !checkAllAliases(key, flags.bools) && !checkAllAliases(key, flags.counts)) { setArg(key, next) i++ } else if (/^(true|false)$/.test(next)) { setArg(key, next) i++ } else { setArg(key, defaultValue(key)) } } } } else if (arg.match(/^-[0-9]$/) && arg.match(negative) && checkAllAliases(arg.slice(1), flags.bools)) { // single-digit boolean alias, e.g: xargs -0 key = arg.slice(1) setArg(key, defaultValue(key)) } else if (arg === '--') { notFlags = args.slice(i + 1) break } else if (configuration['halt-at-non-option']) { notFlags = args.slice(i) break } else { pushPositional(arg) } } // order of precedence: // 1. command line arg // 2. value from env var // 3. value from config file // 4. value from config objects // 5. configured default value applyEnvVars(argv, true) // special case: check env vars that point to config file applyEnvVars(argv, false) setConfig(argv) setConfigObjects() applyDefaultsAndAliases(argv, flags.aliases, defaults, true) applyCoercions(argv) if (configuration['set-placeholder-key']) setPlaceholderKeys(argv) // for any counts either not in args or without an explicit default, set to 0 Object.keys(flags.counts).forEach(function (key) { if (!hasKey(argv, key.split('.'))) setArg(key, 0) }) // '--' defaults to undefined. if (notFlagsOption && notFlags.length) argv[notFlagsArgv] = [] notFlags.forEach(function (key) { argv[notFlagsArgv].push(key) }) if (configuration['camel-case-expansion'] && configuration['strip-dashed']) { Object.keys(argv).filter(key => key !== '--' && key.includes('-')).forEach(key => { delete argv[key] }) } if (configuration['strip-aliased']) { ;([] as string[]).concat(...Object.keys(aliases).map(k => aliases[k])).forEach(alias => { if (configuration['camel-case-expansion'] && alias.includes('-')) { delete argv[alias.split('.').map(prop => camelCase(prop)).join('.')] } delete argv[alias] }) } // Push argument into positional array, applying numeric coercion: function pushPositional (arg: string) { const maybeCoercedNumber = maybeCoerceNumber('_', arg) if (typeof maybeCoercedNumber === 'string' || typeof maybeCoercedNumber === 'number') { argv._.push(maybeCoercedNumber) } } // how many arguments should we consume, based // on the nargs option? function eatNargs (i: number, key: string, args: string[], argAfterEqualSign?: string): number { let ii let toEat = checkAllAliases(key, flags.nargs) // NaN has a special meaning for the array type, indicating that one or // more values are expected. toEat = typeof toEat !== 'number' || isNaN(toEat) ? 1 : toEat if (toEat === 0) { if (!isUndefined(argAfterEqualSign)) { error = Error(__('Argument unexpected for: %s', key)) } setArg(key, defaultValue(key)) return i } let available = isUndefined(argAfterEqualSign) ? 0 : 1 if (configuration['nargs-eats-options']) { // classic behavior, yargs eats positional and dash arguments. if (args.length - (i + 1) + available < toEat) { error = Error(__('Not enough arguments following: %s', key)) } available = toEat } else { // nargs will not consume flag arguments, e.g., -abc, --foo, // and terminates when one is observed. for (ii = i + 1; ii < args.length; ii++) { if (!args[ii].match(/^-[^0-9]/) || args[ii].match(negative) || isUnknownOptionAsArg(args[ii])) available++ else break } if (available < toEat) error = Error(__('Not enough arguments following: %s', key)) } let consumed = Math.min(available, toEat) if (!isUndefined(argAfterEqualSign) && consumed > 0) { setArg(key, argAfterEqualSign) consumed-- } for (ii = i + 1; ii < (consumed + i + 1); ii++) { setArg(key, args[ii]) } return (i + consumed) } // if an option is an array, eat all non-hyphenated arguments // following it... YUM! // e.g., --foo apple banana cat becomes ["apple", "banana", "cat"] function eatArray (i: number, key: string, args: string[], argAfterEqualSign?: string): number { let argsToSet = [] let next = argAfterEqualSign || args[i + 1] // If both array and nargs are configured, enforce the nargs count: const nargsCount = checkAllAliases(key, flags.nargs) if (checkAllAliases(key, flags.bools) && !(/^(true|false)$/.test(next))) { argsToSet.push(true) } else if (isUndefined(next) || (isUndefined(argAfterEqualSign) && /^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next))) { // for keys without value ==> argsToSet remains an empty [] // set user default value, if available if (defaults[key] !== undefined) { const defVal = defaults[key] argsToSet = Array.isArray(defVal) ? defVal : [defVal] } } else { // value in --option=value is eaten as is if (!isUndefined(argAfterEqualSign)) { argsToSet.push(processValue(key, argAfterEqualSign, true)) } for (let ii = i + 1; ii < args.length; ii++) { if ((!configuration['greedy-arrays'] && argsToSet.length > 0) || (nargsCount && typeof nargsCount === 'number' && argsToSet.length >= nargsCount)) break next = args[ii] if (/^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next)) break i = ii argsToSet.push(processValue(key, next, inputIsString)) } } // If both array and nargs are configured, create an error if less than // nargs positionals were found. NaN has special meaning, indicating // that at least one value is required (more are okay). if (typeof nargsCount === 'number' && ((nargsCount && argsToSet.length < nargsCount) || (isNaN(nargsCount) && argsToSet.length === 0))) { error = Error(__('Not enough arguments following: %s', key)) } setArg(key, argsToSet) return i } function setArg (key: string, val: any, shouldStripQuotes: boolean = inputIsString): void { if (/-/.test(key) && configuration['camel-case-expansion']) { const alias = key.split('.').map(function (prop) { return camelCase(prop) }).join('.') addNewAlias(key, alias) } const value = processValue(key, val, shouldStripQuotes) const splitKey = key.split('.') setKey(argv, splitKey, value) // handle populating aliases of the full key if (flags.aliases[key]) { flags.aliases[key].forEach(function (x) { const keyProperties = x.split('.') setKey(argv, keyProperties, value) }) } // handle populating aliases of the first element of the dot-notation key if (splitKey.length > 1 && configuration['dot-notation']) { ;(flags.aliases[splitKey[0]] || []).forEach(function (x) { let keyProperties = x.split('.') // expand alias with nested objects in key const a = ([] as string[]).concat(splitKey) a.shift() // nuke the old key. keyProperties = keyProperties.concat(a) // populate alias only if is not already an alias of the full key // (already populated above) if (!(flags.aliases[key] || []).includes(keyProperties.join('.'))) { setKey(argv, keyProperties, value) } }) } // Set normalize getter and setter when key is in 'normalize' but isn't an array if (checkAllAliases(key, flags.normalize) && !checkAllAliases(key, flags.arrays)) { const keys = [key].concat(flags.aliases[key] || []) keys.forEach(function (key) { Object.defineProperty(argvReturn, key, { enumerable: true, get () { return val }, set (value) { val = typeof value === 'string' ? mixin.normalize(value) : value } }) }) } } function addNewAlias (key: string, alias: string): void { if (!(flags.aliases[key] && flags.aliases[key].length)) { flags.aliases[key] = [alias] newAliases[alias] = true } if (!(flags.aliases[alias] && flags.aliases[alias].length)) { addNewAlias(alias, key) } } function processValue (key: string, val: any, shouldStripQuotes: boolean) { // strings may be quoted, clean this up as we assign values. if (shouldStripQuotes) { val = stripQuotes(val) } // handle parsing boolean arguments --foo=true --bar false. if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) { if (typeof val === 'string') val = val === 'true' } let value = Array.isArray(val) ? val.map(function (v) { return maybeCoerceNumber(key, v) }) : maybeCoerceNumber(key, val) // increment a count given as arg (either no value or value parsed as boolean) if (checkAllAliases(key, flags.counts) && (isUndefined(value) || typeof value === 'boolean')) { value = increment() } // Set normalized value when key is in 'normalize' and in 'arrays' if (checkAllAliases(key, flags.normalize) && checkAllAliases(key, flags.arrays)) { if (Array.isArray(val)) value = val.map((val) => { return mixin.normalize(val) }) else value = mixin.normalize(val) } return value } function maybeCoerceNumber (key: string, value: string | number | null | undefined) { if (!configuration['parse-positional-numbers'] && key === '_') return value if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.bools) && !Array.isArray(value)) { const shouldCoerceNumber = looksLikeNumber(value) && configuration['parse-numbers'] && ( Number.isSafeInteger(Math.floor(parseFloat(`${value}`))) ) if (shouldCoerceNumber || (!isUndefined(value) && checkAllAliases(key, flags.numbers))) { value = Number(value) } } return value } // set args from config.json file, this should be // applied last so that defaults can be applied. function setConfig (argv: Arguments): void { const configLookup = Object.create(null) // expand defaults/aliases, in-case any happen to reference // the config.json file. applyDefaultsAndAliases(configLookup, flags.aliases, defaults) Object.keys(flags.configs).forEach(function (configKey) { const configPath = argv[configKey] || configLookup[configKey] if (configPath) { try { let config = null const resolvedConfigPath = mixin.resolve(mixin.cwd(), configPath) const resolveConfig = flags.configs[configKey] if (typeof resolveConfig === 'function') { try { config = resolveConfig(resolvedConfigPath) } catch (e) { config = e } if (config instanceof Error) { error = config return } } else { config = mixin.require(resolvedConfigPath) } setConfigObject(config) } catch (ex: any) { // Deno will receive a PermissionDenied error if an attempt is // made to load config without the --allow-read flag: if (ex.name === 'PermissionDenied') error = ex else if (argv[configKey]) error = Error(__('Invalid JSON config file: %s', configPath)) } } }) } // set args from config object. // it recursively checks nested objects. function setConfigObject (config: { [key: string]: any }, prev?: string): void { Object.keys(config).forEach(function (key) { const value = config[key] const fullKey = prev ? prev + '.' + key : key // if the value is an inner object and we have dot-notation // enabled, treat inner objects in config the same as // heavily nested dot notations (foo.bar.apple). if (typeof value === 'object' && value !== null && !Array.isArray(value) && configuration['dot-notation']) { // if the value is an object but not an array, check nested object setConfigObject(value, fullKey) } else { // setting arguments via CLI takes precedence over // values within the config file. if (!hasKey(argv, fullKey.split('.')) || (checkAllAliases(fullKey, flags.arrays) && configuration['combine-arrays'])) { setArg(fullKey, value) } } }) } // set all config objects passed in opts function setConfigObjects (): void { if (typeof configObjects !== 'undefined') { configObjects.forEach(function (configObject) { setConfigObject(configObject) }) } } function applyEnvVars (argv: Arguments, configOnly: boolean): void { if (typeof envPrefix === 'undefined') return const prefix = typeof envPrefix === 'string' ? envPrefix : '' const env = mixin.env() Object.keys(env).forEach(function (envVar) { if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) { // get array of nested keys and convert them to camel case const keys = envVar.split('__').map(function (key, i) { if (i === 0) { key = key.substring(prefix.length) } return camelCase(key) }) if (((configOnly && flags.configs[keys.join('.')]) || !configOnly) && !hasKey(argv, keys)) { setArg(keys.join('.'), env[envVar]) } } }) } function applyCoercions (argv: Arguments): void { let coerce: false | CoerceCallback const applied: Set = new Set() Object.keys(argv).forEach(function (key) { if (!applied.has(key)) { // If we haven't already coerced this option via one of its aliases coerce = checkAllAliases(key, flags.coercions) if (typeof coerce === 'function') { try { const value = maybeCoerceNumber(key, coerce(argv[key])) ;(([] as string[]).concat(flags.aliases[key] || [], key)).forEach(ali => { applied.add(ali) argv[ali] = value }) } catch (err) { error = err as Error } } } }) } function setPlaceholderKeys (argv: Arguments): Arguments { flags.keys.forEach((key) => { // don't set placeholder keys for dot notation options 'foo.bar'. if (~key.indexOf('.')) return if (typeof argv[key] === 'undefined') argv[key] = undefined }) return argv } function applyDefaultsAndAliases (obj: { [key: string]: any }, aliases: { [key: string]: string[] }, defaults: { [key: string]: any }, canLog: boolean = false): void { Object.keys(defaults).forEach(function (key) { if (!hasKey(obj, key.split('.'))) { setKey(obj, key.split('.'), defaults[key]) if (canLog) defaulted[key] = true ;(aliases[key] || []).forEach(function (x) { if (hasKey(obj, x.split('.'))) return setKey(obj, x.split('.'), defaults[key]) }) } }) } function hasKey (obj: { [key: string]: any }, keys: string[]): boolean { let o = obj if (!configuration['dot-notation']) keys = [keys.join('.')] keys.slice(0, -1).forEach(function (key) { o = (o[key] || {}) }) const key = keys[keys.length - 1] if (typeof o !== 'object') return false else return key in o } function setKey (obj: { [key: string]: any }, keys: string[], value: any): void { let o = obj if (!configuration['dot-notation']) keys = [keys.join('.')] keys.slice(0, -1).forEach(function (key) { // TODO(bcoe): in the next major version of yargs, switch to // Object.create(null) for dot notation: key = sanitizeKey(key) if (typeof o === 'object' && o[key] === undefined) { o[key] = {} } if (typeof o[key] !== 'object' || Array.isArray(o[key])) { // ensure that o[key] is an array, and that the last item is an empty object. if (Array.isArray(o[key])) { o[key].push({}) } else { o[key] = [o[key], {}] } // we want to update the empty object at the end of the o[key] array, so set o to that object o = o[key][o[key].length - 1] } else { o = o[key] } }) // TODO(bcoe): in the next major version of yargs, switch to // Object.create(null) for dot notation: const key = sanitizeKey(keys[keys.length - 1]) const isTypeArray = checkAllAliases(keys.join('.'), flags.arrays) const isValueArray = Array.isArray(value) let duplicate = configuration['duplicate-arguments-array'] // nargs has higher priority than duplicate if (!duplicate && checkAllAliases(key, flags.nargs)) { duplicate = true if ((!isUndefined(o[key]) && flags.nargs[key] === 1) || (Array.isArray(o[key]) && o[key].length === flags.nargs[key])) { o[key] = undefined } } if (value === increment()) { o[key] = increment(o[key]) } else if (Array.isArray(o[key])) { if (duplicate && isTypeArray && isValueArray) { o[key] = configuration['flatten-duplicate-arrays'] ? o[key].concat(value) : (Array.isArray(o[key][0]) ? o[key] : [o[key]]).concat([value]) } else if (!duplicate && Boolean(isTypeArray) === Boolean(isValueArray)) { o[key] = value } else { o[key] = o[key].concat([value]) } } else if (o[key] === undefined && isTypeArray) { o[key] = isValueArray ? value : [value] } else if (duplicate && !( o[key] === undefined || checkAllAliases(key, flags.counts) || checkAllAliases(key, flags.bools) )) { o[key] = [o[key], value] } else { o[key] = value } } // extend the aliases list with inferred aliases. function extendAliases (...args: Array<{ [key: string]: any } | undefined>) { args.forEach(function (obj) { Object.keys(obj || {}).forEach(function (key) { // short-circuit if we've already added a key // to the aliases array, for example it might // exist in both 'opts.default' and 'opts.key'. if (flags.aliases[key]) return flags.aliases[key] = ([] as string[]).concat(aliases[key] || []) // For "--option-name", also set argv.optionName flags.aliases[key].concat(key).forEach(function (x) { if (/-/.test(x) && configuration['camel-case-expansion']) { const c = camelCase(x) if (c !== key && flags.aliases[key].indexOf(c) === -1) { flags.aliases[key].push(c) newAliases[c] = true } } }) // For "--optionName", also set argv['option-name'] flags.aliases[key].concat(key).forEach(function (x) { if (x.length > 1 && /[A-Z]/.test(x) && configuration['camel-case-expansion']) { const c = decamelize(x, '-') if (c !== key && flags.aliases[key].indexOf(c) === -1) { flags.aliases[key].push(c) newAliases[c] = true } } }) flags.aliases[key].forEach(function (x) { flags.aliases[x] = [key].concat(flags.aliases[key].filter(function (y) { return x !== y })) }) }) }) } // return the 1st set flag for any of a key's aliases (or false if no flag set) function checkAllAliases (key: string, flag: StringFlag): ValueOf | false function checkAllAliases (key: string, flag: BooleanFlag): ValueOf | false function checkAllAliases (key: string, flag: NumberFlag): ValueOf | false function checkAllAliases (key: string, flag: ConfigsFlag): ValueOf | false function checkAllAliases (key: string, flag: CoercionsFlag): ValueOf | false function checkAllAliases (key: string, flag: Flag): ValueOf | false { const toCheck = ([] as string[]).concat(flags.aliases[key] || [], key) const keys = Object.keys(flag) const setAlias = toCheck.find(key => keys.includes(key)) return setAlias ? flag[setAlias] : false } function hasAnyFlag (key: string): boolean { const flagsKeys = Object.keys(flags) as FlagsKey[] const toCheck = ([] as Array<{ [key: string]: any } | string[]>).concat(flagsKeys.map(k => flags[k])) return toCheck.some(function (flag) { return Array.isArray(flag) ? flag.includes(key) : flag[key] }) } function hasFlagsMatching (arg: string, ...patterns: RegExp[]): boolean { const toCheck = ([] as RegExp[]).concat(...patterns) return toCheck.some(function (pattern) { const match = arg.match(pattern) return match && hasAnyFlag(match[1]) }) } // based on a simplified version of the short flag group parsing logic function hasAllShortFlags (arg: string): boolean { // if this is a negative number, or doesn't start with a single hyphen, it's not a short flag group if (arg.match(negative) || !arg.match(/^-[^-]+/)) { return false } let hasAllFlags = true let next: string const letters = arg.slice(1).split('') for (let j = 0; j < letters.length; j++) { next = arg.slice(j + 2) if (!hasAnyFlag(letters[j])) { hasAllFlags = false break } if ((letters[j + 1] && letters[j + 1] === '=') || next === '-' || (/[A-Za-z]/.test(letters[j]) && /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) || (letters[j + 1] && letters[j + 1].match(/\W/))) { break } } return hasAllFlags } function isUnknownOptionAsArg (arg: string): boolean { return configuration['unknown-options-as-args'] && isUnknownOption(arg) } function isUnknownOption (arg: string): boolean { arg = arg.replace(/^-{3,}/, '--') // ignore negative numbers if (arg.match(negative)) { return false } // if this is a short option group and all of them are configured, it isn't unknown if (hasAllShortFlags(arg)) { return false } // e.g. '--count=2' const flagWithEquals = /^-+([^=]+?)=[\s\S]*$/ // e.g. '-a' or '--arg' const normalFlag = /^-+([^=]+?)$/ // e.g. '-a-' const flagEndingInHyphen = /^-+([^=]+?)-$/ // e.g. '-abc123' const flagEndingInDigits = /^-+([^=]+?\d+)$/ // e.g. '-a/usr/local' const flagEndingInNonWordCharacters = /^-+([^=]+?)\W+.*$/ // check the different types of flag styles, including negatedBoolean, a pattern defined near the start of the parse method return !hasFlagsMatching(arg, flagWithEquals, negatedBoolean, normalFlag, flagEndingInHyphen, flagEndingInDigits, flagEndingInNonWordCharacters) } // make a best effort to pick a default value // for an option based on name and type. function defaultValue (key: string) { if (!checkAllAliases(key, flags.bools) && !checkAllAliases(key, flags.counts) && `${key}` in defaults) { return defaults[key] } else { return defaultForType(guessType(key)) } } // return a default value, given the type of a flag., function defaultForType (type: K): DefaultValuesForType[K] { const def: DefaultValuesForType = { [DefaultValuesForTypeKey.BOOLEAN]: true, [DefaultValuesForTypeKey.STRING]: '', [DefaultValuesForTypeKey.NUMBER]: undefined, [DefaultValuesForTypeKey.ARRAY]: [] } return def[type] } // given a flag, enforce a default type. function guessType (key: string): DefaultValuesForTypeKey { let type: DefaultValuesForTypeKey = DefaultValuesForTypeKey.BOOLEAN if (checkAllAliases(key, flags.strings)) type = DefaultValuesForTypeKey.STRING else if (checkAllAliases(key, flags.numbers)) type = DefaultValuesForTypeKey.NUMBER else if (checkAllAliases(key, flags.bools)) type = DefaultValuesForTypeKey.BOOLEAN else if (checkAllAliases(key, flags.arrays)) type = DefaultValuesForTypeKey.ARRAY return type } function isUndefined (num: any): num is undefined { return num === undefined } // check user configuration settings for inconsistencies function checkConfiguration (): void { // count keys should not be set as array/narg Object.keys(flags.counts).find(key => { if (checkAllAliases(key, flags.arrays)) { error = Error(__('Invalid configuration: %s, opts.count excludes opts.array.', key)) return true } else if (checkAllAliases(key, flags.nargs)) { error = Error(__('Invalid configuration: %s, opts.count excludes opts.narg.', key)) return true } return false }) } return { aliases: Object.assign({}, flags.aliases), argv: Object.assign(argvReturn, argv), configuration: configuration, defaulted: Object.assign({}, defaulted), error: error, newAliases: Object.assign({}, newAliases) } } } // if any aliases reference each other, we should // merge them together. function combineAliases (aliases: Dictionary): Dictionary { const aliasArrays: Array = [] const combined: Dictionary = Object.create(null) let change = true // turn alias lookup hash {key: ['alias1', 'alias2']} into // a simple array ['key', 'alias1', 'alias2'] Object.keys(aliases).forEach(function (key) { aliasArrays.push( ([] as string[]).concat(aliases[key], key) ) }) // combine arrays until zero changes are // made in an iteration. while (change) { change = false for (let i = 0; i < aliasArrays.length; i++) { for (let ii = i + 1; ii < aliasArrays.length; ii++) { const intersect = aliasArrays[i].filter(function (v) { return aliasArrays[ii].indexOf(v) !== -1 }) if (intersect.length) { aliasArrays[i] = aliasArrays[i].concat(aliasArrays[ii]) aliasArrays.splice(ii, 1) change = true break } } } } // map arrays back to the hash-lookup (de-dupe while // we're at it). aliasArrays.forEach(function (aliasArray) { aliasArray = aliasArray.filter(function (v, i, self) { return self.indexOf(v) === i }) const lastAlias = aliasArray.pop() if (lastAlias !== undefined && typeof lastAlias === 'string') { combined[lastAlias] = aliasArray } }) return combined } // this function should only be called when a count is given as an arg // it is NOT called to set a default value // thus we can start the count at 1 instead of 0 function increment (orig?: number | undefined): number { return orig !== undefined ? orig + 1 : 1 } // TODO(bcoe): in the next major version of yargs, switch to // Object.create(null) for dot notation: function sanitizeKey (key: string): string { if (key === '__proto__') return '___proto___' return key } function stripQuotes (val: string): string { return ( typeof val === 'string' && (val[0] === "'" || val[0] === '"') && val[val.length - 1] === val[0] ) ? val.substring(1, val.length - 1) : val } yargs-parser-yargs-parser-v21.0.0/package.json000066400000000000000000000046411414461571200213140ustar00rootroot00000000000000{ "name": "yargs-parser", "version": "21.0.0", "description": "the mighty option parser used by yargs", "main": "build/index.cjs", "exports": { ".": [ { "import": "./build/lib/index.js", "require": "./build/index.cjs" }, "./build/index.cjs" ] }, "type": "module", "module": "./build/lib/index.js", "scripts": { "check": "standardx '**/*.ts' && standardx '**/*.js' && standardx '**/*.cjs'", "fix": "standardx --fix '**/*.ts' && standardx --fix '**/*.js' && standardx --fix '**/*.cjs'", "pretest": "rimraf build && tsc -p tsconfig.test.json && cross-env NODE_ENV=test npm run build:cjs", "test": "c8 --reporter=text --reporter=html mocha test/*.cjs", "test:esm": "c8 --reporter=text --reporter=html mocha test/*.mjs", "test:browser": "start-server-and-test 'serve ./ -p 8080' http://127.0.0.1:8080/package.json 'node ./test/browser/yargs-test.cjs'", "pretest:typescript": "npm run pretest", "test:typescript": "c8 mocha ./build/test/typescript/*.js", "coverage": "c8 report --check-coverage", "precompile": "rimraf build", "compile": "tsc", "postcompile": "npm run build:cjs", "build:cjs": "rollup -c", "prepare": "npm run compile" }, "repository": { "type": "git", "url": "https://github.com/yargs/yargs-parser.git" }, "keywords": [ "argument", "parser", "yargs", "command", "cli", "parsing", "option", "args", "argument" ], "author": "Ben Coe ", "license": "ISC", "devDependencies": { "@types/chai": "^4.2.11", "@types/mocha": "^9.0.0", "@types/node": "^16.11.4", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", "@wessberg/rollup-plugin-ts": "^1.2.28", "c8": "^7.3.0", "chai": "^4.2.0", "cross-env": "^7.0.2", "eslint": "^7.0.0", "eslint-plugin-import": "^2.20.1", "eslint-plugin-node": "^11.0.0", "gts": "^3.0.0", "mocha": "^9.0.0", "puppeteer": "^11.0.0", "rimraf": "^3.0.2", "rollup": "^2.22.1", "rollup-plugin-cleanup": "^3.1.1", "serve": "^13.0.0", "standardx": "^7.0.0", "start-server-and-test": "^1.11.2", "ts-transform-default-export": "^1.0.2", "typescript": "^4.0.0" }, "files": [ "browser.js", "build", "!*.d.ts" ], "engines": { "node": ">=12" }, "standardx": { "ignore": [ "build" ] } } yargs-parser-yargs-parser-v21.0.0/release-please-config.json000066400000000000000000000001431414461571200240440ustar00rootroot00000000000000{ "bootstrap-sha": "686ec15fb7e4a996763c60cf6661b2698f28dac4", "packages": { ".": {} } } yargs-parser-yargs-parser-v21.0.0/renovate.json000066400000000000000000000001601414461571200215340ustar00rootroot00000000000000{ "extends": [ "config:base" ], "pinVersions": false, "rebaseStalePrs": true, "gitAuthor": null } yargs-parser-yargs-parser-v21.0.0/rollup.config.js000066400000000000000000000010721414461571200221400ustar00rootroot00000000000000import cleanup from 'rollup-plugin-cleanup' import ts from '@wessberg/rollup-plugin-ts' import transformDefaultExport from 'ts-transform-default-export' const output = { format: 'cjs', file: './build/index.cjs', exports: 'default' } if (process.env.NODE_ENV === 'test') output.sourcemap = true export default { input: './lib/index.ts', output, plugins: [ ts({ transformers: ({ program }) => ({ afterDeclarations: transformDefaultExport(program) }) }), cleanup({ comments: 'none', extensions: ['*'] }) ] } yargs-parser-yargs-parser-v21.0.0/test/000077500000000000000000000000001414461571200200005ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/browser/000077500000000000000000000000001414461571200214635ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/browser/yargs-test.cjs000066400000000000000000000025211414461571200242660ustar00rootroot00000000000000const { deepStrictEqual } = require('assert') const puppeteer = require('puppeteer') // Runs a browser window with a given argv string and options: let browser async function parse (argv, opts) { if (!browser) { browser = await puppeteer.launch() } const page = await browser.newPage() opts = encodeURIComponent(JSON.stringify(opts)) await page.goto(`http://127.0.0.1:8080/test/browser/yargs-test?argv=${encodeURIComponent(argv)}&opts=${opts}`) const element = await page.$('#output') return JSON.parse(await page.evaluate(element => element.textContent, element)) } // The actual tests: async function tests () { { const output = await parse('--hello world --x 102') deepStrictEqual(output, { _: [], hello: 'world', x: 102 }) console.info('✅ parse simple string') } { const output = await parse('--hello world --x 102', { alias: { hello: ['goodbye'], x: ['example'] } }) deepStrictEqual(output, { _: [], hello: 'world', x: 102, example: 102, goodbye: 'world' }) console.info('✅ parse with aliases') } } tests().then(() => { console.info('👌all tests finished') browser.close() }).catch((err) => { console.error(err.stack) console.error('❌some tests failed') process.exitCode = 1 browser.close() }) yargs-parser-yargs-parser-v21.0.0/test/browser/yargs-test.html000066400000000000000000000012741414461571200244570ustar00rootroot00000000000000
yargs-parser-yargs-parser-v21.0.0/test/deno/000077500000000000000000000000001414461571200207255ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/deno/yargs-test.ts000066400000000000000000000032001414461571200233720ustar00rootroot00000000000000/* global Deno */ import { assertEquals } from 'https://deno.land/std/testing/asserts.ts' import parser from '../../deno.ts' // Parser: Deno.test('parse string', () => { const parsed = parser('--foo --bar 99') assertEquals(parsed.foo, true) assertEquals(parsed.bar, 99) }) Deno.test('parse array', () => { const parsed = parser(['--foo', '--bar', '99']) assertEquals(parsed.foo, true) assertEquals(parsed.bar, 99) }) Deno.test('aliases', () => { const parsed = parser(['--bar', '99'], { alias: { bar: ['foo'], foo: ['f'] } }) assertEquals(parsed.bar, 99) assertEquals(parsed.foo, 99) assertEquals(parsed.f, 99) }) const jsonPath = './test/fixtures/config.json' Deno.test('should load options and values from default config if specified', () => { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) assertEquals(argv.herp, 'derp') assertEquals(argv.zoom, 55) assertEquals(argv.zoom, 55) }) // String utilities: Deno.test('convert hyphenated string to camelcase', () => { assertEquals(parser.camelCase('hello-world'), 'helloWorld') }) Deno.test('convert camelcase string to hyphenated', () => { assertEquals(parser.decamelize('helloWorld'), 'hello-world') }) Deno.test('it detects strings that could be parsed as numbers', () => { assertEquals(parser.looksLikeNumber('3293'), true) assertEquals(parser.looksLikeNumber('0x10'), true) assertEquals(parser.looksLikeNumber('0x10'), true) assertEquals(parser.looksLikeNumber('0100'), false) assertEquals(parser.looksLikeNumber('apple'), false) }) yargs-parser-yargs-parser-v21.0.0/test/fixtures/000077500000000000000000000000001414461571200216515ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/fixtures/config.json000066400000000000000000000003571414461571200240160ustar00rootroot00000000000000{ "herp": "derp", "z": 55, "foo": "baz", "version": "1.0.2", "truthy": true, "toString": "method name", "__proto__": { "aaa": 99 }, "bar": { "__proto__": { "bbb": 100 } } } yargs-parser-yargs-parser-v21.0.0/test/fixtures/config.txt000066400000000000000000000000351414461571200236550ustar00rootroot00000000000000AWESOME=banana BATMAN=grumpy yargs-parser-yargs-parser-v21.0.0/test/fixtures/nested_config.json000066400000000000000000000001201414461571200253440ustar00rootroot00000000000000{ "a": "a", "nested": { "foo": "baz", "bar": "bar" }, "b": "b" }yargs-parser-yargs-parser-v21.0.0/test/fixtures/settings.cjs000066400000000000000000000001271414461571200242120ustar00rootroot00000000000000module.exports = { calculate: function (a) { return a + 55 }, herp: 'derp' } yargs-parser-yargs-parser-v21.0.0/test/string-utils.cjs000066400000000000000000000025661414461571200231560ustar00rootroot00000000000000/* global describe, it */ const { strictEqual } = require('assert') const { camelCase, decamelize, looksLikeNumber } = require('../build/index.cjs') describe('string-utils', function () { describe('camelCase', () => { it('converts string with hypen in middle to camel case', () => { strictEqual(camelCase('hello-world'), 'helloWorld') }) it('removes leading hyphens', () => { strictEqual(camelCase('-goodnight-moon'), 'goodnightMoon') }) it('camelCase string stays as is', () => { strictEqual(camelCase('iAmCamelCase'), 'iAmCamelCase') }) it('uppercase string with underscore to camel case', () => { strictEqual(camelCase('NODE_VERSION'), 'nodeVersion') }) }) describe('decamelize', () => { it('adds hyphens back to camelcase string', () => { strictEqual(decamelize('helloWorld'), 'hello-world') }) }) describe('looksLikeNumber', () => { it('it detects strings that could be parsed as numbers', () => { strictEqual(looksLikeNumber('3293'), true) strictEqual(looksLikeNumber('0x10'), true) strictEqual(looksLikeNumber('.1'), true) strictEqual(looksLikeNumber('0.1'), true) strictEqual(looksLikeNumber('0.10'), true) strictEqual(looksLikeNumber('00.1'), false) strictEqual(looksLikeNumber('0100'), false) strictEqual(looksLikeNumber('apple'), false) }) }) }) yargs-parser-yargs-parser-v21.0.0/test/tscc/000077500000000000000000000000001414461571200207345ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/tscc/.gitignore000066400000000000000000000000051414461571200227170ustar00rootroot00000000000000*.js yargs-parser-yargs-parser-v21.0.0/test/tscc/lib000077700000000000000000000000001414461571200225372../../libustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/tscc/optimized.ts000066400000000000000000000025121414461571200233100ustar00rootroot00000000000000import { YargsParser } from './lib/yargs-parser' const parser = new YargsParser({ cwd: () => { return '' }, format: (str, arg) => { return str.replace('%s', arg) }, normalize: (str) => { return str }, resolve: (str) => { return str }, require: () => { throw Error('loading config from files not currently supported in browser') }, env: () => {} }) // Workaround the need for proper nodejs typings and externs // eslint-disable-next-line no-eval const anyDeepEqual = eval('require("assert").strict.deepEqual') as any function deepEqual (actual: T, expected: T, message?: string): void { anyDeepEqual(actual, expected, message) } // Quoted properties aren't allowed in these lint settings. const FLAG_X = 'x' function runTests () { deepEqual(parser.parse([]).argv, { _: [] }, 'empty argv') deepEqual( parser.parse([`--${FLAG_X}=42`]).argv, { _: [], [FLAG_X]: 42 }, 'guessed numeric value') deepEqual( parser.parse([`--${FLAG_X}=str`]).argv, { _: [], [FLAG_X]: 'str' }, 'guessed string value') deepEqual( parser.parse([`--no-${FLAG_X}`]).argv, { _: [], [FLAG_X]: false }, 'guessed boolean negation') // Default values for types: deepEqual( parser.parse([`--${FLAG_X}`]).argv, { _: [], [FLAG_X]: true }, 'guessed boolean default true') console.log('ok') } runTests() yargs-parser-yargs-parser-v21.0.0/test/tscc/package.json000066400000000000000000000002101414461571200232130ustar00rootroot00000000000000{ "name": "optimized-test", "version": "0.0.0", "dependencies": { "@tscc/tscc": "^0.7.4", "@types/node": "^16.11.4" } } yargs-parser-yargs-parser-v21.0.0/test/tscc/tscc.spec.json000066400000000000000000000000611414461571200235110ustar00rootroot00000000000000{ "modules": { "out": "optimized.ts" } } yargs-parser-yargs-parser-v21.0.0/test/tscc/tsconfig.json000066400000000000000000000002461414461571200234450ustar00rootroot00000000000000{ "compilerOptions": { "module": "es2020", "target": "es2017", "moduleResolution": "node" }, "include": [ "./*.ts", "./lib/**/*.ts" ] } yargs-parser-yargs-parser-v21.0.0/test/typescript/000077500000000000000000000000001414461571200222065ustar00rootroot00000000000000yargs-parser-yargs-parser-v21.0.0/test/typescript/tokenize-arg-string.ts000066400000000000000000000105661414461571200264710ustar00rootroot00000000000000/* global describe, it */ import { strictEqual } from 'assert' import { tokenizeArgString } from '../../lib/tokenize-arg-string.js' describe('TokenizeArgString', function () { it('handles unquoted string', function () { const args = tokenizeArgString('--foo 99') strictEqual(args[0], '--foo') strictEqual(args[1], '99') }) it('handles unquoted numbers', function () { const args = tokenizeArgString(['--foo', 9]) strictEqual(args[0], '--foo') strictEqual(args[1], '9') }) it('handles quoted string with no spaces', function () { const args = tokenizeArgString("--foo 'hello'") strictEqual(args[0], '--foo') strictEqual(args[1], "'hello'") }) it('handles single quoted string with spaces', function () { const args = tokenizeArgString("--foo 'hello world' --bar='foo bar'") strictEqual(args[0], '--foo') strictEqual(args[1], "'hello world'") strictEqual(args[2], "--bar='foo bar'") }) it('handles double quoted string with spaces', function () { const args = tokenizeArgString('--foo "hello world" --bar="foo bar"') strictEqual(args[0], '--foo') strictEqual(args[1], '"hello world"') strictEqual(args[2], '--bar="foo bar"') }) it('handles single quoted empty string', function () { const args = tokenizeArgString('--foo \'\' --bar=\'\'') strictEqual(args[0], '--foo') strictEqual(args[1], "''") strictEqual(args[2], "--bar=''") }) it('handles double quoted empty string', function () { const args = tokenizeArgString('--foo "" --bar=""') strictEqual(args[0], '--foo') strictEqual(args[1], '""') strictEqual(args[2], '--bar=""') }) it('handles quoted string with embedded quotes', function () { const args = tokenizeArgString('--foo "hello \'world\'" --bar=\'foo "bar"\'') strictEqual(args[0], '--foo') strictEqual(args[1], '"hello \'world\'"') strictEqual(args[2], '--bar=\'foo "bar"\'') }) // https://github.com/yargs/yargs-parser/pull/100 // https://github.com/yargs/yargs-parser/pull/106 it('ignores unneeded spaces', function () { const args = tokenizeArgString(' foo bar "foo bar" ') strictEqual(args[0], 'foo') strictEqual(args[1], 'bar') strictEqual(args[2], '"foo bar"') }) it('handles boolean options', function () { const args = tokenizeArgString('--foo -bar') strictEqual(args[0], '--foo') strictEqual(args[1], '-bar') }) it('handles empty string', function () { const args = tokenizeArgString('') strictEqual(args.length, 0) }) it('handles array with unquoted string', function () { const args = tokenizeArgString(['--foo', '99']) strictEqual(args[0], '--foo') strictEqual(args[1], '99') }) it('handles array with quoted string with no spaces', function () { const args = tokenizeArgString(['--foo', "'hello'"]) strictEqual(args[0], '--foo') strictEqual(args[1], "'hello'") }) it('handles array with single quoted string with spaces', function () { const args = tokenizeArgString(['--foo', "'hello world'", "--bar='foo bar'"]) strictEqual(args[0], '--foo') strictEqual(args[1], "'hello world'") strictEqual(args[2], "--bar='foo bar'") }) it('handles array with double quoted string with spaces', function () { const args = tokenizeArgString(['--foo', '"hello world"', '--bar="foo bar"']) strictEqual(args[0], '--foo') strictEqual(args[1], '"hello world"') strictEqual(args[2], '--bar="foo bar"') }) it('handles array with single quoted empty string', function () { const args = tokenizeArgString(['--foo', "''", "--bar=''"]) strictEqual(args[0], '--foo') strictEqual(args[1], "''") strictEqual(args[2], "--bar=''") }) it('handles array with double quoted empty string', function () { const args = tokenizeArgString(['--foo', '""', '--bar=""']) strictEqual(args[0], '--foo') strictEqual(args[1], '""') strictEqual(args[2], '--bar=""') }) it('handles array with quoted string with embedded quotes', function () { const args = tokenizeArgString(['--foo', '"hello \'world\'"', '--bar=\'foo "bar"\'']) strictEqual(args[0], '--foo') strictEqual(args[1], '"hello \'world\'"') strictEqual(args[2], '--bar=\'foo "bar"\'') }) it('handles array with boolean options', function () { const args = tokenizeArgString(['--foo', '-bar']) strictEqual(args[0], '--foo') strictEqual(args[1], '-bar') }) }) yargs-parser-yargs-parser-v21.0.0/test/typescript/types.ts000066400000000000000000000004761414461571200237310ustar00rootroot00000000000000/* global describe, it */ import yargsParser from '../../lib/index.js' import * as assert from 'assert' describe('types', function () { it('allows a partial options object to be provided', () => { const argv = yargsParser('--foo 99', { string: 'foo' }) assert.strictEqual(argv.foo, '99') }) }) yargs-parser-yargs-parser-v21.0.0/test/yargs-parser.cjs000066400000000000000000003642051414461571200231320ustar00rootroot00000000000000/* global beforeEach, describe, it */ require('chai').should() const { expect } = require('chai') const fs = require('fs') const parser = require('../build/index.cjs') const path = require('path') describe('yargs-parser', function () { it('should parse a "short boolean"', function () { const parse = parser(['-b']) parse.should.not.have.property('--') parse.should.have.property('b').to.be.ok.and.be.a('boolean') parse.should.have.property('_').with.length(0) }) it('should parse a "long boolean"', function () { const parse = parser('--bool') parse.should.not.have.property('--') parse.should.have.property('bool', true) parse.should.have.property('_').with.length(0) }) it('should place bare options in the _ array', function () { const parse = parser('foo bar baz') parse.should.have.property('_').and.deep.equal(['foo', 'bar', 'baz']) }) it('should set the value of the final option in a group to the next supplied value', function () { const parse = parser(['-cats', 'meow']) parse.should.have.property('c', true) parse.should.have.property('a', true) parse.should.have.property('t', true) parse.should.have.property('s', 'meow') parse.should.have.property('_').with.length(0) }) it('should set the value of a single long option to the next supplied value', function () { const parse = parser(['--pow', 'xixxle']) parse.should.have.property('pow', 'xixxle') parse.should.have.property('_').with.length(0) }) it('should set the value of a single long option to the next supplied value, even if the value is empty', function () { const parse = parser(['--pow', '']) parse.should.have.property('pow', '') parse.should.have.property('_').with.length(0) }) it('should set the value of a single long option if an = was used', function () { const parse = parser(['--pow=xixxle']) parse.should.have.property('pow', 'xixxle') parse.should.have.property('_').with.length(0) }) it('should set the value of multiple long options to the next supplied values relative to each', function () { const parse = parser(['--host', 'localhost', '--port', '555']) parse.should.have.property('host', 'localhost') parse.should.have.property('port', 555) parse.should.have.property('_').with.length(0) }) it('should set the value of multiple long options if = signs were used', function () { const parse = parser(['--host=localhost', '--port=555']) parse.should.have.property('host', 'localhost') parse.should.have.property('port', 555) parse.should.have.property('_').with.length(0) }) it('should still set values appropriately if a mix of short, long, and grouped short options are specified', function () { const parse = parser(['-h', 'localhost', '-fp', '555', 'script.js']) parse.should.have.property('f', true) parse.should.have.property('p', 555) parse.should.have.property('h', 'localhost') parse.should.have.property('_').and.deep.equal(['script.js']) }) it('should still set values appropriately if a mix of short and long options are specified', function () { const parse = parser(['-h', 'localhost', '--port', '555']) parse.should.have.property('h', 'localhost') parse.should.have.property('port', 555) parse.should.have.property('_').with.length(0) }) it('should explicitly set a boolean option to false if preceded by "--no-"', function () { const parse = parser(['--no-moo']) parse.should.have.property('moo', false) parse.should.have.property('_').with.length(0) }) it('should still set values appropriately if we supply a comprehensive list of various types of options', function () { const parse = parser([ '--name=meowmers', 'bare', '-cats', 'woo', '-h', 'awesome', '--multi=quux', '--key', 'value', '-b', '--bool', '--no-meep', '--multi=baz', '--', '--not-a-flag', '-', '-h', '-multi', '--', 'eek' ], { configuration: { 'populate--': false } }) parse.should.have.property('c', true) parse.should.have.property('a', true) parse.should.have.property('t', true) parse.should.have.property('s', 'woo') parse.should.have.property('h', 'awesome') parse.should.have.property('b', true) parse.should.have.property('bool', true) parse.should.have.property('key', 'value') parse.should.have.property('multi').and.deep.equal(['quux', 'baz']) parse.should.have.property('meep', false) parse.should.have.property('name', 'meowmers') parse.should.have.property('_').and.deep.equal(['bare', '--not-a-flag', '-', '-h', '-multi', '--', 'eek']) }) it('should parse numbers appropriately', function () { const argv = parser([ '-x', '1234', '-y', '5.67', '-z', '1e7', '-w', '10f', '--hex', '0xdeadbeef', '789' ]) argv.should.have.property('x', 1234).and.be.a('number') argv.should.have.property('y', 5.67).and.be.a('number') argv.should.have.property('z', 1e7).and.be.a('number') argv.should.have.property('w', '10f').and.be.a('string') argv.should.have.property('hex', 0xdeadbeef).and.be.a('number') argv.should.have.property('_').and.deep.equal([789]) argv._[0].should.be.a('number') }) // addresses: https://github.com/yargs/yargs-parser/issues/33 it('should handle parsing negative #s', function () { const argv = parser([ '-33', '-177', '33', '--n1', '-33', '-n', '-44', '--n2=-55', '--foo.bar', '-33', '-o=-55', '--bounds', '-180', '99', '-180', '90', '--other', '-99', '-220' ], { array: 'bounds', narg: { other: 2 } }) argv._.should.deep.equal([-33, -177, 33]) argv.n1.should.equal(-33) argv.n.should.equal(-44) argv.n2.should.equal(-55) argv.foo.bar.should.equal(-33) argv.o.should.equal(-55) argv.bounds.should.deep.equal([-180, 99, -180, 90]) argv.other.should.deep.equal([-99, -220]) }) it('should handle negative (and positive) numbers with decimal places, with or without a leading 0', function () { const argv = parser([ '-0.1', '-1.1', '-.5', '-.1', '.1', '.5', '--bounds', '-5.1', '-.1', '.1', '--other', '.9', '-.5' ], { array: 'bounds', narg: { other: 2 } }) argv._.should.deep.equal([-0.1, -1.1, -0.5, -0.1, 0.1, 0.5]) argv.bounds.should.deep.equal([-5.1, -0.1, 0.1]) argv.other.should.deep.equal([0.9, -0.5]) }) it('should set the value of a single short option to the next supplied value, even if the value is empty', function () { const parse = parser(['-p', '']) parse.should.have.property('p', '') parse.should.have.property('_').with.length(0) }) it('should not set the next value as the value of a short option if that option is explicitly defined as a boolean', function () { const parse = parser(['-t', 'moo'], { boolean: 't' }) parse.should.have.property('t', true).and.be.a('boolean') parse.should.have.property('_').and.deep.equal(['moo']) }) it('should set boolean options values if the next value is "true" or "false"', function () { const parse = parser(['--verbose', 'false', 'moo', '-t', 'true'], { boolean: ['t', 'verbose'], default: { verbose: true } }) parse.should.have.property('verbose', false).and.be.a('boolean') parse.should.have.property('t', true).and.be.a('boolean') parse.should.have.property('_').and.deep.equal(['moo']) }) it('should not set boolean options values if the next value only contains the words "true" or "false"', function () { const parse = parser(['--verbose', 'aaatrueaaa', 'moo', '-t', 'aaafalseaaa'], { boolean: ['t', 'verbose'] }) parse.should.have.property('verbose', true).and.be.a('boolean') parse.should.have.property('t', true).and.be.a('boolean') parse.should.have.property('_').and.deep.equal(['aaatrueaaa', 'moo', 'aaafalseaaa']) }) it('should not use next value for boolean/number/string configured with zero narg', function () { const parse = parser(['--bool', 'false', '--nr', '7', '--str', 'foo'], { boolean: ['bool'], number: ['nr'], string: ['str'], narg: { bool: 0, nr: 0, str: 0 } }) parse.should.have.property('bool', true).and.be.a('boolean') parse.should.have.property('nr', undefined).and.be.a('undefined') parse.should.have.property('str', '').and.be.a('string') parse.should.have.property('_').and.deep.equal(['false', 7, 'foo']) }) it('should allow defining options as boolean in groups', function () { const parse = parser(['-x', '-z', 'one', 'two', 'three'], { boolean: ['x', 'y', 'z'] }) parse.should.have.property('x', true).and.be.a('boolean') parse.should.not.have.property('y') parse.should.have.property('z', true).and.be.a('boolean') parse.should.have.property('_').and.deep.equal(['one', 'two', 'three']) }) it('should correctly parse dot-notation boolean flags', function () { const parse = parser(['--nested', '--n.v', '--n.y', 'foo'], { boolean: ['nested', 'n.v'] }) parse.should.have.property('nested', true).and.be.a('boolean') parse.should.have.property('n').and.deep.equal({ v: true, y: 'foo' }) }) it('should preserve newlines in option values', function () { let args = parser(['-s', 'X\nX']) args.should.have.property('_').with.length(0) args.should.have.property('s', 'X\nX') // reproduce in bash: // VALUE="new // line" // node program.js --s="$VALUE" args = parser(['--s=X\nX']) args.should.have.property('_').with.length(0) args.should.have.property('s', 'X\nX') }) it('should not convert numbers to type number if explicitly defined as strings', function () { const s = parser(['-s', '0001234'], { string: 's' }).s s.should.be.a('string').and.equal('0001234') const x = parser(['-x', '56'], { string: ['x'] }).x x.should.be.a('string').and.equal('56') }) it('should default numbers to undefined', function () { const n = parser(['-n'], { number: ['n'] }).n expect(n).to.equal(undefined) }) it('should default number to NaN if value is not a valid number', function () { const n = parser(['-n', 'string'], { number: ['n'] }).n expect(n).to.deep.equal(NaN) }) // Fixes: https://github.com/bcoe/yargs/issues/68 it('should parse flag arguments with no right-hand value as strings, if defined as strings', function () { let s = parser(['-s'], { string: ['s'] }).s s.should.be.a('string').and.equal('') s = parser(['-sf'], { string: ['s'] }).s s.should.be.a('string').and.equal('') s = parser(['--string'], { string: ['string'] }).string s.should.be.a('string').and.equal('') }) it('should leave all non-hyphenated values as strings if _ is defined as a string', function () { const s = parser([' ', ' '], { string: ['_'] })._ s.should.have.length(2) s[0].should.be.a('string').and.equal(' ') s[1].should.be.a('string').and.equal(' ') }) describe('normalize', function () { it('should normalize redundant paths', function () { const a = parser(['-s', ['', 'tmp', '..', ''].join(path.sep)], { alias: { s: ['save'] }, normalize: 's' }) a.should.have.property('s', path.sep) a.should.have.property('save', path.sep) }) it('should normalize redundant paths when a value is later assigned', function () { const a = parser(['-s'], { normalize: ['s'] }) a.should.have.property('s', true) a.s = ['', 'path', 'to', 'new', 'dir', '..', '..', ''].join(path.sep) a.s.should.equal(['', 'path', 'to', ''].join(path.sep)) }) it('should normalize when key is also an array', function () { const a = parser(['-s', ['', 'tmp', '..', ''].join(path.sep), ['', 'path', 'to', 'new', 'dir', '..', '..', ''].join(path.sep)], { alias: { s: ['save'] }, normalize: 's', array: 's' }) const expected = [path.sep, ['', 'path', 'to', ''].join(path.sep)] a.should.have.property('s').and.deep.equal(expected) a.should.have.property('save').and.deep.equal(expected) }) it('should allow normalized keys to be enumerated', () => { const a = parser(['-s', ['', 'tmp', '..', ''].join(path.sep)], { alias: { s: ['save'] }, normalize: 's' }) Object.keys(a).should.include('s') }) }) describe('alias', function () { it('should set alias value to the same value as the full option', function () { const argv = parser(['-f', '11', '--zoom', '55'], { alias: { z: ['zoom'] } }) argv.should.have.property('zoom', 55) argv.should.have.property('z', 55) argv.should.have.property('f', 11) }) it('should allow multiple aliases to be specified', function () { const argv = parser(['-f', '11', '--zoom', '55'], { alias: { z: ['zm', 'zoom'] } }) argv.should.have.property('zoom', 55) argv.should.have.property('z', 55) argv.should.have.property('zm', 55) argv.should.have.property('f', 11) }) // regression, see https://github.com/chevex/yargs/issues/63 it('should not add the same key to argv multiple times, when creating camel-case aliases', function () { const argv = parser(['--health-check=banana', '--second-key', 'apple', '-t=blarg'], { alias: { h: ['health-check'], 'second-key': ['s'], 'third-key': ['t'] }, default: { h: 'apple', 'second-key': 'banana', 'third-key': 'third' } }) // before this fix, yargs failed parsing // one but not all forms of an arg. argv.secondKey.should.eql('apple') argv.s.should.eql('apple') argv['second-key'].should.eql('apple') argv.healthCheck.should.eql('banana') argv.h.should.eql('banana') argv['health-check'].should.eql('banana') argv.thirdKey.should.eql('blarg') argv.t.should.eql('blarg') argv['third-key'].should.eql('blarg') }) it('should allow transitive aliases to be specified', function () { const argv = parser(['-f', '11', '--zoom', '55'], { alias: { z: 'zm', zm: 'zoom' } }) argv.should.have.property('zoom', 55) argv.should.have.property('z', 55) argv.should.have.property('zm', 55) argv.should.have.property('f', 11) }) it('should merge two lists of aliases if they collide', function () { const argv = parser(['-f', '11', '--zoom', '55'], { alias: { z: 'zm', zoom: 'zoop', zm: 'zoom' } }) argv.should.have.property('zoom', 55) argv.should.have.property('zoop', 55) argv.should.have.property('z', 55) argv.should.have.property('zm', 55) argv.should.have.property('f', 11) }) it('should set single-digit boolean alias', function () { const argv = parser(['-f', '11', '-0'], { alias: { 0: 'print0' }, boolean: [ 'print0' ] }) argv.should.have.property('print0', true) argv.should.have.property('f', 11) }) it('should not set single-digit alias if no alias defined', function () { const argv = parser(['-f', '11', '-0', '-1']) argv.should.have.property('f', 11) argv._.should.deep.equal([-0, -1]) }) it('should not set single-digit boolean alias if no boolean defined', function () { const argv = parser(['-f', '11', '-9'], { alias: { 0: 'print0' } }) argv.should.have.property('f', 11) argv._.should.deep.equal([-9]) }) it('should be able to negate set single-digit boolean alias', function () { const argv = parser(['--no-9'], { alias: { 9: 'max' }, boolean: [ 'max' ] }) argv.should.have.property('max', false) }) }) it('should assign data after forward slash to the option before the slash', function () { let parse = parser(['-I/foo/bar/baz']) parse.should.have.property('_').with.length(0) parse.should.have.property('I', '/foo/bar/baz') parse = parser(['-xyz/foo/bar/baz']) parse.should.have.property('x', true) parse.should.have.property('y', true) parse.should.have.property('z', '/foo/bar/baz') parse.should.have.property('_').with.length(0) }) const jsonPath = path.resolve(__dirname, './fixtures/config.json') describe('config', function () { // See: https://github.com/chevex/yargs/issues/12 it('should load options and values from default config if specified', function () { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') }) it('should use value from config file, if argv value is using default value', function () { const argv = parser([], { alias: { z: 'zoom' }, config: ['settings'], default: { settings: jsonPath, foo: 'banana' } }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('baz') }) it('should combine values from config file and argv, if argv value is an array', function () { const argv = parser(['--foo', 'bar'], { config: ['settings'], array: ['foo'], default: { settings: jsonPath }, configuration: { 'combine-arrays': true } }) argv.should.have.property('foo').and.deep.equal(['bar', 'baz']) }) it('should use value from config file, if argv key is a boolean', function () { const argv = parser([], { config: ['settings'], default: { settings: jsonPath }, boolean: ['truthy'] }) argv.should.have.property('truthy', true) }) it('should use value from cli, if cli overrides boolean argv key', function () { const argv = parser(['--no-truthy'], { config: ['settings'], default: { settings: jsonPath }, boolean: ['truthy'] }) argv.should.have.property('truthy', false) }) it('should use cli value, if cli value is set and both cli and default value match', function () { const argv = parser(['--foo', 'banana'], { alias: { z: 'zoom' }, config: ['settings'], default: { settings: jsonPath, foo: 'banana' } }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('banana') }) it("should allow config to be set as flag in 'option'", function () { const argv = parser(['--settings', jsonPath, '--foo', 'bar'], { alias: { z: 'zoom' }, config: ['settings'] }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') }) it('should load options and values from a JS file when config has .js extention', function () { const jsPath = path.resolve(__dirname, './fixtures/settings.cjs') const argv = parser(['--settings', jsPath, '--foo', 'bar'], { config: ['settings'] }) argv.should.have.property('herp', 'derp') argv.should.have.property('foo', 'bar') argv.should.have.property('calculate').and.be.a('function') }) it('should raise an appropriate error if JSON file is not found', function () { const argv = parser.detailed(['--settings', 'fake.json', '--foo', 'bar'], { alias: { z: 'zoom' }, config: ['settings'] }) argv.error.message.should.equal('Invalid JSON config file: fake.json') }) // see: https://github.com/bcoe/yargs/issues/172 it('should not raise an exception if config file is set as default argument value', function () { const argv = parser.detailed([], { default: { config: 'foo.json' }, config: ['config'] }) expect(argv.error).to.equal(null) }) it('should load nested options from config file', function () { const jsonPath = path.resolve(__dirname, './fixtures/nested_config.json') const argv = parser(['--settings', jsonPath, '--nested.foo', 'bar'], { config: ['settings'] }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'bar', bar: 'bar' }) }) it('should use nested value from config file, if argv value is using default value', function () { const jsonPath = path.resolve(__dirname, './fixtures/nested_config.json') const argv = parser(['--settings', jsonPath], { config: ['settings'], default: { 'nested.foo': 'banana' } }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'baz', bar: 'bar' }) }) it('allows a custom parsing function to be provided', function () { const jsPath = path.resolve(__dirname, './fixtures/config.txt') const argv = parser(['--settings', jsPath, '--foo', 'bar'], { config: { settings: function (configPath) { // as an example, parse an environment // variable style config: // FOO=99 // BATMAN=grumpy const config = {} const txt = fs.readFileSync(configPath, 'utf-8') txt.split(/\r?\n/).forEach(function (l) { const kv = l.split('=') config[kv[0].toLowerCase()] = kv[1] }) return config } } }) argv.batman.should.equal('grumpy') argv.awesome.should.equal('banana') argv.foo.should.equal('bar') }) it('allows a custom parsing function to be provided as an alias', function () { const jsPath = path.resolve(__dirname, './fixtures/config.json') const argv = parser(['--settings', jsPath, '--foo', 'bar'], { config: { s: function (configPath) { return JSON.parse(fs.readFileSync(configPath, 'utf-8')) } }, alias: { s: ['settings'] } }) argv.should.have.property('herp', 'derp') argv.should.have.property('foo', 'bar') }) it('outputs an error returned by the parsing function', function () { const argv = parser.detailed(['--settings=./package.json'], { config: { settings: function (configPath) { return Error('someone set us up the bomb') } } }) argv.error.message.should.equal('someone set us up the bomb') }) it('outputs an error if thrown by the parsing function', function () { const argv = parser.detailed(['--settings=./package.json'], { config: { settings: function (configPath) { throw Error('someone set us up the bomb') } } }) argv.error.message.should.equal('someone set us up the bomb') }) it('should not pollute the prototype', function () { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') expect({}.bbb).to.equal(undefined) expect({}.aaa).to.equal(undefined) }) }) describe('config objects', function () { it('should load options from config object', function () { const argv = parser(['--foo', 'bar'], { configObjects: [{ apple: 'apple', banana: 42, foo: 'baz', gotcha: null }] }) argv.should.have.property('apple', 'apple') argv.should.have.property('banana', 42) argv.should.have.property('foo', 'bar') argv.should.have.property('gotcha', null) }) it('should use value from config object, if argv value is using default value', function () { const argv = parser([], { configObjects: [{ apple: 'apple', banana: 42, foo: 'baz' }], default: { foo: 'banana' } }) argv.should.have.property('apple', 'apple') argv.should.have.property('banana', 42) argv.should.have.property('foo', 'baz') }) it('should use value from config object to all aliases', function () { const argv = parser([], { configObjects: [{ apple: 'apple', banana: 42, foo: 'baz' }], alias: { a: ['apple'], banana: ['b'] } }) argv.should.have.property('apple', 'apple') argv.should.have.property('a', 'apple') argv.should.have.property('banana', 42) argv.should.have.property('b', 42) argv.should.have.property('foo', 'baz') }) it('should load nested options from config object', function () { const argv = parser(['--nested.foo', 'bar'], { configObjects: [{ a: 'a', nested: { foo: 'baz', bar: 'bar' }, b: 'b' }] }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'bar', bar: 'bar' }) }) it('should use nested value from config object, if argv value is using default value', function () { const argv = parser([], { configObjects: [{ a: 'a', nested: { foo: 'baz', bar: 'bar' }, b: 'b' }], default: { 'nested.foo': 'banana' } }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'baz', bar: 'bar' }) }) it('should load objects with first object having greatest priority', function () { const argv = parser(['--foo', 'bar'], { configObjects: [{ bar: 'baz' }, { bar: 'quux', foo: 'spam' }] }) argv.should.have.property('foo', 'bar') argv.should.have.property('bar', 'baz') }) it('should combine array typed options with alias and camel-case', function () { const argv = parser(['--camEl', 'foo', '--camEl', 'bar', '-a', 'red'], { array: ['cam-el', 'apple'], alias: { apple: 'a' }, configObjects: [{ camEl: 'baz' }, { a: 'sweet' }], configuration: { 'combine-arrays': true, 'camel-case-expansion': true } }) argv['cam-el'].should.deep.equal(['foo', 'bar', 'baz']) argv.apple.should.deep.equal(['red', 'sweet']) }) }) describe('dot notation', function () { it('should allow object graph traversal via dot notation', function () { const argv = parser([ '--foo.bar', '3', '--foo.baz', '4', '--foo.quux.quibble', '5', '--foo.quux.o_O', '--beep.boop' ]) argv.should.have.property('foo').and.deep.equal({ bar: 3, baz: 4, quux: { quibble: 5, o_O: true } }) argv.should.have.property('beep').and.deep.equal({ boop: true }) }) it('should apply defaults to dot notation arguments', function () { const argv = parser([], { default: { 'foo.bar': 99 } }) argv.foo.bar.should.eql(99) }) // see #279 it('should allow default to be overridden when an alias is provided', function () { const argv = parser(['--foo.bar', '200'], { default: { 'foo.bar': 99 } }) argv.foo.bar.should.eql(200) }) // see #279 it('should also override alias', function () { const argv = parser(['--foo.bar', '200'], { alias: { 'foo.bar': ['f'] }, default: { 'foo.bar': 99 } }) argv.f.should.eql(200) }) // see #279 it('should not set an undefined dot notation key', function () { const argv = parser(['--foo.bar', '200'], { default: { 'foo.bar': 99 }, alias: { 'foo.bar': ['f'] } }) ; ('foo.bar' in argv).should.equal(false) }) it('should respect .string() for dot notation arguments', function () { const argv = parser(['--foo.bar', '99', '--bar.foo=99'], { string: ['foo.bar'] }) argv.foo.bar.should.eql('99') argv.bar.foo.should.eql(99) }) it('should populate aliases when dot notation is used', function () { const argv = parser(['--foo.bar', '99'], { alias: { foo: ['f'] } }) argv.f.bar.should.eql(99) argv.foo.bar.should.eql(99) }) // see #267 it('should populate aliases when dot notation is used on camel-cased option', function () { const argv = parser(['--foo-baz.bar', '99'], { alias: { 'foo-baz': ['f'] } }) argv.f.bar.should.eql(99) argv['foo-baz'].bar.should.eql(99) argv.fooBaz.bar.should.eql(99) }) it('should populate aliases when nested dot notation is used', function () { const argv = parser(['--foo.bar.snuh', '99', '--foo.apple', '33', '--foo.bar.cool', '11'], { alias: { foo: ['f'] } }) argv.f.bar.snuh.should.eql(99) argv.foo.bar.snuh.should.eql(99) argv.f.apple.should.eql(33) argv.foo.apple.should.eql(33) argv.f.bar.cool.should.eql(11) argv.foo.bar.cool.should.eql(11) }) it("should allow flags to use dot notation, when separated by '='", function () { const argv = parser(['-f.foo=99']) argv.f.foo.should.eql(99) }) it("should allow flags to use dot notation, when separated by ' '", function () { const argv = parser(['-f.foo', '99']) argv.f.foo.should.eql(99) }) it('should allow flags to use dot notation when no right-hand-side is given', function () { const argv = parser(['-f.foo', '99', '-f.bar']) argv.f.foo.should.eql(99) argv.f.bar.should.eql(true) }) it('should not pollute the prototype', function () { parser(['-f.__proto__.foo', '99', '-x.y.__proto__.bar', '100', '--__proto__', '200']) Object.keys({}.__proto__).length.should.equal(0) // eslint-disable-line expect({}.foo).to.equal(undefined) expect({}.bar).to.equal(undefined) }) }) it('should set boolean and alias using explicit true', function () { const aliased = ['-h', 'true'] const aliasedArgv = parser(aliased, { boolean: ['h'], alias: { h: ['herp'] } }) aliasedArgv.should.have.property('herp', true) aliasedArgv.should.have.property('h', true) aliasedArgv.should.have.property('_').with.length(0) }) // regression, see https://github.com/substack/node-optimist/issues/71 it('should set boolean and --x=true', function () { let parsed = parser(['--boool', '--other=true'], { boolean: ['boool'] }) parsed.should.have.property('boool', true) parsed.should.have.property('other', 'true') parsed = parser(['--boool', '--other=false'], { boolean: ['boool'] }) parsed.should.have.property('boool', true) parsed.should.have.property('other', 'false') }) // regression, see https://github.com/chevex/yargs/issues/66 it('should set boolean options values if next value is "true" or "false" with = as separator', function () { const argv = parser(['--bool=false'], { boolean: ['b'], alias: { b: ['bool'] }, default: { b: true } }) argv.bool.should.eql(false) }) describe('short options', function () { it('should set the value of multiple single short options to the next supplied values relative to each', function () { const parse = parser(['-h', 'localhost', '-p', '555']) parse.should.have.property('h', 'localhost') parse.should.have.property('p', 555) parse.should.have.property('_').with.length(0) }) it('should set the value of a single short option to the next supplied value', function () { const parse = parser(['-h', 'localhost']) parse.should.have.property('h', 'localhost') parse.should.have.property('_').with.length(0) }) it('should expand grouped short options to a hash with a key for each', function () { const parse = parser(['-cats']) parse.should.have.property('c', true) parse.should.have.property('a', true) parse.should.have.property('t', true) parse.should.have.property('s', true) parse.should.have.property('_').with.length(0) }) it('should set n to the numeric value 123', function () { const argv = parser(['-n123']) argv.should.have.property('n', 123) }) it('should set n to the numeric value 123, with n at the end of a group', function () { const argv = parser(['-ab5n123']) argv.should.have.property('a', true) argv.should.have.property('b', true) argv.should.have.property('5', true) argv.should.have.property('n', 123) argv.should.have.property('_').with.length(0) }) it('should set n to the numeric value 123, with = as separator', function () { const argv = parser(['-n=123']) argv.should.have.property('n', 123) }) it('should set n to the numeric value 123, with n at the end of a group and = as separator', function () { const argv = parser(['-ab5n=123']) argv.should.have.property('a', true) argv.should.have.property('b', true) argv.should.have.property('5', true) argv.should.have.property('n', 123) argv.should.have.property('_').with.length(0) }) }) describe('whitespace', function () { it('should be whitespace', function () { const argv = parser(['-x', '\t']) argv.should.have.property('x', '\t') }) }) describe('boolean modifier function', function () { it('should prevent yargs from sucking in the next option as the value of the first option', function () { // Arrange & Act const result = parser(['-b', '123'], { boolean: ['b'] }) // Assert result.should.have.property('b').that.is.a('boolean').and.is.true // eslint-disable-line result.should.have.property('_').and.deep.equal([123]) }) // Fixes: https://github.com/yargs/yargs-parser/issues/283 it('should set boolean numeric option, with numeric option at the end of a group', function () { const result = parser(['-x1'], { boolean: ['x', '1'] }) expect(result).to.have.property('x', true) expect(result).to.have.property('1', true) }) it('should set boolean numeric option, with numeric option at the start of a group', function () { const result = parser(['-1x'], { boolean: ['x', '1'] }) expect(result).to.have.property('x', true) expect(result).to.have.property('1', true) }) it('should set boolean numeric option, with numeric option as part of a group', function () { const result = parser(['-x1b'], { boolean: ['x', '1', 'b'] }) expect(result).to.have.property('x', true) expect(result).to.have.property('1', true) expect(result).to.have.property('b', true) }) }) describe('defaults', function () { function checkNoArgs (opts, hasAlias) { it('should set defaults if no args', function () { const result = parser([], opts) result.should.have.property('flag', true) if (hasAlias) { result.should.have.property('f', true) } }) } function checkExtraArg (opts, hasAlias) { it('should set defaults if one extra arg', function () { const result = parser(['extra'], opts) result.should.have.property('flag', true) result.should.have.property('_').and.deep.equal(['extra']) if (hasAlias) { result.should.have.property('f', true) } }) } function checkStringArg (opts, hasAlias) { it('should set defaults even if arg looks like a string', function () { const result = parser(['--flag', 'extra'], opts) result.should.have.property('flag', true) result.should.have.property('_').and.deep.equal(['extra']) if (hasAlias) { result.should.have.property('f', true) } }) } describe('for options with aliases', function () { const opts = { alias: { flag: ['f'] }, default: { flag: true } } checkNoArgs(opts, true) checkExtraArg(opts, true) }) describe('for typed options without aliases', function () { const opts = { boolean: ['flag'], default: { flag: true } } checkNoArgs(opts) checkExtraArg(opts) checkStringArg(opts) }) describe('for typed options with aliases', function () { const opts = { alias: { flag: ['f'] }, boolean: ['flag'], default: { flag: true } } checkNoArgs(opts, true) checkExtraArg(opts, true) checkStringArg(opts, true) }) describe('for boolean options', function () { [true, false, undefined, null].forEach(function (def) { describe('with explicit ' + def + ' default', function () { const opts = { default: { flag: def }, boolean: ['flag'] } it('should set true if --flag in arg', function () { parser(['--flag'], opts).flag.should.be.true // eslint-disable-line }) it('should set false if --no-flag in arg', function () { parser(['--no-flag'], opts).flag.should.be.false // eslint-disable-line }) it('should set ' + def + ' if no flag in arg', function () { expect(parser([], opts).flag).to.equal(def) }) }) }) describe('without any default value', function () { let opts = null beforeEach(function () { opts = { boolean: ['flag'] } }) it('should set true if --flag in arg', function () { parser(['--flag'], opts).flag.should.be.true // eslint-disable-line }) it('should set false if --no-flag in arg', function () { parser(['--no-flag'], opts).flag.should.be.false // eslint-disable-line }) it('should not add property if no flag in arg', function () { parser([''], opts).should.not.have.property('flag') }) }) // Fixes: https://github.com/bcoe/yargs/issues/341 it('should apply defaults to camel-case form of argument', function () { const argv = parser([], { default: { 'foo-bar': 99 } }) argv.fooBar.should.equal(99) }) // Fixes: https://github.com/yargs/yargs-parser/issues/77 it('should combine dot-notation and camel-case expansion', function () { const argv = parser(['--dot-notation.foo.bar']) argv.should.satisfy(function (args) { return args.dotNotation.foo.bar }) }) }) it('should define option as boolean and set default to true', function () { const argv = parser([], { boolean: ['sometrue'], default: { sometrue: true } }) argv.should.have.property('sometrue', true) }) it('should define option as boolean and set default to false', function () { const argv = parser([], { default: { somefalse: false }, boolean: ['somefalse'] }) argv.should.have.property('somefalse', false) }) it('should set boolean options to false by default', function () { const parse = parser(['moo'], { boolean: ['t', 'verbose'], default: { verbose: false, t: false } }) parse.should.have.property('verbose', false).and.be.a('boolean') parse.should.have.property('t', false).and.be.a('boolean') parse.should.have.property('_').and.deep.equal(['moo']) }) describe('track defaulted', function () { it('should log defaulted options - not specified by user', function () { const parsed = parser.detailed('', { default: { foo: 'abc', 'bar.prop': 33, baz: 'x' }, configObjects: [{ baz: 'xyz' }] }) expect(parsed.argv).to.eql({ _: [], baz: 'xyz', foo: 'abc', bar: { prop: 33 } }) parsed.defaulted.should.deep.equal({ foo: true, 'bar.prop': true }) }) it('should not log defaulted options - specified without value', function () { const parsed = parser.detailed('--foo --bar.prop', { default: { foo: 'abc', 'bar.prop': 33 } }) parsed.argv.should.deep.equal({ _: [], foo: 'abc', bar: { prop: 33 } }) parsed.defaulted.should.deep.equal({}) }) it('should log defaulted options - no aliases included', function () { const parsed = parser.detailed('', { default: { kaa: 'abc' }, alias: { foo: 'kaa' } }) parsed.argv.should.deep.equal({ _: [], kaa: 'abc', foo: 'abc' }) parsed.defaulted.should.deep.equal({ kaa: true }) }) it('setting an alias excludes associated key from defaulted', function () { const parsed = parser.detailed('--foo abc', { default: { kaa: 'abc' }, alias: { foo: 'kaa' } }) parsed.argv.should.deep.equal({ _: [], kaa: 'abc', foo: 'abc' }) parsed.defaulted.should.deep.equal({}) }) }) }) describe('camelCase', function () { function runTests (strict) { if (!strict) { // Skip this test in strict mode because this option is not specified it('should provide options with dashes as camelCase properties', function () { const result = parser(['--some-option']) result.should.have.property('some-option').that.is.a('boolean').and.is.true // eslint-disable-line result.should.have.property('someOption').that.is.a('boolean').and.is.true // eslint-disable-line }) } it('should provide count options with dashes as camelCase properties', function () { const result = parser(['--some-option', '--some-option', '--some-option'], { count: ['some-option'] }) result.should.have.property('some-option', 3) result.should.have.property('someOption', 3) }) it('should provide options with dashes and aliases as camelCase properties', function () { const result = parser(['--some-option'], { alias: { 'some-horse': 'o' } }) result.should.have.property('some-option').that.is.a('boolean').and.is.true // eslint-disable-line result.should.have.property('someOption').that.is.a('boolean').and.is.true // eslint-disable-line }) it('should provide defaults of options with dashes as camelCase properties', function () { const result = parser([], { default: { 'some-option': 'asdf' } }) result.should.have.property('some-option', 'asdf') result.should.have.property('someOption', 'asdf') }) it('should provide aliases of options with dashes as camelCase properties', function () { const result = parser([], { default: { 'some-option': 'asdf' }, alias: { 'some-option': ['o'] } }) result.should.have.property('o', 'asdf') result.should.have.property('some-option', 'asdf') result.should.have.property('someOption', 'asdf') }) it('should not apply camel-case logic to 1-character options', function () { const result = parser(['-p', 'hello'], { alias: { p: 'parallel', P: 'parallel-series' } }) result.should.not.have.property('P', 'hello') result.should.not.have.property('parallel-series', 'hello') result.should.not.have.property('parallelSeries', 'hello') result.should.have.property('parallel', 'hello') result.should.have.property('p', 'hello') }) it('should provide aliases of options with dashes as camelCase properties', function () { const result = parser([], { alias: { o: ['some-option'] }, default: { o: 'asdf' } }) result.should.have.property('o', 'asdf') result.should.have.property('some-option', 'asdf') result.should.have.property('someOption', 'asdf') }) it('should provide aliases with dashes as camelCase properties', function () { const result = parser(['--some-option', 'val'], { alias: { o: 'some-option' } }) result.should.have.property('o').that.is.a('string').and.equals('val') result.should.have.property('some-option').that.is.a('string').and.equals('val') result.should.have.property('someOption').that.is.a('string').and.equals('val') }) // https://github.com/yargs/yargs-parser/issues/95 it('should not duplicate option values when equivalent dashed aliases are provided', function () { const result = parser(['--someOption', 'val'], { alias: { someOption: 'some-option' } }) result.should.have.property('some-option').that.is.a('string').and.equals('val') result.should.have.property('someOption').that.is.a('string').and.equals('val') }) } describe('dashes and camelCase', function () { runTests() }) describe('dashes and camelCase (strict)', function () { runTests(true) }) }) describe('-', function () { it('should set - as value of n', function () { const argv = parser(['-n', '-']) argv.should.have.property('n', '-') argv.should.have.property('_').with.length(0) }) it('should set - as a non-hyphenated value', function () { const argv = parser(['-']) argv.should.have.property('_').and.deep.equal(['-']) }) it('should set - as a value of f', function () { const argv = parser(['-f-']) argv.should.have.property('f', '-') argv.should.have.property('_').with.length(0) }) it('should set b to true and set - as a non-hyphenated value when b is set as a boolean', function () { const argv = parser(['-b', '-'], { boolean: ['b'] }) argv.should.have.property('b', true) argv.should.have.property('_').and.deep.equal(['-']) }) it('should set - as the value of s when s is set as a string', function () { const argv = parser(['-s', '-'], { string: ['s'] }) argv.should.have.property('s', '-') argv.should.have.property('_').with.length(0) }) }) describe('count', function () { it('should count the number of times a boolean is present', function () { let parsed parsed = parser(['-x'], { count: ['verbose'] }) parsed.verbose.should.equal(0) parsed = parser(['--verbose'], { count: ['verbose'] }) parsed.verbose.should.equal(1) parsed = parser(['--verbose', '--verbose'], { count: ['verbose'] }) parsed.verbose.should.equal(2) parsed = parser(['-vvv'], { alias: { v: ['verbose'] }, count: ['verbose'] }) parsed.verbose.should.equal(3) parsed = parser(['--verbose', '--verbose', '-v', '--verbose'], { count: ['verbose'], alias: { v: ['verbose'] } }) parsed.verbose.should.equal(4) parsed = parser(['--verbose', '--verbose', '-v', '-vv'], { count: ['verbose'], alias: { v: ['verbose'] } }) parsed.verbose.should.equal(5) }) it('should not consume the next argument', function () { let parsed = parser(['-v', 'moo'], { count: 'v' }) parsed.v.should.equal(1) parsed.should.have.property('_').and.deep.equal(['moo']) parsed = parser(['--verbose', 'moomoo', '--verbose'], { count: 'verbose' }) parsed.verbose.should.equal(2) parsed.should.have.property('_').and.deep.equal(['moomoo']) }) it('should use a default value as is when no arg given', function () { let parsed = parser([], { count: 'v', default: { v: 3 } }) parsed.v.should.equal(3) parsed = parser([], { count: 'v', default: { v: undefined } }) expect(parsed.v).to.be.undefined // eslint-disable-line parsed = parser([], { count: 'v', default: { v: null } }) expect(parsed.v).to.be.null // eslint-disable-line parsed = parser([], { count: 'v', default: { v: false } }) parsed.v.should.equal(false) parsed = parser([], { count: 'v', default: { v: 'hello' } }) parsed.v.should.equal('hello') }) it('should ignore a default value when arg given', function () { const parsed = parser(['-vv', '-v', '-v'], { count: 'v', default: { v: 1 } }) parsed.v.should.equal(4) }) it('should increment regardless of arg value', function () { const parsed = parser([ '-v', '-v=true', '-v', 'true', '-v=false', '-v', 'false', '--no-v', '-v=999', '-v=foobar' ], { count: 'v' }) parsed.v.should.equal(8) }) it('should add an error if counter is also set as array', function () { const argv = parser.detailed(['--counter', '--counter', '--counter'], { count: ['counter'], array: ['counter'] }) argv.error.message.should.equal('Invalid configuration: counter, opts.count excludes opts.array.') }) it('should add an error if counter is also set as narg', function () { const argv = parser.detailed(['--counter', 'foo', 'bar'], { count: ['counter'], narg: { counter: 2 } }) argv.error.message.should.equal('Invalid configuration: counter, opts.count excludes opts.narg.') }) }) describe('array', function () { it('should group values into an array if the same option is specified multiple times (duplicate-arguments-array=true)', function () { const parse = parser(['-v', 'a', '-v', 'b', '-v', 'c'], { configuration: { 'duplicate-arguments-array': true } }) parse.should.have.property('v').and.deep.equal(['a', 'b', 'c']) parse.should.have.property('_').with.length(0) }) it('should keep only the last value if the same option is specified multiple times (duplicate-arguments-false)', function () { const parse = parser(['-v', 'a', '-v', 'b', '-v', 'c'], { configuration: { 'duplicate-arguments-array': false } }) parse.should.have.property('v').and.equal('c') parse.should.have.property('_').with.length(0) }) it('should default an array to an empty array if passed as first option followed by another', function () { const result = parser(['-a', '-b'], { array: 'a' }) result.should.have.property('a').and.deep.equal([]) }) it('should not attempt to default array if an element has already been populated', function () { const result = parser(['-a', 'foo', 'bar', '-b'], { array: 'a' }) result.should.have.property('a').and.deep.equal(['foo', 'bar']) }) it('should default argument to empty array if no value given', function () { const result = parser(['-b', '--tag'], { array: ['b', 'tag'], default: { tag: [] } }) result.b.should.deep.equal([]) result.tag.should.deep.equal([]) }) it('should place default of argument in array, when default provided', function () { const result = parser(['-b', '--tag'], { array: ['b', 'tag'], default: { b: 33, tag: ['foo'] } }) result.b.should.deep.equal([33]) result.tag.should.deep.equal(['foo']) }) it('should place value of argument in array, when one argument provided', function () { const result = parser(['-b', '33'], { array: ['b'] }) Array.isArray(result.b).should.equal(true) result.b[0].should.equal(33) }) it('should add multiple argument values to the array', function () { const result = parser(['-b', '33', '-b', 'hello'], { array: 'b' }) Array.isArray(result.b).should.equal(true) result.b.should.include(33) result.b.should.include('hello') }) it('should allow array: true, to be set inside an option block', function () { const result = parser(['-b', '33'], { array: 'b' }) Array.isArray(result.b).should.equal(true) result.b.should.include(33) }) // issue #103 it('should default camel-case alias to array type', function () { const result = parser(['--ca-path', 'http://www.example.com'], { array: ['ca-path'] }) Array.isArray(result['ca-path']).should.equal(true) Array.isArray(result.caPath).should.equal(true) }) it('should default alias to array type', function () { const result = parser(['--ca-path', 'http://www.example.com'], { array: 'ca-path', alias: { 'ca-path': 'c' } }) Array.isArray(result['ca-path']).should.equal(true) Array.isArray(result.caPath).should.equal(true) Array.isArray(result.c).should.equal(true) }) // see: https://github.com/bcoe/yargs/issues/162 it('should eat non-hyphenated arguments until hyphenated option is hit', function () { const result = parser(['-a=hello', 'world', '-b', '33', '22', '--foo', 'red', 'green', '--bar=cat', 'dog'], { array: ['a', 'b', 'foo', 'bar'] }) Array.isArray(result.a).should.equal(true) result.a.should.include('hello') result.a.should.include('world') Array.isArray(result.b).should.equal(true) result.b.should.include(33) result.b.should.include(22) Array.isArray(result.foo).should.equal(true) result.foo.should.include('red') result.foo.should.include('green') Array.isArray(result.bar).should.equal(true) result.bar.should.include('cat') result.bar.should.include('dog') }) // see: https://github.com/yargs/yargs-parser/pull/13 it('should support array for --foo= format when the key is a number', function () { const result = parser(['--1=a', 'b'], { array: ['1'] }) Array.isArray(result['1']).should.equal(true) result['1'][0].should.equal('a') result['1'][1].should.equal('b') }) it('should support array for -f= and --bar= format when the value is dashed', function () { const result = parser(['-f=--dog', 'cat', '--bar=--red', 'green'], { array: ['f', 'bar'] }) Array.isArray(result.f).should.equal(true) result.f[0].should.equal('--dog') result.f[1].should.equal('cat') Array.isArray(result.bar).should.equal(true) result.bar[0].should.equal('--red') result.bar[1].should.equal('green') }) it('should create an array when passing an argument twice with same value', function () { const result = parser(['-x', 'val1', '-x', 'val1']) result.should.have.property('x').that.is.an('array').and.to.deep.equal(['val1', 'val1']) }) it('should eat camelCase switch with camelCase array option', function () { const result = parser(['--someOption', '1', '2'], { array: ['someOption'] }) Array.isArray(result.someOption).should.equal(true) result.someOption.should.deep.equal([1, 2]) }) it('should eat hyphenated switch with hyphenated array option', function () { const result = parser(['--some-option', '1', '2'], { array: ['some-option'] }) Array.isArray(result['some-option']).should.equal(true) result['some-option'].should.deep.equal([1, 2]) }) it('should eat camelCase switch with hyphenated array option', function () { const result = parser(['--someOption', '1', '2'], { array: ['some-option'] }) Array.isArray(result['some-option']).should.equal(true) result['some-option'].should.deep.equal([1, 2]) }) it('should eat hyphenated switch with camelCase array option', function () { const result = parser(['--some-option', '1', '2'], { array: ['someOption'] }) Array.isArray(result.someOption).should.equal(true) result.someOption.should.deep.equal([1, 2]) }) // see https://github.com/yargs/yargs-parser/issues/6 it('should respect the type `boolean` option for arrays', function () { const result = parser(['-x=true', 'false'], { array: [{ key: 'x', boolean: true }] }) result.should.have.property('x').that.is.an('array').and.to.deep.equal([true, false]) }) it('should respect type `boolean` without value for arrays', function () { const result = parser(['-x', '-x'], { array: [{ key: 'x', boolean: true }], configuration: { 'flatten-duplicate-arrays': false } }) result.x.should.deep.equal([[true], [true]]) }) it('should respect `boolean negation` for arrays', function () { const result = parser(['--no-bool', '--no-bool'], { array: [{ key: 'bool', boolean: true }], configuration: { 'flatten-duplicate-arrays': false } }) result.bool.should.deep.equal([[false], [false]]) }) it('should respect the type `number` option for arrays', function () { const result = parser(['-x=5', '2'], { array: [{ key: 'x', number: true }] }) result.should.have.property('x').that.is.an('array').and.to.deep.equal([5, 2]) }) it('should respect the type `string` option for arrays', function () { const result = parser(['-x=5', '2'], { configuration: { 'parse-numbers': true }, array: [{ key: 'x', string: true }] }) result.should.have.property('x').that.is.an('array').and.to.deep.equal(['5', '2']) }) it('should eat non-hyphenated arguments until hyphenated option is hit - combined with coercion', function () { const result = parser([ '-a=hello', 'world', '-b', '33', '22', '--foo', 'true', 'false', '--bar=cat', 'dog' ], { array: [ 'a', { key: 'b', integer: true }, { key: 'foo', boolean: true }, 'bar' ] }) Array.isArray(result.a).should.equal(true) result.a.should.include('hello') result.a.should.include('world') Array.isArray(result.b).should.equal(true) result.b.should.include(33) result.b.should.include(22) Array.isArray(result.foo).should.equal(true) result.foo.should.include(true) result.foo.should.include(false) Array.isArray(result.bar).should.equal(true) result.bar.should.include('cat') result.bar.should.include('dog') }) }) describe('nargs', function () { it('should allow the number of arguments following a key to be specified', function () { const result = parser(['--foo', 'apple', 'bar'], { narg: { foo: 2 } }) Array.isArray(result.foo).should.equal(true) result.foo[0].should.equal('apple') result.foo[1].should.equal('bar') }) it('should raise an exception if -f== format is used for a key with no expected argument', function () { const argv = parser.detailed('-f=apple', { narg: { f: 0 } }) argv.error.message.should.equal('Argument unexpected for: f') }) it('should raise an exception if --bar== format is used for a key with no expected argument', function () { const argv = parser.detailed('--bar=apple', { narg: { bar: 0 } }) argv.error.message.should.equal('Argument unexpected for: bar') }) it('should raise an exception if there are not enough arguments following key', function () { const argv = parser.detailed('--foo apple', { narg: { foo: 2 } }) argv.error.message.should.equal('Not enough arguments following: foo') }) it('nargs is applied to aliases', function () { const result = parser(['--bar', 'apple', 'bar'], { narg: { foo: 2 }, alias: { foo: 'bar' } }) Array.isArray(result.foo).should.equal(true) result.foo[0].should.equal('apple') result.foo[1].should.equal('bar') }) it('should apply nargs to flag arguments', function () { const result = parser(['-f', 'apple', 'bar', 'blerg'], { narg: { f: 2 } }) result.f[0].should.equal('apple') result.f[1].should.equal('bar') result._[0].should.equal('blerg') }) it('should support nargs for -f= and --bar= format arguments', function () { const result = parser(['-f=apple', 'bar', 'blerg', '--bar=monkey', 'washing', 'cat'], { narg: { f: 2, bar: 2 } }) result.f[0].should.equal('apple') result.f[1].should.equal('bar') result._[0].should.equal('blerg') result.bar[0].should.equal('monkey') result.bar[1].should.equal('washing') result._[1].should.equal('cat') }) it('should support nargs for -f= and --bar= format arguments with dashed values', function () { const result = parser(['-f=--apple', 'bar', 'blerg', '--bar=-monkey', 'washing', 'cat'], { narg: { f: 2, bar: 2 } }) result.f[0].should.equal('--apple') result.f[1].should.equal('bar') result._[0].should.equal('blerg') result.bar[0].should.equal('-monkey') result.bar[1].should.equal('washing') result._[1].should.equal('cat') }) it('should not modify the input args if an = was used', function () { const expected = ['-f=apple', 'bar', 'blerg', '--bar=monkey', 'washing', 'cat'] const args = expected.slice() parser(args, { narg: { f: 2, bar: 2 } }) args.should.deep.equal(expected) parser.detailed(args, { narg: { f: 2, bar: 2 } }) args.should.deep.equal(expected) }) it('allows multiple nargs to be set at the same time', function () { const result = parser(['--foo', 'apple', 'bar', '--bar', 'banana', '-f'], { narg: { foo: 2, bar: 1 } }) Array.isArray(result.foo).should.equal(true) result.foo[0].should.equal('apple') result.foo[1].should.equal('bar') result.bar.should.equal('banana') result.f.should.equal(true) }) // see: https://github.com/yargs/yargs-parser/pull/13 it('should support nargs for --foo= format when the key is a number', function () { const result = parser(['--1=a', 'b'], { narg: { 1: 2 } }) Array.isArray(result['1']).should.equal(true) result['1'][0].should.equal('a') result['1'][1].should.equal('b') }) it('should not treat flag arguments as satisfying narg requirements', function () { const result = parser.detailed(['--foo', '--bar', '99'], { narg: { foo: 1 } }) result.argv.bar.should.equal(99) result.error.message.should.equal('Not enough arguments following: foo') }) // See: https://github.com/yargs/yargs-parser/issues/232 it('should treat flag arguments as satisfying narg requirements, if nargs-eats-options=true', function () { const result = parser.detailed(['--foo', '--bar', '99', '--batman', 'robin'], { narg: { foo: 2 }, configuration: { 'nargs-eats-options': true } }) result.argv.foo.should.eql(['--bar', 99]) result.argv.batman.should.eql('robin') }) it('should not consume more than configured nargs', function () { const result = parser(['--foo', 'a', 'b'], { narg: { foo: 1 } }) result.foo.should.eql('a') }) it('should ignore undefined configured nargs', function () { const result = parser(['--foo', 'a', 'b'], { narg: { foo: undefined } }) result.foo.should.eql('a') }) it('should default to 1 if configured narg is NaN', function () { const result = parser(['--foo', 'a', 'b'], { narg: { foo: NaN } }) result.foo.should.eql('a') }) }) describe('env vars', function () { it('should apply all env vars if prefix is empty', function () { process.env.ONE_FISH = 'twofish' process.env.RED_FISH = 'bluefish' const result = parser([], { envPrefix: '' }) result.oneFish.should.equal('twofish') result.redFish.should.equal('bluefish') }) // envPrefix falls back to empty string if not a string it('should apply all env vars if prefix is not a string', function () { process.env.ONE_FISH = 'twofish' process.env.RED_FISH = 'bluefish' const result = parser([], { envPrefix: null }) result.oneFish.should.equal('twofish') result.redFish.should.equal('bluefish') }) it('should not apply all env vars if prefix is undefined', function () { process.env.ONE_FISH = 'twofish' process.env.RED_FISH = 'bluefish' const result = parser([], { envPrefix: undefined }) expect(result).to.not.have.property('oneFish') expect(result).to.not.have.property('redFish') }) it('should apply only env vars matching prefix if prefix is valid string', function () { process.env.ONE_FISH = 'twofish' process.env.RED_FISH = 'bluefish' process.env.GREEN_EGGS = 'sam' process.env.GREEN_HAM = 'iam' const result = parser([], { envPrefix: 'GREEN' }) result.eggs.should.equal('sam') result.ham.should.equal('iam') expect(result.oneFish).to.be.undefined // eslint-disable-line expect(result.redFish).to.be.undefined // eslint-disable-line }) it('should set aliases for options defined by env var', function () { process.env.AIRFORCE_ONE = 'two' const result = parser([], { envPrefix: 'AIRFORCE', alias: { 1: ['one', 'uno'] } }) result['1'].should.equal('two') result.one.should.equal('two') result.uno.should.equal('two') }) it('should prefer command line value over env var', function () { process.env.FOO_BAR = 'ignore' const result = parser(['--foo-bar', 'baz'], { envPrefix: '' }) result.fooBar.should.equal('baz') }) it('should respect type for args defined by env var', function () { process.env.MY_TEST_STRING = '1' process.env.MY_TEST_NUMBER = '2' const result = parser([], { string: 'string', envPrefix: 'MY_TEST_' }) result.string.should.equal('1') result.number.should.equal(2) }) it('should set option from aliased env var', function () { process.env.SPACE_X = 'awesome' const result = parser([], { alias: { xactly: 'x' }, envPrefix: 'SPACE' }) result.xactly.should.equal('awesome') }) it('should prefer env var value over configured default', function () { process.env.FOO_BALL = 'wut' process.env.FOO_BOOL = 'true' const result = parser([], { envPrefix: 'FOO', default: { ball: 'baz', bool: false }, boolean: 'bool', string: 'ball' }) result.ball.should.equal('wut') result.bool.should.equal(true) }) const jsonPath = path.resolve(__dirname, './fixtures/config.json') it('should prefer environment variables over config file', function () { process.env.CFG_HERP = 'zerp' const result = parser(['--cfg', jsonPath], { envPrefix: 'CFG', config: 'cfg', string: 'herp', default: { herp: 'nerp' } }) result.herp.should.equal('zerp') }) it('should support an env var value as config file option', function () { process.env.TUX_CFG = jsonPath const result = parser([], { envPrefix: 'TUX', config: ['cfg'], default: { z: 44 } }) result.should.have.property('herp') result.should.have.property('foo') result.should.have.property('version') result.should.have.property('truthy') result.z.should.equal(55) }) it('should prefer cli config file option over env var config file option', function () { process.env.MUX_CFG = path.resolve(__dirname, '../package.json') const result = parser(['--cfg', jsonPath], { envPrefix: 'MUX', config: 'cfg' }) result.should.have.property('herp') result.should.have.property('foo') result.should.have.property('version') result.should.have.property('truthy') result.z.should.equal(55) }) it('should apply all nested env vars', function () { process.env.TEST_A = 'a' process.env.TEST_NESTED_OPTION__FOO = 'baz' process.env.TEST_NESTED_OPTION__BAR = 'bar' const result = parser(['--nestedOption.foo', 'bar'], { envPrefix: 'TEST' }) result.should.have.property('a', 'a') result.should.have.property('nestedOption').and.deep.equal({ foo: 'bar', bar: 'bar' }) }) it('should apply nested env var if argv value is using default value', function () { process.env.TEST_A = 'a' process.env.TEST_NESTED_OPTION__FOO = 'baz' process.env.TEST_NESTED_OPTION__BAR = 'bar' const result = parser([], { envPrefix: 'TEST', default: { 'nestedOption.foo': 'banana' } }) result.should.have.property('a', 'a') result.should.have.property('nestedOption').and.deep.equal({ foo: 'baz', bar: 'bar' }) }) }) describe('configuration', function () { describe('short option groups', function () { it('allows short-option-groups to be disabled', function () { let parse = parser(['-cats=meow'], { configuration: { 'short-option-groups': false } }) parse.cats.should.equal('meow') parse = parser(['-cats', 'meow'], { configuration: { 'short-option-groups': false } }) parse.cats.should.equal('meow') }) }) describe('camel-case expansion', function () { it('does not expand camel-case aliases', function () { const parsed = parser.detailed([], { alias: { 'foo-bar': ['x'] }, configuration: { 'camel-case-expansion': false } }) expect(parsed.newAliases.fooBar).to.equal(undefined) expect(parsed.aliases.fooBar).to.equal(undefined) }) it('does not expand camel-case keys', function () { const parsed = parser.detailed(['--foo-bar=apple'], { configuration: { 'camel-case-expansion': false } }) expect(parsed.argv.fooBar).to.equal(undefined) expect(parsed.argv['foo-bar']).to.equal('apple') }) }) describe('dot notation', function () { it('does not expand dot notation defaults', function () { const parsed = parser([], { default: { 'foo.bar': 'x' }, configuration: { 'dot-notation': false } }) expect(parsed['foo.bar']).to.equal('x') }) it('does not expand dot notation arguments', function () { let parsed = parser(['--foo.bar', 'banana'], { configuration: { 'dot-notation': false } }) expect(parsed['foo.bar']).to.equal('banana') parsed = parser(['--foo.bar=banana'], { configuration: { 'dot-notation': false } }) expect(parsed['foo.bar']).to.equal('banana') }) it('should use value from cli, if cli overrides dot notation default', function () { const parsed = parser(['--foo.bar', 'abc'], { default: { 'foo.bar': 'default' }, configuration: { 'dot-notation': false } }) expect(parsed['foo.bar']).to.equal('abc') }) it('should also override dot notation alias', function () { const parsed = parser(['--foo.bar', 'abc'], { alias: { 'foo.bar': ['alias.bar'] }, default: { 'foo.bar': 'default' }, configuration: { 'dot-notation': false } }) expect(parsed['alias.bar']).to.equal('abc') }) it('does not expand alias of first element of dot notation arguments', function () { const parsed = parser(['--foo.bar', 'banana'], { alias: { foo: ['f'] }, configuration: { 'dot-notation': false } }) expect(parsed['foo.bar']).to.equal('banana') expect(parsed).not.to.include.keys('f.bar') }) // addresses https://github.com/yargs/yargs/issues/716 it('does not append nested-object keys from config to top-level key', function () { const parsed = parser([], { alias: { foo: ['f'] }, configuration: { 'dot-notation': false }, configObjects: [ { 'website.com': { a: 'b', b: 'c' } } ] }) parsed['website.com'].should.deep.equal({ a: 'b', b: 'c' }) }) }) describe('parse-positional-numbers', () => { it('does not parse positionals into numbers when false', function () { const parsed = parser(['5.0', '3'], { configuration: { 'parse-positional-numbers': false } }) expect(parsed._[0]).to.equal('5.0') expect(parsed._[1]).to.equal('3') }) it('parses positionals into numbers by default', function () { const parsed = parser(['5.0', '3']) expect(parsed._[0]).to.equal(5) expect(parsed._[1]).to.equal(3) }) }) describe('parse numbers', function () { it('does not coerce defaults into numbers', function () { const parsed = parser([], { default: { foo: '5' }, configuration: { 'parse-numbers': false } }) expect(parsed.foo).to.equal('5') }) it('does not coerce arguments into numbers', function () { const parsed = parser(['--foo', '5'], { configuration: { 'parse-numbers': false } }) expect(parsed.foo).to.equal('5') }) it('does not coerce positional arguments into numbers', function () { const parsed = parser(['5'], { configuration: { 'parse-numbers': false } }) expect(parsed._[0]).to.equal('5') }) it('parses number if option explicitly set to number type', function () { const parsed = parser(['--foo', '5', '--bar', '6', '--baz', '7'], { number: ['bar', 'baz'], coerce: { baz: val => val }, configuration: { 'parse-numbers': false } }) expect(parsed.foo).to.equal('5') expect(parsed.bar).to.equal(6) expect(parsed.baz).to.equal(7) }) it('should coerce elements of number typed arrays to numbers', function () { const parsed = parser(['--foo', '4', '--foo', '5', '2'], { array: ['foo'], configObjects: [{ foo: ['1', '2', '3'] }], configuration: { 'combine-arrays': true, 'flatten-duplicate-arrays': false } }) expect(parsed.foo).to.deep.equal([[4], [5, 2], [1, 2, 3]]) }) }) describe('boolean negation', function () { it('does not negate arguments prefixed with --no-', function () { const parsed = parser(['--no-dice'], { configuration: { 'boolean-negation': false } }) parsed['no-dice'].should.equal(true) expect(parsed.dice).to.equal(undefined) }) it('negates boolean arguments with correct prefix', function () { const parsed = parser(['--foodice'], { configuration: { 'negation-prefix': 'foo' } }) expect(parsed.dice).to.equal(false) }) }) describe('duplicate arguments array', function () { it('adds duplicate argument to array', function () { const parsed = parser('-x a -x b', { configuration: { 'duplicate-arguments-array': true } }) parsed.x.should.deep.equal(['a', 'b']) }) it('keeps only last argument', function () { const parsed = parser('-x a -x b', { configuration: { 'duplicate-arguments-array': false } }) parsed.x.should.equal('b') }) it('does not interfere with nargs', function () { const parsed = parser('-x a b c -x o p q', { narg: { x: 3 }, configuration: { 'duplicate-arguments-array': false } }) parsed.x.should.deep.equal(['o', 'p', 'q']) }) }) describe('flatten duplicate arrays', function () { it('flattens duplicate array type', function () { const parsed = parser('-x a b -x c d', { array: ['x'], configuration: { 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal(['a', 'b', 'c', 'd']) }) it('nests duplicate array types', function () { const parsed = parser('-x a b -x c d', { array: ['x'], configuration: { 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([['a', 'b'], ['c', 'd']]) }) it('nests duplicate array types of more than 2', function () { const parsed = parser('-x a b -x c d -x e f -x g h', { array: ['x'], configuration: { 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]) }) it('doesn\'t nests single arrays', function () { const parsed = parser('-x a b', { array: ['x'], configuration: { 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal(['a', 'b']) }) it('flattens duplicate array type, when argument uses dot notation', function () { const parsed = parser('-x.foo a -x.foo b', { array: ['x.foo'], configuration: { 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal({ foo: ['a', 'b'] }) }) }) describe('duplicate-arguments-array VS flatten-duplicate-arrays', function () { /* duplicate=false, flatten=false type=array [-x 1 2 3] => [1, 2, 3] [-x 1 2 3 -x 2 3 4] => [2, 3, 4] type=string/number/etc [-x 1 -x 2 -x 3] => 3 duplicate=false, flatten=true type=array [-x 1 2 3] => [1, 2, 3] [-x 1 2 3 -x 2 3 4] => [2, 3, 4] type=string/number/etc [-x 1 -x 2 -x 3] => 3 duplicate=true, flatten=true type=array [-x 1 2 3] => [1, 2, 3] [-x 1 2 3 -x 2 3 4] => [1, 2, 3, 2, 3, 4] type=string/number/etc [-x 1 -x 2 -x 3] => [1, 2, 3] duplicate=true, flatten=false type=array [-x 1 2 3] => [1, 2, 3] [-x 1 2 3 -x 2 3 4] => [[1, 2, 3], [2, 3, 4]] type=string/number/etc [-x 1 -x 2 -x 3] => [1, 2, 3] */ describe('duplicate=false, flatten=false,', function () { describe('type=array', function () { it('[-x 1 2 3] => [1, 2, 3]', function () { const parsed = parser('-x 1 2 3', { array: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([1, 2, 3]) }) it('[-x 1 2 3 -x 2 3 4] => [2, 3, 4]', function () { const parsed = parser('-x 1 2 3 -x 2 3 4', { array: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([2, 3, 4]) }) }) describe('type=number', function () { it('[-x 1 -x 2 -x 3] => 3', function () { const parsed = parser('-x 1 -x 2 -x 3', { number: 'x', configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal(3) }) }) describe('type=boolean', function () { it('[-x true -x true -x false] => false', function () { const parsed = parser('-x true -x true -x false', { boolean: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal(false) }) }) }) describe('duplicate=false, flatten=true,', function () { describe('type=array', function () { it('[-x 1 2 3] => [1, 2, 3]', function () { const parsed = parser('-x 1 2 3', { array: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal([1, 2, 3]) }) it('[-x 1 2 3 -x 2 3 4] => [2, 3, 4]', function () { const parsed = parser('-x 1 2 3 -x 2 3 4', { array: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal([2, 3, 4]) }) }) describe('type=number', function () { it('[-x 1 -x 2 -x 3] => 3', function () { const parsed = parser('-x 1 -x 2 -x 3', { number: 'x', configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal(3) }) }) describe('type=boolean', function () { it('[-x true -x true -x false] => false', function () { const parsed = parser('-x true -x true -x false', { boolean: ['x'], configuration: { 'duplicate-arguments-array': false, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal(false) }) }) }) describe('duplicate=true, flatten=true,', function () { describe('type=array', function () { it('[-x 1 2 3] => [1, 2, 3]', function () { const parsed = parser('-x 1 2 3', { array: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal([1, 2, 3]) }) it('[-x 1 2 3 -x 2 3 4] => [1, 2, 3, 2, 3, 4]', function () { const parsed = parser('-x 1 2 3 -x 2 3 4', { array: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal([1, 2, 3, 2, 3, 4]) }) }) describe('type=number', function () { it('[-x 1 -x 2 -x 3] => [1, 2, 3]', function () { const parsed = parser('-x 1 -x 2 -x 3', { number: 'x', configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal([1, 2, 3]) }) }) describe('type=boolean', function () { // in the casse of boolean arguments, only the last argument is used: it('[-x true -x true -x false] => false', function () { const parsed = parser('-x true -x true -x false', { boolean: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': true } }) parsed.x.should.deep.equal(false) }) }) }) describe('duplicate=true, flatten=false,', function () { describe('type=array', function () { it('[-x 1 -x 2 -x 3] => [[1], [2], [3]]', function () { const parsed = parser('-x 1 -x 2 -x 3', { array: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([[1], [2], [3]]) }) it('[-x 1 2 3 -x 2 3 4] => [[1, 2, 3], [ 2, 3, 4]]', function () { const parsed = parser('-x 1 2 3 -x 2 3 4', { array: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([[1, 2, 3], [2, 3, 4]]) }) }) describe('type=number', function () { it('[-x 1 -x 2 -x 3] => [1, 2, 3]', function () { const parsed = parser('-x 1 -x 2 -x 3', { number: 'x', configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal([1, 2, 3]) }) }) describe('type=boolean', function () { it('[-x true -x true -x false] => false', function () { const parsed = parser('-x true -x true -x false', { boolean: ['x'], configuration: { 'duplicate-arguments-array': true, 'flatten-duplicate-arrays': false } }) parsed.x.should.deep.equal(false) }) }) }) }) describe('populate--', function () { it('should populate "_" by default', function () { const result = parser([ 'bare', '--', '-h', 'eek', '--' ]) result.should.have.property('_').and.deep.equal(['bare', '-h', 'eek', '--']) result.should.not.have.property('--') }) it('should populate "_" when given config with "short-option-groups" false', function () { const result = parser.detailed([ '--', 'foo' ], { configuration: { 'short-option-groups': false } }) result.argv.should.deep.equal({ _: ['foo'] }) result.argv.should.not.have.property('--') result.newAliases.should.deep.equal({}) }) it('should populate the "--" if populate-- is "true"', function () { const result = parser([ '--name=meowmers', 'bare', '-cats', 'woo', 'moxy', '-h', 'awesome', '--multi=quux', '--key', 'value', '-b', '--bool', '--no-meep', '--multi=baz', '--', '--not-a-flag', '-', '-h', '-multi', '--', 'eek' ], { configuration: { 'populate--': true } }) result.should.have.property('c', true) result.should.have.property('a', true) result.should.have.property('t', true) result.should.have.property('s', 'woo') result.should.have.property('h', 'awesome') result.should.have.property('b', true) result.should.have.property('bool', true) result.should.have.property('key', 'value') result.should.have.property('multi').and.deep.equal(['quux', 'baz']) result.should.have.property('meep', false) result.should.have.property('name', 'meowmers') result.should.have.property('_').and.deep.equal(['bare', 'moxy']) result.should.have.property('--').and.deep.equal(['--not-a-flag', '-', '-h', '-multi', '--', 'eek']) }) }) describe('set-placeholder-key', function () { it('should not set placeholder key by default', function () { const parsed = parser([], { string: ['a'] }) parsed.should.not.have.property('a') }) it('should set placeholder key to "undefined"', function () { const parsed = parser([], { array: ['a'], boolean: ['b'], string: ['c'], number: ['d'], count: ['e'], normalize: ['f'], narg: { g: 2 }, coerce: { h: function (arg) { return arg } }, configuration: { 'set-placeholder-key': true } }) parsed.should.have.property('a') expect(parsed.a).to.be.equal(undefined) parsed.should.have.property('b') expect(parsed.b).to.be.equal(undefined) parsed.should.have.property('c') expect(parsed.c).to.be.equal(undefined) parsed.should.have.property('d') expect(parsed.d).to.be.equal(undefined) parsed.should.have.property('e') expect(parsed.f).to.be.equal(undefined) parsed.should.have.property('g') expect(parsed.g).to.be.equal(undefined) parsed.should.have.property('h') expect(parsed.h).to.be.equal(undefined) }) it('should not set placeholder for key with a default value', function () { const parsed = parser([], { string: ['a'], default: { a: 'hello' }, configuration: { 'set-placeholder-key': true } }) parsed.a.should.equal('hello') }) it('should not set placeholder key with dot notation', function () { const parsed = parser([], { string: ['a.b'] }) parsed.should.not.have.property('a') parsed.should.not.have.property('b') parsed.should.not.have.property('a.b') }) it('should not set placeholder key with dot notation when `set-placeholder-key` is `true`', function () { const parsed = parser([], { string: ['a.b'], configuration: { 'set-placeholder-key': true } }) parsed.should.not.have.property('a') parsed.should.not.have.property('b') parsed.should.not.have.property('a.b') }) }) describe('halt-at-non-option', function () { it('gets the entire rest of line', function () { const parse = parser(['--foo', './file.js', '--foo', '--bar'], { configuration: { 'halt-at-non-option': true }, boolean: ['foo', 'bar'] }) parse.should.deep.equal({ foo: true, _: ['./file.js', '--foo', '--bar'] }) }) it('is not influenced by --', function () { const parse = parser( ['--foo', './file.js', '--foo', '--', 'barbar', '--bar'], { configuration: { 'halt-at-non-option': true }, boolean: ['foo', 'bar'] } ) parse.should.deep.equal({ foo: true, _: ['./file.js', '--foo', '--', 'barbar', '--bar'] }) }) it('is not influenced by unknown options', function () { const parse = parser( ['-v', '--long', 'arg', './file.js', '--foo', '--', 'barbar'], { configuration: { 'halt-at-non-option': true }, boolean: ['foo'] } ) parse.should.deep.equal({ v: true, long: 'arg', _: ['./file.js', '--foo', '--', 'barbar'] }) }) }) describe('unknown-options-as-args = true', function () { it('should ignore unknown options in long format separated by =', function () { const argv = parser('--known-arg=1 --unknown-arg=2', { number: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['--unknown-arg=2'], 'known-arg': 1, knownArg: 1 }) }) it('should ignore unknown options in boolean negations', function () { const argv = parser('--no-known-arg --no-unknown-arg', { boolean: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['--no-unknown-arg'], 'known-arg': false, knownArg: false }) }) it('should ignore unknown options in long format separated by space', function () { const argv = parser('--known-arg a --unknown-arg b', { string: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['--unknown-arg', 'b'], 'known-arg': 'a', knownArg: 'a' }) }) it('should ignore unknown options in short dot format separated by equals', function () { const argv = parser('-k.arg=a -u.arg=b', { string: ['k.arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u.arg=b'], k: { arg: 'a' } }) }) it('should ignore unknown options in short dot format separated by space', function () { const argv = parser('-k.arg 1 -u.arg 2', { number: ['k.arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u.arg', 2], k: { arg: 1 } }) }) it('should ignore unknown options in short format separated by equals', function () { const argv = parser('-k=a -u=b', { string: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u=b'], k: 'a' }) }) it('should ignore unknown options in short format followed by hyphen', function () { const argv = parser('-k- -u-', { string: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u-'], k: '-' }) }) it('should ignore unknown options in short format separated by space', function () { const argv = parser('-k 1 -u 2', { number: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u', 2], k: 1 }) }) it('should allow an unknown arg to be used as the value of another flag in short form', function () { const argv = parser('-k -u', { string: ['k'], narg: { k: 1 }, configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: [], k: '-u' }) }) it('should allow an unknown arg to be used as the value of another flag in long form', function () { const argv = parser('--known-arg --unknown-arg', { string: ['known-arg'], narg: { 'known-arg': 1 }, configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: [], knownArg: '--unknown-arg', 'known-arg': '--unknown-arg' }) }) it('should allow an unknown arg to be used as the value of another flag in array form', function () { const argv = parser('--known-arg --unknown-arg1 --unknown-arg2', { array: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: [], knownArg: ['--unknown-arg1', '--unknown-arg2'], 'known-arg': ['--unknown-arg1', '--unknown-arg2'] }) }) it('should ignore unknown options in short format followed by a number', function () { const argv = parser('-k1 -u2', { number: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u2'], k: 1 }) }) it('should ignore unknown options in short format followed by a non-word character', function () { const argv = parser('-k/1/ -u/2/', { string: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-u/2/'], k: '/1/' }) }) it('should ignore unknown options in short format with multiple flags in one argument where an unknown flag is before the end', function () { const argv = parser('-kuv', { boolean: ['k', 'v'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-kuv'] }) }) it('should parse known options in short format with multiple flags in one argument where no unknown flag is in the argument', function () { const argv = parser('-kv', { boolean: ['k', 'v'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: [], k: true, v: true }) }) it('should parse negative numbers', function () { const argv = parser('-k -33', { boolean: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: [-33], k: true }) }) it('should not identify "--" as an unknown option', function () { const argv = parser('-a -k one -1 -- -b -k two -2', { boolean: ['k'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['-a', 'one', -1, '-b', '-k', 'two', '-2'], k: true }) }) it('should not identify "--" as an unknown option when "populate--" is true', function () { const argv = parser('-a -k one -1 -- -b -k two -2', { boolean: ['k'], configuration: { 'populate--': true, 'unknown-options-as-args': true } }) argv.should.deep.equal({ // populate argv._ with everything before the -- _: ['-a', 'one', -1], // and argv['--'] with everything after the -- '--': ['-b', '-k', 'two', '-2'], k: true }) }) // See: https://github.com/yargs/yargs/issues/1869 // ----=hello ---=hello ---- hello. it('should not populate "--" for key values other than "--"', () => { const argv = parser('----=test', { configuration: { 'populate--': true } }) argv._.should.eql(['----=test']) expect(argv['--']).to.equal(undefined) }) // see: https://github.com/yargs/yargs/issues/1489 it('should identify "hasOwnProperty" as unknown option', () => { const argv = parser('--known-arg=1 --hasOwnProperty=33', { number: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['--hasOwnProperty=33'], 'known-arg': 1, knownArg: 1 }) }) it('should coerce unknown options that look numeric into numbers', () => { const argv = parser('--known-arg 33', { boolean: ['known-arg'] }) argv.should.deep.equal({ _: [33], 'known-arg': true, knownArg: true }) }) }) // See: https://github.com/yargs/yargs-parser/issues/231 it('should collect unknown options terminated with digit', function () { const argv = parser('--known-arg=1 --num2', { alias: { num: ['n'] }, number: ['known-arg'], configuration: { 'unknown-options-as-args': true } }) argv.should.deep.equal({ _: ['--num2'], 'known-arg': 1, knownArg: 1 }) }) }) // addresses: https://github.com/yargs/yargs-parser/issues/41 it('defaults to empty array if array option is provided no values', function () { let parsed = parser(['-f'], { alias: { f: 'files' }, array: ['files'] }) parsed.f.should.deep.equal([]) parsed.files.should.deep.equal([]) parsed = parser(['--files'], { alias: { f: 'files' }, array: ['files'] }) parsed.f.should.deep.equal([]) parsed.files.should.deep.equal([]) parsed = parser(['-f', '-y'], { alias: { f: 'files' }, array: ['files'] }) parsed.f.should.deep.equal([]) parsed.files.should.deep.equal([]) }) describe('coerce', function () { it('applies coercion function to simple arguments', function () { const parsed = parser(['--foo', '99'], { coerce: { foo: function (arg) { return arg * -1 } } }) parsed.foo.should.equal(-99) }) it('applies coercion function to aliases', function () { const parsed = parser(['--foo', '99'], { coerce: { f: function (arg) { return arg * -1 } }, alias: { f: ['foo'] } }) parsed.foo.should.equal(-99) parsed.f.should.equal(-99) }) it('applies coercion function to all dot options', function () { const parsed = parser(['--foo.bar', 'nananana'], { coerce: { foo: function (val) { val.bar += ', batman!' return val } } }) parsed.foo.bar.should.equal('nananana, batman!') }) it('applies coercion to defaults', function () { const parsed = parser([], { default: { foo: 'bar' }, coerce: { foo: function (val) { return val.toUpperCase() } } }) parsed.foo.should.equal('BAR') }) it('applies coercion function to an implicit array', function () { const parsed = parser(['--foo', '99', '-f', '33'], { coerce: { f: function (arg) { return arg.map(function (a) { return a * -1 }) } }, alias: { f: ['foo'] } }) parsed.f.should.deep.equal([-99, -33]) parsed.foo.should.deep.equal([-99, -33]) }) it('applies coercion function to an explicit array', function () { const parsed = parser(['--foo', '99', '-f', '33'], { coerce: { f: function (arg) { return arg.map(function (a) { return a * -1 }) } }, array: ['foo'], alias: { f: ['foo'] } }) parsed.f.should.deep.equal([-99, -33]) parsed.foo.should.deep.equal([-99, -33]) }) it('applies coercion function to _', function () { const parsed = parser(['99', '33'], { coerce: { _: function (arg) { return arg.map(function (a) { return a * -1 }) } } }) parsed._.should.deep.equal([-99, -33]) }) // see: https://github.com/yargs/yargs/issues/550 it('coercion function can be used to parse large #s', function () { const fancyNumberParser = function (arg) { if (arg.length > 10) return arg else return parseInt(arg) } const parsed = parser(['--foo', '88888889999990000998989898989898', '--bar', '998'], { coerce: { foo: fancyNumberParser, bar: fancyNumberParser } }) ; (typeof parsed.foo).should.equal('string') parsed.foo.should.equal('88888889999990000998989898989898') ; (typeof parsed.bar).should.equal('number') parsed.bar.should.equal(998) }) it('populates argv.error, if an error is thrown', function () { const parsed = parser.detailed(['--foo', '99'], { coerce: { foo: function (arg) { throw Error('banana') } } }) parsed.error.message.should.equal('banana') }) it('populates argv.error, if an error is thrown for an explicit array', function () { const parsed = parser.detailed(['--foo', '99'], { array: ['foo'], coerce: { foo: function (arg) { throw Error('foo is array: ' + Array.isArray(arg)) } } }) parsed.error.message.should.equal('foo is array: true') }) // see: https://github.com/yargs/yargs-parser/issues/76 it('only runs coercion functions once, even with aliases', function () { let runcount = 0 const func = (arg) => { runcount++ return undefined } parser(['--foo', 'bar'], { alias: { foo: ['f', 'foo-bar', 'bar'], b: ['bar'] }, coerce: { bar: func } }) runcount.should.equal(1) }) }) // see: https://github.com/yargs/yargs-parser/issues/37 it('normalizes all paths in array when provided via config object', function () { const argv = parser(['--foo', 'bar'], { array: ['a'], normalize: ['a'], configObjects: [{ a: ['bin/../a.txt', 'bin/../b.txt'] }] }) argv.a.should.deep.equal(['a.txt', 'b.txt']) }) // see: https://github.com/yargs/yargs/issues/963 it('does not magically convert numeric strings larger than Number.MAX_SAFE_INTEGER', () => { const argv = parser(['--foo', '93940495950949399948393']) argv.foo.should.equal('93940495950949399948393') }) it('does not magically convert scientific notation larger than Number.MAX_SAFE_INTEGER', () => { const argv = parser(['--foo', '33e99999']) argv.foo.should.equal('33e99999') }) it('converts numeric options larger than Number.MAX_SAFE_INTEGER to number', () => { const argv = parser(['--foo', '93940495950949399948393'], { number: ['foo'] }) argv.foo.should.equal(9.39404959509494e+22) }) // see: https://github.com/yargs/yargs/issues/1099 it('does not magically convert options with leading + to number', () => { const argv = parser(['--foo', '+5550100', '--bar', '+5550100'], { number: 'bar' }) argv.foo.should.equal('+5550100') argv.bar.should.equal(5550100) }) // see: https://github.com/yargs/yargs/issues/1099 it('does not magically convert options with leading 0 to number', () => { const argv = parser(['--foo', '000000', '--bar', '000000'], { number: 'bar' }) argv.foo.should.equal('000000') argv.bar.should.equal(0) }) // see: https://github.com/yargs/yargs-parser/issues/101 describe('dot-notation array arguments combined with string arguments', function () { it('parses correctly when dot-notation argument is first', function () { const argv = parser(['--foo.bar', 'baz', '--foo', 'bux']) Array.isArray(argv.foo).should.equal(true) argv.foo[0].bar.should.equal('baz') argv.foo[1].should.equal('bux') }) it('parses correctly when dot-notation argument is last', function () { const argv = parser(['--foo', 'bux', '--foo.bar', 'baz']) Array.isArray(argv.foo).should.equal(true) argv.foo[0].should.equal('bux') argv.foo[1].bar.should.equal('baz') }) it('parses correctly when there are multiple dot-notation arguments', function () { const argv = parser(['--foo.first', 'firstvalue', '--foo', 'bux', '--foo.bar', 'baz', '--foo.bla', 'banana']) Array.isArray(argv.foo).should.equal(true) argv.foo.length.should.equal(4) argv.foo[0].first.should.equal('firstvalue') argv.foo[1].should.equal('bux') argv.foo[2].bar.should.equal('baz') argv.foo[3].bla.should.equal('banana') }) }) // see: https://github.com/yargs/yargs-parser/issues/145 describe('strings with quotes and dashes', () => { it('handles double quoted strings', function () { const args = parser('--foo "hello world" --bar="goodnight\'moon"') args.foo.should.equal('hello world') args.bar.should.equal('goodnight\'moon') const args2 = parser(['--foo', '"hello world"', '--bar="goodnight\'moon"']) args2.foo.should.equal('"hello world"') args2.bar.should.equal('goodnight\'moon') }) it('handles single quoted strings', function () { const args = parser("--foo 'hello world' --bar='goodnight\"moon'") args.foo.should.equal('hello world') args.bar.should.equal('goodnight"moon') const args2 = parser(['--foo', "'hello world'", "--bar='goodnight\"moon'"]) args2.foo.should.equal("'hello world'") args2.bar.should.equal('goodnight"moon') }) it('handles strings with dashes', function () { const args = parser('--foo "-hello world" --bar="--goodnight moon"') args.foo.should.equal('-hello world') args.bar.should.equal('--goodnight moon') }) it('respects inner quotes (string)', function () { const args = parser('cmd --foo ""Hello"" --bar ""World"" --baz="":)""') args.foo.should.equal('"Hello"') args.bar.should.equal('"World"') args.baz.should.equal('":)"') }) it('respects inner quotes (array)', function () { const args = parser(['cmd', '--foo', '"Hello"', '--bar', '"World"', '--baz="":)""']) args.foo.should.equal('"Hello"') args.bar.should.equal('"World"') args.baz.should.equal('":)"') }) }) // see: https://github.com/yargs/yargs-parser/issues/144 it('number/string types should use default when no right-hand value', () => { let argv = parser(['--foo'], { number: ['foo'], default: { foo: 99 } }) argv.foo.should.equal(99) argv = parser(['-b'], { alias: { bar: 'b' }, string: ['bar'], default: { bar: 'hello' } }) argv.bar.should.equal('hello') }) describe('stripping', function () { it('strip-dashed removes expected fields from argv', function () { const argv = parser(['--test-value', '1'], { number: ['test-value'], alias: { 'test-value': ['alt-test'] }, configuration: { 'strip-dashed': true } }) argv.should.deep.equal({ _: [], testValue: 1, altTest: 1 }) }) it('strip-aliased removes expected fields from argv', function () { const argv = parser(['--test-value', '1'], { number: ['test-value'], alias: { 'test-value': ['alt-test'] }, configuration: { 'strip-aliased': true } }) argv.should.deep.equal({ _: [], 'test-value': 1, testValue: 1 }) }) it('strip-aliased and strip-dashed combined removes expected fields from argv', function () { const argv = parser(['--test-value', '1'], { number: ['test-value'], alias: { 'test-value': ['alt-test'] }, configuration: { 'strip-aliased': true, 'strip-dashed': true } }) argv.should.deep.equal({ _: [], testValue: 1 }) }) it('ignores strip-dashed if camel-case-expansion is disabled', function () { const argv = parser(['--test-value', '1'], { number: ['test-value'], configuration: { 'camel-case-expansion': false, 'strip-dashed': true } }) argv.should.deep.equal({ _: [], 'test-value': 1 }) }) it('only removes camel case expansion if keys have hyphen', function () { const argv = parser(['--foo', '1', '-a', '2'], { configuration: { 'strip-aliased': true }, alias: { aliased1: ['Foo'], aliased2: ['A'] } }) argv.should.deep.equal({ _: [], foo: 1, a: 2 }) }) }) describe('prototype collisions', () => { it('parses unknown argument colliding with prototype', () => { const parse = parser(['--toString']) parse.toString.should.equal(true) }) it('parses unknown argument colliding with prototype, when unknown options as args', () => { const parse = parser(['--toString'], { configuration: { 'unknown-options-as-args': true } }) parse._.should.include('--toString') }) it('handles "alias" colliding with prototype', () => { const parse = parser(['-t', '99'], { alias: { toString: ['t'] } }) parse.toString.should.equal(99) parse.t.should.equal(99) parse['to-string'].should.equal(99) }) it('handles multiple args colliding with alias', () => { const parse = parser(['--toString', '88', '--toString', '99']) parse.toString.should.eql([88, 99]) }) it('handle dot notation colliding with alias', () => { const parse = parser(['--toString.cool', 'apple']) parse.toString.cool.should.equal('apple') }) it('handles "arrays" colliding with prototype', () => { const parse = parser(['--toString', '99', '100'], { array: ['toString'] }) parse.toString.should.eql([99, 100]) }) it('handles "arrays" colliding with prototype', () => { const parse = parser(['--toString', '99', '100'], { array: ['toString'] }) parse.toString.should.eql([99, 100]) }) it('handles "strings" colliding with prototype', () => { const parse = parser(['--toString', '99'], { string: ['toString'] }) parse.toString.should.eql('99') }) it('handles "numbers" colliding with prototype', () => { const parse = parser(['--toString', '99'], { number: ['toString'], configuration: { 'parse-numbers': false } }) parse.toString.should.eql(99) }) it('handles "counts" colliding with prototype', () => { const parse = parser(['--toString', '--toString', '--toString'], { count: ['toString'] }) parse.toString.should.eql(3) }) it('handles "normalize" colliding with prototype', () => { const parse = parser(['--toString', './node_modules/chai'], { normalize: ['toString'] }) parse.toString.should.include('node_modules') }) it('handles "normalize" colliding with prototype', () => { const parse = parser(['--toString', './node_modules/chai'], { normalize: ['toString'] }) parse.toString.should.include('node_modules') }) it('handles key in configuration file that collides with prototype', function () { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) argv.toString.should.equal('method name') }) it('handles "nargs" colliding with prototype', () => { const parse = parser(['--toString', 'apple', 'banana', 'batman', 'robin'], { narg: { toString: 3 } }) parse.toString.should.eql(['apple', 'banana', 'batman']) parse._.should.eql(['robin']) }) it('handles "coercions" colliding with prototype', () => { const parse = parser(['--toString', '33'], { coerce: { toString: (val) => { return val * 2 } } }) parse.toString.should.equal(66) }) }) // See: https://github.com/facebook/jest/issues/9517 it('does not collect arguments configured as booleans into implicit array', () => { const parse = parser(['--infinite', 'true', '--infinite', 'true', '--no-infinite'], { boolean: 'infinite' }) parse.infinite.should.equal(false) }) // See: https://github.com/yargs/yargs/issues/1098, // https://github.com/yargs/yargs/issues/1570 describe('array with nargs', () => { it('allows array and nargs to be configured in conjunction, enforcing the nargs value', () => { const parse = parser.detailed(['-a', 'apple', 'banana'], { array: 'a', narg: { a: 1 } }) expect(parse.error).to.be.null // eslint-disable-line parse.argv.a.should.eql(['apple']) parse.argv._.should.eql(['banana']) }) // see; https://github.com/yargs/yargs/issues/1098 it('allows special NaN count to be provided to narg, to indicate one or more array values', () => { const parse = parser.detailed(['-a', 'apple', 'banana'], { array: 'a', narg: { a: NaN } }) expect(parse.error).to.be.null // eslint-disable-line parse.argv.a.should.eql(['apple', 'banana']) }) it('throws error if at least one value not provided for NaN', () => { const parse = parser.detailed(['-a'], { array: 'a', narg: { a: NaN } }) parse.error.message.should.match(/Not enough arguments/) }) it('returns an error if not enough positionals were provided for nargs', () => { const parse = parser.detailed(['-a', '33'], { array: 'a', narg: { a: 2 } }) parse.argv.a.should.eql([33]) parse.error.message.should.equal('Not enough arguments following: a') }) it('returns an error if not enough positionals were provided for nargs even with nargs-eats-options', () => { const parse = parser.detailed(['-a', '33', '--cat'], { narg: { a: 3 }, configuration: { 'nargs-eats-options': true } }) parse.error.message.should.equal('Not enough arguments following: a') }) it('does not raise error if no arguments are provided for boolean option', () => { const parse = parser.detailed(['-a'], { array: 'a', boolean: 'a', narg: { a: NaN } }) expect(parse.error).to.be.null // eslint-disable-line parse.argv.a.should.eql([true]) }) }) describe('greedy-arrays=false', () => { it('does not consume more than one argument after array option', () => { const argv = parser(['--arr', 'foo', 'bar'], { array: 'arr', configuration: { 'greedy-arrays': false } }) argv.arr.should.eql(['foo']) argv._.should.eql(['bar']) }) it('places argument into array when specified multiple times', () => { const argv = parser(['--arr', 99, 'foo', '--arr', 'hello', 'bar'], { array: 'arr', configuration: { 'greedy-arrays': false } }) argv.arr.should.eql([99, 'hello']) argv._.should.eql(['foo', 'bar']) }) it('places boolean arguments into array when specified multiple times', () => { const argv = parser(['--arr', 101, '--arr', 102, '--arr', 'false'], { array: 'arr', boolean: 'arr', configuration: { 'greedy-arrays': false } }) argv.arr.should.eql([true, true, false]) argv._.should.eql([101, 102]) }) }) it('should replace the key __proto__ with the key ___proto___', function () { const argv = parser(['-f.__proto__.foo', '99', '-x.y.__proto__.bar', '100', '--__proto__', '200']) argv.should.eql({ _: [], ___proto___: 200, f: { ___proto___: { foo: 99 } }, x: { y: { ___proto___: { bar: 100 } } } }) }) it('throws error for unsupported Node.js versions', () => { process.env.YARGS_MIN_NODE_VERSION = '55' delete require.cache[require.resolve('../')] expect(() => { require('../') }).to.throw(/yargs parser supports a minimum Node.js version of 55/) delete process.env.YARGS_MIN_NODE_VERSION }) // Refs: https://github.com/yargs/yargs-parser/issues/386 describe('perf', () => { const i = 100000 describe('unknown-options-as-args', () => { it('parses long chain of "-" with reasonable performance', function () { this.timeout(500) const s = (new Array(i).fill('-').join('')) + 'a' const parsed = parser([s], { configuration: { 'unknown-options-as-args': true } }) parsed._[0].should.equal(s) }) it('parses long chain of "-a-a" with reasonable performance', function () { this.timeout(500) const s = '-' + (new Array(i).fill('-a').join('')) + '=35' const parsed = parser([s], { configuration: { 'unknown-options-as-args': true } }) parsed._[0].should.equal(s) }) }) it('parses long chain of "-" with reasonable performance', function () { this.timeout(500) const s = (new Array(i).fill('-').join('')) + 'a' const arg = (new Array(i - 2).fill('-').join('')) + 'a' const parsed = parser([s]) parsed[arg].should.equal(true) }) it('parses long chain of "-a-a" with reasonable performance', function () { this.timeout(500) const s = '-' + (new Array(i).fill('-a').join('')) + '=35' const arg = 'a' + (new Array(i - 1).fill('A').join('')) const parsed = parser([s]) parsed[arg].should.equal(35) }) }) }) yargs-parser-yargs-parser-v21.0.0/test/yargs-parser.mjs000066400000000000000000000173451414461571200231440ustar00rootroot00000000000000import { should, expect } from 'chai' import parser from '../build/lib/index.js' import path from 'path' import { fileURLToPath } from 'url' import { readFileSync } from 'fs' should() describe('yargs-parser (esm)', function () { const __dirname = path.dirname(fileURLToPath(import.meta.url)) const jsonPath = path.resolve(__dirname, './fixtures/config.json') describe('config', function () { it('should load options and values from default config if specified', function () { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') }) it('should use value from config file, if argv value is using default value', function () { const argv = parser([], { alias: { z: 'zoom' }, config: ['settings'], default: { settings: jsonPath, foo: 'banana' } }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('baz') }) it('should combine values from config file and argv, if argv value is an array', function () { const argv = parser(['--foo', 'bar'], { config: ['settings'], array: ['foo'], default: { settings: jsonPath }, configuration: { 'combine-arrays': true } }) argv.should.have.property('foo').and.deep.equal(['bar', 'baz']) }) it('should use value from config file, if argv key is a boolean', function () { const argv = parser([], { config: ['settings'], default: { settings: jsonPath }, boolean: ['truthy'] }) argv.should.have.property('truthy', true) }) it('should use value from cli, if cli overrides boolean argv key', function () { const argv = parser(['--no-truthy'], { config: ['settings'], default: { settings: jsonPath }, boolean: ['truthy'] }) argv.should.have.property('truthy', false) }) it('should use cli value, if cli value is set and both cli and default value match', function () { const argv = parser(['--foo', 'banana'], { alias: { z: 'zoom' }, config: ['settings'], default: { settings: jsonPath, foo: 'banana' } }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('banana') }) it("should allow config to be set as flag in 'option'", function () { const argv = parser(['--settings', jsonPath, '--foo', 'bar'], { alias: { z: 'zoom' }, config: ['settings'] }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') }) // for esm, only support importing json files it('should fail to load options and values from a JS file when config has .js extention', function () { const jsPath = path.resolve(__dirname, './fixtures/settings.cjs') const argv = parser.detailed(['--settings', jsPath, '--foo', 'bar'], { config: ['settings'] }) argv.error.message.should.include('Invalid JSON config file') }) it('should raise an appropriate error if JSON file is not found', function () { const argv = parser.detailed(['--settings', 'fake.json', '--foo', 'bar'], { alias: { z: 'zoom' }, config: ['settings'] }) argv.error.message.should.equal('Invalid JSON config file: fake.json') }) // see: https://github.com/bcoe/yargs/issues/172 it('should not raise an exception if config file is set as default argument value', function () { const argv = parser.detailed([], { default: { config: 'foo.json' }, config: ['config'] }) expect(argv.error).to.equal(null) }) it('should load nested options from config file', function () { const jsonPath = path.resolve(__dirname, './fixtures/nested_config.json') const argv = parser(['--settings', jsonPath, '--nested.foo', 'bar'], { config: ['settings'] }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'bar', bar: 'bar' }) }) it('should use nested value from config file, if argv value is using default value', function () { const jsonPath = path.resolve(__dirname, './fixtures/nested_config.json') const argv = parser(['--settings', jsonPath], { config: ['settings'], default: { 'nested.foo': 'banana' } }) argv.should.have.property('a', 'a') argv.should.have.property('b', 'b') argv.should.have.property('nested').and.deep.equal({ foo: 'baz', bar: 'bar' }) }) it('allows a custom parsing function to be provided', function () { const jsPath = path.resolve(__dirname, './fixtures/config.txt') const argv = parser(['--settings', jsPath, '--foo', 'bar'], { config: { settings: function (configPath) { // as an example, parse an environment // variable style config: // FOO=99 // BATMAN=grumpy const config = {} const txt = readFileSync(configPath, 'utf-8') txt.split(/\r?\n/).forEach(function (l) { const kv = l.split('=') config[kv[0].toLowerCase()] = kv[1] }) return config } } }) argv.batman.should.equal('grumpy') argv.awesome.should.equal('banana') argv.foo.should.equal('bar') }) it('allows a custom parsing function to be provided as an alias', function () { const jsPath = path.resolve(__dirname, './fixtures/config.json') const argv = parser(['--settings', jsPath, '--foo', 'bar'], { config: { s: function (configPath) { return JSON.parse(readFileSync(configPath, 'utf-8')) } }, alias: { s: ['settings'] } }) argv.should.have.property('herp', 'derp') argv.should.have.property('foo', 'bar') }) it('outputs an error returned by the parsing function', function () { const argv = parser.detailed(['--settings=./package.json'], { config: { settings: function (configPath) { return Error('someone set us up the bomb') } } }) argv.error.message.should.equal('someone set us up the bomb') }) it('outputs an error if thrown by the parsing function', function () { const argv = parser.detailed(['--settings=./package.json'], { config: { settings: function (configPath) { throw Error('someone set us up the bomb') } } }) argv.error.message.should.equal('someone set us up the bomb') }) it('should not pollute the prototype', function () { const argv = parser(['--foo', 'bar'], { alias: { z: 'zoom' }, default: { settings: jsonPath }, config: 'settings' }) argv.should.have.property('herp', 'derp') argv.should.have.property('zoom', 55) argv.should.have.property('foo').and.deep.equal('bar') expect({}.bbb).to.equal(undefined) expect({}.aaa).to.equal(undefined) }) }) })yargs-parser-yargs-parser-v21.0.0/tsconfig.json000066400000000000000000000004201414461571200215240ustar00rootroot00000000000000{ "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { "outDir": "build", "rootDir": ".", "sourceMap": false, "target": "es2017", "moduleResolution": "node", "module": "es2020" }, "include": [ "lib/**/*.ts" ] }yargs-parser-yargs-parser-v21.0.0/tsconfig.test.json000066400000000000000000000002031414461571200225010ustar00rootroot00000000000000{ "extends": "./tsconfig.json", "compilerOptions": { "sourceMap": true }, "include": [ "test/typescript/*.ts" ] }yargs-parser-yargs-parser-v21.0.0/yargs-logo.png000066400000000000000000000414311414461571200216150ustar00rootroot00000000000000PNG  IHDRXbKGD pHYsgRtIME  9_iTXtCommentCreated with GIMPd.e IDATxwXoT]cM$^5$^@ (U{K$ؖ; b⦆b5Q).efy>ffϜo!:A&෣n}@;T\\Zb 0;@ (Dٟk5&"ߞؼvc}Bۆq`DT g<#"f`* 9o*Sd [F^cHaDĜc.ʢP:~^t؂Eݰ`Q!xS b`"|ա版9aX@X{U19Y9Oض".0`(SDU.-ƞ1qFrR]/ED 5BoΘ85M@DiN^Xī{Reg}r((еSƘz(pb?% atTN]@:tzĩ[Jdg*'Mv=z!Ԋv +'αg7@߷[PHn=}`i@4wۆ5ErpAWFMY40iau=.(kLj uLV֫[BZ6bu) .0qvPHܽȚ(ru/ u=fic 9|7$h+ #P"b=ţY%'2p!6> *+a mK@BIҺS pPf=Picb|1 @e Gjj$6h vE4̹CMMG([ _#R&BABdn| @Q^7 "Lk u4ӗ_8-2G<ų+h *M"$髱ș(:3!`}WmrSQH kΡg}u졽 %@v׻ymf M{9.k'7EqA_dC9ylϋgW:%@7{=!{C;xp CՇgY-C+W:G 1.?P/±L[=tjz\=Nd} "\R Q46Lk+pRmq\ײZe0Zh ݻ]6ZSTr4kBznP~\T0p{}h-U i4 K4UdeebVD.]z:G ^:~˖ :nx?:(GU;.aj}[gUt{STIv^UKsgal{ήlϞBV/b"W̲sؙy-k3i&!P kU~r.]%u\ڪMp&+FP""0Yp2|m:XKpuKݹ=,,t5(z_6mڀNmi'*^kټoi2i^|}Pts,ߋ+oM^*I;7 ;x|ʼ'3ys19(vx5Gky3'߮>$ 7>"^lZ[Θ/ynfWf3Ek1iD2Ų{˞}x l!| 6>xEE={BBF:)hݐ t@A&R.])%ލڶoU :$v\fҌ,%Ƙz$_d=aҿOeF<=svL _Ǫ`5jϕp1YH"'r]$a+ʀ= sa\"-- O}[@@|݉ΜωI[@A5cJWM[GԸޟr޳/;PxG^fÖxmuÇ9tAc]׸Iv.'ڥqιЁ&WP "q3Ӷ-y8SӲ.'#]]}aslx3c:O*qf~6Ԧs7v_sdcƍe[_k /r|^S3oٜ'G~&pm/o g9tqkK.O-n`Oh+D60\y6޹<QQa / zi3mFs3g~l! v5eJ*zZi޵an^:6+&Kmj̮\y̙4{&rIm.:E e Yz/SGyf8w.Ew[ -{R6`%eJ-P7n{'6#]WIy|u li2{.F+ 1frjvk澽We?.] nKc. Y8EbKrej "Qfetrsj=O>X\aC+xxQ0(@&ZiX592 ȅW^lh3h׵={k?Xl?3ly@fqUlUbtWOaᇱ,q8u1Ac.ذ*69?w=yJj֭ esPaMG(Nf44k<j@ڟ36tmI;4mSn-}=>Km4+'SRk <9FEKvw[r(Psk1Ur,!f|ަHgOyX,a6XfA&7ltFFocPSdcݿ)(ѫWEt\e?!X޿Iu^I=|?ͣTϏNw-tJq’is2.^`z-Rs󌛃"ne׮LJ٭[XDfje5S*;_qf6V1miʉJGv&wS+LQ=,%TTͧ~Ÿd+X.J j@,žbCɏ\SE=)$2U')C#1 =[xڕXf4moD Mi 3zw0DCKVC|ɯdٹ#;BD>cL.IZbc:kڃs}Ū=X6ЫF6 "0ci(ۇ&i]%RKZw;Etvר?RN+j99 s\A:} @ u~l~ 11 dSǕ"cn,V[ t呮 RUi87KH!t+0,e56t]˔Ѿ]{ûl L{#cAsݱASݜ?֐j*(dĞqVN140K w{4c[v1j#6b.rlX.`'m)-b0<_UK9Tr%?i,BvܞK glVL1r%˄iѪ=ODW-E|lO?G34 ,v54 OT해TgSCSl^~[\"0#! b){1=f3"b}-WjND.Q{ `6Nq1G""2 =^ =6BJ/eeb66CES#fa6ĩi2=\`h6nb?s˪؆֎<:^8Z؅d5I$2y7fAJ-7e&X*M&kc57:suhZn#աױRjilxEPf?JDJ8 8#ˠب UE=cw4Z W`mB1䰷X{m"~ML""fiW#?޸7Ɗ ҧTT&bi9/0FANDf!Dd=x)b~]-aw4S@:^|~ ^/]~@:׮?NQPecCG1@yc(?#ݒ%F1@m͙ 89 #{VW,s^D9m-TwلB@bgf+^> 8d9s7˞s$&G||+,ghoA'$CHBtfY Uq "fj\h9fW7T ,[{(:R3`VV*nQ1/,,A#Fol]9ta+qx>hѶjtG(9 !8^oQgW KzӤSAdX3Q?-z7: RV{إeФyΘݹm ]lNz~]Yrr*1D#c?:/*,DAT5fVVW߿ʿaeޕ8u.M^~ހ*14Җ L6F*,8^; TI4JwFP divphysee9 Hݺ󇳿WDei|4GCw{ꓖlWISSJ`.)Ոm ~ "\o" <Ν˪1Ycv;()3n\ f6ё.Ejceh׮fDI14^j6N&ODi6/ nZ(o?zE=?m$-NE+ȀgDBDz2YچaY XvЩgG%J-fcY 2JTY9wSt,Du gʖ ANi*,c6Nߡs!T=-4(1?vQ=%x_O%v=q>ݿ' wb oCt|d=xD1ۂ.F~nԹ#7Nuꨇ$d 1ZBF.$Aゴ 8 J /o32*~ڀ\qꈅ̃+Ѐo?n[ȉw( B+̩0FY3L,>4jn ی.t z`(P^m Jp 2LqQ)'.+by ;1%oOMcχg ȒG,1\窩O!6c(29e U1 CDdL$*CAGcL*1ƖtND=JJʘD #ZTm[ypr@ZZHaGD!s=ZlG+pu>Չ("u|K31>~~QDR[A6F9Tx Ւ'Lq:ܺ ge-:.";Gɼ?| m rMsU(pju2i@c]i/wghX913Ϣ_"rmOOA"ҴgTjٺQQR\F4јrpݻz E..@XtcPўq>0 @ MƪR_ҙp Q@,Cj|@t5C;"dŮz*9':5LIQAwpD!<4!{TAcb;:7,m+H9{nn ruNT?xQD0boW=̮V"!^U6:?*δYſ=ݺE?o@̬C{זԺu#TRzC}@N]&ƸNEAWaNmu*Me_5ijZ@5yc_Y cBQXP=^o$<Pi(3|E#BEq6+]piIc5P疯3~"7-Q̀,¦b@p{pc~p}˜!h 8URX0fhs`?:*Zvo!,AΧU~b /pt8d ahFf,jnGy۠%P׸k ?a[+h =!/@B@F czmz cDDZ@zhVZsįcA5ûl44V*A5 !wɺw'-X-@/fAe7*Ͽp 5m땟(-h8.Cy=%%k܍- 8 "yƛp:Pv_>:D `rW5gL [;+@ (]|0l8F98|-QIA(@t5ACM"m Z0 L45թ]&+^cgg-McԲyCW_럓1a<,yO1$"- & QwN߼bzSLӼ7&ڵ'GУG3c@uOt IDATGp20:Kk^֧SGcEu$[؅;<ꆵ8ADx +ҧsgDlBWP"w]"bΑ-ݽ3}4+e;[& }x "b&V!_}E*"bvΑYG3d/`N#O""b̃ҿCBD/yС#O[e~9XT;jZX%lCJifRfW0"bl^5)&V!?3;4ji˘a/B"b(obiFAU}E0y]jf=bw<KQQz]c\[Q0o_7oN|_نw1#"6~DĜcKM?͠Ν+mܱAm-3PMNaQG,MxǿDAU~-5x3N/+1W8oSaX,Ai2l7}IADDxK\[7qQA\[u-nk;` HwHR 831K(r 4ZF^7%̭c2YڇmF1o"li91cf*ls ;f)ZQtuxnh}SIۗm"x1Y&c}of=Pm7,adӥAʵZ[ڇ؀T_Wzd!ۆF7m}[UTB|tEhakѪ}C@?[oz> S@)ms`ތƌ,#[up-_f bk_@L&Ev.MT-&LZfskp/ReVId9D,š@մ0WAWofڬQ㎾)aToPO?CԼe44 \7<}`wumBM L1MD|/1*kϑ(' ̈́s̬Cv[P-R/:>BS}ܕm ͂y$DD-za]+żcױTH ˤ-Nsbwf6aNqtVpN$TDHa2Fk5"Z؆1gpk÷J2'[VP!ݤ-&lr9 q޶oIUo_E A`h.nz3ט.[URr>;cg3a \+1MgNl%#n( ~hIugr?tAui 'pB-soTu߬i}{'j^wL -qB1V?-"sH]]+WŢ]cvz$H?Fqt*w-cۏsfgV34 gNi=!+ כYs%j۽,}mC^ DȬo+ѳ[8 ""u;b~U7=>7?fM :b1|--~ 3IOP#<&’F: EG]K\Z;V1FM:;DŽtKDĺ4=Y4nI1ר kyNK܅ah(wjQvw-P 50 n7yr᭺ZH@ODl zAsתI u6_7rߌ"]d#z}4jҖ C|ĞWۏc`"B,k*qXoO]gsoYvttngOWqYxwj5n3'o|3N@Ddd[3Zׅ>7<(;Z [769ʍ8EO6Uk&5zVӶq_5fer3wQ9.ѳ6a`*L D0Ի4Ju=DYЧ+}k5oސtuu^}jCzMQ#]ҠFcꐺ&U9bQy3)@ߊKJ$TP DeWL%_L%[DŴ%GiID}o ZTю6 ܜ=W2~š ٿl20P(@6wA&ۘy=~A+}ed.fiK>~g[9.i3Z{j,>}Ylbϳ3^2?gWf}vQAriWkLj-|gfg{ Z a1 QwOa6e".3)lYzo1Vd}m"b(gij@ZeE_{߲*ϞEvjOݘkCtF27&ZEqiip%%e<%S1]oWAE)VƖʵj*QI۶1g#ud&B?mA&{ųkoר?+ --0h# <[O#8k+@U4<]Чΐ(sXPA6U> eGvq¤u3-R>VxF<8l𺴴4?FU-.QjisFJ6LUT}/=޽Ukڍ|R0xؔΝ *ZEb =zCR& ߷sReRqjoiظui=Z&sVVVu2Ϊw7 =5{Anl%ʋo0&BJ?Yub3KO:Qaa)Da9*tw|o[`M%3i699Ey 2Qx.M Ƙ@wԴ J (9]a_uV;es*,(!( Jt ü[؅{6~mkC>~KԡoմUԤ.f?MՂ*..%mǏ%dfJӼ#En-W5Z-ܲk̐oi_m4C1PC?J9ՋWU"xQo29.iv""܃1ָZRۈUӾkhhEk5(#HxwQYUL|l?aV;zoji C{[{DCi /.&fߟgI?SUeJvZRVמHN~7Oǥ8LS'07!zDd8H@Chj{VL[Nњ)^#T϶o~H->$a 8XA~4 5IJ+.$""uujmzc5/wE9;;WFb5@AWI/_){Y񛯹7בdIĵDLԬ ƹh~͛7,;ϥlm O:!0*8}Iպ#̍.ݶ jb|z@AWAvJ}#9^3r Y U'I;:ѥܸmF׊hR}#g v(QaFjQUޫ >_ R~3bZeU'x_̉h;WKbw꾄Є'Q;?ޏ&u7SDoyIi'l(誺.}7xT'oT7] $KICLf?I2nnۻ}2kp=PUX,:'Μ+S.͕>:J~=|& Qٛ2>l21Bpb^I_ݢ k٪TƝ8y[]Q؄nH] U>wPҹq~˽v?Y֛]ŵj{Jen{f_) T㭩Iԫ:kʓ,iO}Oi̚=PU܂_KĔ^kRV{PTR=fp"~:/ÿڷm|yT{\(ZYyiT]қ5"M{(?,ڝ/уR}&dz9uL}ieAGTڼzf֪Hʈ}:ZDV 7ڻDV+45!@𐺇:C:(} oWS3}p6IY5=x= e(n<:5}Y25k%Ԡ(+hjhRWȊZ.{`zfU%D H: 'hӽMjҗˇŜʵlGGG1:$pm{iBO?~5dqћߡb&ѨJN#ߔyAiYڵ/Ҿ7y}Ӳ}{CsvHTZ!tx{^jt*^9E}ď/hQ-#Ev_A:fLSƖR=Rroi*h1QOh .6>64B|mj@AIu5=ك(oL#6kcH?hVt yH4jYˈƛjRv*{YaU2 m~Qi̙=PJ޹˟rL({, nf~dPD2:sKBsFD wqIԢEzAӤ;S)[0hz4n[㹑fy[}J+ײ*(CkHCDd7Qxm'^r1RMc1F7޽I+~׷W9݊jD'bsFiz׶u%u1'"z =PE LiIuiTXP-CӍ߭mә;bULUy|s]usiŧOJ= ;SA9dbLP'M/~}-ڽ*ׇ%YGS@ ;/ID۵߸7ok CJل, .O{Z_.׬_! QbNDQR@ UQՈ.a2/DD 捪WGOtCU/Q>CIYΛNSj*E"^~2I]r-ZTY{M6z*C o#W- 2◴ɩ3X̧r:wjV:WkZþ^ ( u ct7x%/ ʽ޸Q=ڵL`D^屏*u'(eaSd:^cHBzZc'M)=ũ(I&Ak2}p%g[0-,Y}%y\:e9i2.35 KN7on<~=r>z&Cb}QGS'Ү5mYR+E: A֔/KN<:~ F-DjjR_] /|S$h KFGHhez)=^)6|CzM{WԧwjM+`GBj<}C3zsXVU7ZΒ`j&>oةlrWbZTkZ 0AoZqwn1syŬ RPsk/W+nq5՚}LXهnQ+(UZFlA_ru[_=d"(`لVZ KS+;'?UY9L͜iPu o"%"6"HN7o<='V{zu !-Ld2_Q_3g9[>c? + 3z-#]-N~bQ)든 d/V=~]iN+4߉)78ݚߗlrO} UܢwMsuni&jԴ*zƳ)T=xRiv4z(}kRMLa%%eCGnr{ObťSwk~cZYp~S{դp9F2qE6|.~c/xڑ5G;Gդ ٫ׅǒ2])7o=/[-ѓ@{cM٩UY(b?̌9FbuSSôFYU exYu