UNPKG

staticsitegenerators

Version:

A comprehensive, partially automatically generated comparison of static site generators with some minimal meta data about them

129 lines (128 loc) 5.02 kB
/* eslint camelcase:0 */ import naturalCompare from 'string-natural-compare'; import { validateCredentials, getGitHubRepositories } from '@bevry/github-api'; import arrangeKeys from 'arrangekeys'; import crypto from 'node:crypto'; /** The preferred order of keys when arranging entry objects */ const keyorder = 'id name github gitlab bitbucket website license language description created_at updated_at abandoned is extensible stars forks watchers'; /** * Sort an array of entries by name and then by GitHub repository name. * @param data The array of entries with name and optional github properties to sort * @returns Sorted array of entries */ function sort(data) { return data.sort((a, b) => naturalCompare(a.name, b.name, { caseInsensitive: true, }) || naturalCompare(a.github, b.github, { caseInsensitive: true, })); } /** * Compare two values for case-insensitive equality, handling type coercion. * @param a The first value to compare * @param b The second value to compare * @returns True if the values are considered equal, false otherwise */ function same(a, b) { const ta = typeof a, tb = typeof b; if (ta === tb) { if (ta === 'string') { return a.toLowerCase() === b.toLowerCase(); } return a === b; } /* eslint eqeqeq:0 */ return a == b; } /** * Trim redundant data from the listing and enhance with GitHub API data. * @param data An array of raw entries to hydrate with GitHub metadata * @param opts Configuration options for the hydration process including corrective mode, cache duration, and logging function * @returns A promise resolving to both raw and hydrated entry arrays */ export async function hydrate(data, opts = {}) { validateCredentials(); if (opts.corrective == null) opts.corrective = false; if (opts.cache == null) opts.cache = 1000 * 60 * 60 * 24; // one day const rawMap = {}; const hydratedMap = {}; const githubRepos = []; data.forEach(function (entry, index) { delete entry.id; const key = (entry.github && entry.github.toLowerCase()) || index; rawMap[key] = Object.assign({}, arrangeKeys(entry, keyorder)); hydratedMap[key] = Object.assign({ id: crypto .createHash('md5') .update(JSON.stringify({ name: entry.name, website: entry.website, github: entry.github, })) .digest('hex'), }, arrangeKeys(entry, keyorder)); if (entry.github) { githubRepos.push(entry.github); } }); // Log if (opts.log) { opts.log('info', `Fetching the github information, all ${githubRepos.length} of them`); } // Enhance with github data const repos = await getGitHubRepositories(githubRepos); for (const github of repos) { // Prepare const key = github.full_name.toLowerCase(); const raw = rawMap[key]; const hydrated = hydratedMap[key]; // Confirm existence as name may have changed from the listing, for example a repo rename if (raw == null) { if (opts.log) { opts.log('warn', `${github.full_name} is missing, likely due to rename`); } continue; // skip } // Apply github fields const fields = { description: github.description, language: github.language, license: github.license && github.license.key, website: github.homepage && github.homepage.toLowerCase().includes(`github.com/${key}`) && github.homepage, stars: github.stargazers_count, watchers: github.watchers_count, forks: github.forks_count, // @ts-expect-error typescript is wrong created_at: github.created_at, // @ts-expect-error typescript is wrong updated_at: github.updated_at, }; for (const [key, value] of Object.entries(fields)) { const rawValue = raw[key]; if (value) { if (opts.corrective && rawValue && value && same(rawValue, value)) { if (opts.log) { opts.log('note', `trimming ${key} on ${github.full_name} as it is the same as the github data: ${value}`); } delete raw[key]; } if (hydrated[key] == null) { if (opts.log) { opts.log('info', `added ${key} on ${github.full_name} from the github data`); } hydrated[key] = value; } } } hydratedMap[key] = arrangeKeys(hydrated, keyorder); } return { hydrated: sort(Object.keys(hydratedMap).map((k) => hydratedMap[k])), raw: sort(Object.keys(rawMap).map((k) => rawMap[k])), }; }