Comparing version 6.6.0 to 6.7.0-next.1589359313.113cf42cc978617c9039b7eebda5bc3d19ddae96
@@ -6,415 +6,421 @@ /** Comparator for sorting fellows in an array */ | ||
/** A fellow with similarties to other people */ | ||
export default class Fellow { | ||
/** | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::set} | ||
*/ | ||
constructor(value) { | ||
/** URLs used */ | ||
this.urls = new Set() | ||
/** Emails used */ | ||
this.emails = new Set() | ||
/** Map of repository slugs with the contributions from the user */ | ||
this.contributions = new Map() | ||
/** Set of repository slugs that the fellow administers to */ | ||
this.administeredRepositories = new Set() | ||
/** Set of repository slugs that the fellow contributes to */ | ||
this.contributedRepositories = new Set() | ||
/** Set of repository slugs that the fellow maintains */ | ||
this.maintainedRepositories = new Set() | ||
/** Set of repository slugs that the fellow authors */ | ||
this.authoredRepositories = new Set() | ||
let Fellow = /** @class */ (() => { | ||
class Fellow { | ||
/** | ||
* An array of field names that are used to determine if two fellow's are the same. | ||
* Don't need to add usernames for github, twitter, and facebook, as they will be compared via `urls`. | ||
* Can't compare just username, as that is not unique unless comparison's are on the same service, hence why `urls` are used and not `usernames`. | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::set} | ||
*/ | ||
this.idFields = ['urls', 'emails'] | ||
/** An array of field names that are used to determine the fellow's URL */ | ||
this.urlFields = [ | ||
'url', | ||
'homepage', | ||
'web', | ||
'githubUrl', | ||
'twitterUrl', | ||
'facebookUrl', | ||
] | ||
this.set(value) | ||
} | ||
// ----------------------------------- | ||
// Methods | ||
/** | ||
* Sort a list of fellows. | ||
* Uses {@link Fellow::sort} for the comparison. | ||
*/ | ||
static sort(list) { | ||
return list.sort(comparator) | ||
} | ||
/** Flatten lists of fellows into one set of fellows */ | ||
static flatten(lists) { | ||
const fellows = new Set() | ||
for (const list of lists) { | ||
for (const fellow of list) { | ||
fellows.add(fellow) | ||
constructor(value) { | ||
/** URLs used */ | ||
this.urls = new Set() | ||
/** Emails used */ | ||
this.emails = new Set() | ||
/** Map of repository slugs with the contributions from the user */ | ||
this.contributions = new Map() | ||
/** Set of repository slugs that the fellow administers to */ | ||
this.administeredRepositories = new Set() | ||
/** Set of repository slugs that the fellow contributes to */ | ||
this.contributedRepositories = new Set() | ||
/** Set of repository slugs that the fellow maintains */ | ||
this.maintainedRepositories = new Set() | ||
/** Set of repository slugs that the fellow authors */ | ||
this.authoredRepositories = new Set() | ||
/** | ||
* An array of field names that are used to determine if two fellow's are the same. | ||
* Don't need to add usernames for github, twitter, and facebook, as they will be compared via `urls`. | ||
* Can't compare just username, as that is not unique unless comparison's are on the same service, hence why `urls` are used and not `usernames`. | ||
*/ | ||
this.idFields = ['urls', 'emails'] | ||
/** An array of field names that are used to determine the fellow's URL */ | ||
this.urlFields = [ | ||
'url', | ||
'homepage', | ||
'web', | ||
'githubUrl', | ||
'twitterUrl', | ||
'facebookUrl', | ||
] | ||
this.set(value) | ||
} | ||
// ----------------------------------- | ||
// Methods | ||
/** | ||
* Sort a list of fellows. | ||
* Uses {@link Fellow::sort} for the comparison. | ||
*/ | ||
static sort(list) { | ||
return list.sort(comparator) | ||
} | ||
/** Flatten lists of fellows into one set of fellows */ | ||
static flatten(lists) { | ||
const fellows = new Set() | ||
for (const list of lists) { | ||
for (const fellow of list) { | ||
fellows.add(fellow) | ||
} | ||
} | ||
return fellows | ||
} | ||
return fellows | ||
} | ||
/** Compare to another fellow for sorting. */ | ||
compare(other) { | ||
const A = this.name.toLowerCase() | ||
const B = other.name.toLowerCase() | ||
if (A === B) { | ||
return 0 | ||
} else if (A < B) { | ||
return -1 | ||
} else { | ||
return 1 | ||
/** Compare to another fellow for sorting. */ | ||
compare(other) { | ||
const A = this.name.toLowerCase() | ||
const B = other.name.toLowerCase() | ||
if (A === B) { | ||
return 0 | ||
} else if (A < B) { | ||
return -1 | ||
} else { | ||
return 1 | ||
} | ||
} | ||
} | ||
/** | ||
* Compare to another fellow for equivalancy. | ||
* Uses {@link Fellow::idFields} for the comparison. | ||
* @param other The other fellow to compare ourselves with | ||
* @returns Returns `true` if they appear to be the same person, or `false` if not. | ||
*/ | ||
same(other) { | ||
for (const field of this.idFields) { | ||
const value = this[field] | ||
const otherValue = other[field] | ||
if (value && otherValue) { | ||
if (value instanceof Set && otherValue instanceof Set) { | ||
for (const item of value) { | ||
if (otherValue.has(item)) { | ||
return true | ||
/** | ||
* Compare to another fellow for equivalancy. | ||
* Uses {@link Fellow::idFields} for the comparison. | ||
* @param other The other fellow to compare ourselves with | ||
* @returns Returns `true` if they appear to be the same person, or `false` if not. | ||
*/ | ||
same(other) { | ||
for (const field of this.idFields) { | ||
const value = this[field] | ||
const otherValue = other[field] | ||
if (value && otherValue) { | ||
if (value instanceof Set && otherValue instanceof Set) { | ||
for (const item of value) { | ||
if (otherValue.has(item)) { | ||
return true | ||
} | ||
} | ||
} | ||
} else if (Array.isArray(value) && Array.isArray(otherValue)) { | ||
for (const item of value) { | ||
if (otherValue.includes(item)) { | ||
return true | ||
} else if (Array.isArray(value) && Array.isArray(otherValue)) { | ||
for (const item of value) { | ||
if (otherValue.includes(item)) { | ||
return true | ||
} | ||
} | ||
} else if (value === otherValue) { | ||
return true | ||
} | ||
} else if (value === otherValue) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
return false | ||
} | ||
/** | ||
* With the value, see if an existing fellow exists in our singleton list property with the value, otherwise create a new fellow instance with the value and add them to our singleton list. | ||
* Uses {@link Fellow::same} for the comparison. | ||
* @param value The value to create a new fellow instance or find the existing fellow instance with | ||
* @param add Whether to add the created person to the list | ||
* @returns The new or existing fellow instance | ||
*/ | ||
static ensure(value, add = true) { | ||
const newFellow = this.create(value) | ||
for (const existingFellow of this.fellows) { | ||
if (newFellow.same(existingFellow)) { | ||
return existingFellow.set(value) | ||
/** | ||
* With the value, see if an existing fellow exists in our singleton list property with the value, otherwise create a new fellow instance with the value and add them to our singleton list. | ||
* Uses {@link Fellow::same} for the comparison. | ||
* @param value The value to create a new fellow instance or find the existing fellow instance with | ||
* @param add Whether to add the created person to the list | ||
* @returns The new or existing fellow instance | ||
*/ | ||
static ensure(value, add = true) { | ||
const newFellow = this.create(value) | ||
for (const existingFellow of this.fellows) { | ||
if (newFellow.same(existingFellow)) { | ||
return existingFellow.set(value) | ||
} | ||
} | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
} | ||
} | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
/** | ||
* Get a fellow from the singleton list | ||
* @param value The value to fetch the value with | ||
* @returns The fetched fellow, if they exist with that value | ||
*/ | ||
static get(value) { | ||
return this.ensure(value, false) | ||
} | ||
} | ||
/** | ||
* Get a fellow from the singleton list | ||
* @param value The value to fetch the value with | ||
* @returns The fetched fellow, if they exist with that value | ||
*/ | ||
static get(value) { | ||
return this.ensure(value, false) | ||
} | ||
/** | ||
* Add a fellow or a series of people, denoted by the value, to the singleton list | ||
* @param values The fellow or people to add | ||
* @returns An array of the fellow objects for the passed people | ||
*/ | ||
static add(...values) { | ||
const result = [] | ||
for (const value of values) { | ||
if (value instanceof this) { | ||
result.push(this.ensure(value)) | ||
} else if (typeof value === 'string') { | ||
result.push(...value.split(/, +/).map((fellow) => this.ensure(fellow))) | ||
} else if (Array.isArray(value)) { | ||
result.push(...value.map((value) => this.ensure(value))) | ||
} else if (value) { | ||
result.push(this.ensure(value)) | ||
/** | ||
* Add a fellow or a series of people, denoted by the value, to the singleton list | ||
* @param values The fellow or people to add | ||
* @returns An array of the fellow objects for the passed people | ||
*/ | ||
static add(...values) { | ||
const result = [] | ||
for (const value of values) { | ||
if (value instanceof this) { | ||
result.push(this.ensure(value)) | ||
} else if (typeof value === 'string') { | ||
result.push( | ||
...value.split(/, +/).map((fellow) => this.ensure(fellow)) | ||
) | ||
} else if (Array.isArray(value)) { | ||
result.push(...value.map((value) => this.ensure(value))) | ||
} else if (value) { | ||
result.push(this.ensure(value)) | ||
} | ||
} | ||
return result | ||
} | ||
return result | ||
} | ||
/** Create a new Fellow instance with the value, however if the value is already a fellow instance, then just return it */ | ||
static create(value) { | ||
return value instanceof this ? value : new this(value) | ||
} | ||
/** | ||
* Update our fellow with the passed value | ||
* @param fellow A string or object representation of the user | ||
*/ | ||
set(fellow) { | ||
// String format, e.g. | ||
// Benjamin Lupton <b@lupton.cc> (https://balupton.com) | ||
if (typeof fellow === 'string') { | ||
const match = /^([^<(]+)\s*(?:<(.*?)>)?\s*(?:\((.*?)\))?$/.exec(fellow) | ||
if (!match) { | ||
throw new Error('Invalid fellow string') | ||
/** Create a new Fellow instance with the value, however if the value is already a fellow instance, then just return it */ | ||
static create(value) { | ||
return value instanceof this ? value : new this(value) | ||
} | ||
/** | ||
* Update our fellow with the passed value | ||
* @param fellow A string or object representation of the user | ||
*/ | ||
set(fellow) { | ||
// String format, e.g. | ||
// Benjamin Lupton <b@lupton.cc> (https://balupton.com) | ||
if (typeof fellow === 'string') { | ||
const match = /^([^<(]+)\s*(?:<(.*?)>)?\s*(?:\((.*?)\))?$/.exec(fellow) | ||
if (!match) { | ||
throw new Error('Invalid fellow string') | ||
} | ||
const name = (match[1] || '').trim() | ||
const email = (match[2] || '').trim() | ||
const url = (match[3] || '').trim() | ||
if (name) this.name = name | ||
if (email) this.email = email | ||
if (url) this.url = url | ||
} | ||
const name = (match[1] || '').trim() | ||
const email = (match[2] || '').trim() | ||
const url = (match[3] || '').trim() | ||
if (name) this.name = name | ||
if (email) this.email = email | ||
if (url) this.url = url | ||
} | ||
// Data|Fellow Format | ||
else if (typeof fellow === 'object') { | ||
Object.keys(fellow).forEach((key) => { | ||
if (key[0] === '_') return // skip if private | ||
const value = fellow[key] || null | ||
if (value) { | ||
// if any of the url fields, redirect to url setter | ||
if (this.urlFields.includes(key)) { | ||
this.url = value | ||
// Data|Fellow Format | ||
else if (typeof fellow === 'object') { | ||
Object.keys(fellow).forEach((key) => { | ||
if (key[0] === '_') return // skip if private | ||
const value = fellow[key] || null | ||
if (value) { | ||
// if any of the url fields, redirect to url setter | ||
if (this.urlFields.includes(key)) { | ||
this.url = value | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
return this | ||
} | ||
return this | ||
} | ||
// ----------------------------------- | ||
// Accessors | ||
/** | ||
* If the name is empty, we will try to fallback to githubUsername then twitterUsername | ||
* If the name is prefixed with a series of numbers, that is considered the year | ||
* E.g. In `2015+ Bevry Pty Ltd` then `2015+` is the years | ||
* E.g. In `2013-2015 Bevry Pty Ltd` then `2013-2015` is the years | ||
*/ | ||
set name(value /* :string */) { | ||
const match = /^((?:[0-9]+[-+]?)+)?(.+)$/.exec(value) | ||
if (match) { | ||
// fetch the years, but for now, discard it | ||
const years = String(match[1] || '').trim() | ||
if (years) this.years = years | ||
// fetch the name, and apply it | ||
const name = match[2].trim() | ||
if (name) this._name = name | ||
// ----------------------------------- | ||
// Accessors | ||
/** | ||
* If the name is empty, we will try to fallback to githubUsername then twitterUsername | ||
* If the name is prefixed with a series of numbers, that is considered the year | ||
* E.g. In `2015+ Bevry Pty Ltd` then `2015+` is the years | ||
* E.g. In `2013-2015 Bevry Pty Ltd` then `2013-2015` is the years | ||
*/ | ||
set name(value /* :string */) { | ||
const match = /^((?:[0-9]+[-+]?)+)?(.+)$/.exec(value) | ||
if (match) { | ||
// fetch the years, but for now, discard it | ||
const years = String(match[1] || '').trim() | ||
if (years) this.years = years | ||
// fetch the name, and apply it | ||
const name = match[2].trim() | ||
if (name) this._name = name | ||
} | ||
} | ||
} | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
} | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
} | ||
} | ||
return '' | ||
} | ||
/** | ||
* Will determine if the passed URL is a github, facebook, or twitter URL. | ||
* If it is, then it will extract the username and url from it. | ||
* If it was not, then it will set the homepage variable. | ||
*/ | ||
set url(input) { | ||
if (input) { | ||
let url | ||
// github | ||
const githubMatch = /^.+github.com\/([^/]+)\/?$/.exec(input) | ||
if (githubMatch) { | ||
this.githubUsername = githubMatch[1] | ||
url = this.githubUrl = 'https://github.com/' + this.githubUsername | ||
} else { | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
} | ||
return '' | ||
} | ||
/** | ||
* Will determine if the passed URL is a github, facebook, or twitter URL. | ||
* If it is, then it will extract the username and url from it. | ||
* If it was not, then it will set the homepage variable. | ||
*/ | ||
set url(input) { | ||
if (input) { | ||
let url | ||
// github | ||
const githubMatch = /^.+github.com\/([^/]+)\/?$/.exec(input) | ||
if (githubMatch) { | ||
this.githubUsername = githubMatch[1] | ||
url = this.githubUrl = 'https://github.com/' + this.githubUsername | ||
} else { | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
} else { | ||
url = this.homepage = input | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
} else { | ||
url = this.homepage = input | ||
} | ||
} | ||
} | ||
// add url in encrypted and unecrypted forms to urls | ||
this.urls.add(url.replace(/^http:/, 'https:')) | ||
this.urls.add(url.replace(/^https:/, 'http:')) | ||
} | ||
// add url in encrypted and unecrypted forms to urls | ||
this.urls.add(url.replace(/^http:/, 'https:')) | ||
this.urls.add(url.replace(/^https:/, 'http:')) | ||
} | ||
} | ||
/** Fetch the homepage with fallback to one of the service URLs if available */ | ||
get url() { | ||
return ( | ||
this.homepage || | ||
this.githubUrl || | ||
this.facebookUrl || | ||
this.twitterUrl || | ||
'' | ||
) | ||
} | ||
/** Get the field field from the list that isn't empty */ | ||
getFirstField(fields) { | ||
for (const field of fields) { | ||
const value = this[field] | ||
if (value) return value | ||
/** Fetch the homepage with fallback to one of the service URLs if available */ | ||
get url() { | ||
return ( | ||
this.homepage || | ||
this.githubUrl || | ||
this.facebookUrl || | ||
this.twitterUrl || | ||
'' | ||
) | ||
} | ||
return null | ||
} | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// ----------------------------------- | ||
// Formats | ||
/** | ||
* Convert the fellow into the usual string format | ||
* @example `NAME <EMAIL> (URL)` | ||
*/ | ||
toString(format = {}) { | ||
const parts = [] | ||
if (!this.name) return '' | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name | ||
parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
/** Get the field field from the list that isn't empty */ | ||
getFirstField(fields) { | ||
for (const field of fields) { | ||
const value = this[field] | ||
if (value) return value | ||
} | ||
return null | ||
} | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual markdown format | ||
* @example `[NAME](URL) <EMAIL>` | ||
*/ | ||
toMarkdown(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`[${this.name}](${url})`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.has(repoSlug) | ||
}) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— [view contributions](${contributionsURL} "View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}")` | ||
) | ||
/** Get all fellows who maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.has(repoSlug) | ||
}) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual HTML format | ||
*/ | ||
toHTML(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`<a href="${url}">${this.name}</a>`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push( | ||
`<a href="mailto:${this.email}" title="Email ${this.name}"><${this.email}></a>` | ||
) | ||
/** Get all fellows who author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— <a href="${contributionsURL}" title="View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}">view contributions</a>` | ||
) | ||
// ----------------------------------- | ||
// Formats | ||
/** | ||
* Convert the fellow into the usual string format | ||
* @example `NAME <EMAIL> (URL)` | ||
*/ | ||
toString(format = {}) { | ||
const parts = [] | ||
if (!this.name) return '' | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name | ||
parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
} | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
// return | ||
return parts.join(' ') | ||
/** | ||
* Convert the fellow into the usual markdown format | ||
* @example `[NAME](URL) <EMAIL>` | ||
*/ | ||
toMarkdown(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`[${this.name}](${url})`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— [view contributions](${contributionsURL} "View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}")` | ||
) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual HTML format | ||
*/ | ||
toHTML(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`<a href="${url}">${this.name}</a>`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push( | ||
`<a href="mailto:${this.email}" title="Email ${this.name}"><${this.email}></a>` | ||
) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— <a href="${contributionsURL}" title="View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}">view contributions</a>` | ||
) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
} | ||
} | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] | ||
return Fellow | ||
})() | ||
export default Fellow |
'use strict' | ||
Object.defineProperty(exports, '__esModule', { value: true }) | ||
exports.comparator = void 0 | ||
/** Comparator for sorting fellows in an array */ | ||
@@ -9,416 +10,421 @@ function comparator(a, b) { | ||
/** A fellow with similarties to other people */ | ||
class Fellow { | ||
/** | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::set} | ||
*/ | ||
constructor(value) { | ||
/** URLs used */ | ||
this.urls = new Set() | ||
/** Emails used */ | ||
this.emails = new Set() | ||
/** Map of repository slugs with the contributions from the user */ | ||
this.contributions = new Map() | ||
/** Set of repository slugs that the fellow administers to */ | ||
this.administeredRepositories = new Set() | ||
/** Set of repository slugs that the fellow contributes to */ | ||
this.contributedRepositories = new Set() | ||
/** Set of repository slugs that the fellow maintains */ | ||
this.maintainedRepositories = new Set() | ||
/** Set of repository slugs that the fellow authors */ | ||
this.authoredRepositories = new Set() | ||
let Fellow = /** @class */ (() => { | ||
class Fellow { | ||
/** | ||
* An array of field names that are used to determine if two fellow's are the same. | ||
* Don't need to add usernames for github, twitter, and facebook, as they will be compared via `urls`. | ||
* Can't compare just username, as that is not unique unless comparison's are on the same service, hence why `urls` are used and not `usernames`. | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::set} | ||
*/ | ||
this.idFields = ['urls', 'emails'] | ||
/** An array of field names that are used to determine the fellow's URL */ | ||
this.urlFields = [ | ||
'url', | ||
'homepage', | ||
'web', | ||
'githubUrl', | ||
'twitterUrl', | ||
'facebookUrl', | ||
] | ||
this.set(value) | ||
} | ||
// ----------------------------------- | ||
// Methods | ||
/** | ||
* Sort a list of fellows. | ||
* Uses {@link Fellow::sort} for the comparison. | ||
*/ | ||
static sort(list) { | ||
return list.sort(comparator) | ||
} | ||
/** Flatten lists of fellows into one set of fellows */ | ||
static flatten(lists) { | ||
const fellows = new Set() | ||
for (const list of lists) { | ||
for (const fellow of list) { | ||
fellows.add(fellow) | ||
constructor(value) { | ||
/** URLs used */ | ||
this.urls = new Set() | ||
/** Emails used */ | ||
this.emails = new Set() | ||
/** Map of repository slugs with the contributions from the user */ | ||
this.contributions = new Map() | ||
/** Set of repository slugs that the fellow administers to */ | ||
this.administeredRepositories = new Set() | ||
/** Set of repository slugs that the fellow contributes to */ | ||
this.contributedRepositories = new Set() | ||
/** Set of repository slugs that the fellow maintains */ | ||
this.maintainedRepositories = new Set() | ||
/** Set of repository slugs that the fellow authors */ | ||
this.authoredRepositories = new Set() | ||
/** | ||
* An array of field names that are used to determine if two fellow's are the same. | ||
* Don't need to add usernames for github, twitter, and facebook, as they will be compared via `urls`. | ||
* Can't compare just username, as that is not unique unless comparison's are on the same service, hence why `urls` are used and not `usernames`. | ||
*/ | ||
this.idFields = ['urls', 'emails'] | ||
/** An array of field names that are used to determine the fellow's URL */ | ||
this.urlFields = [ | ||
'url', | ||
'homepage', | ||
'web', | ||
'githubUrl', | ||
'twitterUrl', | ||
'facebookUrl', | ||
] | ||
this.set(value) | ||
} | ||
// ----------------------------------- | ||
// Methods | ||
/** | ||
* Sort a list of fellows. | ||
* Uses {@link Fellow::sort} for the comparison. | ||
*/ | ||
static sort(list) { | ||
return list.sort(comparator) | ||
} | ||
/** Flatten lists of fellows into one set of fellows */ | ||
static flatten(lists) { | ||
const fellows = new Set() | ||
for (const list of lists) { | ||
for (const fellow of list) { | ||
fellows.add(fellow) | ||
} | ||
} | ||
return fellows | ||
} | ||
return fellows | ||
} | ||
/** Compare to another fellow for sorting. */ | ||
compare(other) { | ||
const A = this.name.toLowerCase() | ||
const B = other.name.toLowerCase() | ||
if (A === B) { | ||
return 0 | ||
} else if (A < B) { | ||
return -1 | ||
} else { | ||
return 1 | ||
/** Compare to another fellow for sorting. */ | ||
compare(other) { | ||
const A = this.name.toLowerCase() | ||
const B = other.name.toLowerCase() | ||
if (A === B) { | ||
return 0 | ||
} else if (A < B) { | ||
return -1 | ||
} else { | ||
return 1 | ||
} | ||
} | ||
} | ||
/** | ||
* Compare to another fellow for equivalancy. | ||
* Uses {@link Fellow::idFields} for the comparison. | ||
* @param other The other fellow to compare ourselves with | ||
* @returns Returns `true` if they appear to be the same person, or `false` if not. | ||
*/ | ||
same(other) { | ||
for (const field of this.idFields) { | ||
const value = this[field] | ||
const otherValue = other[field] | ||
if (value && otherValue) { | ||
if (value instanceof Set && otherValue instanceof Set) { | ||
for (const item of value) { | ||
if (otherValue.has(item)) { | ||
return true | ||
/** | ||
* Compare to another fellow for equivalancy. | ||
* Uses {@link Fellow::idFields} for the comparison. | ||
* @param other The other fellow to compare ourselves with | ||
* @returns Returns `true` if they appear to be the same person, or `false` if not. | ||
*/ | ||
same(other) { | ||
for (const field of this.idFields) { | ||
const value = this[field] | ||
const otherValue = other[field] | ||
if (value && otherValue) { | ||
if (value instanceof Set && otherValue instanceof Set) { | ||
for (const item of value) { | ||
if (otherValue.has(item)) { | ||
return true | ||
} | ||
} | ||
} | ||
} else if (Array.isArray(value) && Array.isArray(otherValue)) { | ||
for (const item of value) { | ||
if (otherValue.includes(item)) { | ||
return true | ||
} else if (Array.isArray(value) && Array.isArray(otherValue)) { | ||
for (const item of value) { | ||
if (otherValue.includes(item)) { | ||
return true | ||
} | ||
} | ||
} else if (value === otherValue) { | ||
return true | ||
} | ||
} else if (value === otherValue) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
return false | ||
} | ||
/** | ||
* With the value, see if an existing fellow exists in our singleton list property with the value, otherwise create a new fellow instance with the value and add them to our singleton list. | ||
* Uses {@link Fellow::same} for the comparison. | ||
* @param value The value to create a new fellow instance or find the existing fellow instance with | ||
* @param add Whether to add the created person to the list | ||
* @returns The new or existing fellow instance | ||
*/ | ||
static ensure(value, add = true) { | ||
const newFellow = this.create(value) | ||
for (const existingFellow of this.fellows) { | ||
if (newFellow.same(existingFellow)) { | ||
return existingFellow.set(value) | ||
/** | ||
* With the value, see if an existing fellow exists in our singleton list property with the value, otherwise create a new fellow instance with the value and add them to our singleton list. | ||
* Uses {@link Fellow::same} for the comparison. | ||
* @param value The value to create a new fellow instance or find the existing fellow instance with | ||
* @param add Whether to add the created person to the list | ||
* @returns The new or existing fellow instance | ||
*/ | ||
static ensure(value, add = true) { | ||
const newFellow = this.create(value) | ||
for (const existingFellow of this.fellows) { | ||
if (newFellow.same(existingFellow)) { | ||
return existingFellow.set(value) | ||
} | ||
} | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
} | ||
} | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
/** | ||
* Get a fellow from the singleton list | ||
* @param value The value to fetch the value with | ||
* @returns The fetched fellow, if they exist with that value | ||
*/ | ||
static get(value) { | ||
return this.ensure(value, false) | ||
} | ||
} | ||
/** | ||
* Get a fellow from the singleton list | ||
* @param value The value to fetch the value with | ||
* @returns The fetched fellow, if they exist with that value | ||
*/ | ||
static get(value) { | ||
return this.ensure(value, false) | ||
} | ||
/** | ||
* Add a fellow or a series of people, denoted by the value, to the singleton list | ||
* @param values The fellow or people to add | ||
* @returns An array of the fellow objects for the passed people | ||
*/ | ||
static add(...values) { | ||
const result = [] | ||
for (const value of values) { | ||
if (value instanceof this) { | ||
result.push(this.ensure(value)) | ||
} else if (typeof value === 'string') { | ||
result.push(...value.split(/, +/).map((fellow) => this.ensure(fellow))) | ||
} else if (Array.isArray(value)) { | ||
result.push(...value.map((value) => this.ensure(value))) | ||
} else if (value) { | ||
result.push(this.ensure(value)) | ||
/** | ||
* Add a fellow or a series of people, denoted by the value, to the singleton list | ||
* @param values The fellow or people to add | ||
* @returns An array of the fellow objects for the passed people | ||
*/ | ||
static add(...values) { | ||
const result = [] | ||
for (const value of values) { | ||
if (value instanceof this) { | ||
result.push(this.ensure(value)) | ||
} else if (typeof value === 'string') { | ||
result.push( | ||
...value.split(/, +/).map((fellow) => this.ensure(fellow)) | ||
) | ||
} else if (Array.isArray(value)) { | ||
result.push(...value.map((value) => this.ensure(value))) | ||
} else if (value) { | ||
result.push(this.ensure(value)) | ||
} | ||
} | ||
return result | ||
} | ||
return result | ||
} | ||
/** Create a new Fellow instance with the value, however if the value is already a fellow instance, then just return it */ | ||
static create(value) { | ||
return value instanceof this ? value : new this(value) | ||
} | ||
/** | ||
* Update our fellow with the passed value | ||
* @param fellow A string or object representation of the user | ||
*/ | ||
set(fellow) { | ||
// String format, e.g. | ||
// Benjamin Lupton <b@lupton.cc> (https://balupton.com) | ||
if (typeof fellow === 'string') { | ||
const match = /^([^<(]+)\s*(?:<(.*?)>)?\s*(?:\((.*?)\))?$/.exec(fellow) | ||
if (!match) { | ||
throw new Error('Invalid fellow string') | ||
/** Create a new Fellow instance with the value, however if the value is already a fellow instance, then just return it */ | ||
static create(value) { | ||
return value instanceof this ? value : new this(value) | ||
} | ||
/** | ||
* Update our fellow with the passed value | ||
* @param fellow A string or object representation of the user | ||
*/ | ||
set(fellow) { | ||
// String format, e.g. | ||
// Benjamin Lupton <b@lupton.cc> (https://balupton.com) | ||
if (typeof fellow === 'string') { | ||
const match = /^([^<(]+)\s*(?:<(.*?)>)?\s*(?:\((.*?)\))?$/.exec(fellow) | ||
if (!match) { | ||
throw new Error('Invalid fellow string') | ||
} | ||
const name = (match[1] || '').trim() | ||
const email = (match[2] || '').trim() | ||
const url = (match[3] || '').trim() | ||
if (name) this.name = name | ||
if (email) this.email = email | ||
if (url) this.url = url | ||
} | ||
const name = (match[1] || '').trim() | ||
const email = (match[2] || '').trim() | ||
const url = (match[3] || '').trim() | ||
if (name) this.name = name | ||
if (email) this.email = email | ||
if (url) this.url = url | ||
} | ||
// Data|Fellow Format | ||
else if (typeof fellow === 'object') { | ||
Object.keys(fellow).forEach((key) => { | ||
if (key[0] === '_') return // skip if private | ||
const value = fellow[key] || null | ||
if (value) { | ||
// if any of the url fields, redirect to url setter | ||
if (this.urlFields.includes(key)) { | ||
this.url = value | ||
// Data|Fellow Format | ||
else if (typeof fellow === 'object') { | ||
Object.keys(fellow).forEach((key) => { | ||
if (key[0] === '_') return // skip if private | ||
const value = fellow[key] || null | ||
if (value) { | ||
// if any of the url fields, redirect to url setter | ||
if (this.urlFields.includes(key)) { | ||
this.url = value | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
return this | ||
} | ||
return this | ||
} | ||
// ----------------------------------- | ||
// Accessors | ||
/** | ||
* If the name is empty, we will try to fallback to githubUsername then twitterUsername | ||
* If the name is prefixed with a series of numbers, that is considered the year | ||
* E.g. In `2015+ Bevry Pty Ltd` then `2015+` is the years | ||
* E.g. In `2013-2015 Bevry Pty Ltd` then `2013-2015` is the years | ||
*/ | ||
set name(value /* :string */) { | ||
const match = /^((?:[0-9]+[-+]?)+)?(.+)$/.exec(value) | ||
if (match) { | ||
// fetch the years, but for now, discard it | ||
const years = String(match[1] || '').trim() | ||
if (years) this.years = years | ||
// fetch the name, and apply it | ||
const name = match[2].trim() | ||
if (name) this._name = name | ||
// ----------------------------------- | ||
// Accessors | ||
/** | ||
* If the name is empty, we will try to fallback to githubUsername then twitterUsername | ||
* If the name is prefixed with a series of numbers, that is considered the year | ||
* E.g. In `2015+ Bevry Pty Ltd` then `2015+` is the years | ||
* E.g. In `2013-2015 Bevry Pty Ltd` then `2013-2015` is the years | ||
*/ | ||
set name(value /* :string */) { | ||
const match = /^((?:[0-9]+[-+]?)+)?(.+)$/.exec(value) | ||
if (match) { | ||
// fetch the years, but for now, discard it | ||
const years = String(match[1] || '').trim() | ||
if (years) this.years = years | ||
// fetch the name, and apply it | ||
const name = match[2].trim() | ||
if (name) this._name = name | ||
} | ||
} | ||
} | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
} | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
} | ||
} | ||
return '' | ||
} | ||
/** | ||
* Will determine if the passed URL is a github, facebook, or twitter URL. | ||
* If it is, then it will extract the username and url from it. | ||
* If it was not, then it will set the homepage variable. | ||
*/ | ||
set url(input) { | ||
if (input) { | ||
let url | ||
// github | ||
const githubMatch = /^.+github.com\/([^/]+)\/?$/.exec(input) | ||
if (githubMatch) { | ||
this.githubUsername = githubMatch[1] | ||
url = this.githubUrl = 'https://github.com/' + this.githubUsername | ||
} else { | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
} | ||
return '' | ||
} | ||
/** | ||
* Will determine if the passed URL is a github, facebook, or twitter URL. | ||
* If it is, then it will extract the username and url from it. | ||
* If it was not, then it will set the homepage variable. | ||
*/ | ||
set url(input) { | ||
if (input) { | ||
let url | ||
// github | ||
const githubMatch = /^.+github.com\/([^/]+)\/?$/.exec(input) | ||
if (githubMatch) { | ||
this.githubUsername = githubMatch[1] | ||
url = this.githubUrl = 'https://github.com/' + this.githubUsername | ||
} else { | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
} else { | ||
url = this.homepage = input | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
} else { | ||
url = this.homepage = input | ||
} | ||
} | ||
} | ||
// add url in encrypted and unecrypted forms to urls | ||
this.urls.add(url.replace(/^http:/, 'https:')) | ||
this.urls.add(url.replace(/^https:/, 'http:')) | ||
} | ||
// add url in encrypted and unecrypted forms to urls | ||
this.urls.add(url.replace(/^http:/, 'https:')) | ||
this.urls.add(url.replace(/^https:/, 'http:')) | ||
} | ||
} | ||
/** Fetch the homepage with fallback to one of the service URLs if available */ | ||
get url() { | ||
return ( | ||
this.homepage || | ||
this.githubUrl || | ||
this.facebookUrl || | ||
this.twitterUrl || | ||
'' | ||
) | ||
} | ||
/** Get the field field from the list that isn't empty */ | ||
getFirstField(fields) { | ||
for (const field of fields) { | ||
const value = this[field] | ||
if (value) return value | ||
/** Fetch the homepage with fallback to one of the service URLs if available */ | ||
get url() { | ||
return ( | ||
this.homepage || | ||
this.githubUrl || | ||
this.facebookUrl || | ||
this.twitterUrl || | ||
'' | ||
) | ||
} | ||
return null | ||
} | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.has(repoSlug) | ||
}) | ||
} | ||
/** Get all fellows who author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// ----------------------------------- | ||
// Formats | ||
/** | ||
* Convert the fellow into the usual string format | ||
* @example `NAME <EMAIL> (URL)` | ||
*/ | ||
toString(format = {}) { | ||
const parts = [] | ||
if (!this.name) return '' | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name | ||
parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
/** Get the field field from the list that isn't empty */ | ||
getFirstField(fields) { | ||
for (const field of fields) { | ||
const value = this[field] | ||
if (value) return value | ||
} | ||
return null | ||
} | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual markdown format | ||
* @example `[NAME](URL) <EMAIL>` | ||
*/ | ||
toMarkdown(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`[${this.name}](${url})`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.has(repoSlug) | ||
}) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— [view contributions](${contributionsURL} "View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}")` | ||
) | ||
/** Get all fellows who maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.has(repoSlug) | ||
}) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual HTML format | ||
*/ | ||
toHTML(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`<a href="${url}">${this.name}</a>`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push( | ||
`<a href="mailto:${this.email}" title="Email ${this.name}"><${this.email}></a>` | ||
) | ||
/** Get all fellows who author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.has(repoSlug) | ||
}) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— <a href="${contributionsURL}" title="View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}">view contributions</a>` | ||
) | ||
// ----------------------------------- | ||
// Formats | ||
/** | ||
* Convert the fellow into the usual string format | ||
* @example `NAME <EMAIL> (URL)` | ||
*/ | ||
toString(format = {}) { | ||
const parts = [] | ||
if (!this.name) return '' | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name | ||
parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
} | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
// return | ||
return parts.join(' ') | ||
/** | ||
* Convert the fellow into the usual markdown format | ||
* @example `[NAME](URL) <EMAIL>` | ||
*/ | ||
toMarkdown(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`[${this.name}](${url})`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push(`<${this.email}>`) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— [view contributions](${contributionsURL} "View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}")` | ||
) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** | ||
* Convert the fellow into the usual HTML format | ||
*/ | ||
toHTML(format = {}) { | ||
if (!this.name) return '' | ||
const parts = [] | ||
// copyright | ||
if (format.displayCopyright) parts.push('Copyright ©') | ||
if (format.displayYears && this.years) parts.push(this.years) | ||
// name + url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) parts.push(`<a href="${url}">${this.name}</a>`) | ||
else parts.push(this.name) | ||
if (format.displayEmail && this.email) { | ||
parts.push( | ||
`<a href="mailto:${this.email}" title="Email ${this.name}"><${this.email}></a>` | ||
) | ||
} | ||
// contributions | ||
if ( | ||
format.displayContributions && | ||
format.githubRepoSlug && | ||
this.githubUsername | ||
) { | ||
const contributionsURL = `https://github.com/${format.githubRepoSlug}/commits?author=${this.githubUsername}` | ||
parts.push( | ||
`— <a href="${contributionsURL}" title="View the GitHub contributions of ${this.name} on repository ${format.githubRepoSlug}">view contributions</a>` | ||
) | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
} | ||
} | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] | ||
return Fellow | ||
})() | ||
exports.default = Fellow | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] |
# History | ||
## v6.7.0 2020 May 13 | ||
- Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) | ||
## v6.6.0 2020 May 8 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "fellow", | ||
"version": "6.6.0", | ||
"version": "6.7.0-next.1589359313.113cf42cc978617c9039b7eebda5bc3d19ddae96", | ||
"description": "Fellow is a package for creating people that can be unified by their shared values via a singleton list on the class", | ||
@@ -55,3 +55,7 @@ "homepage": "https://github.com/bevry/fellow", | ||
"wishlistURL": "https://bevry.me/wishlist", | ||
"travisTLD": "com" | ||
"travisTLD": "com", | ||
"githubUsername": "bevry", | ||
"githubRepository": "fellow", | ||
"githubSlug": "bevry/fellow", | ||
"npmPackageName": "fellow" | ||
} | ||
@@ -62,6 +66,6 @@ }, | ||
"maintainers": [ | ||
"Benjamin Lupton <b@lupton.cc> (http://balupton.com)" | ||
"Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)" | ||
], | ||
"contributors": [ | ||
"Benjamin Lupton <b@lupton.cc> (http://balupton.com)" | ||
"Benjamin Lupton <b@lupton.cc> (https://github.com/balupton)" | ||
], | ||
@@ -117,3 +121,3 @@ "bugs": { | ||
], | ||
"types": "source/index.ts", | ||
"types": "./compiled-types/", | ||
"type": "commonjs", | ||
@@ -124,17 +128,17 @@ "main": "edition-esnext/index.js", | ||
"devDependencies": { | ||
"@bevry/update-contributors": "^1.0.1", | ||
"@typescript-eslint/eslint-plugin": "^2.30.0", | ||
"@typescript-eslint/parser": "^2.30.0", | ||
"assert-helpers": "^6.2.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-bevry": "^3.4.0", | ||
"@bevry/update-contributors": "^1.5.0", | ||
"@typescript-eslint/eslint-plugin": "^2.33.0", | ||
"@typescript-eslint/parser": "^2.33.0", | ||
"assert-helpers": "^6.4.0", | ||
"eslint": "^7.0.0", | ||
"eslint-config-bevry": "^3.5.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-babel": "^5.3.0", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"kava": "^4.4.0", | ||
"kava": "^5.0.0", | ||
"prettier": "^2.0.5", | ||
"projectz": "^1.19.1", | ||
"projectz": "^2.1.0", | ||
"surge": "^0.21.3", | ||
"typedoc": "^0.17.6", | ||
"typescript": "^3.8.3", | ||
"typescript": "^3.9.2", | ||
"valid-directory": "^1.6.0", | ||
@@ -145,11 +149,12 @@ "valid-module": "^1.0.0" | ||
"our:clean": "rm -Rf ./docs ./edition* ./es2015 ./es5 ./out ./.next", | ||
"our:compile": "npm run our:compile:edition-browsers && npm run our:compile:edition-esnext", | ||
"our:compile": "npm run our:compile:edition-browsers && npm run our:compile:edition-esnext && npm run our:compile:types", | ||
"our:compile:edition-browsers": "tsc --module ESNext --target ES2019 --outDir ./edition-browsers --project tsconfig.json && test -d edition-browsers/source && ( mv edition-browsers/source edition-temp && rm -Rf edition-browsers && mv edition-temp edition-browsers ) || true", | ||
"our:compile:edition-esnext": "tsc --module commonjs --target ESNext --outDir ./edition-esnext --project tsconfig.json && test -d edition-esnext/source && ( mv edition-esnext/source edition-temp && rm -Rf edition-esnext && mv edition-temp edition-esnext ) || true", | ||
"our:compile:types": "tsc --project tsconfig.json --emitDeclarationOnly --declaration --declarationDir ./compiled-types/ --declarationMap", | ||
"our:deploy": "echo no need for this project", | ||
"our:meta": "npm run our:meta:contributors && npm run our:meta:docs && npm run our:meta:projectz", | ||
"our:meta:contributors": "npx @bevry/update-contributors", | ||
"our:meta:contributors": "update-contributors", | ||
"our:meta:docs": "npm run our:meta:docs:typedoc", | ||
"our:meta:docs:typedoc": "rm -Rf ./docs && npx typedoc --mode file --exclude '**/+(*test*|node_modules)' --excludeExternals --name \"$npm_package_name\" --readme ./README.md --out ./docs ./source", | ||
"our:meta:projectz": "npx projectz compile", | ||
"our:meta:docs:typedoc": "rm -Rf ./docs && typedoc --mode file --exclude '**/+(*test*|node_modules)' --excludeExternals --name \"$npm_package_name\" --readme ./README.md --out ./docs ./source", | ||
"our:meta:projectz": "projectz compile", | ||
"our:release": "npm run our:release:prepare && npm run our:release:check-changelog && npm run our:release:check-dirty && npm run our:release:tag && npm run our:release:push", | ||
@@ -164,8 +169,7 @@ "our:release:check-changelog": "cat ./HISTORY.md | grep v$npm_package_version || (echo add a changelog entry for v$npm_package_version && exit -1)", | ||
"our:test": "npm run our:verify && npm test", | ||
"our:verify": "npm run our:verify:directory && npm run our:verify:eslint && npm run our:verify:module && npm run our:verify:prettier && npm run our:verify:typescript", | ||
"our:verify:directory": "npx valid-directory", | ||
"our:verify:eslint": "npx eslint --fix --ignore-pattern '**/*.d.ts' --ignore-pattern '**/vendor/' --ignore-pattern '**/node_modules/' --ext .mjs,.js,.jsx,.ts,.tsx ./source", | ||
"our:verify:module": "npx valid-module", | ||
"our:verify": "npm run our:verify:directory && npm run our:verify:eslint && npm run our:verify:module && npm run our:verify:prettier", | ||
"our:verify:directory": "valid-directory", | ||
"our:verify:eslint": "eslint --fix --ignore-pattern '**/*.d.ts' --ignore-pattern '**/vendor/' --ignore-pattern '**/node_modules/' --ext .mjs,.js,.jsx,.ts,.tsx ./source", | ||
"our:verify:module": "valid-module", | ||
"our:verify:prettier": "prettier --write .", | ||
"our:verify:typescript": "tsc --noEmit --project tsconfig.json", | ||
"test": "node ./edition-esnext/test.js" | ||
@@ -172,0 +176,0 @@ }, |
@@ -55,3 +55,3 @@ <!-- TITLE/ --> | ||
<script type="module"> | ||
import pkg from '//cdn.pika.dev/fellow/^6.6.0' | ||
import pkg from '//cdn.pika.dev/fellow/^6.7.0' | ||
</script> | ||
@@ -64,3 +64,3 @@ ``` | ||
<script type="module"> | ||
import pkg from '//unpkg.com/fellow@^6.6.0' | ||
import pkg from '//unpkg.com/fellow@^6.7.0' | ||
</script> | ||
@@ -73,3 +73,3 @@ ``` | ||
<script type="module"> | ||
import pkg from '//dev.jspm.io/fellow@6.6.0' | ||
import pkg from '//dev.jspm.io/fellow@6.7.0' | ||
</script> | ||
@@ -116,3 +116,3 @@ ``` | ||
<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/fellow/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/fellow">view contributions</a></li></ul> | ||
<ul><li><a href="https://github.com/balupton">Benjamin Lupton</a> — <a href="https://github.com/bevry/fellow/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/fellow">view contributions</a></li></ul> | ||
@@ -137,4 +137,3 @@ <h3>Sponsors</h3> | ||
<ul><li><a href="http://balupton.com">Benjamin Lupton</a> — <a href="https://github.com/bevry/fellow/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/fellow">view contributions</a></li> | ||
<li><a href="http://github.com/apps/dependabot-preview">dependabot-preview[bot]</a> — <a href="https://github.com/bevry/fellow/commits?author=dependabot-preview[bot]" title="View the GitHub contributions of dependabot-preview[bot] on repository bevry/fellow">view contributions</a></li></ul> | ||
<ul><li><a href="https://github.com/balupton">Benjamin Lupton</a> — <a href="https://github.com/bevry/fellow/commits?author=balupton" title="View the GitHub contributions of Benjamin Lupton on repository bevry/fellow">view contributions</a></li></ul> | ||
@@ -141,0 +140,0 @@ <a href="https://github.com/bevry/fellow/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a> |
@@ -9,5 +9,6 @@ { | ||
"strict": true, | ||
"target": "ESNext" | ||
"target": "ESNext", | ||
"lib": [] | ||
}, | ||
"include": ["source"] | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
72223
11
1453
1
153