feat: Firefox MV3 add-on via extension-shared workspace package#32
Merged
Conversation
Pre-cursor to extracting the cross-browser extension code out of extension-chrome so that extension-firefox (issue #23) can consume the same source without duplication. This commit creates only the package shell + a smoke test; subsequent tasks move code into it.
Moves all of packages/extension-chrome/src (background, popup, options, lib/) and packages/extension-chrome/test (chrome stub + 96 unit tests) into the new @gitmarks/extension-shared workspace package. extension-chrome becomes a thin shell: manifest + vite config + four entry files that import from the shared package. Why: extension-firefox (issue #23) needs the same source. Without this refactor we would duplicate ~2k LoC across browsers. No behavior change. All 96 unit tests still pass; 4 Playwright e2e pass, 2 skipped (unchanged). extension-chrome's dist/manifest.json is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sion-polyfill Source files now use browser.* uniformly: - background.ts, popup.ts, options.ts each import 'browser from "webextension-polyfill"' at the top, which side-effect-registers the polyfill. - All value-position chrome.X.Y(...) calls switched to browser.X.Y(...). Type-position references (chrome.bookmarks.BookmarkTreeNode etc.) kept as-is via @types/chrome. - lib/apply-remote.ts, id-mapping.ts, listeners.ts, machine-id.ts, reconcile.ts, settings.ts each import browser and use browser.* for all API calls. - test/setup.ts stubs the same object under both globalThis.chrome AND globalThis.browser so the existing test assertions continue to work. - vitest.config.ts gains a resolve.alias that redirects "webextension-polyfill" to test/webextension-polyfill-stub.ts (a Proxy over globalThis.browser) so the real polyfill — which throws in Node/jsdom — is never loaded during unit tests. - popup.ts getActiveTab() return type updated from chrome.tabs.Tab to browser.Tabs.Tab (polyfill type) to satisfy the type-checker. No behavior change in Chrome (the polyfill aliases chrome.* under browser.*). 96 unit tests + 4 e2e passing + 2 skipped — unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Plain Vite multi-entry build (crxjs is Chrome-only). Manifest is literal JSON copied into dist/ post-build via scripts/copy-manifest.mjs. Targets Firefox 121+ for MV3 service-worker parity. browser_specific_settings declares the gecko id and strict_min_version. Source files are minimal shells that re-export from @gitmarks/extension-shared — all the actual code (popup, options, background, lib/) is shared with extension-chrome via that workspace package. Closes #23. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e doc sync - packages/extension-firefox/README.md: full first-run + manual smoke test guide, architecture diagram, known-limitations cross-reference - Root README: adds extension-shared and extension-firefox to the packages table, marks Firefox done in the roadmap, updates the architecture diagram - CLAUDE.md: documents the new 4-package layout (core + shared + 2 shells) and marks the Firefox roadmap entry done - packages/extension-chrome/README.md: clarifies it's now a thin shell paired with extension-firefox - packages/extension-shared/test/webextension-polyfill-stub.ts: fix TS index-signature error (Proxy target type) Closes #23 — the Firefox MV3 add-on is functional. Manual smoke test checklist in the new README guides verification in a real browser.
This was referenced May 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ships
@gitmarks/extension-firefox— a Firefox MV3 add-on that consumes the same source as the Chrome extension. To do that without code duplication, this PR also extracts the bulk ofextension-chromeinto a new@gitmarks/extension-sharedworkspace package;extension-chromebecomes a thin shell.Four commits (per the plan-driven workflow):
9b08636)94acf29) — moves allsrc/+test/from extension-chrome into extension-shared; adds anexportsmap; recreates Chrome shell entries.60579ba) — 27 value-position replacements; type refs stay on@types/chrome; test stub now exposes bothchromeandbrowserglobals.89a3472) — manifest.json (literal, no crxjs), plain Vite multi-entry build, scripts/copy-manifest.mjs, thin shell entries.Architecture (final)
```
packages/
├── core/ # unchanged — schemas, GitHub client, mutations
├── extension-shared/ # NEW — all cross-browser source (96 unit tests)
├── extension-chrome/ # thin Chrome shell (manifest + crxjs + e2e)
└── extension-firefox/ # NEW thin Firefox shell (manifest + plain Vite)
```
`webextension-polyfill` lets the shared source use `browser.` uniformly. Chrome's `chrome.` is auto-aliased; Firefox exposes `browser.*` natively.
Out of scope (deferred)
Test plan
Closes #23