Monorepo for userscripts. Each site gets its own folder with clean TypeScript source. On every push to main, GitHub Actions lints, typechecks, and builds — then commits the compiled .user.js files back so they're always available via raw.githubusercontent.com.
scripts/
<site-name>/
src/
meta.ts ← @userscript header block (comment only)
index.ts ← entry point
utils.ts ← helpers, split as needed
dist/
<site-name>.user.js ← compiled output (committed by CI)
tampermonkey-loader.user.js ← install this in Tampermonkey once
- Copy the
example-sitefolder:cp -r scripts/example-site scripts/my-new-site
- Update
meta.tswith the correct@matchURLs and script name. - Write your code in
src/. Split into as many files as you like — esbuild bundles it all. - Update
tampermonkey-loader.user.jswith the new raw URL. - Install the loader in Tampermonkey. Done.
npm install
npm run build # build all scripts once
npm run build:watch # rebuild on save
npm run lint # ESLint
npm run typecheck # tsc --noEmitThe dist/ files are committed locally too, so you can point the loader at your local dev server or just let CI handle it.
The tampermonkey-loader.user.js file is the only thing you install in Tampermonkey. It fetches the compiled dist/*.user.js from this repo's raw GitHub URL and eval()s it at runtime. This means:
- You never have to touch Tampermonkey again after the initial install
- Iterate freely in
src/, commit, CI builds it, the loader picks it up automatically - Cache-busting via
?_=<timestamp>prevents stale CDN responses during active dev
The workflow (.github/workflows/build.yml):
- Runs on push to
mainwhensrc/files change - Lints and typechecks
- Builds all scripts
- Commits any changed
dist/files back with[skip ci]to prevent loops