diff --git a/src/cache.ts b/src/cache.ts index b40e0ff..ca1ba2a 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -5,7 +5,7 @@ export function stringifyCache(cache: Sponsorship[]): string { return JSON.stringify( cache, (_key, value) => { - if (value && value.type === 'Buffer' && Array.isArray(value.data)) { + if (value?.type === 'Buffer' && Array.isArray(value.data)) { return Buffer.from(value.data).toString('base64') } return value diff --git a/src/cli.ts b/src/cli.ts index bb38559..d523af7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,7 +3,7 @@ import cac from 'cac' import { version } from '../package.json' import { run } from './run' -const RE_FILTER = /([<>=]+)(\d+)/ +const RE_FILTER = /^(<=?|>=?)(\d+)$/ const cli = cac('contributors-svg') .version(version) .help() @@ -37,7 +37,11 @@ cli.parse() * @param template */ function createFilterFromString(template: string): ContribkitConfig['filter'] { - const [_, op, value] = template.split(RE_FILTER) + const match = RE_FILTER.exec(template) + if (!match) + throw new Error(`Unable to parse filter template ${template}`) + + const [, op, value] = match const num = Number.parseInt(value) if (op === '<') return s => s.monthlyDollars < num diff --git a/src/configs/index.ts b/src/configs/index.ts index d7d5250..a680997 100644 --- a/src/configs/index.ts +++ b/src/configs/index.ts @@ -69,13 +69,13 @@ export async function loadConfig(inlineConfig: ContribkitConfig = {}): Promise(tier => ({ + const tierMappings = tiers.map(tier => ({ monthlyDollars: tier.monthlyDollars ?? 0, tier, sponsors: [], @@ -88,8 +88,9 @@ export function partitionTiers(sponsors: Sponsorship[], tiers: Tier[], includePa if (finalSponsors.length !== 1) throw new Error(`There should be exactly one tier with no \`monthlyDollars\`, but got ${finalSponsors.length}`) + sponsors.sort((a, b) => Date.parse(a.createdAt!) - Date.parse(b.createdAt!)) + sponsors - .sort((a, b) => Date.parse(a.createdAt!) - Date.parse(b.createdAt!)) .filter(s => s.monthlyDollars > 0 || includePastSponsors) // Past sponsors monthlyDollars is -1 .forEach((sponsor) => { const tier = tierMappings.find(t => sponsor.monthlyDollars >= t.monthlyDollars) ?? tierMappings[0] diff --git a/src/processing/image.ts b/src/processing/image.ts index d29a58a..187f689 100644 --- a/src/processing/image.ts +++ b/src/processing/image.ts @@ -51,7 +51,7 @@ export async function resolveAvatars( if (pngBuffer) { // Store the highest resolution version we use of the original image // Stored in webp to save space - ship.sponsor.avatarBuffer = await resizeImage(pngBuffer, 120, 'webp') + ship.sponsor.avatarBuffer = await resizeImage(pngBuffer, 'webp', 120) } }))) } @@ -59,8 +59,8 @@ export async function resolveAvatars( const cache = new Map>() export async function resizeImage( image: Buffer, - size = 100, format: ImageFormat, + size = 100, ) { const cacheKey = `${size}:${format}` if (cache.has(image)) { diff --git a/src/processing/svg.ts b/src/processing/svg.ts index 94a297c..bda2196 100644 --- a/src/processing/svg.ts +++ b/src/processing/svg.ts @@ -42,13 +42,13 @@ export async function generateBadge( const { size } = preset.avatar let avatar = sponsor.avatarBuffer! if (size < 50) { - avatar = await resizeImage(avatar, 50, imageFormat) + avatar = await resizeImage(avatar, imageFormat, 50) } else if (size < 80) { - avatar = await resizeImage(avatar, 80, imageFormat) + avatar = await resizeImage(avatar, imageFormat, 80) } else if (imageFormat === 'png') { - avatar = await resizeImage(avatar, 120, imageFormat) + avatar = await resizeImage(avatar, imageFormat, 120) } const avatarBase64 = avatar.toString('base64') @@ -122,5 +122,5 @@ ${this.body} } function encodeHtmlEntities(str: string) { - return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') + return String(str).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"') } diff --git a/src/providers/afdian.ts b/src/providers/afdian.ts index 3dc915e..9c339bc 100644 --- a/src/providers/afdian.ts +++ b/src/providers/afdian.ts @@ -109,5 +109,7 @@ export async function fetchAfdianSponsors(options: ContribkitConfig['afdian'] = } function md5(token: string, params: string, ts: number, userId: string) { - return createHash('md5').update(`${token}params${params}ts${ts}user_id${userId}`).digest('hex') + return createHash('md5') // NOSONAR(typescript:S4790): Afdian requires MD5 request signatures. + .update(`${token}params${params}ts${ts}user_id${userId}`) + .digest('hex') } diff --git a/src/providers/githubContributors.ts b/src/providers/githubContributors.ts index 5ee56a3..a690f7c 100644 --- a/src/providers/githubContributors.ts +++ b/src/providers/githubContributors.ts @@ -58,7 +58,7 @@ export async function fetchGitHubContributors( }, ) - if (!response || !response.length) + if (!response?.length) break allContributors.push(...response) diff --git a/src/providers/gitlabContributors.ts b/src/providers/gitlabContributors.ts index 92cf637..515e7a4 100644 --- a/src/providers/gitlabContributors.ts +++ b/src/providers/gitlabContributors.ts @@ -61,7 +61,7 @@ export async function fetchGitlabContributors( }, ) - if (!response || !response.length) + if (!response?.length) break allContributors.push(...response) diff --git a/src/providers/index.ts b/src/providers/index.ts index db10d17..9ef5765 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -29,7 +29,7 @@ export const ProvidersMap = { export function guessProviders(config: ContribkitConfig) { const items: ProviderName[] = [] const credentials = getCredentials(config) - if (config.github && config.github.login) + if (config.github?.login) items.push('github') if (credentials.patreon?.token) @@ -44,7 +44,7 @@ export function guessProviders(config: ContribkitConfig) { if (credentials.polar?.token) items.push('polar') - if (config.liberapay && config.liberapay.login) + if (config.liberapay?.login) items.push('liberapay') if (config.githubContributors?.login && credentials.githubContributors?.token) diff --git a/src/providers/opencollective.ts b/src/providers/opencollective.ts index 10a2f93..04b296d 100644 --- a/src/providers/opencollective.ts +++ b/src/providers/opencollective.ts @@ -70,13 +70,12 @@ export async function fetchOpenCollectiveSponsors( sponsors.push(...(nodes || [])) - if ((nodes.length) !== 0) { - if (totalCount > offset + nodes.length) - offset += nodes.length - else - offset = undefined - } - else { offset = undefined } + if (nodes.length === 0) + offset = undefined + else if (totalCount > offset + nodes.length) + offset += nodes.length + else + offset = undefined } while (offset) } @@ -99,13 +98,12 @@ export async function fetchOpenCollectiveSponsors( const totalCount = data.data.account.transactions.totalCount monthlyTransactions.push(...(nodes || [])) - if ((nodes.length) !== 0) { - if (totalCount > offset + nodes.length) - offset += nodes.length - else - offset = undefined - } - else { offset = undefined } + if (nodes.length === 0) + offset = undefined + else if (totalCount > offset + nodes.length) + offset += nodes.length + else + offset = undefined } while (offset) const sponsorships: [string, Sponsorship][] = sponsors @@ -139,12 +137,12 @@ export async function fetchOpenCollectiveSponsors( const transactionsBySponsorId: Map = monthlySponsorships.reduce((map, [id, sponsor]) => { const existingSponsor = map.get(id) if (existingSponsor) { - const createdAt = new Date(sponsor.createdAt!) - const existingSponsorCreatedAt = new Date(existingSponsor.createdAt!) + const createdAt = toDate(sponsor.createdAt) + const existingSponsorCreatedAt = toDate(existingSponsor.createdAt) if (createdAt >= existingSponsorCreatedAt) map.set(id, sponsor) - else if (new Date(existingSponsorCreatedAt.getFullYear(), existingSponsorCreatedAt.getMonth(), 1) === new Date(createdAt.getFullYear(), createdAt.getMonth(), 1)) + else if (isSameMonth(existingSponsorCreatedAt, createdAt)) existingSponsor.monthlyDollars += sponsor.monthlyDollars } else { map.set(id, sponsor) } @@ -156,8 +154,8 @@ export async function fetchOpenCollectiveSponsors( .reduce((map, [id, sponsor]) => { const existingSponsor = map.get(id) if (existingSponsor) { - const createdAt = new Date(sponsor.createdAt!) - const existingSponsorCreatedAt = new Date(existingSponsor.createdAt!) + const createdAt = toDate(sponsor.createdAt) + const existingSponsorCreatedAt = toDate(existingSponsor.createdAt) if (createdAt >= existingSponsorCreatedAt) map.set(id, sponsor) } @@ -195,7 +193,7 @@ function createSponsorFromOrder(order: any): [string, Sponsorship] | undefined { avatarUrl: order.fromAccount.imageUrl, websiteUrl: normalizeUrl(getBestUrl(order.fromAccount.socialLinks)), linkUrl: `https://opencollective.com/${slug}`, - socialLogins: getSocialLogins(order.fromAccount.socialLinks, slug), + socialLogins: getSocialLogins(slug, order.fromAccount.socialLinks), }, isOneTime: order.frequency === 'ONETIME', monthlyDollars, @@ -242,14 +240,14 @@ function createSponsorFromTransaction(transaction: any, excludeOrders: string[], type: getAccountType(account.type), login: slug, avatarUrl: account.imageUrl, - websiteUrl: normalizeUrl(getBestUrl(account.socialLinks || [])), + websiteUrl: normalizeUrl(getBestUrl(account.socialLinks ?? [])), linkUrl: `https://opencollective.com/${slug}`, - socialLogins: getSocialLogins(account.socialLinks || [], slug), + socialLogins: getSocialLogins(slug, account.socialLinks), }, isOneTime: transaction.order?.frequency === 'ONETIME', monthlyDollars, privacyLevel: sponseesMode ? 'PUBLIC' : (account.isIncognito ? 'PRIVATE' : 'PUBLIC'), - tierName: transaction.order?.tier?.name || transaction.tier?.name, + tierName: transaction.order?.tier?.name ?? transaction.tier?.name, createdAt: sponseesMode ? transaction.createdAt : transaction.order?.frequency === 'ONETIME' @@ -432,13 +430,21 @@ function getBestUrl(socialLinks: SocialLink[]): string | undefined { return urls[0] } +function isSameMonth(a: Date, b: Date) { + return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() +} + +function toDate(value: string | undefined) { + return new Date(value ?? Number.NaN) +} + const RE_GITHUB_URL = /github\.com\/([^/]*)/ -function getSocialLogins(socialLinks: SocialLink[] = [], opencollectiveLogin: string): Record { +function getSocialLogins(opencollectiveLogin: string, socialLinks: SocialLink[] = []): Record { const socialLogins: Record = {} for (const link of socialLinks) { if (link.type === 'GITHUB') { - const login = link.url.match(RE_GITHUB_URL)?.[1] + const login = RE_GITHUB_URL.exec(link.url)?.[1] if (login) socialLogins.github = login } diff --git a/src/renders/circles.ts b/src/renders/circles.ts index aac2a8f..be9c9f8 100644 --- a/src/renders/circles.ts +++ b/src/renders/circles.ts @@ -1,6 +1,11 @@ import type { ContribkitRenderer, Sponsorship } from '../types' import { generateBadge, SvgComposer } from '../processing/svg' +interface SponsorHierarchy extends Sponsorship { + children?: Sponsorship[] + id?: string +} + export const circlesRenderer: ContribkitRenderer = { name: 'contribkit:circles', async renderSVG(config, sponsors) { @@ -18,20 +23,21 @@ export const circlesRenderer: ContribkitRenderer = { function defaultInterop(sponsor: Sponsorship) { return sponsor.monthlyDollars < 0 ? radiusPast - : lerp(radiusMin, radiusMax, (Math.max(0.1, sponsor.monthlyDollars || 0) / amountMax) ** 0.9) + : lerp(radiusMin, radiusMax, (Math.max(0.1, sponsor.monthlyDollars ?? 0) / amountMax) ** 0.9) } if (!config.includePastSponsors) sponsors = sponsors.filter(sponsor => sponsor.monthlyDollars > 0) - const root = hierarchy({ ...sponsors[0], children: sponsors, id: 'root' }) + const rootData: SponsorHierarchy = { ...sponsors[0], children: sponsors, id: 'root' } + const root = hierarchy(rootData) .sum(d => weightInterop(d, amountMax)) - .sort((a, b) => (b.value || 0) - (a.value || 0)) + .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)) - const p = pack() + const p = pack() p.size([config.width, config.width]) p.padding(config.width / 400) - const circles = p(root as any).descendants().slice(1) + const circles = p(root).descendants().slice(1) for (const circle of circles) { composer.addRaw(await generateBadge( diff --git a/src/run.ts b/src/run.ts index 41f8e48..6f71ba5 100644 --- a/src/run.ts +++ b/src/run.ts @@ -41,7 +41,7 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { : config.cacheFile, ) - const providers = resolveProviders(config.providers || guessProviders(config)) + const providers = resolveProviders(config.providers ?? guessProviders(config)) if (config.renders?.length) { const names = new Set() @@ -63,60 +63,24 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { t.info(`Fetching sponsorships from ${i.name}...`) let sponsors = await i.fetchSponsors(config) sponsors.forEach(s => s.provider = i.name) - sponsors = await config.onSponsorsFetched?.(sponsors, i.name) || sponsors + sponsors = (await config.onSponsorsFetched?.(sponsors, i.name)) ?? sponsors t.success(`${sponsors.length} sponsorships fetched from ${i.name}`) allSponsors.push(...sponsors) } // Custom hook - allSponsors = await config.onSponsorsAllFetched?.(allSponsors) || allSponsors + allSponsors = (await config.onSponsorsAllFetched?.(allSponsors)) ?? allSponsors // Merge sponsors { const sponsorsMergeMap = new Map>() - function pushGroup(group: Sponsorship[]) { - const existingSets = new Set(group.map(s => sponsorsMergeMap.get(s)).filter(notNullish)) - let set: Set - if (existingSets.size === 1) { - set = [...existingSets.values()][0] - } - else if (existingSets.size === 0) { - set = new Set(group) - } - // Multiple sets, merge them into one - else { - set = new Set() - for (const s of existingSets) { - for (const i of s) - set.add(i) - } - } - - for (const s of group) { - set.add(s) - sponsorsMergeMap.set(s, set) - } - } - - function matchSponsor(sponsor: Sponsorship, matcher: SponsorMatcher) { - if (matcher.provider && sponsor.provider !== matcher.provider) - return false - if (matcher.login && sponsor.sponsor.login !== matcher.login) - return false - if (matcher.name && sponsor.sponsor.name !== matcher.name) - return false - if (matcher.type && sponsor.sponsor.type !== matcher.type) - return false - return true - } - for (const rule of config.mergeSponsors || []) { if (typeof rule === 'function') { for (const ship of allSponsors) { const result = rule(ship, allSponsors) if (result) - pushGroup(result) + pushSponsorGroup(sponsorsMergeMap, result) } } else { @@ -126,7 +90,7 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { t.warn(`No sponsor matched for ${JSON.stringify(matcher)}`) return matched }) - pushGroup(group) + pushSponsorGroup(sponsorsMergeMap, group) } } @@ -137,23 +101,11 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { for (const [provider, login] of Object.entries(ship.sponsor.socialLogins)) { const matched = allSponsors.filter(s => s.sponsor.login === login && s.provider === provider) if (matched) - pushGroup([ship, ...matched]) + pushSponsorGroup(sponsorsMergeMap, [ship, ...matched]) } } } - function mergeSponsors(main: Sponsorship, sponsors: Sponsorship[]) { - const all = [main, ...sponsors] - main.isOneTime = all.every(s => s.isOneTime) - main.expireAt = all.map(s => s.expireAt).filter(notNullish).sort((a, b) => b.localeCompare(a))[0] - main.createdAt = all.map(s => s.createdAt).filter(notNullish).sort((a, b) => a.localeCompare(b))[0] - main.monthlyDollars = all.every(s => s.monthlyDollars === -1) - ? -1 - : all.filter(s => s.monthlyDollars > 0).reduce((a, b) => a + b.monthlyDollars, 0) - main.provider = [...new Set(all.map(s => s.provider))].join('+') - return main - } - const removeSponsors = new Set() const groups = new Set(sponsorsMergeMap.values()) for (const group of groups) { @@ -218,10 +170,10 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { allSponsors.sort((a, b) => b.monthlyDollars - a.monthlyDollars // DESC amount || Date.parse(b.createdAt!) - Date.parse(a.createdAt!) // DESC date - || (b.sponsor.login || b.sponsor.name).localeCompare(a.sponsor.login || a.sponsor.name), // ASC name + || (b.sponsor.login ?? b.sponsor.name).localeCompare(a.sponsor.login ?? a.sponsor.name), // ASC name ) - allSponsors = await config.onSponsorsReady?.(allSponsors) || allSponsors + allSponsors = (await config.onSponsorsReady?.(allSponsors)) ?? allSponsors if (config.renders?.length) { t.info(`Generating with ${config.renders.length} renders...`) @@ -230,7 +182,7 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { ...fullConfig, ...renderOptions, } - const renderer = builtinRenderers[mergedOptions.renderer || 'tiers'] + const renderer = builtinRenderers[mergedOptions.renderer ?? 'tiers'] await applyRenderer( renderer, config, @@ -241,7 +193,7 @@ export async function run(inlineConfig?: ContribkitConfig, t = consola) { })) } else { - const renderer = builtinRenderers[fullConfig.renderer || 'tiers'] + const renderer = builtinRenderers[fullConfig.renderer ?? 'tiers'] await applyRenderer( renderer, config, @@ -260,7 +212,7 @@ export async function applyRenderer( t = consola, ) { sponsors = [...sponsors] - sponsors = await renderOptions.onBeforeRenderer?.(sponsors) || sponsors + sponsors = (await renderOptions.onBeforeRenderer?.(sponsors)) ?? sponsors const logPrefix = c.dim`[${renderOptions.name}]` const dir = resolve(process.cwd(), config.outputDir) @@ -271,14 +223,13 @@ export async function applyRenderer( if (!renderOptions.includePrivate) sponsors = sponsors.filter(s => s.privacyLevel !== 'PRIVATE') - if (!renderOptions.imageFormat) - renderOptions.imageFormat = 'webp' + renderOptions.imageFormat ??= 'webp' const processingSvg = (async () => { let svgWebp = await renderer.renderSVG(renderOptions, sponsors) if (renderOptions.onSvgGenerated) { - svgWebp = await renderOptions.onSvgGenerated(svgWebp) || svgWebp + svgWebp = (await renderOptions.onSvgGenerated(svgWebp)) ?? svgWebp } return svgWebp })() @@ -286,59 +237,95 @@ export async function applyRenderer( if (renderOptions.formats) { let svgPng: Promise | undefined - await Promise.all([ - renderOptions.formats.map(async (format) => { - if (!outputFormats.includes(format)) - throw new Error(`Unsupported format: ${format}`) + await Promise.all(renderOptions.formats.map(async (format) => { + if (!outputFormats.includes(format)) + throw new Error(`Unsupported format: ${format}`) - const path = join(dir, `${renderOptions.name}.${format}`) + const path = join(dir, `${renderOptions.name}.${format}`) - let data: string | Buffer + let data: string | Buffer - if (format === 'svg') { - t.info(`${logPrefix} Composing SVG...`) - data = await processingSvg - } + if (format === 'svg') { + t.info(`${logPrefix} Composing SVG...`) + data = await processingSvg + } + else if (format === 'json') { + data = JSON.stringify(sponsors, null, 2) + } + else { + svgPng ??= renderer.renderSVG({ + ...renderOptions, + imageFormat: 'png', + }, sponsors) + + const pngSvg = await svgPng + data = format === 'png' + ? await svgToPng(pngSvg) + : await svgToWebp(pngSvg) + } - if (format === 'json') { - data = JSON.stringify(sponsors, null, 2) - } + await fsp.writeFile(path, data) - if (format === 'png' || format === 'webp') { - if (!svgPng) { - // Sharp can't render embedded Webp so re-generate with png - // https://github.com/lovell/sharp/issues/4254 - svgPng = renderer.renderSVG({ - ...renderOptions, - imageFormat: 'png', - }, sponsors) - } + t.success(`${logPrefix} Wrote to ${r(path)}`) + })) + } +} - if (format === 'png') { - data = await svgToPng(await svgPng) - } +function pushSponsorGroup(sponsorsMergeMap: Map>, group: Sponsorship[]) { + const existingSets = new Set(group.map(s => sponsorsMergeMap.get(s)).filter(notNullish)) + let set = existingSets.values().next().value + if (!set) { + set = new Set(group) + } + // Multiple sets, merge them into one + else if (existingSets.size > 1) { + set = new Set() + for (const existingSet of existingSets) { + for (const sponsor of existingSet) + set.add(sponsor) + } + } - if (format === 'webp') { - data = await svgToWebp(await svgPng) - } - } + for (const sponsor of group) { + set.add(sponsor) + sponsorsMergeMap.set(sponsor, set) + } +} - await fsp.writeFile(path, data!) +function matchSponsor(sponsor: Sponsorship, matcher: SponsorMatcher) { + if (matcher.provider && sponsor.provider !== matcher.provider) + return false + if (matcher.login && sponsor.sponsor.login !== matcher.login) + return false + if (matcher.name && sponsor.sponsor.name !== matcher.name) + return false + if (matcher.type && sponsor.sponsor.type !== matcher.type) + return false + return true +} - t.success(`${logPrefix} Wrote to ${r(path)}`) - }), - ]) - } +function mergeSponsors(main: Sponsorship, sponsors: Sponsorship[]) { + const all = [main, ...sponsors] + main.isOneTime = all.every(s => s.isOneTime) + main.expireAt = all.map(s => s.expireAt).filter(notNullish).sort((a, b) => b.localeCompare(a))[0] + main.createdAt = all.map(s => s.createdAt).filter(notNullish).sort((a, b) => a.localeCompare(b))[0] + main.monthlyDollars = all.every(s => s.monthlyDollars === -1) + ? -1 + : all.filter(s => s.monthlyDollars > 0).reduce((a, b) => a + b.monthlyDollars, 0) + main.provider = [...new Set(all.map(s => s.provider))].join('+') + return main } +type Replacement = ((sponsor: Sponsorship) => string) | [string, string] + function normalizeReplacements(replaces: ContribkitMainConfig['replaceLinks']) { const array = (Array.isArray(replaces) ? replaces : [replaces]).filter(notNullish) - const entries = array.map((i) => { - if (!i) - return [] - if (typeof i === 'function') - return [i] - return Object.entries(i) as [string, string][] - }).flat() + const entries: Replacement[] = [] + for (const item of array) { + if (typeof item === 'function') + entries.push(item) + else + entries.push(...Object.entries(item) as [string, string][]) + } return entries } diff --git a/src/types.ts b/src/types.ts index a85df63..cafd88f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -64,7 +64,7 @@ export interface Sponsorship { createdAt?: string expireAt?: string isOneTime?: boolean - provider?: ProviderName | string + provider?: string /** * Raw data from provider */ @@ -76,6 +76,7 @@ export const outputFormats = ['svg', 'png', 'webp', 'json'] as const export type OutputFormat = typeof outputFormats[number] export type ProviderName = 'github' | 'patreon' | 'opencollective' | 'afdian' | 'polar' | 'liberapay' | 'githubContributors' | 'gitlabContributors' | 'crowdinContributors' | 'githubContributions' +export type HookResult = PromiseLike | T | void | undefined | null export type GitHubAccountType = 'user' | 'organization' export type SponsorshipMode = 'sponsors' | 'sponsees' @@ -340,12 +341,12 @@ export interface ContribkitRenderOptions { /** * Hook to modify sponsors data before rendering. */ - onBeforeRenderer?: (sponsors: Sponsorship[]) => PromiseLike | void | Sponsorship[] + onBeforeRenderer?: (sponsors: Sponsorship[]) => HookResult /** * Hook to get or modify the SVG before writing. */ - onSvgGenerated?: (svg: string) => PromiseLike | string | void | undefined | null + onSvgGenerated?: (svg: string) => HookResult } export interface ContribkitConfig extends ProvidersConfig, ContribkitRenderOptions { @@ -427,17 +428,17 @@ export interface ContribkitConfig extends ProvidersConfig, ContribkitRenderOptio /** * Hook to modify sponsors data for each provider. */ - onSponsorsFetched?: (sponsors: Sponsorship[], provider: ProviderName | string) => PromiseLike | void | Sponsorship[] + onSponsorsFetched?: (sponsors: Sponsorship[], provider: string) => HookResult /** * Hook to modify merged sponsors data before fetching the avatars. */ - onSponsorsAllFetched?: (sponsors: Sponsorship[]) => PromiseLike | void | Sponsorship[] + onSponsorsAllFetched?: (sponsors: Sponsorship[]) => HookResult /** * Hook to modify sponsors data before rendering. */ - onSponsorsReady?: (sponsors: Sponsorship[]) => PromiseLike | void | Sponsorship[] + onSponsorsReady?: (sponsors: Sponsorship[]) => HookResult /** * Url to fallback avatar. @@ -457,7 +458,7 @@ export interface ContribkitConfig extends ProvidersConfig, ContribkitRenderOptio } export interface SponsorMatcher extends Partial> { - provider?: ProviderName | string + provider?: string } export type ContribkitMainConfig = Omit