Something went wrong. Try again.
Browser extension: detect and subscribe to standard.site publications on ATProto
Something went wrong. Try again.
3.7 kB · 89 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990// The dev channel: a build of the extension you can install beside the store// one and still tell apart. The two installs already have different IDs (the// unpacked build keeps the manifest `key`, the store assigns its own), so// Chrome is happy to run both; what is missing is a way to see which toolbar// tile and which row in chrome://extensions is which.//// Three changes, all applied to dist/ after the build://// - the icon PNGs are replaced with the inverted ones from brand/icons-dev/// (rendered by scripts/render-icons.mjs). Overwriting the files rather than// adding new manifest entries means the popup header picture and anything// else pointing at /icons/... follows along.// - the manifest name gets a suffix, which is what chrome://extensions, the// extensions menu, and the default action tooltip show.// - `version_name` records the commit the build came from, which the popup// footer shows in place of the bare version.//// Nothing here touches public/, so a production build cannot pick it up.// Turn it on with SUBSTANDARD_CHANNEL=dev (npm run dev sets it by default);// vite.config.ts calls applyDevChannel from a writeBundle hook.
import { execFileSync } from 'node:child_process'import { copyFileSync, readFileSync, writeFileSync } from 'node:fs'import { join, resolve } from 'node:path'
/** Icon sizes the manifest declares, and that brand/icons-dev/ mirrors. */export const DEV_ICON_SIZES = [16, 32, 48, 128]
export const DEV_NAME_SUFFIX = ' (dev)'
/** * The dev channel's manifest. The `key` stays, so the extension ID stays, so * the OAuth redirect URI registered for the dev ID keeps working. Applying it * twice is the same as applying it once. * * `build` (a short sha, or undefined outside a git checkout) goes in * `version_name`, not in `version`: Chrome only accepts up to four * dot-separated integers there and refuses to load anything else. Both are * built from `manifest.version`, so a rebuild at a new commit replaces the * stamp instead of appending to it. */export function devManifest(manifest, build) { const name = manifest.name.endsWith(DEV_NAME_SUFFIX) ? manifest.name : manifest.name + DEV_NAME_SUFFIX const dev = { ...manifest, name } if (build) dev.version_name = `${manifest.version}+${build}` return dev}
/** * The commit this build came from, `abc1234` or `abc1234-dirty`, or undefined * if git cannot say — a tarball, or a checkout with no commits yet. The dev * channel is a local build, so uncommitted work is the normal case and the * sha alone would claim more than the build can back up. */export function buildStamp(root) { // stderr is dropped: "not a git repository" is an answer here, not a fault // worth printing on every rebuild. const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], }).trim() try { const sha = git('rev-parse', '--short', 'HEAD') return git('status', '--porcelain') ? `${sha}-dirty` : sha } catch { return undefined }}
/** * Rewrite a built dist/ in place for the dev channel. Returns the new name * and version_name, for the build log. */export function applyDevChannel(root, dist) { for (const size of DEV_ICON_SIZES) { copyFileSync( resolve(root, 'brand', 'icons-dev', `icon${size}.png`), join(dist, 'icons', `icon${size}.png`), ) } const file = join(dist, 'manifest.json') const manifest = devManifest(JSON.parse(readFileSync(file, 'utf8')), buildStamp(root)) writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`) return manifest}