Comparing version 6.9.0 to 6.10.0-next.1591777722.c7e0d54a50cee276c3f89d274ba6eb86a83b69b2
@@ -6,421 +6,415 @@ /** Comparator for sorting fellows in an array */ | ||
/** A fellow with similarties to other people */ | ||
let Fellow = /** @class */ (() => { | ||
class Fellow { | ||
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() | ||
/** | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::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`. | ||
*/ | ||
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) | ||
} | ||
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 | ||
} | ||
/** 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 | ||
} | ||
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 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 | ||
} | ||
/** | ||
* 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) | ||
} | ||
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) | ||
} | ||
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) | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
} | ||
/** | ||
* 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)) | ||
} | ||
} | ||
/** | ||
* 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)) | ||
} | ||
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) | ||
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') | ||
} | ||
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 | ||
} | ||
/** | ||
* 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') | ||
// 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 | ||
} | ||
} | ||
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 | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
return this | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
// ----------------------------------- | ||
// 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 | ||
} | ||
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 | ||
} | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
/** | ||
* 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) | ||
} | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
} | ||
} | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
} | ||
/** 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 | ||
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 | ||
} else { | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
} else { | ||
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 | ||
} | ||
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 || | ||
'' | ||
) | ||
} | ||
/** 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 | ||
} | ||
/** 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 | ||
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}>`) | ||
} | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
} | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.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 maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.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 author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.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>` | ||
) | ||
} | ||
// ----------------------------------- | ||
// 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(' ') | ||
// 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>` | ||
) | ||
} | ||
/** | ||
* 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(' ') | ||
} | ||
// return | ||
return parts.join(' ') | ||
} | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] | ||
return Fellow | ||
})() | ||
export default Fellow | ||
} | ||
/** A singleton attached to the class that stores it's instances to enable convergence of data */ | ||
Fellow.fellows = [] |
@@ -10,421 +10,416 @@ 'use strict' | ||
/** A fellow with similarties to other people */ | ||
let Fellow = /** @class */ (() => { | ||
class Fellow { | ||
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() | ||
/** | ||
* Construct our fellow instance with the value | ||
* @param value The value used to set the properties of the fellow, forwarded to {@link Fellow::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`. | ||
*/ | ||
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) | ||
} | ||
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 | ||
} | ||
/** 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 | ||
} | ||
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 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 | ||
} | ||
/** | ||
* 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) | ||
} | ||
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) | ||
} | ||
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) | ||
if (add) { | ||
this.fellows.push(newFellow) | ||
return newFellow | ||
} else { | ||
throw new Error(`Fellow by ${value} does not exist`) | ||
} | ||
/** | ||
* 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)) | ||
} | ||
} | ||
/** | ||
* 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)) | ||
} | ||
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) | ||
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') | ||
} | ||
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 | ||
} | ||
/** | ||
* 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') | ||
// 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 | ||
} | ||
} | ||
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 | ||
} | ||
// if not a url field, e.g. name or email | ||
else { | ||
this[key] = value | ||
} | ||
} | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
return this | ||
}) | ||
} else { | ||
throw new Error('Invalid fellow input') | ||
} | ||
// ----------------------------------- | ||
// 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 | ||
} | ||
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 | ||
} | ||
/** | ||
* Fetch the user's name, otherwise their usernames | ||
*/ | ||
get name() { | ||
return ( | ||
this._name || | ||
this.githubUsername || | ||
this.twitterUsername || | ||
this.facebookUsername || | ||
'' | ||
) | ||
} | ||
/** | ||
* 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) | ||
} | ||
/** Add the email to the set instead of replacing it */ | ||
set email(value) { | ||
if (value) { | ||
this.emails.add(value) | ||
} | ||
} | ||
/** Fetch the first email that was applied, otherwise an empty string */ | ||
get email() { | ||
for (const email of this.emails) { | ||
return email | ||
} | ||
/** 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 | ||
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 | ||
} else { | ||
const facebookMatch = /^.+facebook.com\/([^/]+)\/?$/.exec(input) | ||
if (facebookMatch) { | ||
this.facebookUsername = facebookMatch[1] | ||
url = this.facebookUrl = | ||
'https://facebook.com/' + this.facebookUsername | ||
const twitterMatch = /^.+twitter.com\/([^/]+)\/?$/.exec(input) | ||
if (twitterMatch) { | ||
this.twitterUsername = twitterMatch[1] | ||
url = this.twitterUrl = | ||
'https://twitter.com/' + this.twitterUsername | ||
} else { | ||
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 | ||
} | ||
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 || | ||
'' | ||
) | ||
} | ||
/** 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 | ||
} | ||
/** 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 | ||
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}>`) | ||
} | ||
// ----------------------------------- | ||
// Repositories | ||
/** Get all fellows who administrate a particular repository */ | ||
static administersRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.administeredRepositories.has(repoSlug) | ||
}) | ||
// url | ||
const url = format.urlFields | ||
? this.getFirstField(format.urlFields) | ||
: this.url | ||
if (url) { | ||
parts.push(`(${url})`) | ||
} | ||
/** Get all fellows who contribute to a particular repository */ | ||
static contributesRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.contributedRepositories.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 maintain a particular repository */ | ||
static maintainsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.maintainedRepositories.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 author a particular repository */ | ||
static authorsRepository(repoSlug) { | ||
return this.fellows.filter(function (fellow) { | ||
return fellow.authoredRepositories.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>` | ||
) | ||
} | ||
// ----------------------------------- | ||
// 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(' ') | ||
// 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>` | ||
) | ||
} | ||
/** | ||
* 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(' ') | ||
} | ||
// 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.10.0 2020 June 10 | ||
- Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) | ||
## v6.9.0 2020 May 22 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "fellow", | ||
"version": "6.9.0", | ||
"version": "6.10.0-next.1591777722.c7e0d54a50cee276c3f89d274ba6eb86a83b69b2", | ||
"description": "Fellow is a package for creating people that can be unified by their shared values via a singleton list on the class", | ||
@@ -117,2 +117,16 @@ "homepage": "https://github.com/bevry/fellow", | ||
} | ||
}, | ||
{ | ||
"description": "TypeScript source code made to be compatible with Deno", | ||
"directory": "edition-deno", | ||
"entry": "index.ts", | ||
"tags": [ | ||
"typescript", | ||
"import", | ||
"deno" | ||
], | ||
"engines": { | ||
"deno": true, | ||
"browsers": true | ||
} | ||
} | ||
@@ -125,24 +139,27 @@ ], | ||
"module": "edition-browsers/index.js", | ||
"deno": "edition-deno/index.ts", | ||
"devDependencies": { | ||
"@bevry/update-contributors": "^1.7.0", | ||
"@typescript-eslint/eslint-plugin": "^2.34.0", | ||
"@typescript-eslint/parser": "^2.34.0", | ||
"assert-helpers": "^6.6.0", | ||
"eslint": "^7.0.0", | ||
"eslint-config-bevry": "^3.7.0", | ||
"@bevry/update-contributors": "^1.8.0", | ||
"@typescript-eslint/eslint-plugin": "^3.2.0", | ||
"@typescript-eslint/parser": "^3.2.0", | ||
"assert-helpers": "^6.7.0", | ||
"eslint": "^7.2.0", | ||
"eslint-config-bevry": "^3.8.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-babel": "^5.3.0", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"kava": "^5.2.0", | ||
"kava": "^5.3.0", | ||
"make-deno-edition": "^0.1.2", | ||
"prettier": "^2.0.5", | ||
"projectz": "^2.3.0", | ||
"projectz": "^2.5.0", | ||
"surge": "^0.21.3", | ||
"typedoc": "^0.17.7", | ||
"typescript": "^3.9.3", | ||
"valid-directory": "^2.1.0", | ||
"valid-module": "^1.2.0" | ||
"typescript": "^3.9.5", | ||
"valid-directory": "^2.2.0", | ||
"valid-module": "^1.3.0" | ||
}, | ||
"scripts": { | ||
"our:clean": "rm -Rf ./docs ./edition* ./es2015 ./es5 ./out ./.next", | ||
"our:compile": "npm run our:compile:edition-browsers && npm run our:compile:edition-esnext && npm run our:compile:types", | ||
"our:compile": "npm run our:compile:deno && npm run our:compile:edition-browsers && npm run our:compile:edition-esnext && npm run our:compile:types", | ||
"our:compile:deno": "make-deno-edition --attempt", | ||
"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 ) )", | ||
@@ -149,0 +166,0 @@ "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 ) )", |
@@ -51,2 +51,8 @@ <!-- TITLE/ --> | ||
<a href="https://deno.land" title="Deno is a secure runtime for JavaScript and TypeScript, it is an alternative for Node.js"><h3>Deno</h3></a> | ||
``` typescript | ||
import pkg from 'https://unpkg.com/fellow@^6.10.0/edition-deno/index.ts' | ||
``` | ||
<a href="https://www.pika.dev/cdn" title="100% Native ES Modules CDN"><h3>pika</h3></a> | ||
@@ -56,3 +62,3 @@ | ||
<script type="module"> | ||
import pkg from '//cdn.pika.dev/fellow/^6.9.0' | ||
import pkg from '//cdn.pika.dev/fellow/^6.10.0' | ||
</script> | ||
@@ -65,3 +71,3 @@ ``` | ||
<script type="module"> | ||
import pkg from '//unpkg.com/fellow@^6.9.0' | ||
import pkg from '//unpkg.com/fellow@^6.10.0' | ||
</script> | ||
@@ -74,3 +80,3 @@ ``` | ||
<script type="module"> | ||
import pkg from '//dev.jspm.io/fellow@6.9.0' | ||
import pkg from '//dev.jspm.io/fellow@6.10.0' | ||
</script> | ||
@@ -86,3 +92,4 @@ ``` | ||
<li><code>fellow/edition-esnext/index.js</code> is <a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a> compiled against <a href="https://en.wikipedia.org/wiki/ECMAScript#ES.Next" title="ECMAScript Next">ESNext</a> for <a href="https://nodejs.org" title="Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine">Node.js</a> with <a href="https://nodejs.org/dist/latest-v5.x/docs/api/modules.html" title="Node/CJS Modules">Require</a> for modules</li> | ||
<li><code>fellow/edition-browsers/index.js</code> is <a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a> compiled against <a href="https://en.wikipedia.org/wiki/ECMAScript#10th_Edition_-_ECMAScript_2019" title="ECMAScript ES2019">ES2019</a> for web browsers with <a href="https://babeljs.io/docs/learn-es2015/#modules" title="ECMAScript Modules">Import</a> for modules</li></ul> | ||
<li><code>fellow/edition-browsers/index.js</code> is <a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a> compiled against <a href="https://en.wikipedia.org/wiki/ECMAScript#10th_Edition_-_ECMAScript_2019" title="ECMAScript ES2019">ES2019</a> for web browsers with <a href="https://babeljs.io/docs/learn-es2015/#modules" title="ECMAScript Modules">Import</a> for modules</li> | ||
<li><code>fellow/edition-deno/index.ts</code> is <a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a> source code made to be compatible with <a href="https://deno.land" title="Deno is a secure runtime for JavaScript and TypeScript, it is an alternative to Node.js">Deno</a></li></ul> | ||
@@ -89,0 +96,0 @@ <!-- /INSTALL --> |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
87193
12
1882
160
18
1