├── .gitignore ├── LICENSE ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | test.js 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 sporeball 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yeow 2 | 3 | 4 | 5 | **yeow** is a CLI helper with attitude — part [meow](https://github.com/sindresorhus/meow), part [yargs](https://github.com/yargs/yargs), and built to spite the both of them. 6 | 7 | things yeow **does**: 8 | - parse arguments 9 | - assert types 10 | - fail if given incorrect arguments 11 | 12 | things yeow **doesn't do**: 13 | - negate arguments with `--no-` 14 | - interpret arguments as an array 15 | - restrict valid values to a predetermined list 16 | 17 | ### install 18 | ``` 19 | $ npm i --save yeow 20 | ``` 21 | 22 | ### usage 23 | ```js 24 | #!/usr/bin/env node 25 | const args = require("yeow")({ 26 | "exclamation": { 27 | type: "string", 28 | required: true 29 | }, 30 | "int": { 31 | type: "number", 32 | aliases: "--integer", 33 | default: 16 34 | } 35 | }); 36 | 37 | console.log(args); 38 | ``` 39 | ``` 40 | $ ./logger.js "yeow!" 41 | { exclamation: 'yeow!', int: 16 } 42 | 43 | $ ./logger.js "cool!" --integer 100 44 | { exclamation: 'cool!', int: 100 } 45 | ``` 46 | 47 | ## API 48 | 49 | ### yeow(obj) 50 | returns an `object` of parsed arguments from the passed `obj`. 51 | 52 | #### obj 53 | type: `object` 54 | 55 | each key is a human-readable argument name. the value is an object with any of: 56 | 57 | ##### type 58 | type: `string` 59 | 60 | type of the argument. 61 | 62 | possible values: 63 | - `string` 64 | - `number` 65 | - `file` 66 | 67 | if this is omitted, the argument will become a simple `true`/`false` flag. 68 | 69 | ##### required 70 | type: `boolean`\ 71 | default: `false` 72 | 73 | whether the argument is required. required arguments ignore `aliases` and `default`. 74 | 75 | the *n*-th argument for which `required` is set to `true` **must** be passed as the *n*-th argument to the program. 76 | 77 | ##### missing 78 | type: `string` 79 | 80 | error message to output if the argument is omitted. 81 | 82 | if the argument is not `required`, this will be ignored. 83 | 84 | ##### aliases 85 | type: `string` 86 | 87 | valid aliases for the argument, space-slash-space separated. 88 | 89 | example values: 90 | - `-a` 91 | - `-a / --argument` 92 | 93 | if the argument is `required`, this will be ignored. 94 | 95 | ##### extensions 96 | type: `string` 97 | 98 | valid file extensions for the argument, space-slash-space separated. 99 | 100 | example values: 101 | - `.txt` 102 | - `.js / .jsx` 103 | 104 | if the argument's `type` is not `file`, this will be ignored.\ 105 | if this is omitted, the argument will accept files of any extension. 106 | 107 | ##### default 108 | type: `string | number` 109 | 110 | a default value the argument will have if it is omitted. the value's type should match the argument's `type`. 111 | 112 | if the argument is `required`, this will be ignored. 113 | 114 | ##### invalid 115 | type: `string` 116 | 117 | error message to output if the argument is passed with an invalid type. 118 | 119 | ### example 120 | ```js 121 | const args = require("yeow")({ 122 | "script": { 123 | type: "file", 124 | extensions: ".js", 125 | required: true, 126 | missing: "a file must be passed", 127 | invalid: "not a .js file" 128 | }, 129 | "delay": { 130 | type: "number", 131 | aliases: "-d / --delay", 132 | default: 1 133 | }, 134 | "input": { 135 | type: "string", 136 | aliases: "--input", 137 | }, 138 | "verbose": { 139 | aliases: "-v / --verbose" 140 | } 141 | }); 142 | ``` 143 | 144 | ### donate 145 | you can support the development of this project and others via Patreon: 146 | 147 | [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dsporeball%26type%3Dpledges%26suffix%3D%252Fmonth&style=for-the-badge)](https://patreon.com/sporeball) 148 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | index.js 3 | yeow core 4 | copyright (c) 2021 sporeball 5 | MIT license 6 | */ 7 | 8 | const yeow = obj => { 9 | let v = process.argv.slice(2); 10 | let a = {}; 11 | let O = Object.keys(obj).reduce((a, c, i) => (a[(x => obj[x].required)(c) ? 0 : 1].push(c), a), [[], []]).flat(); 12 | 13 | for (let k of O) { 14 | let K = obj[k]; 15 | let c, D, E; 16 | 17 | if (K.required) { 18 | if (t(v[(D = O.indexOf(k))]) == "none") { 19 | X(K.missing || `missing required argument ${k}`); 20 | } 21 | c = true; 22 | } else { 23 | c = (D = K.aliases ? v.findIndex(x => K.aliases.split(" / ").includes(x)) + 1 : -1) > 0; 24 | } 25 | 26 | if (K.extensions) E = K.extensions.split(" / "); 27 | 28 | if (K.type && c && (t(v[D]) == "none" || (K.type != "string" && t(v[D]) != K.type))) { 29 | X(K.invalid || `argument ${k} has invalid type (expected ${K.type}, got ${t(v[D])})`); 30 | } 31 | 32 | if (K.type == "file" && c && E && !E.some(x => v[D].endsWith(x))) { 33 | X(K.invalid || `argument ${k} has invalid extension (expected ${E.length > 1 ? "one of " : ""}${K.extensions})`); 34 | } 35 | 36 | a[k] = K.type === undefined ? D > 0 : 37 | K.type == "number" ? c ? +v[D] : +K.default : 38 | c ? v[D] : K.default; 39 | } 40 | 41 | return a; 42 | } 43 | 44 | const X = e => { 45 | console.log(`\u001B[31merror:\u001B[39m ${e}`); 46 | process.exit(1); 47 | }; 48 | 49 | const t = v => v === undefined || v.startsWith("-") ? "none" : v.match(/\w+\.\w+/) ? "file" : v.match(/^0$|^[1-9]+[0-9]*$/) ? "number" : "string"; 50 | 51 | module.exports = yeow; 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeow", 3 | "version": "0.2.0", 4 | "description": "CLI helper with attitude", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/sporeball/yeow.git" 12 | }, 13 | "keywords": [ 14 | "cli", 15 | "helper", 16 | "command", 17 | "line", 18 | "argument", 19 | "parser", 20 | "args", 21 | "argv" 22 | ], 23 | "author": "sporeball", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/sporeball/yeow/issues" 27 | }, 28 | "homepage": "https://github.com/sporeball/yeow#readme" 29 | } 30 | --------------------------------------------------------------------------------