mirror of
https://github.com/all-contributors/cli.git
synced 2025-01-10 05:56:29 +00:00
Modify contributor generation system
This commit is contained in:
parent
24a897019b
commit
f2c8733e03
14 changed files with 543 additions and 255 deletions
16
.all-contributorsrc
Normal file
16
.all-contributorsrc
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"projectOwner": "jfmengels",
|
||||||
|
"projectName": "all-contributors-cli",
|
||||||
|
"imageSize": 100,
|
||||||
|
"contributors": [{
|
||||||
|
"login": "jfmengels",
|
||||||
|
"name": "Jeroen Engels",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/3869412?v=3",
|
||||||
|
"html_url": "https://github.com/jfmengels",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
53
README.md
53
README.md
|
@ -37,46 +37,55 @@ Where:
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
You can configure the project by creating a `.all-contributorsrc` JSON file.
|
### Add contributing section
|
||||||
|
|
||||||
|
If you don't already have a Contributing section in a Markdown file, create one. Then add the following comment tags section to it. Don't worry, they're visible only to those that read the raw file. The tags **must** be at the beginning of the line.
|
||||||
|
|
||||||
|
```md
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
<!-- CONTRIBUTORS:START - Do not remove or modify this section -->
|
||||||
|
<!-- CONTRIBUTORS:END -->
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a `.all-contributorsrc` file
|
||||||
|
|
||||||
|
You must create a `.all-contributorsrc` JSON file. The data used to generate the contributors list will be stored in here, and you can configure how you want `all-contributors-cli` to generate the list.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"file": "README.md",
|
"file": "README.md",
|
||||||
"owner": "jfmengels",
|
"owner": "jfmengels",
|
||||||
"emoji": {
|
"types": {
|
||||||
"cheerful": ":smiley:"
|
"cheerful": {
|
||||||
}
|
"symbol": ":smiley:"
|
||||||
}
|
|
||||||
```
|
|
||||||
or creating a `all-contributors` updating the `package.json` file:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "all-contributors-cli",
|
|
||||||
"...": "...",
|
|
||||||
"all-contributors": {
|
|
||||||
"file": "README.md",
|
|
||||||
"owner": "jfmengels"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"contributors": [{
|
||||||
|
"login": "jfmengels",
|
||||||
|
"...": "..."
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
These are the keys you can specify:
|
These are the keys you can specify:
|
||||||
- `emoji`: Specify custom emoji, can override the documented emojis. It doesn't really have to be emojis really.
|
|
||||||
- `file`: File to write the list of contributors in. Default: 'README.md'
|
- `file`: File to write the list of contributors in. Default: 'README.md'
|
||||||
|
- `projectOwner`: Name of the user the project is hosted by. Example: `jfmengels/all-contributor-cli` --> `jfmengels`. Mandatory.
|
||||||
|
- `projectName`: Name of the project. Example: `jfmengels/all-contributor-cli` --> `all-contributor-cli`. Mandatory.
|
||||||
|
- `types`: Specify custom symbols or link templates for contribution types. Can override the documented types.
|
||||||
- `imageSize`: Size (in px) of the user's avatar. Default: 100.
|
- `imageSize`: Size (in px) of the user's avatar. Default: 100.
|
||||||
- `owner`: Name of the user the project is hosted by. Example: `jfmengels/all-contributor-cli` --> `jfmengels`. By default will be parsed from the repo's homepage in `package.json` (TODO).
|
- `contributorsPerLine`: Maximum number of columns for the contributors table. Default: 7.
|
||||||
- `project`: Name of the project. Example: `jfmengels/all-contributor-cli` --> `all-contributor-cli`. Default: Name of the project written in the `package.json` file (TODO).
|
- `template`: Define your own template to generate the contributor list.
|
||||||
- `template`: Define your own contributor template. Please read the code to see what you can define (**warning**: not sure it will work well after several tries).
|
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
|
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
|
||||||
|
|
||||||
Contributor | Contributions
|
<!-- CONTRIBUTORS:START - Do not remove or modify this section -->
|
||||||
:---: | :---:
|
| [![Jeroen Engels](https://avatars.githubusercontent.com/u/3869412?v=3&s=100)<br /><sub>Jeroen Engels</sub>](https://github.com/jfmengels)<br />[💻](https://github.com/jfmengels/all-contributors-cli/commits?author=jfmengels) [📖](https://github.com/jfmengels/all-contributors-cli/commits?author=jfmengels) [⚠️](https://github.com/jfmengels/all-contributors-cli/commits?author=jfmengels) |
|
||||||
[![Jeroen Engels](https://avatars.githubusercontent.com/u/3869412?v=3&s=100)<br />Jeroen Engels](https://github.com/jfmengels) | [💻📖⚠️](https://github.com/jfmengels/all-contributors-cli/commits?author=jfmengels)
|
| :---: |
|
||||||
|
<!-- CONTRIBUTORS:END -->
|
||||||
|
|
||||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification.
|
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification.
|
||||||
Contributions of any kind welcome!
|
Contributions of any kind welcome!
|
||||||
|
|
50
cli.js
50
cli.js
|
@ -5,20 +5,22 @@ var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var assign = require('lodash.assign');
|
var assign = require('lodash.assign');
|
||||||
|
|
||||||
|
var generate = require('./lib/generate');
|
||||||
var markdown = require('./lib/markdown');
|
var markdown = require('./lib/markdown');
|
||||||
var getUserInfo = require('./lib/github');
|
var getUserInfo = require('./lib/github');
|
||||||
var defaultEmojis = require('./lib/emoji');
|
|
||||||
var addContributor = require('./lib/addContributor');
|
|
||||||
|
|
||||||
var cwd = process.cwd();
|
var cwd = process.cwd();
|
||||||
var defaultRCFile = path.join(cwd, '.all-contributorsrc');
|
var defaultRCFile = path.join(cwd, '.all-contributorsrc');
|
||||||
|
|
||||||
var argv = require('yargs')
|
var argv = require('yargs')
|
||||||
|
.command('generate', 'Generate the list of contributors')
|
||||||
|
.usage('Usage: $0 generate')
|
||||||
.command('add', 'add a new contributor')
|
.command('add', 'add a new contributor')
|
||||||
.usage('Usage: $0 add <username> <contribution>')
|
.usage('Usage: $0 add <username> <contribution>')
|
||||||
.demand(2)
|
.demand(2)
|
||||||
.default('config', defaultRCFile)
|
.default('config', defaultRCFile)
|
||||||
.default('file', 'README.md')
|
.default('file', 'README.md')
|
||||||
|
.default('contributorsPerLine', 7)
|
||||||
.config('config', function(configPath) {
|
.config('config', function(configPath) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
@ -29,28 +31,40 @@ var argv = require('yargs')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.default('emoji', {})
|
.help('help')
|
||||||
.pkgConf('all-contributors')
|
|
||||||
.argv;
|
.argv;
|
||||||
|
|
||||||
argv.emoji = assign({}, defaultEmojis, argv.emoji);
|
|
||||||
argv.username = argv._[1];
|
|
||||||
argv.contributions = argv._[2].split(',');
|
|
||||||
argv.file = path.join(cwd, argv.file);
|
argv.file = path.join(cwd, argv.file);
|
||||||
|
|
||||||
|
function startGeneration(argv, cb) {
|
||||||
|
markdown.read(argv.file, function(error, fileContent) {
|
||||||
|
if (error) {
|
||||||
|
return cb(error);
|
||||||
|
}
|
||||||
|
var newFileContent = generate(argv, argv.contributors, fileContent);
|
||||||
|
markdown.write(argv.file, newFileContent, cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error) {
|
||||||
|
return console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[0] === 'generate') {
|
||||||
|
startGeneration(argv, onError);
|
||||||
|
} else if (argv[0] === 'add') {
|
||||||
|
// Fetch user
|
||||||
|
argv.username = argv._[1];
|
||||||
|
argv.contributions = argv._[2].split(',');
|
||||||
getUserInfo(argv.username, function(error, user) {
|
getUserInfo(argv.username, function(error, user) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return console.error(error);
|
return console.error(error);
|
||||||
}
|
}
|
||||||
markdown.read(argv.file, function(error, fileContent) {
|
// TODO
|
||||||
if (error) {
|
// Add him to the contributors
|
||||||
return console.error(error);
|
// Save rc file with updated contributors key
|
||||||
|
startGeneration(argv, onError);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
var newFileContent = addContributor(argv, user, fileContent);
|
|
||||||
markdown.write(argv.file, newFileContent, function(error, fileContent) {
|
|
||||||
if (error) {
|
|
||||||
return console.error(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var uniq = require('lodash.uniq');
|
|
||||||
var values = require('lodash.values');
|
|
||||||
var template = require('lodash.template');
|
|
||||||
var findIndex = require('lodash.findindex');
|
|
||||||
|
|
||||||
function listAllContributors(start, lines) {
|
|
||||||
var i = 0;
|
|
||||||
while (start + i < lines.length && lines[start + i].indexOf('[![') === 0) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return lines.slice(start, start + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultTemplate =
|
|
||||||
'[![<%= user.name %>](<%= user.avatar_url %>&s=<%= options.imageSize %>)' +
|
|
||||||
'<br /><%= user.name %>](<%= user.html_url %>)' +
|
|
||||||
' | [<%= contributions %>](https://github.com/<%= options.projectOwner %>/<%= options.projectName %>/commits?author=<%= user.login %>)';
|
|
||||||
|
|
||||||
function contributorEntry(options, user, existingContributions) {
|
|
||||||
var contributions = uniq((existingContributions || []).concat(
|
|
||||||
options.contributions
|
|
||||||
.map(function(contribution) {
|
|
||||||
return options.emoji[contribution];
|
|
||||||
})
|
|
||||||
)).join('');
|
|
||||||
|
|
||||||
var contributionTemplate = template(options.template || defaultTemplate);
|
|
||||||
|
|
||||||
return contributionTemplate({
|
|
||||||
user: user,
|
|
||||||
contributions: contributions,
|
|
||||||
options: options
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseContributionTypes(options, line) {
|
|
||||||
return values(options.emoji)
|
|
||||||
.filter(function findExistingContribution(type) {
|
|
||||||
return line.indexOf(type) !== -1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function upsertContributor(options, user, existingContributors) {
|
|
||||||
var contributor = contributorEntry(options, user);
|
|
||||||
var existingContributorIndex = findIndex(existingContributors, function(cont) {
|
|
||||||
return cont.indexOf(user.login) !== 1 && cont.indexOf(user.name) !== -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingContributorIndex === -1) {
|
|
||||||
return [].concat(existingContributors, contributor);
|
|
||||||
}
|
|
||||||
|
|
||||||
var contributionTypes = parseContributionTypes(options, existingContributors[existingContributorIndex]);
|
|
||||||
return [].concat(
|
|
||||||
existingContributors.slice(0, existingContributorIndex),
|
|
||||||
contributorEntry(options, user, contributionTypes),
|
|
||||||
existingContributors.slice(existingContributorIndex + 1)
|
|
||||||
).join('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function addContributor(options, user, fileContent) {
|
|
||||||
var lines = fileContent.split('\n');
|
|
||||||
var contributorListStart = findIndex(lines, function(line) {
|
|
||||||
return line.indexOf(':---: | :---:') !== -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
var existingContributors = listAllContributors(contributorListStart + 1, lines);
|
|
||||||
var contributors = upsertContributor(options, user, existingContributors);
|
|
||||||
|
|
||||||
return [].concat(
|
|
||||||
lines.slice(0, contributorListStart + 1),
|
|
||||||
contributors,
|
|
||||||
lines.slice(contributorListStart + existingContributors.length + 1)
|
|
||||||
).join('\n')
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
import test from 'ava';
|
|
||||||
import addContributor from './addContributor';
|
|
||||||
|
|
||||||
function getUserLine(content, {login}) {
|
|
||||||
return content
|
|
||||||
.split('\n')
|
|
||||||
.filter(line => line.indexOf(login) !== -1)
|
|
||||||
[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function fixtures() {
|
|
||||||
const options = {
|
|
||||||
projectOwner: 'kentcdodds',
|
|
||||||
projectName: 'all-contributors',
|
|
||||||
imageSize: 100,
|
|
||||||
contributions: ['doc'],
|
|
||||||
emoji: {
|
|
||||||
code: ':code:',
|
|
||||||
doc: ':doc:',
|
|
||||||
test: ':test:'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const jfmengelsUser = {
|
|
||||||
login: 'jfmengels',
|
|
||||||
name: 'Jeroen Engels',
|
|
||||||
html_url: 'https://github.com/jfmengels',
|
|
||||||
avatar_url: 'https://avatars.githubusercontent.com/u/3869412?v=3'
|
|
||||||
};
|
|
||||||
|
|
||||||
const kentcdoddsUser = {
|
|
||||||
login: 'kentcdodds',
|
|
||||||
name: 'Kent C. Dodds',
|
|
||||||
html_url: 'https://github.com/kentcdodds',
|
|
||||||
avatar_url: 'https://avatars.githubusercontent.com/u/1500684?v=3'
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = `
|
|
||||||
# project
|
|
||||||
|
|
||||||
Description
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
These people contributed to the project:
|
|
||||||
:---: | :---:
|
|
||||||
[![Kent C. Dodds](https://avatars.githubusercontent.com/u/1500684?v=3&s=100)<br />Kent C. Dodds](https://github.com/kentcdodds) | [:doc:](https://github.com/kentcdodds/all-contributors/commits?author=kentcdodds)
|
|
||||||
[![Divjot Singh](https://avatars1.githubusercontent.com/u/6177621?s=130)<br />Divjot Singh](http://bogas04.github.io) | [:doc:](https://github.com/kentcdodds/all-contributors/commits?author=bogas04)
|
|
||||||
|
|
||||||
Thanks a lot guys!
|
|
||||||
`;
|
|
||||||
|
|
||||||
return {options, jfmengelsUser, kentcdoddsUser, content};
|
|
||||||
}
|
|
||||||
|
|
||||||
test('should add a new contributor to the end of the list', t => {
|
|
||||||
t.pass(1);
|
|
||||||
const {options, jfmengelsUser, content} = fixtures();
|
|
||||||
const expected = `
|
|
||||||
# project
|
|
||||||
|
|
||||||
Description
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
These people contributed to the project:
|
|
||||||
:---: | :---:
|
|
||||||
[![Kent C. Dodds](https://avatars.githubusercontent.com/u/1500684?v=3&s=100)<br />Kent C. Dodds](https://github.com/kentcdodds) | [:doc:](https://github.com/kentcdodds/all-contributors/commits?author=kentcdodds)
|
|
||||||
[![Divjot Singh](https://avatars1.githubusercontent.com/u/6177621?s=130)<br />Divjot Singh](http://bogas04.github.io) | [:doc:](https://github.com/kentcdodds/all-contributors/commits?author=bogas04)
|
|
||||||
[![Jeroen Engels](https://avatars.githubusercontent.com/u/3869412?v=3&s=100)<br />Jeroen Engels](https://github.com/jfmengels) | [:doc:](https://github.com/kentcdodds/all-contributors/commits?author=jfmengels)
|
|
||||||
|
|
||||||
Thanks a lot guys!
|
|
||||||
`;
|
|
||||||
|
|
||||||
t.is(addContributor(options, jfmengelsUser, content), expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should be able to inject several contributions', t => {
|
|
||||||
t.pass(1);
|
|
||||||
const {options, jfmengelsUser, content} = fixtures();
|
|
||||||
options.contributions = ['doc', 'code'];
|
|
||||||
const expected = '[![Jeroen Engels](https://avatars.githubusercontent.com/u/3869412?v=3&s=100)<br />Jeroen Engels](https://github.com/jfmengels) | [:doc::code:](https://github.com/kentcdodds/all-contributors/commits?author=jfmengels)';
|
|
||||||
|
|
||||||
const result = addContributor(options, jfmengelsUser, content);
|
|
||||||
|
|
||||||
t.is(getUserLine(result, jfmengelsUser), expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should be able to specify a new template in options', t => {
|
|
||||||
t.pass(1);
|
|
||||||
const {options, jfmengelsUser, content} = fixtures();
|
|
||||||
options.template = '<%= user.login %> made awesome contributions!';
|
|
||||||
const expected = 'jfmengels made awesome contributions!';
|
|
||||||
|
|
||||||
const result = addContributor(options, jfmengelsUser, content);
|
|
||||||
|
|
||||||
t.is(getUserLine(result, jfmengelsUser), expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not modify content when adding an existing contributor that has the given contribution types', t => {
|
|
||||||
t.pass(1);
|
|
||||||
const {options, kentcdoddsUser, content} = fixtures();
|
|
||||||
const expected = content;
|
|
||||||
|
|
||||||
const result = addContributor(options, kentcdoddsUser, content);
|
|
||||||
t.is(result, expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should add new contributions types to an existing contributor', t => {
|
|
||||||
t.pass(1);
|
|
||||||
const {options, kentcdoddsUser, content} = fixtures();
|
|
||||||
options.contributions = ['doc', 'code'];
|
|
||||||
const expected = '[![Kent C. Dodds](https://avatars.githubusercontent.com/u/1500684?v=3&s=100)<br />Kent C. Dodds](https://github.com/kentcdodds) | [:doc::code:](https://github.com/kentcdodds/all-contributors/commits?author=kentcdodds)';
|
|
||||||
|
|
||||||
const result = addContributor(options, kentcdoddsUser, content);
|
|
||||||
|
|
||||||
t.is(getUserLine(result, kentcdoddsUser), expected);
|
|
||||||
});
|
|
18
lib/emoji.js
18
lib/emoji.js
|
@ -1,18 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
code: '💻',
|
|
||||||
plugin: '🔌',
|
|
||||||
tool: '🔧',
|
|
||||||
doc: '📖',
|
|
||||||
question: '❓',
|
|
||||||
test: '⚠️',
|
|
||||||
bug: '🐛',
|
|
||||||
example: '💡',
|
|
||||||
blog: '📝',
|
|
||||||
tutorial: '✅',
|
|
||||||
video: '📹',
|
|
||||||
talk: '📢',
|
|
||||||
design: '🎨',
|
|
||||||
review: '👀'
|
|
||||||
};
|
|
22
lib/generate/fixtures/contributors.json
Normal file
22
lib/generate/fixtures/contributors.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"kentcdodds": {
|
||||||
|
"login": "kentcdodds",
|
||||||
|
"name": "Kent C. Dodds",
|
||||||
|
"html_url": "http://kentcdodds.com",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/1500684",
|
||||||
|
"contributions": [
|
||||||
|
"doc",
|
||||||
|
"review",
|
||||||
|
"question"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bogas04": {
|
||||||
|
"login": "bogas04",
|
||||||
|
"name": "Divjot Singh",
|
||||||
|
"html_url": "http://bogas04.github.io",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/6177621",
|
||||||
|
"contributions": [
|
||||||
|
"review"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
59
lib/generate/formatContributionType.js
Normal file
59
lib/generate/formatContributionType.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash/fp');
|
||||||
|
|
||||||
|
var linkToCommits = 'https://github.com/<%= options.projectOwner %>/<%= options.projectName %>/commits?author=<%= contributor.login %>'
|
||||||
|
var linkToIssues = 'https://github.com/<%= options.projectOwner %>/<%= options.projectName %>/issues?q=author%3A<%= contributor.login %>';
|
||||||
|
|
||||||
|
var linkTemplate = _.template('[<%= symbol %>](<%= url %>)');
|
||||||
|
|
||||||
|
var defaultTypes = {
|
||||||
|
blog: { symbol: '📝' },
|
||||||
|
bug: {
|
||||||
|
symbol: '🐛',
|
||||||
|
link: linkToIssues
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
symbol: '💻',
|
||||||
|
link: linkToCommits
|
||||||
|
},
|
||||||
|
design: { symbol: '🎨' },
|
||||||
|
doc: {
|
||||||
|
symbol: '📖',
|
||||||
|
link: linkToCommits
|
||||||
|
},
|
||||||
|
example: { symbol: '💡' },
|
||||||
|
plugin: { symbol: '🔌' },
|
||||||
|
question: { symbol: '❓' },
|
||||||
|
review: { symbol: '👀' },
|
||||||
|
talk: { symbol: '📢' },
|
||||||
|
test: {
|
||||||
|
symbol: '⚠️',
|
||||||
|
link: linkToCommits
|
||||||
|
},
|
||||||
|
translation: { symbol: '🌍' },
|
||||||
|
tool: { symbol: '🔧' },
|
||||||
|
tutorial: { symbol: '✅' },
|
||||||
|
video: { symbol: '📹' }
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function formatContribution(options, contributor, contribution) {
|
||||||
|
var types = _.assign(defaultTypes, options.types);
|
||||||
|
var type = types[contribution.type || contribution];
|
||||||
|
var templateData = {
|
||||||
|
symbol: type.symbol,
|
||||||
|
contributor: contributor,
|
||||||
|
options: options
|
||||||
|
};
|
||||||
|
|
||||||
|
if (contribution.url) {
|
||||||
|
return linkTemplate(_.assign({url: contribution.url}, templateData));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.link) {
|
||||||
|
var url = _.template(type.link)(templateData);
|
||||||
|
return linkTemplate(_.assign({url: url}, templateData));
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.symbol;
|
||||||
|
};
|
136
lib/generate/formatContributionType.test.js
Normal file
136
lib/generate/formatContributionType.test.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import test from 'ava';
|
||||||
|
import formatContributionType from './formatContributionType';
|
||||||
|
import contributors from './fixtures/contributors.json';
|
||||||
|
|
||||||
|
const fixtures = () => {
|
||||||
|
const options = {
|
||||||
|
projectOwner: 'jfmengels',
|
||||||
|
projectName: 'all-contributors-cli',
|
||||||
|
imageSize: 100
|
||||||
|
};
|
||||||
|
return {options};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('should return corresponding symbol', t => {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'tool'), '🔧');
|
||||||
|
t.is(formatContributionType(options, contributor, 'question'), '❓');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return link to commits', t => {
|
||||||
|
t.plan(3);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
const expectedLink = '(https://github.com/jfmengels/all-contributors-cli/commits?author=kentcdodds)';
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'code'), '[💻]' + expectedLink);
|
||||||
|
t.is(formatContributionType(options, contributor, 'doc'), '[📖]' + expectedLink);
|
||||||
|
t.is(formatContributionType(options, contributor, 'test'), '[⚠️]' + expectedLink);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return link to issues', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
const expected = '[🐛](https://github.com/jfmengels/all-contributors-cli/issues?q=author%3Akentcdodds)';
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'bug'), expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should make any symbol into a link if contribution is an object', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
const contribution = {
|
||||||
|
type: 'tool',
|
||||||
|
url: 'www.foo.bar'
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, contribution), '[🔧](www.foo.bar)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should override url for given types', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
const contribution = {
|
||||||
|
type: 'code',
|
||||||
|
url: 'www.foo.bar'
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, contribution), '[💻](www.foo.bar)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should be able to add types to the symbol list', t => {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
options.types = {
|
||||||
|
cheerful: { symbol: ':smiley:' }
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'cheerful'), ':smiley:');
|
||||||
|
t.is(formatContributionType(options, contributor, {
|
||||||
|
type: 'cheerful',
|
||||||
|
url: 'www.foo.bar'
|
||||||
|
}), '[:smiley:](www.foo.bar)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should be able to add types with template to the symbol list', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
options.types = {
|
||||||
|
web: {
|
||||||
|
symbol: ':web:',
|
||||||
|
link: 'www.<%= contributor.login %>.com'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'web'), '[:web:](www.kentcdodds.com)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should be able to override existing types', t => {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
options.types = {
|
||||||
|
code: { symbol: ':smiley:' }
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'code'), ':smiley:');
|
||||||
|
t.is(formatContributionType(options, contributor, {
|
||||||
|
type: 'code',
|
||||||
|
url: 'www.foo.bar'
|
||||||
|
}), '[:smiley:](www.foo.bar)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should be able to override existing templates', t => {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
options.types = {
|
||||||
|
code: {
|
||||||
|
symbol: ':web:',
|
||||||
|
link: 'www.<%= contributor.login %>.com'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.is(formatContributionType(options, contributor, 'code'), '[:web:](www.kentcdodds.com)');
|
||||||
|
t.is(formatContributionType(options, contributor, {
|
||||||
|
type: 'code',
|
||||||
|
url: 'www.foo.bar'
|
||||||
|
}), '[:web:](www.foo.bar)');
|
||||||
|
});
|
39
lib/generate/formatContributor.js
Normal file
39
lib/generate/formatContributor.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash/fp');
|
||||||
|
|
||||||
|
var formatContributionType = require('./formatContributionType');
|
||||||
|
|
||||||
|
var avatarTemplate = _.template('![<%= contributor.name %>](<%= contributor.avatar_url %>)');
|
||||||
|
var avatarBlockTemplate = _.template('[<%= avatar %><br /><sub><%= contributor.name %></sub>](<%= contributor.html_url %>)');
|
||||||
|
var contributorTemplate = _.template('<%= avatarBlock %><br /><%= contributions %>');
|
||||||
|
|
||||||
|
function defaultTemplate(templateData) {
|
||||||
|
var avatar = avatarTemplate(templateData);
|
||||||
|
var avatarBlock = avatarBlockTemplate(_.assign({ avatar: avatar }, templateData));
|
||||||
|
return contributorTemplate(_.assign({ avatarBlock: avatarBlock }, templateData));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAvatarUrl(options, contributor) {
|
||||||
|
var avatarUrl = contributor.avatar_url;
|
||||||
|
var paramJoiner = _.includes('?', avatarUrl) ? '&' : '?';
|
||||||
|
return _.assign(contributor, {
|
||||||
|
avatar_url: avatarUrl + paramJoiner + 's=' + options.imageSize
|
||||||
|
});
|
||||||
|
'<%= contributor.avatar_url %>?s=<%= options.imageSize %>'
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function formatContributor(options, contributor) {
|
||||||
|
var contributions = contributor.contributions.map(
|
||||||
|
_.partial(formatContributionType, [options, contributor])
|
||||||
|
).join(' ');
|
||||||
|
|
||||||
|
var templateData = {
|
||||||
|
contributions: contributions,
|
||||||
|
contributor: updateAvatarUrl(options, contributor),
|
||||||
|
options: options
|
||||||
|
};
|
||||||
|
|
||||||
|
var customTemplate = options.template && _.template(options.template);
|
||||||
|
return (customTemplate || defaultTemplate)(templateData);
|
||||||
|
};
|
69
lib/generate/formatContributor.test.js
Normal file
69
lib/generate/formatContributor.test.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import test from 'ava';
|
||||||
|
import _ from 'lodash/fp';
|
||||||
|
import formatContributor from './formatContributor';
|
||||||
|
import contributors from './fixtures/contributors.json';
|
||||||
|
|
||||||
|
function fixtures() {
|
||||||
|
const options = {
|
||||||
|
projectOwner: 'jfmengels',
|
||||||
|
projectName: 'all-contributors-cli',
|
||||||
|
imageSize: 100
|
||||||
|
};
|
||||||
|
return {options};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('should format a simple contributor', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = _.assign(contributors.kentcdodds, { contributions: ['review'] });
|
||||||
|
const {options} = fixtures();
|
||||||
|
|
||||||
|
const expected = '[![Kent C. Dodds](https://avatars1.githubusercontent.com/u/1500684?s=100)<br /><sub>Kent C. Dodds</sub>](http://kentcdodds.com)<br />👀';
|
||||||
|
|
||||||
|
t.is(formatContributor(options, contributor), expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should format contributor with complex contribution types', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
|
||||||
|
const expected = '[![Kent C. Dodds](https://avatars1.githubusercontent.com/u/1500684?s=100)<br /><sub>Kent C. Dodds</sub>](http://kentcdodds.com)<br />[📖](https://github.com/jfmengels/all-contributors-cli/commits?author=kentcdodds) 👀 ❓';
|
||||||
|
|
||||||
|
t.is(formatContributor(options, contributor), expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should format contributor using custom template', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
const {options} = fixtures();
|
||||||
|
options.template = '<%= contributor.name %> is awesome!'
|
||||||
|
|
||||||
|
const expected = 'Kent C. Dodds is awesome!';
|
||||||
|
|
||||||
|
t.is(formatContributor(options, contributor), expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should add image size to url', t => {
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
const {options} = fixtures();
|
||||||
|
const contributor = contributors.kentcdodds;
|
||||||
|
options.template = '<%= contributor.name %> at <%= contributor.avatar_url %>'
|
||||||
|
|
||||||
|
var contributionWithoutQuestionMarkUrl = _.assign(contributor, {
|
||||||
|
avatar_url: 'www.some-url-without-question-mark.com'
|
||||||
|
});
|
||||||
|
var contributionWithQuestionMarkUrl = _.assign(contributor, {
|
||||||
|
avatar_url: 'www.some-url-with-question-mark.com?v=3'
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(formatContributor(options, contributionWithoutQuestionMarkUrl),
|
||||||
|
'Kent C. Dodds at www.some-url-without-question-mark.com?s=100'
|
||||||
|
);
|
||||||
|
t.is(formatContributor(options, contributionWithQuestionMarkUrl),
|
||||||
|
'Kent C. Dodds at www.some-url-with-question-mark.com?v=3&s=100'
|
||||||
|
);
|
||||||
|
});
|
41
lib/generate/index.js
Normal file
41
lib/generate/index.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash/fp');
|
||||||
|
var formatContributor = require('./formatContributor');
|
||||||
|
|
||||||
|
function injectBetweenTags(fileContent, newContent) {
|
||||||
|
var lines = fileContent.split('\n');
|
||||||
|
var openingTagIndex = _.findIndex(_.startsWith('<!-- CONTRIBUTORS:START'), lines);
|
||||||
|
var closingTagIndex = _.findIndex(_.startsWith('<!-- CONTRIBUTORS:END'), lines);
|
||||||
|
return [].concat(
|
||||||
|
lines.slice(0, openingTagIndex + 1),
|
||||||
|
newContent,
|
||||||
|
lines.slice(closingTagIndex)
|
||||||
|
).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLine(contributors) {
|
||||||
|
return '| ' + contributors.join(' | ') + ' |';
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateContributorsList(options, contributors) {
|
||||||
|
return _.flow(
|
||||||
|
_.map(function(contributor) {
|
||||||
|
return formatContributor(options, contributor);
|
||||||
|
}),
|
||||||
|
_.chunk(options.contributorsPerLine),
|
||||||
|
_.map(formatLine),
|
||||||
|
_.join('\n'),
|
||||||
|
function appendColumns(content) {
|
||||||
|
var nbColumns = Math.min(options.contributorsPerLine, contributors.length);
|
||||||
|
return content + '\n' + _.repeat(nbColumns, '| :---: ') + '|';
|
||||||
|
}
|
||||||
|
)(contributors);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function generate(options, contributors, fileContent) {
|
||||||
|
return _.flow(
|
||||||
|
generateContributorsList,
|
||||||
|
_.partial(injectBetweenTags, [fileContent])
|
||||||
|
)(options, contributors);
|
||||||
|
};
|
98
lib/generate/index.test.js
Normal file
98
lib/generate/index.test.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import test from 'ava';
|
||||||
|
import generate from './';
|
||||||
|
import contributors from './fixtures/contributors.json';
|
||||||
|
|
||||||
|
function getUserLine(content, {login}) {
|
||||||
|
return content
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => line.indexOf(login) !== -1)
|
||||||
|
[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixtures() {
|
||||||
|
const options = {
|
||||||
|
projectOwner: 'kentcdodds',
|
||||||
|
projectName: 'all-contributors',
|
||||||
|
imageSize: 100,
|
||||||
|
contributorsPerLine: 5,
|
||||||
|
contributors: contributors,
|
||||||
|
template: '<%= contributor.name %> is awesome!'
|
||||||
|
};
|
||||||
|
|
||||||
|
const jfmengels = {
|
||||||
|
login: 'jfmengels',
|
||||||
|
name: 'Jeroen Engels',
|
||||||
|
html_url: 'https://github.com/jfmengels',
|
||||||
|
avatar_url: 'https://avatars.githubusercontent.com/u/3869412?v=3',
|
||||||
|
contributions: ['doc']
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = [
|
||||||
|
'# project',
|
||||||
|
'',
|
||||||
|
'Description',
|
||||||
|
'',
|
||||||
|
'## Contributors',
|
||||||
|
'These people contributed to the project:',
|
||||||
|
'<!-- CONTRIBUTORS:START -->',
|
||||||
|
'###Some content that will be replace###',
|
||||||
|
'<!-- CONTRIBUTORS:END -->',
|
||||||
|
'',
|
||||||
|
'Thanks a lot guys!'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
return {options, jfmengels, content};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('should replace the content between the CONTRIBUTORS tags by a table of contributors', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const {kentcdodds, bogas04} = contributors;
|
||||||
|
const {options, jfmengels, content} = fixtures();
|
||||||
|
const contributorList = [kentcdodds, bogas04, jfmengels];
|
||||||
|
const expected = [
|
||||||
|
'# project',
|
||||||
|
'',
|
||||||
|
'Description',
|
||||||
|
'',
|
||||||
|
'## Contributors',
|
||||||
|
'These people contributed to the project:',
|
||||||
|
'<!-- CONTRIBUTORS:START -->',
|
||||||
|
'| Kent C. Dodds is awesome! | Divjot Singh is awesome! | Jeroen Engels is awesome! |',
|
||||||
|
'| :---: | :---: | :---: |',
|
||||||
|
'<!-- CONTRIBUTORS:END -->',
|
||||||
|
'',
|
||||||
|
'Thanks a lot guys!'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const result = generate(options, contributorList, content);
|
||||||
|
|
||||||
|
t.is(result, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should split contributors into multiples lines when there are too many', t => {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
const {kentcdodds, bogas04} = contributors;
|
||||||
|
const {options, jfmengels, content} = fixtures();
|
||||||
|
const contributorList = [kentcdodds, kentcdodds, kentcdodds, kentcdodds, kentcdodds, kentcdodds, kentcdodds];
|
||||||
|
const expected = [
|
||||||
|
'# project',
|
||||||
|
'',
|
||||||
|
'Description',
|
||||||
|
'',
|
||||||
|
'## Contributors',
|
||||||
|
'These people contributed to the project:',
|
||||||
|
'<!-- CONTRIBUTORS:START -->',
|
||||||
|
'| Kent C. Dodds is awesome! | Kent C. Dodds is awesome! | Kent C. Dodds is awesome! | Kent C. Dodds is awesome! | Kent C. Dodds is awesome! |',
|
||||||
|
'| Kent C. Dodds is awesome! | Kent C. Dodds is awesome! |',
|
||||||
|
'| :---: | :---: | :---: | :---: | :---: |',
|
||||||
|
'<!-- CONTRIBUTORS:END -->',
|
||||||
|
'',
|
||||||
|
'Thanks a lot guys!'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const result = generate(options, contributorList, content);
|
||||||
|
|
||||||
|
t.is(result, expected);
|
||||||
|
});
|
|
@ -24,6 +24,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/jfmengels/all-contributors-cli#readme",
|
"homepage": "https://github.com/jfmengels/all-contributors-cli#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"lodash": "^4.5.1",
|
||||||
"lodash.assign": "^4.0.4",
|
"lodash.assign": "^4.0.4",
|
||||||
"lodash.findindex": "^4.2.0",
|
"lodash.findindex": "^4.2.0",
|
||||||
"lodash.template": "^4.2.1",
|
"lodash.template": "^4.2.1",
|
||||||
|
@ -32,11 +33,6 @@
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
"yargs": "^4.2.0"
|
"yargs": "^4.2.0"
|
||||||
},
|
},
|
||||||
"all-contributors": {
|
|
||||||
"projectOwner": "jfmengels",
|
|
||||||
"projectName": "all-contributors-cli",
|
|
||||||
"imageSize": 100
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "^0.12.0"
|
"ava": "^0.12.0"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue