Skip to content

Instantly share code, notes, and snippets.

@web-padawan
Created October 17, 2021 12:31
Show Gist options
  • Save web-padawan/79c8110c98fa53106030675bca9b8fa6 to your computer and use it in GitHub Desktop.
Save web-padawan/79c8110c98fa53106030675bca9b8fa6 to your computer and use it in GitHub Desktop.
GitHub dependents
#!/usr/bin/env node
const cliProgress = require('cli-progress');
const gh = require('github-url-to-object');
const meow = require('meow');
const puppeteer = require('puppeteer');
const usage = `Usage
$ github-dependents <repository>
Options
--stars-threshold, -t Stars threshold
--debug Show Puppeteer browser
Example
$ github-dependents lit/lit --stars-threshold 100`;
const cli = meow(usage);
if (cli.input.length === 0) {
console.log(usage);
process.exit(1);
}
const githubRepo = gh(cli.input[0]);
const run = async () => {
const browser = await puppeteer.launch({
headless: !cli.flags.debug,
});
try {
const page = await browser.newPage();
await page.goto(`${githubRepo.https_url}/network/dependents`);
let totalDependentsNumber;
try {
totalDependentsNumber = await page.$eval('.btn-link.selected', (el) =>
parseInt(el.textContent.replace(/\D/g, ''))
);
} catch (error) {
console.error(`Repo ${githubRepo.https_url} is not found`);
process.exit(1);
}
const progressBar = new cliProgress.Bar({}, cliProgress.Presets.shades_classic);
progressBar.start(totalDependentsNumber, 0);
let dependents = [];
while (1) {
try {
await page.waitForSelector('[data-test-selector="pagination"]');
} catch (error) {
// GitHub enabled the abuse detection mechanism, cooling down a minute
await page.waitForTimeout(60000);
await page.reload();
}
const dependentsFromCurrentPage = await page.$$eval('.Box-row', (rows) =>
rows.map((row) => {
const dependant = {};
dependant.repository =
'https://github.com/' +
row.querySelector('[data-hovercard-type="repository"]').getAttribute('href').replace(/^\//, '');
dependant.stars = parseInt(row.querySelector('.octicon-star').nextSibling.textContent.replace(/\D/g, ''));
return dependant;
})
);
dependents = dependents.concat(dependentsFromCurrentPage);
progressBar.update(dependents.length);
const nextButtonSelector = '[data-test-selector="pagination"] *:nth-child(2)';
const tagName = await page.$eval(nextButtonSelector, (el) => el.tagName);
if (tagName === 'A') {
await page.click(nextButtonSelector);
} else {
// the end of the dependents list reached
progressBar.setTotal(dependents.length);
progressBar.stop();
break;
}
}
if (cli.flags.starsThreshold) {
dependents = dependents.filter((dependant) => dependant.stars > cli.flags.starsThreshold);
}
dependents.sort((a, b) => (b.stars > a.stars ? 1 : -1));
console.table(dependents);
await browser.close();
} catch (error) {
await browser.close();
console.error(error);
}
};
run();
{
"name": "github-dependents",
"version": "0.0.0",
"description": "Get the sorted list of dependents from github.com/<project>/network/dependents",
"main": "index.js",
"bin": "index.js",
"dependencies": {
"cli-progress": "^3.9.1",
"github-url-to-object": "^4.0.6",
"meow": "^9.0.0",
"puppeteer": "^10.2.0"
},
"scripts": {
"lint": "eslint *.js"
},
"files": [
"index.js"
],
"keywords": [
"github",
"dependents",
"puppeteer"
],
"author": "Serhii Kulykov <iamkulykov@gmail.com>",
"license": "MIT",
"devDependencies": {
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0",
"prettier": "^2.4.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment