Skip to content

Commit

Permalink
Add filter option
Browse files Browse the repository at this point in the history
Closes GH-32.
  • Loading branch information
wooorm committed Sep 23, 2024
1 parent 3a5acff commit f05fbda
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 30 deletions.
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @typedef {import('remark-contributors').Contributor} Contributor
* @typedef {import('./lib/index.js').Filter} Filter
* @typedef {import('./lib/index.js').Options} Options
*/

export {default} from './lib/index.js'
export {defaultFilter, default} from './lib/index.js'
79 changes: 53 additions & 26 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@
* @property {Social | undefined} social
* Social profile.
*
* @typedef RawContributor
* Contributor found by `contributorsFromGit`.
* @property {number} commits
* Number of commits.
* @property {string} name
* Name.
* @property {string} email
* Email.
*
* @typedef ContributorsModule
* Contributors module.
* @property {Array<Contributor> | null | undefined} [contributors]
* Named export.
* @property {Array<Contributor> | null | undefined} [default]
* Default export.
*
* @callback Filter
* Filter contributors.
* @param {RawContributor} contributor
* Contributor found by `contributorsFromGit`.
* @param {Record<string, unknown>} metadata
* Associated metadata found in `package.json` or `options.contributors`.
* @returns {boolean}
* Whether to include the contributor.
*
* @typedef Options
* Configuration.
* @property {boolean | null | undefined} [appendIfMissing=false]
Expand All @@ -48,12 +48,23 @@
* @property {string | null | undefined} [cwd]
* Working directory from which to resolve a `contributors` module, if any
* (default: `file.cwd`).
* @property {Filter | null | undefined} [filter=defaultFilter]
* Filter contributors (default: `defaultFilter`).
* @property {number | null | undefined} [limit=0]
* Limit the rendered contributors (default: `0`);
* `0` (or lower) includes all contributors;
* if `limit` is given, only the top `<limit>` contributors, sorted by commit
* count, are rendered.
*
* @typedef RawContributor
* Contributor found by `contributorsFromGit`.
* @property {number} commits
* Number of commits.
* @property {string} email
* Email.
* @property {string} name
* Name.
*
* @typedef Social
* Social profile.
* @property {string} text
Expand Down Expand Up @@ -81,6 +92,27 @@ const noreply = '@users.noreply.github.com'
const headingExpression = /^contributors$/i
const idFields = ['email', 'name', 'github', 'social.url']

/**
* Default filter for contributors;
* currently filters out Greenkeeper.
*
* @param {RawContributor} contributor
* Contributor found by `contributorsFromGit`.
* @param {Record<string, unknown>} metadata
* Associated metadata found in `package.json` or `options.contributors`.
* @returns {boolean}
* Whether to include the contributor.
* @satisfies {Filter}
*/
export function defaultFilter(contributor, metadata) {
return (
!contributor.email.endsWith('@greenkeeper.io') &&
contributor.name.toLowerCase() !== 'greenkeeper' &&
metadata.github !== 'greenkeeper[bot]' &&
metadata.github !== 'greenkeeperio-bot'
)
}

/**
* Generate a list of Git contributors.
*
Expand All @@ -100,6 +132,7 @@ const idFields = ['email', 'name', 'github', 'social.url']
export default function remarkGitContributors(options) {
const settings =
typeof options === 'string' ? {contributors: options} : options || {}
const filter = settings.filter || defaultFilter

/**
* Transform.
Expand Down Expand Up @@ -140,9 +173,8 @@ export default function remarkGitContributors(options) {
indexContributor(indices, /** @type {Contributor} */ (packageData.author))

