{"action":"opened","number":494,"pull_request":{"url":"https://api.github.com/repos/yargs/yargs/pulls/494","id":68727486,"html_url":"https://github.com/yargs/yargs/pull/494","diff_url":"https://github.com/yargs/yargs/pull/494.diff","patch_url":"https://github.com/yargs/yargs/pull/494.patch","issue_url":"https://api.github.com/repos/yargs/yargs/issues/494","number":494,"state":"open","locked":false,"title":"[WIP] Add .commandDir(dir) to API to apply all command modules from a relative directory","user":{"login":"nexdrew","id":1929625,"avatar_url":"https://avatars.githubusercontent.com/u/1929625?v=3","gravatar_id":"","url":"https://api.github.com/users/nexdrew","html_url":"https://github.com/nexdrew","followers_url":"https://api.github.com/users/nexdrew/followers","following_url":"https://api.github.com/users/nexdrew/following{/other_user}","gists_url":"https://api.github.com/users/nexdrew/gists{/gist_id}","starred_url":"https://api.github.com/users/nexdrew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nexdrew/subscriptions","organizations_url":"https://api.github.com/users/nexdrew/orgs","repos_url":"https://api.github.com/users/nexdrew/repos","events_url":"https://api.github.com/users/nexdrew/events{/privacy}","received_events_url":"https://api.github.com/users/nexdrew/received_events","type":"User","site_admin":false},"body":"This replaces #463, opting to require modules from a single directory at a time via `require-directory` (instead of supporting globs).\r\n\r\nThe implementation is slightly trickier than I originally thought, but it allows a consumer to do things like the following.\r\n\r\n### Example setup\r\n\r\n**directory structure**:\r\n\r\n```\r\ncli.js\r\ncommands/\r\n  people.js\r\n  places.js\r\n  things.js\r\n  common_cmds/\r\n    details.js\r\n    search.js\r\n  people_cmds/\r\n    call.js\r\n  places_cmds/\r\n    nav.js\r\n```\r\n\r\n**cli.js**:\r\n\r\n```js\r\n#!/usr/bin/env node\r\nrequire('yargs')\r\n  .commandDir('commands')\r\n  .help()\r\n  .wrap(40)\r\n  .argv\r\n```\r\n\r\n**commands/people.js**:\r\n\r\n```js\r\nexports.command = 'people <command>'\r\nexports.desc = 'Work with people'\r\nexports.builder = function (yargs) {\r\n  return yargs.commandDir('common_cmds').commandDir('people_cmds')\r\n}\r\nexports.handler = function (argv) {}\r\n```\r\n\r\n**commands/places.js**:\r\n\r\n```js\r\nexports.command = 'places <command>'\r\nexports.desc = 'Work with places'\r\nexports.builder = function (yargs) {\r\n  return yargs.commandDir('common_cmds').commandDir('places_cmds')\r\n}\r\nexports.handler = function (argv) {}\r\n```\r\n\r\n**commands/things.js**:\r\n\r\n```js\r\nexports.command = 'things <command>'\r\nexports.desc = 'Work with things'\r\nexports.builder = function (yargs) {\r\n  return yargs.commandDir('common_cmds')\r\n}\r\nexports.handler = function (argv) {}\r\n```\r\n\r\n**commands/common_cmds/details.js**:\r\n\r\n```js\r\nexports.command = 'details <name>'\r\nexports.desc = 'Get details by name'\r\nexports.builder = {}\r\nexports.handler = function (argv) {\r\n  console.log('getting %s details for name: %s', argv._[0], argv.name)\r\n}\r\n```\r\n\r\n### Example CLI\r\n\r\n```\r\n$ ./cli.js --help\r\nCommands:\r\n  people <command>  Work with people\r\n  places <command>  Work with places\r\n  things <command>  Work with things\r\n\r\nOptions:\r\n  --help  Show help            [boolean]\r\n```\r\n\r\n```\r\n$ ./cli.js people --help\r\ncli.js people <command>\r\n\r\nCommands:\r\n  details <name>  Get details by name\r\n  search <query>  Search via query\r\n  call <person>   Call someone\r\n\r\nOptions:\r\n  --help  Show help            [boolean]\r\n```\r\n\r\n```\r\n$ ./cli.js people details       \r\ncli.js people details <name>\r\n\r\nOptions:\r\n  --help  Show help            [boolean]\r\n\r\nNot enough non-option arguments: got 0, need at least 1\r\n```\r\n\r\n```\r\n$ ./cli.js people details Andrew\r\ngetting people details for name: Andrew\r\n```\r\n\r\n### API\r\n\r\nThis adds one method to the API: `.commandDir(dir, [opts])`\r\n\r\n`dir` is a directory name which can/should be relative to the module calling the method.\r\n\r\n`opts` is an options object that is passed to `require-directory`, allowing consumers to customize the behavior. In the initial implementation, the following opts are valid:\r\n\r\n- `opts.recurse`: boolean, default `false`\r\n\r\n    Should yargs require command modules from all nested directories? The default is `false` so subcommands can be nested instead of flattened.\r\n\r\n- `opts.extensions`: array of strings, default `['js']`\r\n\r\n    The types of files to include when requiring.\r\n\r\n- `opts.visit`: function\r\n\r\n    A synchronous function that is called for each command module encountered. Accepts `commandObject`, `pathToFile`, and `filename` as arguments. Returns `commandObject` to include the command; any falsey value to exclude/skip it.\r\n\r\n- `opts.include`: RegExp or function\r\n\r\n    Whitelist certain modules. See [require-directory whitelisting](https://www.npmjs.com/package/require-directory#whitelisting) for details.\r\n\r\n- `opts.exclude`: RegExp or function\r\n\r\n    Blacklist certain modules. See [require-directory blacklisting](https://www.npmjs.com/package/require-directory#blacklisting) for details.\r\n\r\n### Notes\r\n\r\nThis also includes a change that allows a command module to only define `command` and `description`, which better supports requiring JSON modules (though I'm not sure why someone would want to do that) and fulfills (at least) a part of #492.\r\n\r\nI considered adding support for using the filename of a command module as its `command` string if the module itself doesn't define `command`, but I think it'd be nice to support this for _all_ command modules (i.e. not just when using `.commandDir()`), so I'd like to tackle that separately/subsequently.\r\n\r\nI don't have strong feelings about the method name `commandDir`. Would be happy to change it if folks like something else better. As always, feedback is welcomed!\r\n\r\n### Still lacking\r\n\r\n- [ ] Tests!\r\n- [ ] README update","created_at":"2016-05-03T15:44:24Z","updated_at":"2016-05-03T15:44:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/yargs/yargs/pulls/494/commits","review_comments_url":"https://api.github.com/repos/yargs/yargs/pulls/494/comments","review_comment_url":"https://api.github.com/repos/yargs/yargs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/yargs/yargs/issues/494/comments","statuses_url":"https://api.github.com/repos/yargs/yargs/statuses/8b2374d321b84837fc5a63b838e261364a8be0ab","head":{"label":"yargs:command-dir","ref":"command-dir","sha":"8b2374d321b84837fc5a63b838e261364a8be0ab","user":{"login":"yargs","id":16504989,"avatar_url":"https://avatars.githubusercontent.com/u/16504989?v=3","gravatar_id":"","url":"https://api.github.com/users/yargs","html_url":"https://github.com/yargs","followers_url":"https://api.github.com/users/yargs/followers","following_url":"https://api.github.com/users/yargs/following{/other_user}","gists_url":"https://api.github.com/users/yargs/gists{/gist_id}","starred_url":"https://api.github.com/users/yargs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yargs/subscriptions","organizations_url":"https://api.github.com/users/yargs/orgs","repos_url":"https://api.github.com/users/yargs/repos","events_url":"https://api.github.com/users/yargs/events{/privacy}","received_events_url":"https://api.github.com/users/yargs/received_events","type":"Organization","site_admin":false},"repo":{"id":14210523,"name":"yargs","full_name":"yargs/yargs","owner":{"login":"yargs","id":16504989,"avatar_url":"https://avatars.githubusercontent.com/u/16504989?v=3","gravatar_id":"","url":"https://api.github.com/users/yargs","html_url":"https://github.com/yargs","followers_url":"https://api.github.com/users/yargs/followers","following_url":"https://api.github.com/users/yargs/following{/other_user}","gists_url":"https://api.github.com/users/yargs/gists{/gist_id}","starred_url":"https://api.github.com/users/yargs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yargs/subscriptions","organizations_url":"https://api.github.com/users/yargs/orgs","repos_url":"https://api.github.com/users/yargs/repos","events_url":"https://api.github.com/users/yargs/events{/privacy}","received_events_url":"https://api.github.com/users/yargs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/yargs/yargs","description":" yargs the modern, pirate-themed successor to optimist.","fork":false,"url":"https://api.github.com/repos/yargs/yargs","forks_url":"https://api.github.com/repos/yargs/yargs/forks","keys_url":"https://api.github.com/repos/yargs/yargs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yargs/yargs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yargs/yargs/teams","hooks_url":"https://api.github.com/repos/yargs/yargs/hooks","issue_events_url":"https://api.github.com/repos/yargs/yargs/issues/events{/number}","events_url":"https://api.github.com/repos/yargs/yargs/events","assignees_url":"https://api.github.com/repos/yargs/yargs/assignees{/user}","branches_url":"https://api.github.com/repos/yargs/yargs/branches{/branch}","tags_url":"https://api.github.com/repos/yargs/yargs/tags","blobs_url":"https://api.github.com/repos/yargs/yargs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yargs/yargs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yargs/yargs/git/refs{/sha}","trees_url":"https://api.github.com/repos/yargs/yargs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yargs/yargs/statuses/{sha}","languages_url":"https://api.github.com/repos/yargs/yargs/languages","stargazers_url":"https://api.github.com/repos/yargs/yargs/stargazers","contributors_url":"https://api.github.com/repos/yargs/yargs/contributors","subscribers_url":"https://api.github.com/repos/yargs/yargs/subscribers","subscription_url":"https://api.github.com/repos/yargs/yargs/subscription","commits_url":"https://api.github.com/repos/yargs/yargs/commits{/sha}","git_commits_url":"https://api.github.com/repos/yargs/yargs/git/commits{/sha}","comments_url":"https://api.github.com/repos/yargs/yargs/comments{/number}","issue_comment_url":"https://api.github.com/repos/yargs/yargs/issues/comments{/number}","contents_url":"https://api.github.com/repos/yargs/yargs/contents/{+path}","compare_url":"https://api.github.com/repos/yargs/yargs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yargs/yargs/merges","archive_url":"https://api.github.com/repos/yargs/yargs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yargs/yargs/downloads","issues_url":"https://api.github.com/repos/yargs/yargs/issues{/number}","pulls_url":"https://api.github.com/repos/yargs/yargs/pulls{/number}","milestones_url":"https://api.github.com/repos/yargs/yargs/milestones{/number}","notifications_url":"https://api.github.com/repos/yargs/yargs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yargs/yargs/labels{/name}","releases_url":"https://api.github.com/repos/yargs/yargs/releases{/id}","deployments_url":"https://api.github.com/repos/yargs/yargs/deployments","created_at":"2013-11-07T17:25:51Z","updated_at":"2016-05-03T14:47:37Z","pushed_at":"2016-05-03T14:25:31Z","git_url":"git://github.com/yargs/yargs.git","ssh_url":"git@github.com:yargs/yargs.git","clone_url":"https://github.com/yargs/yargs.git","svn_url":"https://github.com/yargs/yargs","homepage":"http://yargs.js.org/","size":1335,"stargazers_count":1601,"watchers_count":1601,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":107,"mirror_url":null,"open_issues_count":43,"forks":107,"open_issues":43,"watchers":1601,"default_branch":"master"}},"base":{"label":"yargs:master","ref":"master","sha":"82d4412cfabf242526de54f37fbc98583545884e","user":{"login":"yargs","id":16504989,"avatar_url":"https://avatars.githubusercontent.com/u/16504989?v=3","gravatar_id":"","url":"https://api.github.com/users/yargs","html_url":"https://github.com/yargs","followers_url":"https://api.github.com/users/yargs/followers","following_url":"https://api.github.com/users/yargs/following{/other_user}","gists_url":"https://api.github.com/users/yargs/gists{/gist_id}","starred_url":"https://api.github.com/users/yargs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yargs/subscriptions","organizations_url":"https://api.github.com/users/yargs/orgs","repos_url":"https://api.github.com/users/yargs/repos","events_url":"https://api.github.com/users/yargs/events{/privacy}","received_events_url":"https://api.github.com/users/yargs/received_events","type":"Organization","site_admin":false},"repo":{"id":14210523,"name":"yargs","full_name":"yargs/yargs","owner":{"login":"yargs","id":16504989,"avatar_url":"https://avatars.githubusercontent.com/u/16504989?v=3","gravatar_id":"","url":"https://api.github.com/users/yargs","html_url":"https://github.com/yargs","followers_url":"https://api.github.com/users/yargs/followers","following_url":"https://api.github.com/users/yargs/following{/other_user}","gists_url":"https://api.github.com/users/yargs/gists{/gist_id}","starred_url":"https://api.github.com/users/yargs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yargs/subscriptions","organizations_url":"https://api.github.com/users/yargs/orgs","repos_url":"https://api.github.com/users/yargs/repos","events_url":"https://api.github.com/users/yargs/events{/privacy}","received_events_url":"https://api.github.com/users/yargs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/yargs/yargs","description":" yargs the modern, pirate-themed successor to optimist.","fork":false,"url":"https://api.github.com/repos/yargs/yargs","forks_url":"https://api.github.com/repos/yargs/yargs/forks","keys_url":"https://api.github.com/repos/yargs/yargs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yargs/yargs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yargs/yargs/teams","hooks_url":"https://api.github.com/repos/yargs/yargs/hooks","issue_events_url":"https://api.github.com/repos/yargs/yargs/issues/events{/number}","events_url":"https://api.github.com/repos/yargs/yargs/events","assignees_url":"https://api.github.com/repos/yargs/yargs/assignees{/user}","branches_url":"https://api.github.com/repos/yargs/yargs/branches{/branch}","tags_url":"https://api.github.com/repos/yargs/yargs/tags","blobs_url":"https://api.github.com/repos/yargs/yargs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yargs/yargs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yargs/yargs/git/refs{/sha}","trees_url":"https://api.github.com/repos/yargs/yargs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yargs/yargs/statuses/{sha}","languages_url":"https://api.github.com/repos/yargs/yargs/languages","stargazers_url":"https://api.github.com/repos/yargs/yargs/stargazers","contributors_url":"https://api.github.com/repos/yargs/yargs/contributors","subscribers_url":"https://api.github.com/repos/yargs/yargs/subscribers","subscription_url":"https://api.github.com/repos/yargs/yargs/subscription","commits_url":"https://api.github.com/repos/yargs/yargs/commits{/sha}","git_commits_url":"https://api.github.com/repos/yargs/yargs/git/commits{/sha}","comments_url":"https://api.github.com/repos/yargs/yargs/comments{/number}","issue_comment_url":"https://api.github.com/repos/yargs/yargs/issues/comments{/number}","contents_url":"https://api.github.com/repos/yargs/yargs/contents/{+path}","compare_url":"https://api.github.com/repos/yargs/yargs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yargs/yargs/merges","archive_url":"https://api.github.com/repos/yargs/yargs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yargs/yargs/downloads","issues_url":"https://api.github.com/repos/yargs/yargs/issues{/number}","pulls_url":"https://api.github.com/repos/yargs/yargs/pulls{/number}","milestones_url":"https://api.github.com/repos/yargs/yargs/milestones{/number}","notifications_url":"https://api.github.com/repos/yargs/yargs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yargs/yargs/labels{/name}","releases_url":"https://api.github.com/repos/yargs/yargs/releases{/id}","deployments_url":"https://api.github.com/repos/yargs/yargs/deployments","created_at":"2013-11-07T17:25:51Z","updated_at":"2016-05-03T14:47:37Z","pushed_at":"2016-05-03T14:25:31Z","git_url":"git://github.com/yargs/yargs.git","ssh_url":"git@github.com:yargs/yargs.git","clone_url":"https://github.com/yargs/yargs.git","svn_url":"https://github.com/yargs/yargs","homepage":"http://yargs.js.org/","size":1335,"stargazers_count":1601,"watchers_count":1601,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":107,"mirror_url":null,"open_issues_count":43,"forks":107,"open_issues":43,"watchers":1601,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/yargs/yargs/pulls/494"},"html":{"href":"https://github.com/yargs/yargs/pull/494"},"issue":{"href":"https://api.github.com/repos/yargs/yargs/issues/494"},"comments":{"href":"https://api.github.com/repos/yargs/yargs/issues/494/comments"},"review_comments":{"href":"https://api.github.com/repos/yargs/yargs/pulls/494/comments"},"review_comment":{"href":"https://api.github.com/repos/yargs/yargs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/yargs/yargs/pulls/494/commits"},"statuses":{"href":"https://api.github.com/repos/yargs/yargs/statuses/8b2374d321b84837fc5a63b838e261364a8be0ab"}},"merged":false,"mergeable":null,"mergeable_state":"unknown","merged_by":null,"comments":0,"review_comments":0,"commits":3,"additions":49,"deletions":5,"changed_files":3}}