hacker-feeds-cli
Version:
A command line tool for hacker news & product hunt feeds.
128 lines (117 loc) • 4.26 kB
JavaScript
const axios = require('axios');
const chalk = require('chalk');
const ora = require('ora');
const t = require('./i18n');
const cheerio = require('cheerio');
const { omitBy, isNil } = require('lodash');
const GITHUB_URL = 'https://github.com';
function omitNil(object) {
return omitBy(object, isNil);
}
function removeDefaultAvatarSize(src) {
/* istanbul ignore if */
if (!src) {
return src;
}
return src.replace(/\?s=.*$/, '');
}
async function fetchRepositories({ language = '', since = 'daily', spokenLanguage = '' } = {}) {
const url = `${GITHUB_URL}/trending/${language}?since=${since}&spoken_language_code=${spokenLanguage}`;
const { data } = await axios.get(url, {
responseType: 'text',
});
const $ = cheerio.load(data);
return (
$('.Box article.Box-row')
.get()
// eslint-disable-next-line complexity
.map((repo) => {
const $repo = $(repo);
const title = $repo.find('.h3').text().trim();
const [username, repoName] = title.split('/').map((v) => v.trim());
const relativeUrl = $repo.find('.h3').find('a').attr('href');
const currentPeriodStarsString =
$repo.find('.float-sm-right').text().trim() || /* istanbul ignore next */ '';
const builtBy = $repo
.find('span:contains("Built by")')
.find('[data-hovercard-type="user"]')
.map((i, user) => {
const altString = $(user).children('img').attr('alt');
const avatarUrl = $(user).children('img').attr('src');
return {
username: altString ? altString.slice(1) : /* istanbul ignore next */ null,
href: `${GITHUB_URL}${user.attribs.href}`,
avatar: removeDefaultAvatarSize(avatarUrl),
};
})
.get();
const colorNode = $repo.find('.repo-language-color');
const langColor = colorNode.length ? colorNode.css('background-color') : null;
const langNode = $repo.find('[itemprop=programmingLanguage]');
const lang = langNode.length ? langNode.text().trim() : /* istanbul ignore next */ null;
return omitNil({
author: username,
repo: repoName,
avatar: `${GITHUB_URL}/${username}.png`,
repo_link: `${GITHUB_URL}${relativeUrl}`,
desc: $repo.find('p.my-1').text().trim() || '',
lang: lang,
languageColor: langColor,
stars: parseInt(
$repo
.find(".mr-3 svg[aria-label='star']")
.first()
.parent()
.text()
.trim()
.replace(',', '') || /* istanbul ignore next */ '0',
10,
),
forks: parseInt(
$repo.find("svg[aria-label='fork']").first().parent().text().trim().replace(',', '') ||
/* istanbul ignore next */ '0',
10,
),
added_stars: parseInt(
currentPeriodStarsString.split(' ')[0].replace(',', '') ||
/* istanbul ignore next */ '0',
10,
),
builtBy,
});
})
);
}
async function fetchGitHubTrending(since = 'daily', language = '') {
const spinner = ora(t('spinner.load')).start();
try {
const items = await fetchRepositories({ language, since });
spinner.stop();
console.log(
chalk.green(`-----------------------------------------
🐙 ${t('github.title')}
-----------------------------------------
`),
);
items.forEach(({ repo, repo_link, desc, lang, stars, added_stars, author }) => {
console.log(
`${t('github.repo')}: `,
chalk.cyan(repo),
' | ',
`${t('github.lang')}: `,
chalk.cyan(lang),
` | ${t('github.star')}: `,
chalk.cyan(stars),
` | ${t(`github.addedStar.${since}`)}: `,
chalk.cyan(added_stars),
);
desc && console.log(`${t('github.desc')}: `, chalk.green(desc));
console.log(`${t('github.link')}: `, chalk.dim(repo_link));
console.log(chalk.cyan(`-----------------------------------------`));
});
} catch (e) {
console.log(e);
spinner.fail(t('spinner.fail'));
}
}
module.exports = fetchGitHubTrending;