if (Array.isArray(packageData.contributors)) {
let index = -1
while (++index < packageData.contributors.length) {
indexContributor(indices, packageData.contributors[index])
for (const contributor of packageData.contributors) {
indexContributor(indices, contributor)
}
}

Expand Down Expand Up @@ -183,10 +215,9 @@ export default function remarkGitContributors(options) {

/** @type {Array<CleanContributor>} */
let contributors = []
let index = -1

while (++index < gitContributors.length) {
const {name, email, commits} = gitContributors[index]
for (const gitContributor of gitContributors) {
const {name, email, commits} = gitContributor

if (!email) {
file.message(
Expand All @@ -199,8 +230,11 @@ export default function remarkGitContributors(options) {
continue
}

const metadata =
indices.email[email] || indices.name[name.toLowerCase()] || {}
const nameLower = name.toLowerCase()

const metadata = {
...(indices.email[email] || indices.name[nameLower])
}

if (email.endsWith(noreply)) {
metadata.github = email
Expand All @@ -209,12 +243,7 @@ export default function remarkGitContributors(options) {
indexValue(indices.github, metadata.github, metadata)
}

if (
email.endsWith('@greenkeeper.io') ||
name.toLowerCase() === 'greenkeeper' ||
metadata.github === 'greenkeeper[bot]' ||
metadata.github === 'greenkeeperio-bot'
) {
if (!filter(gitContributor, metadata)) {
continue
}

Expand Down Expand Up @@ -289,10 +318,8 @@ export default function remarkGitContributors(options) {
}
// Whether an existing contributor was found that matched an id field.
let found = false
let idIndex = -1

while (++idIndex < idFields.length) {
const idField = idFields[idIndex]
for (const idField of idFields) {
/** @type {unknown} */
const id = dlv(contributor, idField)

Expand Down
33 changes: 32 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`defaultFilter(contributor, metadata)`](#defaultfiltercontributor-metadata)
* [`unified().use(remarkGitContributors[, options])`](#unifieduseremarkgitcontributors-options)
* [`Contributor`](#contributor)
* [`Filter`](#filter)
* [`Options`](#options)
* [Examples](#examples)
* [Example: CLI](#example-cli)
Expand Down Expand Up @@ -126,6 +128,11 @@ MIT
This package exports no identifiers.
The default export is [`remarkGitContributors`][api-remark-git-contributors].

### `defaultFilter(contributor, metadata)`

Default filter for contributors ([`Filter`][api-filter]);
currently filters out Greenkeeper.

### `unified().use(remarkGitContributors[, options])`

Generate a list of Git contributors.
Expand Down Expand Up @@ -158,6 +165,21 @@ type).
type Contributor = Record<string, unknown> | string
```
### `Filter`
Filter contributors (TypeScript type).
###### Parameters
* `contributor` (`Contributor`)
— contributor found by `contributorsFromGit`
* `metadata` (`Record<string, unknown>`)
— associated metadata found in `package.json` or `options.contributors`
###### Returns
Whether to include the contributor (`boolean`).
### `Options`
Configuration (TypeScript type).
Expand All @@ -175,6 +197,9 @@ Configuration (TypeScript type).
throws if no contributors are found or given
* `cwd` (`string`, default: `file.cwd`)
— working directory from which to resolve a `contributors` module, if any
* `filter` ([`Filter`][api-filter], default:
[`defaultFilter`][api-default-filter])
— filter contributors
* `limit` (`number`, default: `0`)
— limit the rendered contributors;
`0` (or lower) includes all contributors;
Expand Down Expand Up @@ -384,7 +409,9 @@ export default contributors
## Types

This package is fully typed with [TypeScript][].
It exports the additional types [`Contributor`][api-contributor] and
It exports the additional types
[`Contributor`][api-contributor],
[`Filter`][api-filter], and
[`Options`][api-options].

## Compatibility
Expand Down Expand Up @@ -511,6 +538,10 @@ abide by its terms.

[api-contributor]: #contributor

[api-default-filter]: #defaultfiltercontributor-metadata

[api-filter]: #filter

[api-options]: #options

[api-remark-git-contributors]: #unifieduseremarkgitcontributors-options
37 changes: 35 additions & 2 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* @property {boolean | null | undefined} [broken]
* Break the `package.json` (default: `false`).
*
* @typedef {[string, string]} User
* @typedef {[name: string, email: string]} User
* User name and email.
*/

Expand Down Expand Up @@ -59,7 +59,7 @@ test('remark-git-contributors', async function (t) {
await t.test('should expose the public api', async function () {
assert.deepEqual(
Object.keys(await import('remark-git-contributors')).sort(),
['default']
['default', 'defaultFilter']
)
})

Expand Down Expand Up @@ -356,6 +356,39 @@ test('remark-git-contributors', async function (t) {
}
})

await t.test('should support a custom `filter`', async function () {
const cwd = temporary()
const [input] = await getFixtures('00')

await createPackage(cwd)
await createCommits(cwd, {
users: [
['beep', '[email protected]'],
['bea', 'äkta@människor.io']
]
})

const file = await remark()
.use(remarkGfm)
.use(remarkGitContributors, {
filter(d) {
return d.name !== 'beep'
}
})
.process(new VFile({cwd, path: 'input.md', value: input}))

assert.equal(
String(file),
'# Contributors\n\n| Name |\n| :------ |\n| **bea** |\n'
)
assert.deepEqual(
file.messages.map(function (d) {
return d.reason
}),
['Unexpected missing social handle for contributor `äkta@människor.io`']
)
})

await t.test('should work w/ invalid twitter', async function () {
const cwd = temporary()
const [input, expected] = await getFixtures('00')
Expand Down

0 comments on commit f05fbda

Please sign in to comment.