{"id":17452365,"url":"https://github.com/omar-azmi/github_aid_ts","last_synced_at":"2026-04-07T17:31:35.804Z","repository":{"id":217200622,"uuid":"743173675","full_name":"omar-azmi/github_aid_ts","owner":"omar-azmi","description":"A Chromium and Firefox extension for viewing github Repository sizes, and Downloading files/subdirectories. It also features a simple tooling workflow that is capable of auto-generate web-extensions with: pure Typescript, Deno, and ES-modules. Along with the ability to bundle with minification and common-code-splitting.","archived":false,"fork":false,"pushed_at":"2024-05-20T05:39:23.000Z","size":3493,"stargazers_count":7,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T18:50:33.017Z","etag":null,"topics":["build-tool","chrome-extension","deno","deno-examples","deno-sample","extension","githu-api","github-extension","no-nodejs","no-npm","repo-download","repo-size","typescript","web-extension"],"latest_commit_sha":null,"homepage":"https://chromewebstore.google.com/detail/github-aid/abfbcnoemiciiljhpngefacedfgebdcn","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/omar-azmi.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-14T14:58:49.000Z","updated_at":"2025-02-01T19:01:08.000Z","dependencies_parsed_at":"2024-05-20T06:45:26.392Z","dependency_job_id":null,"html_url":"https://github.com/omar-azmi/github_aid_ts","commit_stats":null,"previous_names":["omar-azmi/github_aid_ts"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/omar-azmi/github_aid_ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omar-azmi%2Fgithub_aid_ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omar-azmi%2Fgithub_aid_ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omar-azmi%2Fgithub_aid_ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omar-azmi%2Fgithub_aid_ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/omar-azmi","download_url":"https://codeload.github.com/omar-azmi/github_aid_ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omar-azmi%2Fgithub_aid_ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31522233,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"ssl_error","status_checked_at":"2026-04-07T16:28:06.951Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build-tool","chrome-extension","deno","deno-examples","deno-sample","extension","githu-api","github-extension","no-nodejs","no-npm","repo-download","repo-size","typescript","web-extension"],"created_at":"2024-10-17T23:06:11.601Z","updated_at":"2026-04-07T17:31:35.770Z","avatar_url":"https://github.com/omar-azmi.png","language":"TypeScript","readme":"# Github Aid\n\nThis is a Chromium and Firefox extension for viewing github Repository file and folder sizes, in addition to ~~Bulk downloading them~~.\n\n\u003e [!note]\n\u003e TODO: The download feature has yet to be implemented\n\n\n### Downloads\n\n[![Chrome Web Store](https://img.shields.io/chrome-web-store/v/abfbcnoemiciiljhpngefacedfgebdcn?style=for-the-badge\u0026logo=googlechrome\u0026logoColor=green\u0026label=CHROME%20WEB%20STORE\u0026color=green)](https://chromewebstore.google.com/detail/abfbcnoemiciiljhpngefacedfgebdcn)\n[![Firefox Web Store](https://img.shields.io/amo/v/github-aid?style=for-the-badge\u0026logo=firefox\u0026logoColor=red\u0026label=FIREFOX%20WEB%20STORE\u0026color=red)](https://addons.mozilla.org/firefox/addon/github-aid/)\n\n[![Edge Web Store](https://img.shields.io/chrome-web-store/v/abfbcnoemiciiljhpngefacedfgebdcn?style=for-the-badge\u0026logo=microsoftedge\u0026logoColor=blue\u0026logoWidth=33\u0026label=EDGE%20WEB%20STORE\u0026color=blue)](https://microsoftedge.microsoft.com/addons/detail/gmmbjkoglimgjbdembmnllcepiejlped)\n[![GitHub Release](https://img.shields.io/github/v/release/omar-azmi/github_aid_ts?display_name=tag\u0026style=for-the-badge\u0026logo=github\u0026logoColor=white\u0026logoWidth=37\u0026label=GITHUB%20RELEASE\u0026color=black)](https://github.com/omar-azmi/github_aid_ts/releases/latest)\n\n### Obligatory screenshots\n\n\u003cp float=\"left\"\u003e\n  \u003cimg src=\"./public/screenshots/desktop.png\" style=\"width: 75%;\" /\u003e\n  \u003cimg src=\"./public/screenshots/mobile.png\" style=\"width: 22%;\" /\u003e\n  \u003cimg src=\"./public/screenshots/desktop_option.png\" style=\"width: 98%;\" /\u003e\n\u003c/p\u003e\n\n## Why?\n\n- because two of the popular extensions which did previously work, suddenly stopped working since december 2023.\n  - [github-repo-size](https://github.com/harshjv/github-repo-size)\n  - [enhanced-github](https://github.com/softvar/enhanced-github)\n\n- the codebase of both uses NodeJs, which comes along with a lot of boilerplate\n\n- both used `manifest v2`, which will be deprecated in chromium sometime later this year\n\n- neither looks good and usable at the same time in mobile display\n\n-  wanted to do a fun weekend project, try out some GraphQL apis, and be done with the annoyance of constant non-functioning extensions\n\n\n## Problems you may encounter:\n\n1) The buttons are not appearing.\n- Fixes:\n  - Refresh the page. Github is an SPA (single-page application), so there are times when the background script does get reloaded upon navigation from one page to another.\n  - Make sure that you did not disable all buttons in the option page's layout section. If you're having touble, consider clicking on the red \"reset\" button to clear any misconfigurations and return to factory settings.\n  - Make sure that storage permission has been granted to this extension, otherwise the script will fail to load the default configuration, and terminate fatally.\n\n2) One of the buttons flashes in red, and nothing happens.\n- This is because the request query sent to github's server has failed, possibly due to one of the following reasons:\n  - Github's server is overloaded, and it decided to reject your request (quite common).\n\tIn this case, try switching your api method in the options page, and reload your webpage and retry.\n  - You are viewing a private repo, and the access token that you have (or may lack) is not authorized for pulling up data of that private repository.\n  - Your Github Access Token key is invalid, or has expired.\n  - You (and your Token) have reached their per hour request limit, or that you've been sending too many requests too quickly.\n\tThe request data rate for github is: 50 requests per hour for non-authorized tokenless users, and 5000 per hour for authorized users.\n\tMoreover, the REST api in this extension used 2 requests for fetching file and folder sizes.\n  - There might be a bug in the code, or github's api might have changed.\n  In that case, I'd appreciate if you could report the issue on github: https://github.com/omar-azmi/github_aid_ts/issues\n\n\n## How the internals differ\n\nThis project provides a good minimal-boilerplate example of how one can generate a web-extension, while:\n- only using typescript, and no NodeJs boilerplate or 3rd-party typescript dependencies\n- uncoupling all of the following three major parts of the code:\n  - library code (responsible for fetching and parsing data from `api.github.com`), coded under [`/src/lib/`](./src/lib/)\n  - user interface code (responsible for configuring and customizing user options), coded under [`/src/html/`](./src/html/)\n  - background script (responsible for injecting this extension's UI into `github.com`), coded under [`/src/js/`](./src/js/)\n- being able to transpile, bundle, minify, and package all in one go. all thanks to the incredible [esbuild tool](https://github.com/evanw/esbuild), and its Deno compatibility plugin [esbuild_deno_loader](https://github.com/lucacasonato/esbuild_deno_loader)\n\nIf you look at a typical extension development codebase, you'll encounter:\n- a lot of node-specific files\n- ton of external developmental dependencies\n- lots of config files for the dependencies\n- lots of tasks defined by the dependencies that need to be run sequentially for the build process to work\n- having to strictly adhere to a certain directory structure set by the dependencies (or their config files)\n- a nighmare when trying to reference a simple typescript relative import\n  - for instance, you'll need to use the `\".js\"` extension suffix in some occasions. other times the extension part has to be dropped\n  - same directory relative import cannot be done, and has to be done relative to the project's root (where `package.json` lies)\n  - having to adjust to framework specific import prefix characters\n\nall in all, that will lead to a highly coupled codebase, and it'll be incredibly difficult to extract just one part of it for testing, or reusability elsewhere.\n\n\n## The Deno build process\n\n### Building it yourself\n\nto build this yourself, make sure that you first have [`Deno`](https://deno.com/) installed. then, in your terminal (or cmd), run:\n```shell\ndeno task build-all\n```\nand you should get an unpacked distribution of the extension in the [`/dist/github_aid_ts-v*`](./dist/github_aid_ts-v0.1.0/) directory.\n\nother available commands (tasks):\n| command | general description |\n|---------|---------------------|\n| `deno task clean`              | delete the [`/dist/`](./dist/) folder |\n| `deno task clean --js-only`    | delete only the javascript files under the [`/dist/`](./dist/) folder |\n| `deno task build-1`            | copy all non-typescript source files to the [`/dist/`](./dist/) folder |\n| `deno task build-2`            | bundle endpoint typescript files inside of [`/src/`](./src/),\u003cbr\u003eand mirror the resulting javascript files to the [`/dist/`](./dist/) folder |\n| add an additional `--log-only` \u003cbr\u003e flag to any of the commands above | execute the task without actually writing or deleting any files |\n| `deno task build-all-log-only` | runs `deno task build-1 --log-only`\u003cbr\u003eand then `deno task build-2 --log-only` |\n\n\n### Build diagram\n```mermaid\n---\ntitle: \"Build process\"\n---\nflowchart LR\n\tStartNode([\"deno task build-all\"])\n\tStartNode --\u003e|\"1\"| 877701([\"deno task build-1\"])\n\tStartNode --\u003e|\"2\"| 247711([\"deno task build-2\"])\n\t247711 --\u003e 557486\n\t877701 --\u003e 977090\n\n\tsubgraph 557486[\"./build_2.ts\"]\n\t\t418522[\"\n\t\t\tcollect each '.ts' file under\n\t\t\tthe script endpoints:\n\t\t\t- '/src/html/*.ts'\n\t\t\t- '/src/js/*.ts'\n\t\t\"] --\u003e 287901\n\n\t\t287901 --\u003e 176317[\"\n\t\t\twrite the resulting js-code files from\n\t\t\tmemory to the '/dist/' directory, mirroring\n\t\t\ttheir original (entry-point) location.\n\t\t\tunfortunately, the non-endpoint\n\t\t\tsplit-code js files end up directly\n\t\t\tunder '/dist/', which may look ugly\n\t\t\tfrom a distribution perspective\n\t\t\"]\n\n\t\tsubgraph 287901[\"./build_tools.ts --\u0026gt; doubleCompileFiles()\"]\n\t\tdirection LR\n\t\t\t728262[\"\n\t\t\t\trun esbuild bundling with:\n\t\t\t\t- deno plugin, for resolving import paths\n\t\t\t\t- minification + tree-shaking, to\n\t\t\t\tremove unimported exported objects\n\t\t\t\t- code-splitting, to preserve common\n\t\t\t\timports across different endpoints\n\t\t\t\t- writing disabled, so that the bundled\n\t\t\t\tfile results are stored in-memory\n\t\t\t\"] --\u003e 558940[\"\n\t\t\t\tnow that the standalone bundled code files\n\t\t\t\tare free of external dependencies,\n\t\t\t\ton each individual output js code file:\n\t\t\t\t- run esbuild transform with minification,\n\t\t\t\tto remove dead-code within the same file\n\t\t\t\"]\n\t\tend\n\tend\n\t\n\t557486 --\u003e EndNode([\"end\"])\n\n\tsubgraph 977090[\"./build_1.ts\"]\n\t\t708084[\"\n\t\t\tcopy all non-typescript\n\t\t\tfiles from '/src/' to '/dist/'\n\t\t\"]\n\tend\n```\n\n\n### General import map\n```mermaid\n---\ntitle: \"Import graph\"\n---\nflowchart TD\n\t591514((\"deps.ts\")) --\u003e 530727(((\"option.ts\\n(endpoint)\")))\n\t423910((\"typedefs.ts\")) --\u003e 709575(((\"content_script.ts\\n(endpoint)\")))\n\t405595((\"modify_ui.ts\")) --\u003e 709575\n\t591514 --\u003e 709575\n\tsubgraph 325333[\"/src/lib/\"]\n\t\t591514 --\u003e 650261((\"gh_rest_api.ts\"))\n\t\t423910 --\u003e 650261\n\t\t591514 --\u003e 517060((\"gh_graphql_api.ts\"))\n\t\t423910 --\u003e 517060\n\t\t650261 --\u003e 405595\n\t\t517060 --\u003e 405595\n\t\t591514 --\u003e 405595\n\t\t423910 --\u003e 405595\n\tend\n\tsubgraph 201358[\"/src/js/\"]\n\t\t709575 --\u003e|\"dynamic import as\\nchromeURL('/js/content_script.js')\"| 798822(((\"content_script\\n_extension_adapter.ts\\n(endpoint)\")))\n\tend\n\tsubgraph 843058[\"/src/html/\"]\n\t\t530727 --\u003e|\"imported as\\n'./option.js'\"| 635635[[\"option.html\"]]\n\tend\n```\n\n## Rationale for Code-Splitting, and its caveates\n\n### What is Code-Splitting, and how does it differ from regular bundling?\nsuppose you have a total of 5 source code files (`A`, `B`, `C`, `D`, and `E`), with the following dependency graph:\n```mermaid\n---\ntitle: \"source dependency graph\"\n---\nflowchart TD\n\t222161((\"C\")) --\u003e 339264(((\"A\")))\n\t842277((\"D\")) --\u003e 339264\n\t842277 --\u003e 633740(((\"B\")))\n\t394730((\"E\")) --\u003e 633740\n\t842277 --\u003e 222161\n\t842277 --\u003e 394730\n\tsubgraph 977297[\"bundling endpoints\"]\n\t\t633740\n\t\t339264\n\tend\n```\n\nif you were to bundle the endpoints `A` and `B` without code-splitting, you'd get two independently operated pieces of bundled code:\n```mermaid\n---\ntitle: \"bundled endpoints\"\n---\nflowchart TD\n\tsubgraph 280910[\"bundle B\"]\n\t\t735571[\"Union(\\nIntersect(D, B.imports),\\nIntersect(D, E.imports)\\n)\"] --\u003e 985393[\"Intersect(E, B.imports)\"]\n\t\t735571 --\u003e 633740(((\"B\")))\n\t\t985393 --\u003e 633740\n\tend\n\tsubgraph 977297[\"bundle A\"]\n\t\t384214[\"Union(\\nIntersect(D, A.imports),\\nIntersect(D, C.imports)\\n)\"] --\u003e 592871[\"Intersect(C, A.imports)\"]\n\t\t592871 --\u003e 339264(((\"A\")))\n\t\t384214 --\u003e 339264\n\tend\n```\n\nthis is where a potential problem can occur: if there was a runtime-unique shared piece of code inside of `D`, then inconsistencies will arise when the two pieces of bundled codes try to interoperate. \u003cbr\u003e\na few examples of runtime-unique shared pieces of code would be:\n- a javascript `Symbol`. both `bundleA` and `bundleB` will define the symbol separately, thereby becoming incompatible\n- mutative side-effects inside of `D`. if, for instance, `D.injectButton` was a function that would inject a button into your html and then set a `D.button_injected = true` flag inside so that duplicates of buttons are not made in future calls.\nIf both `bundleA` and `bundleB` call `D.injectButton` several times, then we'd get two buttons instead of one, because now, the flags are defined independently. \u003cbr\u003e\nhad we imported from `D` instead of bundling, we would've expected to see a single injected button.\n\nif we bundle with code-splitting enabled, we would get the following output dependency graph, and our runtime-unique shared pieces of code will interoperate correctly:\n```mermaid\n---\ntitle: \"Diagram Title\"\n---\nflowchart\n\t842277((\"split D = Union(\n\t\tIntersect(D, A.imports),\n\t\tIntersect(D, C.imports),\n\t\tIntersect(D, B.imports),\n\t\tIntersect(D, E.imports)\n\t)\")) --\u003e 912727[\"Intersect(C, A.imports)\"]\n\t842277 --\u003e 339264(((\"A\")))\n\t842277 --\u003e 633740(((\"B\")))\n\t842277 --\u003e 517056[\"Intersect(E, B.imports)\"]\n\tsubgraph 650622[\"split-bundle A\"]\n\t\t912727 --\u003e 339264\n\tend\n\tsubgraph 367445[\"split-bundle B\"]\n\t\t517056 --\u003e 633740\n\tend\n```\n\nbundling with code-splitting also comes with the advantage of smaller total output file size.\n\n### Caveates of Code-Splitting specific to browser-extensions development\n\nfirst, you need to know that the extension is supposed to be a single javascript file that is executed after your designated injection-target website has loaded (this being \"github.com\" for this extension). \u003cbr\u003e\nthe javascript file to be ran is specified in the manifest file ([`/src/manifest.json`](./src/manifest.json)), under `manifest[\"content_scripts\"][0][\"js\"][0]`. \u003cbr\u003e\nthe global context available to this javascript of yours is simply the same as the one available to the target webpage (i.e. you are in the `window` environment), in addition to having a limited number of browser-extension features, such as: `chrome.storage` and `chrome.runtime` (or in the case of firefox: `browser.storage` and `browser.runtime`). \u003cbr\u003e\nmoreover, by web-security design, you cannot do static imports in any content_script (so `import {x, y, z} from \"./abc.js\"` is disallowed). \u003cbr\u003e\nhowever, you *can* dynamically import using `const {x, y, z} = await import(\"./abc.js\")`, but it **will** require that the target webpage has access to that imported file, which it currently doesn't, because the file sits locally in your browser. \u003cbr\u003e\nhence, you will need to give the target website *access* to your imported javascript files, which is done by specifying the `manifest[\"web_accessible_resources\"]` entry in the manifest file. \u003cbr\u003e\nhere's how it should look: \u003cbr\u003e\n```ts\n// manifest.json\n{\n\t// ...\n\t\"web_accessible_resources\": [\n\t\t{\n\t\t\t\"resources\": [\"*.js\"],\n\t\t\t\"matches\": [\"\u003call_urls\u003e\"]\n\t\t}\n\t],\n\t// ...\n}\n```\nwith that, you've solved the problem that you initiated, and made your extension less secure along the way. \u003cbr\u003e\nalternatively you can choose to bundle without code-splitting, and avoid all the fuss. but where's the fun in that? \u003cbr\u003e\nsee this [stackexchange answer](https://stackoverflow.com/a/53033388), where I found this information from. \u003cbr\u003e\nalso see [`/src/js/content_script_extension_adapter.ts`](./src/js/content_script_extension_adapter.ts#L28) for the dynamic imports being done in this extension.\n\n\n## License\n\nsee [`license.md`](./license.md). but for a quick summary:\n- you may not execute a crawling script onto this project's contents\n- you may not feed any part of the project's contents to an AI training system, or use it as an AI chat prompt\n- you may not copy-paste this project and make it your own\n- you may not hold me liable for any damages this code might result in\n\n## How to get a Github Access Token\n\n- First of all, you'll need to be logged into your github account (Duh!).\n- Navigate to this github page: [Generate new token (classic)](https://github.com/settings/tokens/new) (https://github.com/settings/tokens/new).\n- Set an `Expiration` date for your token, (you'll probably want to choose `No expiration`).\n- In the `Select scopes` section, under the `repo` checkbox:\n  - enable only the `public_repo` checkbox if you will NOT be viewing your private repository's stats.\n  - enable the whole `repo` group checkbox otherwise.\n- Scroll to the bottom and click on the `Generate token` button.\n- You will now be presented with the access token. MAKE SURE TO COPY AND SAVE IT SECURELY RIGHT NOW! This is a one time preview of you key, and it will disappear forever after you've closed the dialog.\n- Paste the token into this browser extension's options page.\n\nFor a visual guide, see one of:\n- https://www.geeksforgeeks.org/how-to-generate-personal-access-token-in-github/\n- https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens\n\nBut remember NOT to check any scope boxes besides the `repo` one.\nHaving anything else checked is dangerous if your access key gets leaked,\nand someone decides to maliciously delete your projects, or hold them for ransom.\n(as if that has ever happened)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomar-azmi%2Fgithub_aid_ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fomar-azmi%2Fgithub_aid_ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomar-azmi%2Fgithub_aid_ts/lists"}