├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github └── contributing.md ├── .gitignore ├── .travis.yml ├── .verb.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── examples ├── get.js ├── patch.js ├── put.js ├── request.js └── topics.js ├── index.js ├── package.json └── test ├── support └── auth.js └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | 9 | "globals": { 10 | "document": false, 11 | "navigator": false, 12 | "window": false 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-spacing": [2, { "before": true, "after": true }], 18 | "block-spacing": [2, "always"], 19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 20 | "comma-dangle": [2, "never"], 21 | "comma-spacing": [2, { "before": false, "after": true }], 22 | "comma-style": [2, "last"], 23 | "constructor-super": 2, 24 | "curly": [2, "multi-line"], 25 | "dot-location": [2, "property"], 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "generator-star-spacing": [2, { "before": true, "after": true }], 29 | "handle-callback-err": [2, "^(err|error)$" ], 30 | "indent": [2, 2, { "SwitchCase": 1 }], 31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 32 | "keyword-spacing": [2, { "before": true, "after": true }], 33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 34 | "new-parens": 2, 35 | "no-array-constructor": 2, 36 | "no-caller": 2, 37 | "no-class-assign": 2, 38 | "no-cond-assign": 2, 39 | "no-const-assign": 2, 40 | "no-control-regex": 2, 41 | "no-debugger": 2, 42 | "no-delete-var": 2, 43 | "no-dupe-args": 2, 44 | "no-dupe-class-members": 2, 45 | "no-dupe-keys": 2, 46 | "no-duplicate-case": 2, 47 | "no-empty-character-class": 2, 48 | "no-eval": 2, 49 | "no-ex-assign": 2, 50 | "no-extend-native": 2, 51 | "no-extra-bind": 2, 52 | "no-extra-boolean-cast": 2, 53 | "no-extra-parens": [2, "functions"], 54 | "no-fallthrough": 2, 55 | "no-floating-decimal": 2, 56 | "no-func-assign": 2, 57 | "no-implied-eval": 2, 58 | "no-inner-declarations": [2, "functions"], 59 | "no-invalid-regexp": 2, 60 | "no-irregular-whitespace": 2, 61 | "no-iterator": 2, 62 | "no-label-var": 2, 63 | "no-labels": 2, 64 | "no-lone-blocks": 2, 65 | "no-mixed-spaces-and-tabs": 2, 66 | "no-multi-spaces": 2, 67 | "no-multi-str": 2, 68 | "no-multiple-empty-lines": [2, { "max": 1 }], 69 | "no-native-reassign": 0, 70 | "no-negated-in-lhs": 2, 71 | "no-new": 2, 72 | "no-new-func": 2, 73 | "no-new-object": 2, 74 | "no-new-require": 2, 75 | "no-new-wrappers": 2, 76 | "no-obj-calls": 2, 77 | "no-octal": 2, 78 | "no-octal-escape": 2, 79 | "no-proto": 0, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-return-assign": 2, 83 | "no-self-compare": 2, 84 | "no-sequences": 2, 85 | "no-shadow-restricted-names": 2, 86 | "no-spaced-func": 2, 87 | "no-sparse-arrays": 2, 88 | "no-this-before-super": 2, 89 | "no-throw-literal": 2, 90 | "no-trailing-spaces": 0, 91 | "no-undef": 2, 92 | "no-undef-init": 2, 93 | "no-unexpected-multiline": 2, 94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 95 | "no-unreachable": 2, 96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 97 | "no-useless-call": 0, 98 | "no-with": 2, 99 | "one-var": [0, { "initialized": "never" }], 100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 101 | "padded-blocks": [0, "never"], 102 | "quotes": [2, "single", "avoid-escape"], 103 | "radix": 2, 104 | "semi": [2, "always"], 105 | "semi-spacing": [2, { "before": false, "after": true }], 106 | "space-before-blocks": [2, "always"], 107 | "space-before-function-paren": [2, "never"], 108 | "space-in-parens": [2, "never"], 109 | "space-infix-ops": 2, 110 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 112 | "use-isnan": 2, 113 | "valid-typeof": 2, 114 | "wrap-iife": [2, "any"], 115 | "yoda": [2, "never"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary 11 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to topics 2 | 3 | First and foremost, thank you! We appreciate that you want to contribute to topics, your time is valuable, and your contributions mean a lot to us. 4 | 5 | ## Important! 6 | 7 | By contributing to this project, you: 8 | 9 | * Agree that you have authored 100% of the content 10 | * Agree that you have the necessary rights to the content 11 | * Agree that you have received the necessary permissions from your employer to make the contributions (if applicable) 12 | * Agree that the content you contribute may be provided under the Project license(s) 13 | 14 | ## Getting started 15 | 16 | **What does "contributing" mean?** 17 | 18 | Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: 19 | 20 | - Updating or correcting documentation 21 | - Feature requests 22 | - Bug reports 23 | 24 | If you'd like to learn more about contributing in general, the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) has a lot of useful information. 25 | 26 | **Showing support for topics** 27 | 28 | Please keep in mind that open source software is built by people like you, who spend their free time creating things the rest the community can use. 29 | 30 | Don't have time to contribute? No worries, here are some other ways to show your support for topics: 31 | 32 | - star the [project](https://github.com/jonschlinkert/topics) 33 | - tweet your support for topics 34 | 35 | ## Issues 36 | 37 | ### Before creating an issue 38 | 39 | Please try to determine if the issue is caused by an underlying library, and if so, create the issue there. Sometimes this is difficult to know. We only ask that you attempt to give a reasonable attempt to find out. Oftentimes the readme will have advice about where to go to create issues. 40 | 41 | Try to follow these guidelines 42 | 43 | - **Avoid creating issues for implementation help**. It's much better for discoverability, SEO, and semantics - to keep the issue tracker focused on bugs and feature requests - to ask implementation-related questions on [stackoverflow.com][so] 44 | - **Investigate the issue**: 45 | - **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue. 46 | - Create the issue in the appropriate repository. 47 | 48 | ### Creating an issue 49 | 50 | Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue: 51 | 52 | - **version**: please note the version of topics are you using 53 | - **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using 54 | - **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/) 55 | 56 | ### Closing issues 57 | 58 | The original poster or the maintainer's of topics may close an issue at any time. Typically, but not exclusively, issues are closed when: 59 | 60 | - The issue is resolved 61 | - The project's maintainers have determined the issue is out of scope 62 | - An issue is clearly a duplicate of another issue, in which case the duplicate issue will be linked. 63 | - A discussion has clearly run its course 64 | 65 | 66 | ## Next steps 67 | 68 | **Tips for creating idiomatic issues** 69 | 70 | Spending just a little extra time to review best practices and brush up on your contributing skills will, at minimum, make your issue easier to read, easier to resolve, and more likely to be found by others who have the same or similar issue in the future. At best, it will open up doors and potential career opportunities by helping you be at your best. 71 | 72 | The following resources were hand-picked to help you be the most effective contributor you can be: 73 | 74 | - The [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) is a great place for newcomers to start, but there is also information for experienced contributors there. 75 | - Take some time to learn basic markdown. We can't stress this enough. Don't start pasting code into GitHub issues before you've taken a moment to review this [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) 76 | - The GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/) is another great markdown resource. 77 | - Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/). 78 | 79 | At the very least, please try to: 80 | 81 | - Use backticks to wrap code. This ensures that it retains its formatting and isn't modified when it's rendered by GitHub, and makes the code more readable to others 82 | - When applicable, use syntax highlighting by adding the correct language name after the first "code fence" 83 | 84 | 85 | [so]: http://stackoverflow.com/questions/tagged/topics 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '8' 9 | - '7' 10 | - '6' 11 | - '5' 12 | - '4' 13 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | This library uses [github-base][]. Visit that library's [github repository][github-base] for documentation about all available options and authentication options. 4 | 5 | ```js 6 | var topics = require('{%= name %}'); 7 | ``` 8 | 9 | **Example response** 10 | 11 | Status: 200 OK 12 | 13 | ```json 14 | { 15 | "names": [ 16 | "octocat", 17 | "atom", 18 | "electron", 19 | "API" 20 | ] 21 | } 22 | ``` 23 | 24 | ## API 25 | 26 | {%= apidocs("index.js") %} 27 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017, Jon Schlinkert. 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # topics [![NPM version](https://img.shields.io/npm/v/topics.svg?style=flat)](https://www.npmjs.com/package/topics) [![NPM monthly downloads](https://img.shields.io/npm/dm/topics.svg?style=flat)](https://npmjs.org/package/topics) [![NPM total downloads](https://img.shields.io/npm/dt/topics.svg?style=flat)](https://npmjs.org/package/topics) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/topics.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/topics) 2 | 3 | > Get and update GitHub repository topics. 4 | 5 | Follow this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), for updates on this project and others. 6 | 7 | ## Install 8 | 9 | Install with [npm](https://www.npmjs.com/): 10 | 11 | ```sh 12 | $ npm install --save topics 13 | ``` 14 | 15 | ## Usage 16 | 17 | This library uses [github-base](https://github.com/jonschlinkert/github-base). Visit that library's [github repository](https://github.com/jonschlinkert/github-base) for documentation about all available options and authentication options. 18 | 19 | ```js 20 | var topics = require('topics'); 21 | ``` 22 | 23 | **Example response** 24 | 25 | Status: 200 OK 26 | 27 | ```json 28 | { 29 | "names": [ 30 | "octocat", 31 | "atom", 32 | "electron", 33 | "API" 34 | ] 35 | } 36 | ``` 37 | 38 | ## API 39 | 40 | ### [topics](index.js#L30) 41 | 42 | List all topics for a repository. The main export is a function that calls the [.get](#get) method. 43 | 44 | **Params** 45 | 46 | * `owner` **{String}**: Either `owner/repo` combination, or `owner` if `repo` is the second argument. 47 | * `repo` **{String|Object}**: Repository name or options. 48 | * `options` **{Object}** 49 | * `returns` **{Promise}** 50 | 51 | **Example** 52 | 53 | ```js 54 | var options = { 55 | username: 'your_username', 56 | password: 'your_password' 57 | }; 58 | 59 | topics('micromatch/micromatch', options) 60 | .then(function(topics) { 61 | console.log('topics', topics); 62 | }) 63 | .catch(console.error) 64 | ``` 65 | 66 | ### [.request](index.js#L58) 67 | 68 | Create a topics request with the given `method`, `owner`, `repo` and `options`. 69 | 70 | **Params** 71 | 72 | * `method` **{String}** 73 | * `owner` **{String}**: Either `owner/repo` combination, or `owner` if `repo` is the second argument. 74 | * `repo` **{String|Object}**: Repository name or options. 75 | * `options` **{Object}** 76 | * `returns` **{Promise}** 77 | 78 | **Example** 79 | 80 | ```js 81 | var options = { 82 | username: 'your_username', 83 | password: 'your_password' 84 | }; 85 | 86 | topics.request('get', 'micromatch', 'micromatch', options) 87 | .then(function(topics) { 88 | console.log('topics', topics); 89 | }) 90 | .catch(console.error) 91 | ``` 92 | 93 | ### [.get](index.js#L109) 94 | 95 | List all topics for a repository. 96 | 97 | **Params** 98 | 99 | * `owner` **{String}**: Either `owner/repo` combination, or `owner` if `repo` is the second argument. 100 | * `repo` **{String|Object}**: Repository name or options. 101 | * `options` **{Object}** 102 | * `returns` **{Promise}** 103 | 104 | **Example** 105 | 106 | ```js 107 | var options = { 108 | username: 'your_username', 109 | password: 'your_password' 110 | }; 111 | 112 | topics.get('micromatch/micromatch', options) 113 | .then(function(topics) { 114 | console.log('topics', topics); 115 | }) 116 | .catch(console.error) 117 | ``` 118 | 119 | ### [.put](index.js#L142) 120 | 121 | Replace all topics for a repository. 122 | 123 | **Params** 124 | 125 | * `owner` **{String}**: Either `owner/repo` combination, or `owner` if `repo` is the second argument. 126 | * `repo` **{String|Object}**: Repository name or options. 127 | * `options` **{Object}** 128 | * `returns` **{Promise}** 129 | 130 | **Example** 131 | 132 | ```js 133 | var options = { 134 | username: 'your_username', 135 | password: 'your_password', 136 | 137 | // new topics to use (overwrites all existing topics) 138 | names: [ 139 | 'foo', 140 | 'bar', 141 | 'baz' 142 | ] 143 | }; 144 | 145 | topics.put('micromatch/micromatch', options) 146 | .then(function(topics) { 147 | console.log('topics', topics); 148 | }) 149 | .catch(console.error) 150 | ``` 151 | 152 | ### [.patch](index.js#L177) 153 | 154 | Gets all topics for a repository and then replaces the existing topics with one or more additional topics defined on `options.names`. Send an empty array (`[] 155 | 156 | **Params** 157 | 158 | * `owner` **{String}**: Either `owner/repo` combination, or `owner` if `repo` is the second argument. 159 | * `repo` **{String|Object}**: Repository name or options. 160 | * `options` **{Object}** 161 | * `returns` **{Promise}** 162 | 163 | **Example** 164 | 165 | ```js 166 | var options = { 167 | username: 'your_username', 168 | password: 'your_password', 169 | 170 | // new topics to use (overwrites all existing topics) 171 | names: [ 172 | 'foo', 173 | 'bar', 174 | 'baz' 175 | ] 176 | }; 177 | 178 | topics.patch('micromatch/micromatch', options) 179 | .then(function(topics) { 180 | console.log('topics', topics); 181 | }) 182 | .catch(console.error) 183 | ``` 184 | 185 | ### [.normalize](index.js#L202) 186 | 187 | Utility for normalizing options. This is already used in 188 | necessary places in the other request methods, but it's exposed for 189 | unit tests and debugging. 190 | 191 | **Params** 192 | 193 | * `owner` **{String}** 194 | * `repo` **{String}** 195 | * `options` **{String}** 196 | * `returns` **{Object}**: Returns the options object to use with the request methods. 197 | 198 | ## About 199 | 200 | ### Related projects 201 | 202 | You might also be interested in these projects: 203 | 204 | * [commits](https://www.npmjs.com/package/commits): List the commits on a GitHub repository. | [homepage](https://github.com/jonschlinkert/commits "List the commits on a GitHub repository.") 205 | * [gists](https://www.npmjs.com/package/gists): Methods for working with the GitHub Gist API. Node.js/JavaScript | [homepage](https://github.com/jonschlinkert/gists "Methods for working with the GitHub Gist API. Node.js/JavaScript") 206 | * [github-base](https://www.npmjs.com/package/github-base): JavaScript wrapper that greatly simplifies working with GitHub's API. | [homepage](https://github.com/jonschlinkert/github-base "JavaScript wrapper that greatly simplifies working with GitHub's API.") 207 | * [github-contributors](https://www.npmjs.com/package/github-contributors): Generate a markdown or JSON list of contributors for a project using the GitHub API. | [homepage](https://github.com/jonschlinkert/github-contributors "Generate a markdown or JSON list of contributors for a project using the GitHub API.") 208 | * [github-metadata](https://www.npmjs.com/package/github-metadata): Gather GitHub metadata about a repository. | [homepage](https://github.com/doowb/github-metadata "Gather GitHub metadata about a repository.") 209 | * [github-traffic](https://www.npmjs.com/package/github-traffic): Get the Github traffic for the specified repository | [homepage](https://github.com/doowb/github-traffic "Get the Github traffic for the specified repository") 210 | * [github-trees](https://www.npmjs.com/package/github-trees): Get a tree from a GitHub repository. | [homepage](https://github.com/jonschlinkert/github-trees "Get a tree from a GitHub repository.") 211 | * [repos](https://www.npmjs.com/package/repos): Pull down a list of GitHub repositories for the specified user or org, and save… [more](https://github.com/jonschlinkert/repos) | [homepage](https://github.com/jonschlinkert/repos "Pull down a list of GitHub repositories for the specified user or org, and save to a local JSON file.") 212 | 213 | ### Contributing 214 | 215 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 216 | 217 | Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards. 218 | 219 | ### Contributors 220 | 221 | | **Commits** | **Contributor** | 222 | | --- | --- | 223 | | 10 | [jonschlinkert](https://github.com/jonschlinkert) | 224 | | 2 | [doowb](https://github.com/doowb) | 225 | 226 | ### Building docs 227 | 228 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 229 | 230 | To generate the readme, run the following command: 231 | 232 | ```sh 233 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 234 | ``` 235 | 236 | ### Running tests 237 | 238 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 239 | 240 | ```sh 241 | $ npm install && npm test 242 | ``` 243 | 244 | ### Author 245 | 246 | **Jon Schlinkert** 247 | 248 | * [github/jonschlinkert](https://github.com/jonschlinkert) 249 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 250 | 251 | ### License 252 | 253 | Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). 254 | Released under the [MIT License](LICENSE). 255 | 256 | *** 257 | 258 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on August 21, 2017._ -------------------------------------------------------------------------------- /examples/get.js: -------------------------------------------------------------------------------- 1 | 2 | var auth = require('../test/support/auth'); 3 | var topics = require('..'); 4 | 5 | topics.get('micromatch/micromatch', auth) 6 | .then(function(topics) { 7 | console.log('topics', topics); 8 | }) 9 | .catch(console.error) 10 | -------------------------------------------------------------------------------- /examples/patch.js: -------------------------------------------------------------------------------- 1 | 2 | var auth = require('../test/support/auth'); 3 | var topics = require('..'); 4 | 5 | var opts = Object.assign({}, auth, {names: ['nodejs']}); 6 | 7 | topics.patch('micromatch', 'micromatch', opts) 8 | .then(function(topics) { 9 | console.log('topics', topics); 10 | }) 11 | .catch(console.error) 12 | -------------------------------------------------------------------------------- /examples/put.js: -------------------------------------------------------------------------------- 1 | 2 | var auth = require('../test/support/auth'); 3 | var topics = require('..'); 4 | 5 | var opts = Object.assign({}, auth, { 6 | names: [ 7 | 'glob', 8 | 'glob-pattern', 9 | 'glob-matching', 10 | 'matcher', 11 | 'bash', 12 | 'extended-globs', 13 | 'javascript', 14 | 'multimatch', 15 | 'minimatch', 16 | 'regex', 17 | 'regular-expression', 18 | 'extglob', 19 | 'globbing', 20 | 'wildmat', 21 | 'nodejs' 22 | ] 23 | }); 24 | 25 | topics.put('micromatch/micromatch', opts) 26 | .then(function(topics) { 27 | console.log('topics', topics); 28 | }) 29 | .catch(console.error) 30 | -------------------------------------------------------------------------------- /examples/request.js: -------------------------------------------------------------------------------- 1 | 2 | var auth = require('../test/support/auth'); 3 | var topics = require('..'); 4 | 5 | topics.request('GET', 'micromatch', 'micromatch', auth) 6 | .then(function(topics) { 7 | console.log('topics', topics); 8 | }) 9 | .catch(console.error) 10 | -------------------------------------------------------------------------------- /examples/topics.js: -------------------------------------------------------------------------------- 1 | 2 | var auth = require('../test/support/auth'); 3 | var topics = require('..'); 4 | 5 | topics('micromatch/micromatch', auth) 6 | .then(function(topics) { 7 | console.log('topics', topics); 8 | }) 9 | .catch(console.error) 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var GitHub = require('github-base'); 4 | var isObject = require('isobject'); 5 | var union = require('arr-union'); 6 | 7 | /** 8 | * List all topics for a repository. The main export is a function that 9 | * calls the [.get](#get) method. 10 | * 11 | * ```js 12 | * var options = { 13 | * username: 'your_username', 14 | * password: 'your_password' 15 | * }; 16 | * 17 | * topics('micromatch/micromatch', options) 18 | * .then(function(topics) { 19 | * console.log('topics', topics); 20 | * }) 21 | * .catch(console.error) 22 | * ``` 23 | * @param {String} `owner` Either `owner/repo` combination, or `owner` if `repo` is the second argument. 24 | * @param {String|Object} `repo` Repository name or options. 25 | * @param {Object} `options` 26 | * @return {Promise} 27 | * @api public 28 | */ 29 | 30 | function topics(owner, repo, options) { 31 | return topics.request('get', owner, repo, options); 32 | } 33 | 34 | /** 35 | * Create a topics request with the given `method`, `owner`, `repo` 36 | * and `options`. 37 | * 38 | * ```js 39 | * var options = { 40 | * username: 'your_username', 41 | * password: 'your_password' 42 | * }; 43 | * 44 | * topics.request('get', 'micromatch', 'micromatch', options) 45 | * .then(function(topics) { 46 | * console.log('topics', topics); 47 | * }) 48 | * .catch(console.error) 49 | * ``` 50 | * @param {String} `method` 51 | * @param {String} `owner` Either `owner/repo` combination, or `owner` if `repo` is the second argument. 52 | * @param {String|Object} `repo` Repository name or options. 53 | * @param {Object} `options` 54 | * @return {Promise} 55 | * @api public 56 | */ 57 | 58 | topics.request = function(method, owner, repo, options) { 59 | if (typeof method !== 'string') { 60 | return Promise.reject(new TypeError('expected method to be a string')); 61 | } 62 | if (typeof owner === 'undefined') { 63 | return Promise.reject(new TypeError('expected a string or object')); 64 | } 65 | 66 | // opts for `new GitHub` and opts for `put` 67 | // need to have separate keys to work correctly 68 | var opts = topics.normalize(owner, repo, options); 69 | var data = {names: arrayify(opts.names)}; 70 | delete opts.names; 71 | 72 | var github = new GitHub(opts); 73 | var config = Object.assign({}, opts, data); 74 | var key = method.toLowerCase(); 75 | 76 | return new Promise(function(resolve, reject) { 77 | github[key]('/repos/:owner/:repo/topics', config, function(err, res) { 78 | if (err) { 79 | reject(err); 80 | return; 81 | } 82 | resolve(res); 83 | }); 84 | }); 85 | }; 86 | 87 | /** 88 | * List all topics for a repository. 89 | * 90 | * ```js 91 | * var options = { 92 | * username: 'your_username', 93 | * password: 'your_password' 94 | * }; 95 | * 96 | * topics.get('micromatch/micromatch', options) 97 | * .then(function(topics) { 98 | * console.log('topics', topics); 99 | * }) 100 | * .catch(console.error) 101 | * ``` 102 | * @param {String} `owner` Either `owner/repo` combination, or `owner` if `repo` is the second argument. 103 | * @param {String|Object} `repo` Repository name or options. 104 | * @param {Object} `options` 105 | * @return {Promise} 106 | * @api public 107 | */ 108 | 109 | topics.get = function(owner, repo, options) { 110 | return topics.request('get', owner, repo, options); 111 | }; 112 | 113 | /** 114 | * Replace all topics for a repository. 115 | * 116 | * ```js 117 | * var options = { 118 | * username: 'your_username', 119 | * password: 'your_password', 120 | * 121 | * // new topics to use (overwrites all existing topics) 122 | * names: [ 123 | * 'foo', 124 | * 'bar', 125 | * 'baz' 126 | * ] 127 | * }; 128 | * 129 | * topics.put('micromatch/micromatch', options) 130 | * .then(function(topics) { 131 | * console.log('topics', topics); 132 | * }) 133 | * .catch(console.error) 134 | * ``` 135 | * @param {String} `owner` Either `owner/repo` combination, or `owner` if `repo` is the second argument. 136 | * @param {String|Object} `repo` Repository name or options. 137 | * @param {Object} `options` 138 | * @return {Promise} 139 | * @api public 140 | */ 141 | 142 | topics.put = function(owner, repo, options) { 143 | return topics.request('put', owner, repo, options); 144 | }; 145 | 146 | /** 147 | * Gets all topics for a repository and then replaces the existing 148 | * topics with one or more additional topics defined on `options.names`. 149 | * Send an empty array (`[]`) to clear all topics from the repository. 150 | * 151 | * ```js 152 | * var options = { 153 | * username: 'your_username', 154 | * password: 'your_password', 155 | * 156 | * // new topics to use (overwrites all existing topics) 157 | * names: [ 158 | * 'foo', 159 | * 'bar', 160 | * 'baz' 161 | * ] 162 | * }; 163 | * 164 | * topics.patch('micromatch/micromatch', options) 165 | * .then(function(topics) { 166 | * console.log('topics', topics); 167 | * }) 168 | * .catch(console.error) 169 | * ``` 170 | * @param {String} `owner` Either `owner/repo` combination, or `owner` if `repo` is the second argument. 171 | * @param {String|Object} `repo` Repository name or options. 172 | * @param {Object} `options` 173 | * @return {Promise} 174 | * @api public 175 | */ 176 | 177 | topics.patch = function(owner, repo, options) { 178 | return topics.get(owner, repo, options) 179 | .then(function(res) { 180 | if (res.message && /invalid/i.test(res.message)) { 181 | return Promise.resolve(res); 182 | } 183 | 184 | var opts = topics.normalize(owner, repo, options); 185 | opts.names = union([], res.names, opts.names); 186 | return topics.put(opts); 187 | }); 188 | }; 189 | 190 | /** 191 | * Utility for normalizing options. This is already used in 192 | * necessary places in the other request methods, but it's exposed for 193 | * unit tests and debugging. 194 | * 195 | * @param {String} `owner` 196 | * @param {String} `repo` 197 | * @param {String} `options` 198 | * @return {Object} Returns the options object to use with the request methods. 199 | * @api public 200 | */ 201 | 202 | topics.normalize = function(owner, repo, options) { 203 | if (isObject(repo)) { 204 | options = Object.assign({}, repo, options); 205 | var segs = owner.split('/'); 206 | owner = segs.shift(); 207 | repo = segs.pop(); 208 | } else if (isObject(owner)) { 209 | options = Object.assign({}, owner, repo, options); 210 | options = owner; 211 | owner = null; 212 | repo = null; 213 | } 214 | 215 | var defaults = { 216 | headers: { 217 | accept: 'application/vnd.github.mercy-preview+json' 218 | }, 219 | owner: owner, 220 | repo: repo 221 | }; 222 | 223 | return Object.assign({}, defaults, options); 224 | }; 225 | 226 | function arrayify(val) { 227 | return val ? (Array.isArray(val) ? val : [val]) : []; 228 | } 229 | 230 | /** 231 | * Expose `topics` 232 | */ 233 | 234 | module.exports = topics; 235 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "topics", 3 | "description": "Get and update GitHub repository topics.", 4 | "version": "1.0.1", 5 | "homepage": "https://github.com/jonschlinkert/topics", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "repository": "jonschlinkert/topics", 8 | "bugs": { 9 | "url": "https://github.com/jonschlinkert/topics/issues" 10 | }, 11 | "license": "MIT", 12 | "files": [ 13 | "index.js" 14 | ], 15 | "main": "index.js", 16 | "engines": { 17 | "node": ">=4" 18 | }, 19 | "scripts": { 20 | "test": "mocha" 21 | }, 22 | "dependencies": { 23 | "arr-union": "^3.1.0", 24 | "github-base": "^0.5.4", 25 | "isobject": "^3.0.1" 26 | }, 27 | "devDependencies": { 28 | "minimist": "^1.2.0", 29 | "data-store": "^1.0.0", 30 | "mocha": "^3.5.0", 31 | "gulp-format-md": "^1.0.0" 32 | }, 33 | "keywords": [ 34 | "topics" 35 | ], 36 | "verb": { 37 | "toc": false, 38 | "layout": "default", 39 | "tasks": [ 40 | "readme" 41 | ], 42 | "plugins": [ 43 | "gulp-format-md" 44 | ], 45 | "related": { 46 | "list": [ 47 | "github-base", 48 | "github-contributors", 49 | "github-metadata", 50 | "github-traffic", 51 | "github-trees", 52 | "commits", 53 | "gists", 54 | "repos" 55 | ] 56 | }, 57 | "lint": { 58 | "reflinks": true 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/support/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var opts = {alias: {password: 'p', username: 'u'}}; 4 | var argv = require('minimist')(process.argv.slice(2), opts); 5 | var Store = require('data-store'); 6 | var store = new Store('github-base-tests'); 7 | var auth = store.get('auth'); 8 | 9 | if (!auth) { 10 | auth = {}; 11 | auth.username = argv.username || argv._[0] || process.env.GITHUB_USERNAME; 12 | auth.password = argv.password || argv._[1] || process.env.GITHUB_PASSWORD; 13 | } 14 | 15 | if (auth.username && auth.password) { 16 | store.set('auth', auth); 17 | } else { 18 | console.error('please specify authentication details'); 19 | console.error('--username, -u (or first argument)'); 20 | console.error('--password, -p (or second argument)'); 21 | process.exit(1); 22 | } 23 | 24 | module.exports = auth; 25 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var assert = require('assert'); 5 | var auth = require('./support/auth'); 6 | var topics = require('..'); 7 | var original; 8 | var fixtures = { 9 | put: ['glob', 'glob-pattern', 'glob-matching', 'matcher', 'bash', 'extended-globs', 'javascript', 'multimatch', 'minimatch', 'regex', 'regular-expression', 'extglob', 'globbing', 'wildmat', 'nodejs'], 10 | patch: ['match', 'matching'] 11 | }; 12 | 13 | describe('topics', function() { 14 | before(function() { 15 | // get micromatch's topics before they're modified by tests 16 | return topics.get('micromatch/micromatch', auth) 17 | .then(function(results) { 18 | original = Object.assign({}, results); 19 | }); 20 | }); 21 | 22 | after(function() { 23 | // ensure that micromatch's topics are restored to original 24 | var opts = Object.assign({}, original, auth); 25 | return topics.put('micromatch/micromatch', opts); 26 | }); 27 | 28 | describe('main export', function() { 29 | it('should export a function', function() { 30 | assert.equal(typeof topics, 'function'); 31 | }); 32 | 33 | it('should export a .get method', function() { 34 | assert.equal(typeof topics.get, 'function'); 35 | }); 36 | 37 | it('should export a .put method', function() { 38 | assert.equal(typeof topics.put, 'function'); 39 | }); 40 | 41 | it('should export a .patch method', function() { 42 | assert.equal(typeof topics.patch, 'function'); 43 | }); 44 | 45 | it('should throw an error when invalid args are passed', function() { 46 | return topics() 47 | .catch(function(err) { 48 | assert(err); 49 | }); 50 | }); 51 | 52 | it('should return "bad credentials" when auth is bad', function() { 53 | this.timeout(10000); 54 | 55 | var opts = {username: 'bad', password: 'credentials'}; 56 | 57 | return topics('micromatch/micromatch', opts) 58 | .then(function(res) { 59 | if (res.message === 'Maximum number of login attempts exceeded. Please try again later.') { 60 | return; 61 | } 62 | 63 | assert.deepEqual(res, { 64 | message: 'Bad credentials', 65 | documentation_url: 'https://developer.github.com/v3' 66 | }); 67 | }); 68 | }); 69 | }); 70 | 71 | describe('.request', function() { 72 | it('should return "bad credentials" when auth is bad', function() { 73 | this.timeout(10000); 74 | 75 | var opts = { 76 | username: 'bad', 77 | password: 'credentials' 78 | }; 79 | return topics.request('get', 'micromatch/micromatch', opts) 80 | .then(function(res) { 81 | if (res.message === 'Maximum number of login attempts exceeded. Please try again later.') { 82 | return; 83 | } 84 | assert.deepEqual(res, { 85 | message: 'Bad credentials', 86 | documentation_url: 'https://developer.github.com/v3' 87 | }); 88 | }); 89 | }); 90 | 91 | it('should take owner and repo on options', function() { 92 | this.timeout(5000); 93 | 94 | var options = { 95 | username: auth.username, 96 | password: auth.password, 97 | owner: 'micromatch', 98 | repo: 'micromatch' 99 | }; 100 | 101 | return topics.request('get', options) 102 | .then(function(res) { 103 | if (res.message === 'Maximum number of login attempts exceeded. Please try again later.') { 104 | return; 105 | } 106 | 107 | assert(Array.isArray(res.names)); 108 | assert(res.names.length > 1); 109 | }); 110 | }); 111 | }); 112 | 113 | describe('.get', function() { 114 | it('should return "bad credentials" when auth is bad', function() { 115 | this.timeout(10000); 116 | 117 | var opts = { 118 | username: 'bad', 119 | password: 'credentials' 120 | }; 121 | return topics.get('micromatch/micromatch', opts) 122 | .then(function(res) { 123 | if (res.message === 'Maximum number of login attempts exceeded. Please try again later.') { 124 | return; 125 | } 126 | assert.deepEqual(res, { 127 | message: 'Bad credentials', 128 | documentation_url: 'https://developer.github.com/v3' 129 | }); 130 | }); 131 | }); 132 | 133 | it('should take owner and repo on options', function() { 134 | this.timeout(5000); 135 | 136 | var options = { 137 | username: auth.username, 138 | password: auth.password, 139 | owner: 'micromatch', 140 | repo: 'micromatch' 141 | }; 142 | 143 | return topics.get(options) 144 | .then(function(res) { 145 | if (res.message === 'Maximum number of login attempts exceeded. Please try again later.') { 146 | return; 147 | } 148 | 149 | assert(Array.isArray(res.names)); 150 | assert(res.names.length > 1); 151 | }); 152 | }); 153 | }); 154 | 155 | describe('.put', function() { 156 | it('should overwrite topics', function() { 157 | this.timeout(5000); 158 | 159 | var options = { 160 | username: auth.username, 161 | password: auth.password, 162 | owner: 'micromatch', 163 | repo: 'micromatch', 164 | names: fixtures.put.slice() 165 | }; 166 | 167 | return topics.put(options) 168 | .then(function() { 169 | return topics('micromatch/micromatch', auth) 170 | .then(function(res) { 171 | assert.deepEqual(res, {names: fixtures.put.slice()}); 172 | }); 173 | }); 174 | }); 175 | }); 176 | 177 | describe('.patch', function() { 178 | it('should overwrite topics', function() { 179 | this.timeout(5000); 180 | 181 | var options = { 182 | username: auth.username, 183 | password: auth.password, 184 | owner: 'micromatch', 185 | repo: 'micromatch', 186 | names: fixtures.patch.slice() 187 | }; 188 | 189 | return topics.patch(options) 190 | .then(function() { 191 | return topics('micromatch/micromatch', auth) 192 | .then(function(res) { 193 | assert.deepEqual(res, { 194 | names: fixtures.put.slice().concat(fixtures.patch.slice()) 195 | }); 196 | }); 197 | }); 198 | }); 199 | }); 200 | }); 201 | --------------------------------------------------------------------------------