Compare commits

...

523 Commits

Author SHA1 Message Date
GitHub Actions ba3d8838e3 chore(release): @wxt-dev/module-svelte v2.0.3
vhs / vhs (push) Waiting to run
2025-01-28 21:17:33 +00:00
Eli e9b6f5f97c fix: Upgrade vite-plugin-svelte to support Vite 6 (#1375)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-01-28 15:09:01 -06:00
Aaron 5b6c82d38f chore: Fix formatting 2025-01-28 14:43:38 -06:00
Aaron 5b41b70abe docs: Add SECURITY.md 2025-01-28 14:41:56 -06:00
Okinea Dev 93f624610f chore: add pull request template (#1372)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-01-28 08:03:26 -06:00
Okinea Dev 73bdd49471 chore: simplify FUNDING.yml (#1371) 2025-01-28 07:32:59 -06:00
GitHub Actions 635f3f0898 chore(release): wxt v0.19.26
vhs / vhs (push) Waiting to run
2025-01-28 06:46:49 +00:00
Deniz Uğur a53ee6d590 fix(context): Deduplicate wxt:content-script-started (#1364)
Co-authored-by: DenizUgur <DenizUgur@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-01-28 00:41:12 -06:00
GitHub Actions 665b9194cf chore(release): @wxt-dev/storage v1.1.0
vhs / vhs (push) Waiting to run
2025-01-28 06:40:20 +00:00
Gabriel Roldán 4690418c94 Fix: Remove duplicated css rule from vue template (#1369) 2025-01-27 16:00:09 -06:00
Chengxi 20eea2a7d1 feat: Add storage.clear (#1368)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-01-27 07:22:19 -06:00
rxliuli ac7b6760f2 docs: Add missing bracket in example (#1367) 2025-01-25 01:14:48 -06:00
GitHub Actions a593cfbbb6 chore(release): wxt v0.19.25
vhs / vhs (push) Waiting to run
2025-01-22 05:08:46 +00:00
Aaron a205f2a0a8 chore: Update lockfile 2025-01-21 23:03:09 -06:00
Aaron 8e2badc92b fix: Drop support for vite 6.0.9+ until websocket fix is finished 2025-01-21 23:02:22 -06:00
redwoodlid 7c18d9c0b8 docs: Add react language ID to README (#1347) 2025-01-15 10:25:40 -06:00
Aaron 55a1f39450 Update changelog 2025-01-15 10:23:36 -06:00
GitHub Actions d708eca78e chore(release): wxt v0.19.24
vhs / vhs (push) Waiting to run
2025-01-12 22:48:50 +00:00
1natsu 5d7e2516b6 fix: autoMount not working if anchor is already present (#1350) 2025-01-12 16:43:07 -06:00
Eric fb6a29872b docs: Add "HTML to Markdown" to the homepage (#1345) 2025-01-10 11:28:27 -06:00
rxliuli 187206062e docs: Update content script UI examples to use Svelte v5 (#1342) 2025-01-07 23:51:46 -06:00
Aaron 2615e29e52 docs: Add section to FAQ about teleports/portals 2025-01-07 23:37:53 -06:00
Aaron 8ca176d37e docs: Update i18n.md 2025-01-06 17:40:57 -06:00
Aaron cb2bf43f30 docs: Update content-scripts.md 2025-01-06 17:36:43 -06:00
Aaron 1719d17cff docs: Fix typo 2025-01-06 17:28:31 -06:00
Aaron 32afb8d97c docs: Improve manifest docs, mention entrypoint-specific options 2025-01-06 17:28:09 -06:00
Aaron 72b8a29d76 docs: Improve entrypoint docs 2025-01-06 17:09:20 -06:00
Aaron af0a32d478 docs: Add missing tabs in project structure 2025-01-06 16:59:23 -06:00
Nikolay Ponomarev 060e85f90e docs: Add missing web_accessible_resources to injectScript example (#1337) 2025-01-06 16:23:41 -06:00
Aaron 654a54ac79 docs: Update link 2025-01-05 10:14:49 -06:00
ntnyq ef24358942 docs: Fix unocss readme (#1329)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-01-04 17:01:43 -06:00
Aaron e5e67e39bd docs: Add paragraph on browser binary auto-discovery 2025-01-01 17:23:04 -06:00
Ahmed Dinar 228304bef9 docs: Added "Blync" to the homepage (#1319) 2025-01-01 08:56:07 -06:00
Paulo Pinto 78f97f32a2 fix(web-ext): Correctly pass browserConsole option to open devtools in Firefox (#1308) 2024-12-29 12:35:38 -06:00
Abhigyan Trips 459662a6c1 docs: Added "ZenGram" to the homepage (#1313) 2024-12-29 12:26:14 -06:00
Aaron b150a52471 fix(init): Remove "experimental" from bun package manager 2024-12-25 12:52:13 -06:00
Aaron 028c601515 chore: Fix typo in CLI docs 2024-12-25 12:51:41 -06:00
Aaron af382ef065 Fix changelog 2024-12-25 09:47:41 -06:00
GitHub Actions 91ef7f2ab5 chore(release): wxt v0.19.23
vhs / vhs (push) Waiting to run
2024-12-25 15:44:50 +00:00
Aaron 0bf7279c8f fix: Properly close readline instance on close (#1278) 2024-12-25 09:40:39 -06:00
Aaron 62525d33c6 fix: Allow runtime registered content scripts to not have matches (#1306) 2024-12-25 09:39:37 -06:00
Aaron 88ff40e301 docs: Fix typo in FAQ 2024-12-23 09:09:08 -06:00
Aaron 1a72a0cdc6 docs: Add paragraph on adding permissions 2024-12-22 09:14:31 -06:00
Amit Yadav 2b07b4bc12 docs: Fix invalid CSS color (#1299) 2024-12-22 09:09:49 -06:00
Abhigyan Trips 06c09e7c87 feat(hooks): Move entrypoints:resolved before debug logs and add entrypoints:found (#1292)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-12-20 21:09:31 -06:00
Amit Yadav 30b96ac87c docs: Added "Strong Password Generator" to the homepage (#1290) 2024-12-19 00:20:09 -06:00
Amit Yadav 2e9b5cffe8 docs: Added "Show IP Extension" to the homepage (#1289) 2024-12-18 17:13:11 -06:00
Aaron 3e1641a6ec docs: Add more comments on entrypoint options 2024-12-17 14:05:37 -06:00
GitHub Actions d64ca52bb0 chore(release): @wxt-dev/module-react v1.1.3
vhs / vhs (push) Waiting to run
2024-12-17 19:07:47 +00:00
Aaron 8c645fa1ce fix: Use react 19 in @wxt-dev/module-react and templates/react (#1285) 2024-12-17 12:57:50 -06:00
Nishu 79c9c89923 chore: Upgrade React to v19 in demo (#1280) 2024-12-17 12:30:48 -06:00
Aaron 11d93fe724 docs: Add pkg.pr.new docs to CONTRIBUTING.md 2024-12-17 12:05:47 -06:00
Aaron 039c561a80 chore: Simplify keyboard shortcuts (#1284) 2024-12-17 11:55:50 -06:00
GitHub Actions 64a704fc72 chore(release): @wxt-dev/module-svelte v2.0.2
vhs / vhs (push) Waiting to run
2024-12-17 16:41:07 +00:00
Aaron 4fbf137d15 fix: Svelte production errors and warnings on resolve conditions (#1283) 2024-12-17 10:35:39 -06:00
Luca Dalli ff065ab88b docs: Added "Cursorful" to the homepage (#1282) 2024-12-17 10:29:43 -06:00
Aaron 7fdf3fb968 docs: Add missing brackets to example 2024-12-16 16:12:00 -06:00
Aaron 7ebd451409 docs: Document how to use WASM 2024-12-16 16:07:16 -06:00
Aaron a7a48a8a75 docs: Add sponsors to wxt.dev homepage 2024-12-16 14:38:30 -06:00
Aaron 27298b7f22 docs: Sync READMEs 2024-12-16 14:35:16 -06:00
Aaron 565ce93a9a docs: Add sponsors section to README 2024-12-16 14:32:46 -06:00
Aaron 0bc6088b6a chore: Fix formatting 2024-12-16 13:48:41 -06:00
Aaron 2fb99e8fbb docs: Add netlify badge to homepage 2024-12-16 13:48:17 -06:00
Aaron cd2050c534 chore: Add code of conduct 2024-12-16 11:25:19 -06:00
aklinker1 5b557d0224 docs: Update wxt init GIF 2024-12-13 00:38:13 +00:00
Aaron 0137d79ba0 docs: Update colors to improve contrast ratio (#1275)
Co-authored-by: aklinker1 <aklinker1@users.noreply.github.com>
2024-12-12 18:36:24 -06:00
GitHub Actions e05fbf4fb5 chore(release): wxt v0.19.22
vhs / vhs (push) Waiting to run
2024-12-10 23:11:47 +00:00
Aaron 72e6ca5063 fix: Exclude entire import.meta.env object from content script output (#1267) 2024-12-10 17:06:25 -06:00
GitHub Actions af9e842fe0 chore(release): wxt v0.19.21
vhs / vhs (push) Waiting to run
2024-12-10 18:05:17 +00:00
Aaron 230962686f fix: Exclude skipped entrypoints from manifest (#1265) 2024-12-10 11:59:44 -06:00
GitHub Actions 413cd9354f chore(release): wxt v0.19.20
vhs / vhs (push) Waiting to run
2024-12-09 20:12:43 +00:00
不游 64a42de31f docs: fix colored-diff in es-modules.md (#1264) 2024-12-09 07:42:54 -06:00
Aaron f7f03e2669 fix: Import entrypoint improvements (#1207) 2024-12-08 12:10:05 -06:00
Aaron 1dc7c34bfd fix: Only show reopen browser shortcut when it can be done (#1263) 2024-12-08 08:55:01 -06:00
Aaron 0fd5c3846c docs: Fix gramar 2024-12-07 08:57:17 -06:00
Aaron e42e92f9df docs: Update "Using WXT" instructions 2024-12-07 08:56:13 -06:00
Kepi e45c91f941 docs: Add "EyeDropper" to homepage (#1260)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2024-12-07 08:47:33 -06:00
1natsu 8f71d80f94 feat: autoMount content script UIs (#1210)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-12-07 08:21:52 -06:00
Aaron Klinker 78e896e5fb docs: Remove duplicate section from upgrade docs 2024-12-07 01:12:23 -06:00
GitHub Actions a643852387 chore(release): wxt v0.19.19
vhs / vhs (push) Waiting to run
2024-12-06 00:28:20 +00:00
Nishu e221252325 fix: Exclude skipped entrypoints from Firefox sources zip (#1238)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2024-12-05 18:23:00 -06:00
Aaron 88f126e658 fix: Ensure content scripts are registered immediately in dev mode (#1253) 2024-12-05 18:16:38 -06:00
Aaron 17af05acc8 fix: Prevent changing dev server port when reloading config (#1241) 2024-12-05 11:48:49 -06:00
Jeremie Corpinot fd436a0d63 docs: Add "aesthetic Notion, styled" to homepage (#1247) 2024-12-04 16:00:23 -06:00
Nishu 0d796b63c2 feat: Keyboard shortcut to reopen the browser without restarting the dev command (#1211)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
Co-authored-by: Florian Metz <me@timeraa.dev>
2024-12-03 21:42:44 -06:00
GitHub Actions cb143d2bbc chore(release): wxt v0.19.18
vhs / vhs (push) Waiting to run
2024-12-03 23:00:59 +00:00
Aaron f3e7758521 types: Add overloads to ContentScriptContext#addEventListener for different targets (#1245) 2024-12-03 16:55:33 -06:00
Aaron 23ef101954 chore: Refactor findEntrypoints to return all entrypoints with skipped set properly (#1244) 2024-12-03 12:36:11 -06:00
spookyuser c2f5efbde4 docs: Update injectScript docs to match example from examples repo (#1240)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-12-03 12:23:33 -06:00
Takuya HARA 4ce178c592 docs: Changed "Chrome Extension" to "web extension" (#1229)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-12-03 11:07:46 -06:00
dependabot[bot] 7aa30afa7f chore(deps): bump codecov/codecov-action from 4 to 5 (#1237)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-01 16:13:47 -06:00
GitHub Actions 90aaed202c chore(release): @wxt-dev/module-svelte v2.0.1
vhs / vhs (push) Waiting to run
2024-12-01 15:33:52 +00:00
Aaron c92ed13d66 fix: Modify dev vite.resolve.conditions to support Vite 6 + Svelte 5 (#1230) 2024-12-01 09:28:30 -06:00
Jaguar Zhou 0e4953fbfc fix: correct dev.reloadCommand restriction to consider only commands with suggested keys (#1226) 2024-11-29 12:07:09 -06:00
roechsli 799d687061 docs: Add "QA Compass" to homepage (#1223) 2024-11-28 10:34:20 -06:00
Aaron 866eb0694c Cleanup changelog 2024-11-28 08:48:40 -06:00
GitHub Actions e5723aab03 chore(release): wxt v0.19.17
vhs / vhs (push) Waiting to run
2024-11-28 14:42:49 +00:00
Jaguar Zhou f51f5dd19f doc: remove dead links generated by typedoc (#1222) 2024-11-28 08:33:11 -06:00
Aaron aae5f0789d fix: Make content script matches optional (#1220) 2024-11-28 00:00:18 -06:00
1natsu 586fe3950c fix: Correctly remove child elements with integrated UI remove (#1219) 2024-11-27 22:39:25 -06:00
Aaron 241c907c65 fix: Automatically convert MV3 content_security_policy to MV2 (#1168)
Co-authored-by: windmillcode0 <michaelodumosu57@gmail.com>
Co-authored-by: windmillcode0 <shieldmousetower734@gmail.com>
2024-11-27 10:14:32 -06:00
Aaron aab4244d84 fix: Update vite dependency range to support v6 (#1215) 2024-11-26 14:26:11 -06:00
Aaron 767a3a987c chore(vue-template): Fix incompatible typescript and vue-tsc versions (#1208) 2024-11-25 15:25:42 -06:00
Aaron a2924d872a fix: Revert "Validate entrypoints after hooks have modified them (54baf21)"
This reverts commit 54baf21490.
2024-11-25 15:08:55 -06:00
Aaron 54baf21490 fix: Validate entrypoints after hooks have modified them 2024-11-25 09:29:46 -06:00
Nishu 5a3790a7d3 fix: ESLint config being generated when ESLint is not installed. (#1198)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-11-22 08:54:05 -06:00
Aaron 236e099b81 docs: Update windows config for persisting browser profile 2024-11-22 08:53:16 -06:00
GitHub Actions 9b54bc4704 chore(release): @wxt-dev/storage v1.0.1
vhs / vhs (push) Waiting to run
2024-11-22 14:41:52 +00:00
Aaron ffb99af719 fix: use browser for mv2 storage (#1200) 2024-11-22 08:36:29 -06:00
Jaguar Zhou 90bb145d91 chore(vue-template): remove references to Vetur and TypeScript Vue Plugin (Volar) (#1197) 2024-11-22 08:21:06 -06:00
Mira Dobrovolskaya 190c87d120 docs: fix typo in entrypoints options example (#1194) 2024-11-21 11:15:10 -06:00
Tran Ngoc Tan 4c511d5728 docs: Add "YouTube Hider" to homepage (#1193) 2024-11-21 03:07:45 +01:00
Florian Metz b8ecbc5a54 docs: fix analyze typo in type (#1187) 2024-11-20 08:46:42 -06:00
Nithur 8c3d756187 docs: Add "Wandpen" to homepage (#1191)
Co-authored-by: Florian Metz <me@timeraa.dev>
2024-11-20 10:18:15 +01:00
Patato cfdc6b3e6b chore: Update Svelte template to Svelte 5 (#1184)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-11-17 14:58:09 -06:00
Aaron 226e6c9fbe docs: Add FAQ around configuring browser startup 2024-11-16 11:33:58 -06:00
Aaron db41f27072 feat: New server:created, server:started, and server:closed hooks (#1179) 2024-11-16 09:29:32 -06:00
Aaron db025cd3a2 fix: Re-initialize WXT modules correctly during development (#1176) 2024-11-16 08:56:08 -06:00
GitHub Actions 57229c69c1 chore(release): @wxt-dev/module-solid v1.1.3
vhs / vhs (push) Waiting to run
2024-11-15 15:46:51 +00:00
GitHub Actions 8f3a90eeb2 chore(release): @wxt-dev/module-react v1.1.2
vhs / vhs (push) Waiting to run
2024-11-15 15:38:36 +00:00
GitHub Actions a360965d92 chore(release): @wxt-dev/module-vue v1.0.2
vhs / vhs (push) Waiting to run
2024-11-15 15:28:53 +00:00
Aaron eb1ace6d20 fix: Use config:resolved hook to update config instead of ready (#1178) 2024-11-15 09:08:14 -06:00
GitHub Actions 497c902283 chore(release): wxt v0.19.16
vhs / vhs (push) Waiting to run
2024-11-15 14:57:29 +00:00
Aaron 4d4453b830 feat(hooks): Add new config:resolved hook (#1177) 2024-11-15 08:52:22 -06:00
GitHub Actions ef53580ed1 chore(release): wxt v0.19.15
vhs / vhs (push) Waiting to run
2024-11-14 16:41:09 +00:00
Aaron 52e5387d47 fix: Add extension ID to event used to invalidate ContentScriptContext (#1175) 2024-11-14 10:30:51 -06:00
Aaron f5b7f7e0e5 docs: Cleanup changelog 2024-11-14 10:11:39 -06:00
Aaron 592bdbe4d9 chore: Speed up sync-releases workflow by not building packages 2024-11-14 10:09:40 -06:00
Aaron 6fc227bad6 chore: Init changelog for storage package 2024-11-14 10:03:55 -06:00
Aaron d74f6b0735 feat: Extract wxt/storage to its own package, @wxt-dev/storage (#1129)
vhs / vhs (push) Waiting to run
2024-11-14 10:00:08 -06:00
dsrrlldr 62ea796045 docs: Add "Invoice Generator" and "Monthly Bill Tracker" to homepage (#1174) 2024-11-14 08:01:11 -06:00
Nishu 064704c9b7 fix: Add "/" to PublicPath and browser.runtime.getURL (#1171)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-11-14 02:02:00 -06:00
Qiwei Yang c846a1febf docs: Add "Text Search Pro" to homepage (#1172) 2024-11-14 01:07:53 -06:00
Aaron c3959e8427 fix: Respect configOrPath for dev server (#1169) 2024-11-13 16:25:03 -06:00
GitHub Actions e770f9b6b7 chore(release): wxt v0.19.14
vhs / vhs (push) Waiting to run
2024-11-13 15:06:31 +00:00
Aaron 67f35615a2 chore(deps): Bump dev and non-breaking major dependencies (#1167) 2024-11-13 08:32:29 -06:00
Aaron 5c7b6098a2 chore(deps): Upgrade all non-major dependencies (#1164) 2024-11-13 08:19:40 -06:00
Willow f7d12b7486 docs: add missing cd command (#1163) 2024-11-13 08:08:32 -06:00
rxliuli 3481313859 feat: automatically disable 'Show warning about Self-XSS when pasing code' in new chrome (#1159) 2024-11-13 08:07:37 -06:00
Aaron 2f23556893 Update changelog 2024-11-13 08:07:15 -06:00
GitHub Actions 330e39159a chore(release): @wxt-dev/module-svelte v2.0.0
vhs / vhs (push) Waiting to run
2024-11-13 14:04:41 +00:00
kongmoumou aa14676f91 fix: Throw when config file does not exist (#1156)
Co-authored-by: kongmoumou <kongmoumou@users.noreply.github.com>
2024-11-13 07:59:49 -06:00
Goran Nikic 199f330324 docs: Add "propbar.co.uk" to homepage (#1149)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-11-13 07:59:14 -06:00
jasonbla 63dee97ac3 docs: fix incorrect syntax for MatchPattern (#1148) 2024-11-13 07:56:58 -06:00
nirtamir2 b121ed2fe2 docs: Fix solid-js example (#1145) 2024-11-13 07:56:34 -06:00
Johnson Towoju 607e1162e5 docs: Add "YouTube Simple View" to homepage (#1142)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-11-13 07:55:41 -06:00
dsrrlldr b4d569a915 docs: Add "Keyword Rank Checker" to homepage (#1141) 2024-11-13 07:52:30 -06:00
Aaron 4fac364542 feat!: Svelte 5 support (#1104) 2024-11-13 07:48:57 -06:00
Aaron 51c7ee5fa8 chore: Update FUNDING.yml 2024-11-11 17:19:20 -06:00
Aaron 9b780f2d00 docs: Add FAQ about component library styles in content scripts 2024-11-11 16:36:04 -06:00
Aaron f5aec7ec9b chore: Create FUNDING.yml 2024-11-11 15:29:36 -06:00
Florian Metz 5d991eda31 feat(storage): Support storage items in batch functions (#990)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-30 23:36:24 -05:00
Bread Grocery 0318a34046 docs: Fix typo in popup and options EntrypointOptions (#1121) 2024-10-29 08:05:13 -05:00
1natsu e7c66304d3 chore: add "@types/chrome" to templates (#1119) 2024-10-28 14:33:21 -05:00
Ayub Kokabi 4603ebb511 docs: Add missing closing bracket (#1112)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-28 14:33:10 -05:00
GitHub Actions ea570c12b7 chore(release): @wxt-dev/i18n v0.2.3
vhs / vhs (push) Waiting to run
2024-10-28 13:48:20 +00:00
Bread Grocery c17ce34a27 fix: Prevent app crashes from parse errors due to incomplete file saves (#1114) 2024-10-28 08:43:21 -05:00
Berkin Anik 0e4d3ad8ab docs: fix typo in introduction page (#1117) 2024-10-28 08:30:23 -05:00
Aaron 4304f71df2 docs: Add section about env vars in the manifest 2024-10-23 09:05:07 -05:00
Aaron bb5ea34396 docs: Cleanup typos and broken links 2024-10-23 09:04:43 -05:00
GitHub Actions c30adb409c chore(release): @wxt-dev/i18n v0.2.2
vhs / vhs (push) Waiting to run
2024-10-23 13:44:57 +00:00
Bread Grocery ab83031462 feat: Add support for configuring the locales directory (#1109) 2024-10-23 08:39:30 -05:00
imnaK cd7285c3ef docs: fix wrong anchor to "Environment Variables" (#1107) 2024-10-22 22:58:22 -05:00
Aaron bdb775c88d Update changelog 2024-10-22 09:58:51 -05:00
Aaron d20793d5e6 docs: Use full URLs in README so they work on the docs site 2024-10-22 09:50:57 -05:00
GitHub Actions ae1e276577 chore(release): wxt v0.19.13
vhs / vhs (push) Waiting to run
2024-10-22 13:45:03 +00:00
Craig Slusher c031c6e82e feat(env): Load env from .env.[browser] variants (#1078)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-22 08:36:24 -05:00
Malix d06f8128f5 enhance(docs): comparison to real footnotes (#1096)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-22 08:35:23 -05:00
Florian Metz 6ba12e9736 feat: unocss module (#1043)
vhs / vhs (push) Waiting to run
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-22 08:28:02 -05:00
Aaron 0092556c1a fix: Don't use #private member variables in ContentScriptContext (#1103) 2024-10-22 08:04:50 -05:00
Semro 9721bca5a1 docs: Add "Sync Watch" extension to homepage (#1098) 2024-10-21 19:48:03 -05:00
Aaron 1b138f86b9 docs: Remove duplicate env data 2024-10-21 08:48:52 -05:00
GitHub Actions e839671f13 chore(release): wxt v0.19.12
vhs / vhs (push) Waiting to run
2024-10-19 19:02:32 +00:00
Aaron 842c158f96 chore: Enable extensionApi: chrome in template projects (#1083) 2024-10-19 02:38:45 -05:00
GitHub Actions b14c9107fe chore(release): @wxt-dev/i18n v0.2.1
vhs / vhs (push) Waiting to run
2024-10-19 05:49:58 +00:00
Aaron 21894d2890 chore: Fix typo in internal function name 2024-10-19 00:45:07 -05:00
Aaron 22b529453f fix: Add missing "type" keyword to type export in generated file 2024-10-19 00:43:38 -05:00
Aaron 4ec2ece85f docs: Add missing redirect for dead link 2024-10-19 00:13:11 -05:00
Aaron cee1424a4c fix: Ensure consistent hook execution order and add docs (#1081) 2024-10-18 22:08:14 -05:00
Florian Metz d09cf6d4b1 feat(config): Add outDirTemplate for customizing output directory structure (#1074)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-18 16:32:11 -05:00
MultiViewer 9affa07639 docs: Add "MultiViewer Companion" to homepage (#1079) 2024-10-18 13:39:05 -05:00
Aaron cde060ff4f docs: Add migration step for Plasmo --tag to WXT --mode 2024-10-18 13:00:01 -05:00
Florian Metz 1a9cd438d9 chore: shrink down on dependencies (#1050) 2024-10-18 10:47:12 -05:00
Aaron 1dbb5a468d feat: Add support for WXT_ environment variable prefix (#1076) 2024-10-18 10:43:39 -05:00
Aaron 1fe01290c0 docs: Rewrite and restructure the documentation website (#933)
Co-authored-by: aklinker1 <aklinker1@users.noreply.github.com>
2024-10-18 09:28:26 -05:00
Avi 1b900e942b Update UsingWxtSection.vue (#1063) 2024-10-14 16:14:28 -05:00
John Wu 8e527dc96b docs: Add "WebChat" to homepage (#1061)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-11 13:39:07 -05:00
uncenter 1f7ad781df docs: Add "Catppuccin for GitHub File Explorer Icons" to homepage (#1060) 2024-10-11 13:29:18 -05:00
dsrrlldr c2b151be11 docs: Add "Habit Tracker" to homepage (#1054) 2024-10-06 07:57:11 -05:00
Aaron a0b63e7350 ci: Add missing packages to sync-releases workflow 2024-10-04 18:20:46 -05:00
Aaron 10902351be Fix changelog 2024-10-04 18:19:33 -05:00
GitHub Actions 1c3009a48f chore(release): @wxt-dev/i18n v0.2.0
vhs / vhs (push) Waiting to run
2024-10-04 23:12:34 +00:00
Aaron 6c6087249f fix!: Remove invalid options argument (#1048) 2024-10-04 18:07:57 -05:00
Aaron 539d482f71 chore: Remove TS compilerOptions.noUnusedLocals because Oxlint already does it (#1047) 2024-10-04 16:48:35 -05:00
Aaron cab97bca1e perf: Downgrade esbuild so a single version is shared between sub-dependencies (#1045) 2024-10-04 16:48:25 -05:00
Florian Metz 6020083b89 perf: Replace execa with nano-spawn for smaller package size (#1042) 2024-10-04 16:38:06 -05:00
GitHub Actions 947b24f1b0 chore(release): @wxt-dev/i18n v0.1.1
vhs / vhs (push) Waiting to run
2024-10-04 18:24:18 +00:00
windmillcode0 2c5ba64b61 fix: Friendly error messages for null and undefined values inside message files (#1041)
Co-authored-by: windmillcode0 <michaelodumosu57@gmail.com>
2024-10-04 13:19:35 -05:00
Aaron faa4fcb718 chore: Upgrade all non-major dependencies (#1040) 2024-10-03 18:47:16 -05:00
dependabot[bot] 8e9b67fea2 chore(deps): bump execa from 9.3.1 to 9.4.0 (#1031)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 18:24:43 -05:00
dependabot[bot] 659bdd46a0 chore(deps): bump linkedom from 0.18.4 to 0.18.5 (#1034)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:25:47 -05:00
dependabot[bot] edcc411d26 chore(deps-dev): bump changelogen from 0.5.5 to 0.5.7 (#1019)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:25:11 -05:00
dependabot[bot] e022423bad chore(deps): bump @vitejs/plugin-vue from 5.1.1 to 5.1.4 (#1020)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:13:24 -05:00
dependabot[bot] 84e1fd44eb chore(deps): bump esbuild from 0.23.0 to 0.24.0 (#1018)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:13:00 -05:00
dependabot[bot] b68d1ff828 chore(deps): bump magicast from 0.3.4 to 0.3.5 (#1017)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:11:46 -05:00
dependabot[bot] c31a5c30ed chore(deps): bump stefanzweifel/git-auto-commit-action from 4 to 5 (#1022)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 13:10:26 -05:00
Aaron 5b408c2bc2 chore: Update dependabot configuration 2024-10-03 13:09:01 -05:00
mezannic ca29419db6 chore: remove email from changelog (#1027) 2024-10-03 13:04:33 -05:00
Aaron ab26bdc7c1 fix: Use directory name when zip.name and package.json#name are not provided (#1028) 2024-10-03 12:26:36 -05:00
GitHub Actions d270250633 chore(release): wxt v0.19.11
vhs / vhs (push) Waiting to run
2024-10-02 13:59:29 +00:00
Florian Metz d75f64df79 feat(zip): wxt zip --sources and auto sources for opera (#1014)
Co-authored-by: Timeraa <Timeraa@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-02 08:55:08 -05:00
Gurvir Singh 5faa5d7537 fix: #1005 fixed, by updating type-definations to getItem method. (#1007)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-10-02 08:20:32 -05:00
mezannic 177223c625 fix: Reverse env files priority (#1016)
Co-authored-by: Mathieu Ezannic <mathieu.ezannic@plebicom.com>
2024-10-01 16:00:52 -05:00
deyu a64ff22917 docs: Add "Discord Message Translator" extension to homepage (#1011) 2024-09-29 21:57:23 -05:00
Gourav Goyal 0bf08329b0 docs: Add "ChatGPT Writer" to homepage (#1006) 2024-09-27 20:50:21 -05:00
Guilherme Oenning e3f00d2797 docs: Add "Dofollow Links for SEO" to homepage (#1001)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-09-27 08:03:06 -05:00
Andy Zhou 09de72895e docs: Add "social-media-copilot" to homepage (#1000) 2024-09-27 08:02:23 -05:00
Aaron c0aa12089e chore: Move some files around (#996) 2024-09-25 20:54:40 -05:00
Aaron 0248f58785 chore: Add Aaron back to changelog contributions 2024-09-25 20:11:02 -05:00
Florian Metz 247cd66f74 feat(zip): hooks (#993)
Co-authored-by: Timeraa <Timeraa@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-09-25 20:08:54 -05:00
GitHub Actions 4a1bd41df9 chore(release): wxt v0.19.10
vhs / vhs (push) Waiting to run
2024-09-22 14:20:12 +00:00
1natsu efcb593718 perf: Reduce hypersensitive onChange of watcher (#978)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-09-22 09:12:29 -05:00
Okinea Dev a82c7cbf71 docs: Improved docs and links (#970)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-09-22 08:45:30 -05:00
renxinyan db60e79a99 docs: Correct the type example of allFrames (#988) 2024-09-21 19:03:07 -05:00
Jarle Mathiesen 722eff145f docs: Add "YouTube Summarized" to homepage (#986) 2024-09-20 07:55:58 -05:00
Zain Hasan 282458748d docs: Fix small typo in introduction (#983) 2024-09-16 21:20:53 -05:00
the-syndrome 26197f11a3 docs: examples reference outDir vs. outputDir (#982) 2024-09-15 07:58:52 -05:00
aklinker1 9d560f9798 docs: Update wxt init GIF 2024-09-14 20:45:33 +00:00
Okinea Dev 786292c95e docs: Add wxt init demo GIF (#974)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
Co-authored-by: okineadev <okineadev@users.noreply.github.com>
2024-09-14 15:44:15 -05:00
rxliuli e760ee444e chore(template): set type to "module" for Svelte template (#977) 2024-09-12 08:07:11 -05:00
kongmoumou 78f91fefb6 chore: Continuous publish using pkg.pr.new (#955) 2024-09-11 00:40:42 -05:00
Aaron Klinker fda1e18f48 types: Fix ExtensionRunnerConfig.chromiumPref type
Closes #964
2024-09-10 16:29:36 -05:00
hikiko4ern c94168e2d1 fix: Fix config manifest type (#973) 2024-09-10 16:13:08 -05:00
GitHub Actions 3eb505dbde chore(release): wxt v0.19.9 2024-09-04 21:39:24 +00:00
1natsu fbe502fddf chore: Cleanup leftover E2E test artifacts (#968) 2024-09-04 16:35:18 -05:00
1natsu c143a9978d fix: Properly overload import.meta.env with WXT's own environment globals (#966) 2024-09-03 14:35:10 -05:00
Aaron Klinker e3555b663d Update description after 447d011 2024-09-03 14:34:32 -05:00
Aaron Klinker df934d252c Correct version number after 447d011 2024-09-03 14:31:27 -05:00
Aaron Klinker 447d011b8f docs: Retroactively mention breaking change in v0.18.5
See https://github.com/wxt-dev/wxt/discussions/950#discussioncomment-10512337
2024-09-03 14:30:10 -05:00
Aaron abe1263d27 feat: Use @types/chrome for config manifest type (#969) 2024-09-03 14:11:24 -05:00
Aaron Klinker 143b5ac81f docs: Add docs around importEntrypoint to relevant functions 2024-09-03 13:40:59 -05:00
dependabot[bot] 87fbb1f4c3 chore(deps): bump execa from 9.3.0 to 9.3.1 (#957)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 12:12:22 -05:00
dependabot[bot] 62f11bf605 chore(deps): bump sharp from 0.33.4 to 0.33.5 (#959)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 12:10:02 -05:00
dependabot[bot] 3ead54b58e chore(deps): bump unimport from 3.9.1 to 3.11.1 (#960)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 12:08:44 -05:00
dependabot[bot] 580f4f3cfe chore(deps): bump ora from 8.0.1 to 8.1.0 (#961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-03 12:08:04 -05:00
1natsu da0accebe8 fix: duplicate BuildOutput.publicAssets (#951)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2024-09-03 12:06:21 -05:00
Aaron eb65d0b42a docs: Add link to testing permission changes in Firefox 2024-09-03 07:34:43 -05:00
Alex Nguyen c2784135b0 docs: Fix typo in frontend-frameworks.md (#952) 2024-09-01 19:43:11 -05:00
Aaron 61d54bd1f9 fix: Allow adding multiple hyphens in an entrypoint name (#949) 2024-08-30 04:56:23 -05:00
Aaron 0bd94fce3b chore: Add oxlint for linting (#947) 2024-08-30 04:14:20 -05:00
Amal Chandran 2a97c06443 docs: Add "Draftly" to homepage (#941)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-08-30 02:51:32 -05:00
Aaron 8213aa776a chore: Fix type error (#946) 2024-08-30 02:45:36 -05:00
Aaron 026f78254d chore: Upgrade unbuild from 1.1.0 to 1.1.1 2024-08-29 08:19:56 -05:00
Aaron 0ebb013ba6 types(modules): Use NestedHooks instead of Partial for hooks object 2024-08-29 07:12:58 -05:00
Aaron c5f78d0c8d feat(modules): Add wxt.hook alias for wxt.hooks.hook 2024-08-29 07:12:13 -05:00
GitHub Actions 8771676e69 chore(release): wxt v0.19.8 2024-08-29 11:53:13 +00:00
1natsu aebbbb0685 fix: Fallback to GitHub API for listing templates when ungh is down (#944)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-08-29 06:48:38 -05:00
Aaron 68183b648e chore(types): Cleanup types in wxt/browser/chrome (#932) 2024-08-23 08:49:39 -05:00
Aaron Klinker 45809c0198 chore: Update lockfile 2024-08-19 23:08:40 -05:00
Aaron Klinker 13163c9ba1 chore(dev-deps): Upgrade @aklinker1/buildc from v1.0.11 to v1.1.0 2024-08-19 21:49:01 -05:00
1natsu aedbba3f3d perf: Ignore output directories for all vite.watcher (#924) 2024-08-19 21:44:27 -05:00
GitHub Actions 763dbd5ee8 chore(release): wxt v0.19.7 2024-08-19 15:40:58 +00:00
Aaron 5821ae0e8c feat: New @wxt-dev/i18n package (#758) 2024-08-19 09:18:01 -05:00
Aaron 751706d2ae docs: Fix module name for wxt/browser/chrome 2024-08-19 09:08:53 -05:00
Aaron cf8554718d feat(modules): Add addAlias helper (#928) 2024-08-18 15:20:12 -05:00
Aaron 5f653b3ff9 fix: Ensure TSConfig paths start with ../ or ./ so they are valid (#927) 2024-08-18 15:18:23 -05:00
Aaron 5f2e1c38a7 chore: Remove warning log for missing public directory 2024-08-18 15:10:07 -05:00
Aaron 8a9d8be8a4 feat(testing): Run WXT modules when setting up test environment (#926) 2024-08-18 15:08:45 -05:00
Aaron 067b2eccf3 fix(testing): Stub chrome and browser globals with fakeBrowser automatically (#925) 2024-08-18 14:56:15 -05:00
Aaron 2e1bd6ec93 chore: Update demo package cache behavior 2024-08-18 14:49:29 -05:00
Aaron b463cef1c2 Update changelog 2024-08-16 15:59:58 -05:00
GitHub Actions adf9d39d69 chore(release): wxt v0.19.6 2024-08-16 20:16:57 +00:00
1natsu b1699592e4 perf: Ignore non-source code from the file watcher (#919) 2024-08-16 15:01:18 -05:00
Aaron 8675abfced docs: Update ESM content script docs with example 2024-08-16 09:34:32 -05:00
Aaron a1fc19e47c chore(bench): Setup benchmark script for testing browser open timing (#917) 2024-08-15 16:51:06 -05:00
Aaron 8235de04d2 chore: Add profiling docs to CONTRIBUTING.md 2024-08-15 16:50:42 -05:00
Aaron 46a324adc2 fix(types): Don't report type errors when using string templates with browser.i18n.getMessage (#916) 2024-08-14 23:44:11 -05:00
Aaron c0af62316d fix: Typo in sidepanel options (browse_stylebrowser_style) (#914) 2024-08-14 07:46:42 -05:00
GitHub Actions 2fd3503694 chore(release): wxt v0.19.5 2024-08-12 15:36:58 +00:00
Aaron e6529e6e41 fix: Update chromium setting for enabling content script sourcemaps 2024-08-12 10:32:25 -05:00
1natsu 72673ca366 fix: #907 move wxt devDeps execa to dependencies (#908) 2024-08-10 12:01:44 -05:00
Florian Metz cbda49ba7b feat: allow excluding files when zipping (#906) 2024-08-07 20:15:33 -05:00
hikiko4ern 0ab32e5afa feat: allow zipping hidden files by listing them explicitly in includeSources (#902) 2024-08-07 20:13:33 -05:00
GitHub Actions 36cfcd0acb chore(release): @wxt-dev/module-solid v1.1.2 2024-08-06 13:07:15 +00:00
GitHub Actions f746d46247 chore(release): @wxt-dev/module-svelte v1.0.1 2024-08-06 12:29:43 +00:00
GitHub Actions a2de6cbe44 chore(release): @wxt-dev/module-vue v1.0.1 2024-08-06 04:23:00 +00:00
GitHub Actions e632a2964a chore(release): @wxt-dev/module-react v1.1.1 2024-08-06 01:55:29 +00:00
GitHub Actions f736a14651 chore(release): @wxt-dev/auto-icons v1.0.2 2024-08-05 23:52:51 +00:00
uncenter a8b0d40959 docs(auto-icons): fix configuration example typo (#905) 2024-08-05 18:48:54 -05:00
Aaron 4c5504d139 Update changelog 2024-08-04 10:18:02 -05:00
GitHub Actions fb86deb388 chore(release): wxt v0.19.4 2024-08-04 14:31:30 +00:00
Aaron 3a8e6135ac fix: Fix createIframeUi page option types 2024-08-04 09:27:46 -05:00
Aaron a38de0c03f feat: Add injectScript helper (#900) 2024-08-04 09:16:46 -05:00
Aaron ea97410fb4 fix(types): PublicPath type resolution with extensionApi: "chrome" (#901) 2024-08-04 08:59:45 -05:00
hikiko4ern e36549ddce fix: do not clear .wxt/tsconfig.json in findEntrypoints if it exists (#898) 2024-08-03 21:59:12 -05:00
KnightYoshi 10851ae01c docs: add missing extension-apis segment (#896) 2024-08-03 19:28:53 -05:00
GitHub Actions d553ef6b29 chore(release): wxt v0.19.3 2024-08-02 21:48:05 +00:00
Aaron 15bf0da82e fix(content-script-context): Fix initialization logic for Firefox (#895) 2024-08-02 16:43:40 -05:00
hikiko4ern fc246ffaac fix: add consola to wxt dependencies (#892) 2024-08-01 19:05:28 -05:00
Florian Metz 9785eff21e chore: add more metadata for npm (#885) 2024-08-01 18:00:00 -05:00
Himanshu Patil 52fbd2c99e docs: Improve prepare:types hook JSDoc (#886) 2024-08-01 17:11:20 -05:00
GitHub Actions 648ae4fb8d chore(release): @wxt-dev/auto-icons v1.0.1 2024-07-30 18:23:12 +00:00
Aaron Klinker bbeeabc9ea Revert version change for auto-icons module 2024-07-30 13:18:35 -05:00
web-dahuyou 35cf6e7bc0 docs: Add "NiceTab" to homepage (#882) 2024-07-30 13:12:00 -05:00
Florian Metz 78822acdac fix(auto-icons): path option (#880)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-30 13:06:41 -05:00
不游 aea123890f docs: Add "1Proompt" to homepage (#879) 2024-07-29 07:50:28 -05:00
Matt Strayer c369f4a955 docs: Add "mindful" to homepage (#878) 2024-07-28 16:33:58 -05:00
GitHub Actions 16d8240406 chore(release): wxt v0.19.2 2024-07-28 15:49:52 +00:00
Aaron 24cd01dd31 fix: Don't default to dev mode for production builds when using vite-node loader (#877) 2024-07-28 10:45:32 -05:00
Aaron fb22beadb8 fix: Remove unused top-level functions and variables when loading entrypoints with the vite-node loader (#875) 2024-07-28 09:27:29 -05:00
Aaron Klinker fa85177648 docs(modules): Adjust wording 2024-07-27 17:04:36 -05:00
Aaron Klinker ed07a49dd2 docs: Update README and homepage features with WXT modules 2024-07-27 16:57:33 -05:00
Aaron 09c9de0a60 docs: Fix environment variable reference (#870) 2024-07-27 16:15:28 -05:00
Aaron 523296b29a chore(deps): Upgrade all dependencies (#869) 2024-07-27 16:07:50 -05:00
Aaron Klinker ef67ef47aa chore: Update template package names 2024-07-27 15:37:48 -05:00
Aaron Klinker cb5eb68d62 Fix changelog 2024-07-27 15:11:27 -05:00
GitHub Actions 2e9ab75e2b chore(release): wxt v0.19.1 2024-07-27 20:09:05 +00:00
Aaron 0c2d795800 pref: Tree-shake unrelated code inside vite-node entrypoint loader (#867) 2024-07-27 15:03:23 -05:00
Aaron 97aa173f7e fix: Define web globals when importing entrypoints (#865) 2024-07-27 14:48:55 -05:00
Aaron 9eb64d94d6 docs: Add README for auto-icons module 2024-07-26 14:00:37 -05:00
Florian Metz a0971cab52 feat: Auto Icons Module (#851)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-25 23:41:53 -05:00
Aaron 127562e9d0 ci: Fix breaking changes link generation in changelog 2024-07-25 22:48:05 -05:00
Aaron 038e7450e0 Fix diff 2024-07-25 22:40:33 -05:00
Aaron 6c00763187 docs: Add missing ssr.noExternal docs to vite-node loader 2024-07-25 22:38:21 -05:00
Aaron 2da0f3d686 Fix changelog 2024-07-25 22:14:06 -05:00
GitHub Actions 742d2f1536 chore(release): wxt v0.19.0 2024-07-26 01:03:48 +00:00
Aaron b4b69783d5 docs: Add upgrade guide and breaking changes (#860) 2024-07-25 19:59:15 -05:00
Aaron c8ddc66cfa feat!: Use vite-node to load entrypoints by default (#859)
BREAKING CHANGE: Switch from using `jiti` to import entrypoints during the build process by default to `vite-node`.

To continue using `jiti`, add the following to your `wxt.config.ts` file:

```ts
export default defineConfig({
  entrypointLoader: "jiti",
})
```

NOTE: "jiti" is deprecated and will be removed in the next major version, `v0.20.0`.
2024-07-25 17:25:46 -05:00
Aaron 2b6ff8d30f docs: Update labels in content script UI positioning screenshot 2024-07-25 11:45:49 -05:00
Aaron d79d7ed778 feat(hooks): Add prepare:publicPaths hook (#858) 2024-07-25 11:32:16 -05:00
Aaron Klinker ab234c0f45 docs: Review full docs, fix typos, re-order sections 2024-07-25 00:10:04 -05:00
Aaron Klinker eeb43c6bf4 docs: Fix typo 2024-07-24 23:22:14 -05:00
Aaron 17bda1eb34 feat(storage): init option and rename defaultValue to fallback (#827) 2024-07-24 23:18:16 -05:00
Aaron 96e28c415c docs: Extract comparison comments to footnotes (#853) 2024-07-24 23:17:01 -05:00
Florian Metz 1f9f6d9681 chore: update readme (#852) 2024-07-24 21:11:28 -05:00
Aaron f1bb7cb0c2 feat(experimental): First-class support for excluding webextension-polyfill (#847) 2024-07-24 17:09:56 -05:00
Aaron 5eb5be7123 perf(size)!: Switch from tsup to unbuild for building WXT (#848) 2024-07-24 08:48:48 -05:00
Florian Metz 886b2081c2 fix: wrong module hook type (#849) 2024-07-23 20:02:26 -05:00
Radek Grebski c69e51ce50 docs: Added "RespondEasy" to homepage (#844)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-23 09:14:59 -05:00
Hagatopaxi 861efa3415 docs: fix typo (#845) 2024-07-22 23:53:00 -05:00
Aaron 792b57542d updtae lockfile 2024-07-21 11:53:34 -05:00
Aaron 97e6678e5b Upgrade vitest's listed version 2024-07-21 11:46:23 -05:00
Aaron 66c4ec3f99 chore(dev-deps): Upgrade happy-dom from 13 to 14 (#843) 2024-07-21 11:44:52 -05:00
Aaron 8988d6a241 chore(deps): Upgrade minimatch from 9 to 10 (#840) 2024-07-21 11:42:33 -05:00
Aaron b52856bea4 chore(deps): Upgrade webextension-polyfill from 0.10 to 0.12 (#842) 2024-07-21 11:39:40 -05:00
Aaron 16fee19b85 chore(deps): Upgrade ora from 7 to 8 (#841) 2024-07-21 11:39:03 -05:00
Aaron 14011c19cb chore(deps): Upgrade vite-node from 1.6 to 2.0 (#839) 2024-07-21 11:36:02 -05:00
Aaron 005a9dff16 chore(deps): Upgrade esbuild from 0.19.12 to 0.23.0 (#838) 2024-07-21 11:33:39 -05:00
Aaron 3109bbadbe chore(deps): Upgrade async-mutex from 0.4.1 to 0.5.0 (#837) 2024-07-21 11:31:35 -05:00
Aaron 34ae689569 chore(dev-deps): Upgrade vitest from 1.6 to 2.0 (#836) 2024-07-21 11:24:23 -05:00
Aaron 1f216dd57b chore(deps): Bump all non-major dependencies (#834) 2024-07-21 10:01:51 -05:00
GitHub Actions a7963e23db chore(release): wxt v0.18.15 2024-07-21 13:43:02 +00:00
Aaron 069bd6c435 fix: Externalize app config during dependency optimization (#833) 2024-07-21 08:37:09 -05:00
Aaron 88a1244ca9 fix: Build latest version of package before packing 2024-07-21 08:36:45 -05:00
Aaron 7ee391f688 fix: Don't throw error if localization file is missing (#832) 2024-07-21 07:53:38 -05:00
Aaron 2b3eab9c2a docs: Add link to google's update testing tool in migration guide and improve host permission docs 2024-07-20 11:33:17 -05:00
eetann a3fc9e1e18 docs: fix link to unimport (#826) 2024-07-17 07:40:34 -05:00
Aaron 559d32e6eb Update README.md 2024-07-12 08:19:59 -05:00
Aaron f904b79367 Update README.md 2024-07-12 08:19:33 -05:00
eetann 9cd7cca81c docs: fix links to Guide pages (#821) 2024-07-12 07:25:11 -05:00
GitHub Actions 3366c1e35f chore(release): wxt v0.18.14 2024-07-09 16:03:18 +00:00
Aaron 82be31a408 fix(modules): Add types from all wxt node_modules, not just ones with config (#817) 2024-07-09 10:58:41 -05:00
GitHub Actions d767f3f598 chore(release): wxt v0.18.13 2024-07-09 15:07:38 +00:00
Ayub Kokabi 48967c7c5f feat: add XPath support to getAnchor() (#813)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-09 10:02:00 -05:00
Aaron 6c4da7225d fix: Add debug logs for vite builder (#816) 2024-07-09 09:58:59 -05:00
多吃点 e9025fb13e docs: Update manifest.md (#815) 2024-07-09 08:12:38 -05:00
jy00566722 8db6b3187b docs: Add "Exchange Rate Converter" extensions to homepage (#808) 2024-07-07 14:41:48 -05:00
Aaron a19f9e12c4 feat(config): dev.server.hostname (#807) 2024-07-07 09:31:38 -05:00
Farzad Soltani a82eb46511 docs: fix grammar (#806) 2024-07-07 09:16:03 -05:00
GitHub Actions e50f2b047a chore(release): wxt v0.18.12 2024-07-07 14:10:47 +00:00
Aaron 1b41cd26ee chore(deps): Upgrade web-ext-run (0.2.0 to 0.2.1) (#804) 2024-07-07 08:57:29 -05:00
Aaron e32fbed1b7 docs: Update README (#802) 2024-07-06 08:29:23 -05:00
sacrosanctic f8acdd6352 doc: detail issues with vite plugins (#799)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-06 08:01:22 -05:00
ntnyq 9ea89c6340 fix: Add missing name to ESLint v9 autoImports config (#801) 2024-07-06 08:01:10 -05:00
Margarita Balkheeva a521a80d43 docs: Add "MaxFocus" extension to homepage (#795)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-04 14:38:44 -05:00
sacrosanctic 4c6a57dd33 docs: rephrase a paragraph (#797) 2024-07-03 16:16:41 -05:00
Florian Metz 3ce8c8b1f4 perf: create zip using streams (#793) 2024-07-03 01:37:58 -05:00
deyu 4ee8f7837e docs: Add "Vanilla Pudding" extension to homepage (#794) 2024-07-03 01:36:32 -05:00
Aaron 1e8e5488af feat: Support runtime config in app.config.ts (#792) 2024-07-02 17:19:54 -05:00
GitHub Actions 5704765cc4 chore(release): wxt v0.18.11 2024-07-02 16:40:05 +00:00
GitHub Actions 8c7adbf463 chore(release): @wxt-dev/module-react v1.1.0 2024-07-02 16:34:30 +00:00
Aaron 8c4436efe8 Update solid template to @wxt-dev/module-solid v1.1.1 (#789) 2024-07-02 11:29:53 -05:00
GitHub Actions 5e361c0085 chore(release): @wxt-dev/module-solid v1.1.1 2024-07-02 16:23:54 +00:00
Aaron ccfc207e92 fix: Use prepare instead of postinstall for local dev setup (#788) 2024-07-02 11:18:44 -05:00
GitHub Actions 36be4ce771 chore(release): @wxt-dev/module-solid v1.1.0 2024-07-02 15:37:46 +00:00
KnightYoshi 18f8a387b0 feat: add eslint 9 config support (#762)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-02 10:32:32 -05:00
C.C 48c2c07557 docs: Fix manifest typo (#787) 2024-07-02 07:09:07 -05:00
Aaron 61548a9478 fix(dev): Catch error when attempting to reload a tab in a saved tab group (#786) 2024-07-01 16:11:00 -05:00
dependabot[bot] fd678e9572 chore(deps): bump oven-sh/setup-bun from 1 to 2 (#785)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 15:40:10 -05:00
Aaron 63fb913f76 docs: New example search (#751) 2024-06-30 09:01:42 -05:00
Aaron faffe20a62 chore(deps): Upgrade non-major deps (#778) 2024-06-29 15:00:15 -05:00
Aaron 3ed8bd10fd chore: Upgrade buildc to v1.0.10 (#780) 2024-06-29 14:09:56 -05:00
Asakura Mizu 6f3f270573 fix: Respect custom outDir when cleaning and zipping (#774) 2024-06-28 09:05:40 -05:00
Aaron 5347cb4626 chore: Replace consola with wxt.logger (#776) 2024-06-28 09:04:21 -05:00
Aaron d6b4d2d1f6 chore(dev-deps): Upgrade buildc to v1.0.9 2024-06-27 16:47:35 -05:00
Aaron 0d7ae287da chore(dev-deps): Upgrade buildc to v1.0.8 2024-06-27 16:38:40 -05:00
Aaron 90cb3bd3bf feat: Enable auto-imports for JSX/TSX files (#773) 2024-06-27 00:05:45 -05:00
Prakhar 🔱 Man of Creativity 60eb97c4b2 docs: Update examples URL (#768)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-26 13:52:10 -05:00
Aaron 0ffc6eae3e Fix comment typo 2024-06-25 15:12:59 -05:00
GitHub Actions 06a8c5fb0e chore(release): wxt v0.18.10 2024-06-25 20:03:59 +00:00
Aaron eb86413f35 chore: Consolidate unimport code into a built-in module (#771) 2024-06-25 14:59:28 -05:00
Aaron b29d49cbf1 fix: Await prepare:types hook 2024-06-24 10:04:44 -05:00
Aaron c0a0edb30c feat(modules): Allow adding generated public files (#769) 2024-06-24 08:46:45 -05:00
Aaron b15a933c75 feat: Add prepare:types hook to extend .wxt/ directory generation (#767) 2024-06-23 15:12:28 -05:00
Malix 1851be22a7 docs: Update CRXJS Firefox Compatibility (#764)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-23 10:32:09 -05:00
Aaron 39f6c29e4f chore: Refactor package manager test fixtures 2024-06-21 09:10:22 -05:00
GitHub Actions 936e89fbfb chore(release): wxt v0.18.9 2024-06-20 15:34:16 +00:00
Aaron ad365ecde4 feat(experimental): Replace viteRuntime option with entrypointImporter option, and implement vite-node importer (#761) 2024-06-20 10:27:19 -05:00
GitHub Actions b4283e37bd chore(release): wxt v0.18.8 2024-06-20 14:03:16 +00:00
Aaron b467ad760a fix(modules): addViteConfig ignored user vite config (#760) 2024-06-20 08:59:46 -05:00
Aaron 9fc6408ef9 chore: Add E2E test for addImportPreset 2024-06-20 08:52:45 -05:00
Aaron 394cb1a520 docs: Add 'WorkFlowy MultiFlow' to homepage 2024-06-19 14:16:58 -05:00
Aaron ffec88549b fix: Ignore .wxt file changes in dev (#755) 2024-06-19 08:33:52 -05:00
Ziheng Xun 4758bea187 docs: update sandbox.md (#754) 2024-06-19 08:06:08 -05:00
Aaron 6671471f75 feat(dev): Reload extension when public files change (#752) 2024-06-19 08:05:56 -05:00
Aaron ae78dc7fb3 chore: Refactor client web socket util (#753) 2024-06-18 22:47:30 -05:00
Aaron b62af3d007 chore(dev-deps): Upgrade @aklinker1/buildc to 1.0.7 (#748) 2024-06-18 00:11:14 -05:00
Aaron 43ae44ecd5 fix: Don't load plugins twice in HTML pages (#746) 2024-06-17 23:41:39 -05:00
Aaron 5bab9d2f59 chore: Fix build script flags (#745) 2024-06-17 23:41:10 -05:00
Aaron 7edf1c8103 fix: Upgrade wxt peer to >= 0.18.6
This closes https://github.com/wxt-dev/wxt/issues/743
2024-06-17 14:42:24 -05:00
Aaron 53b4eac955 docs: Update wxt init command for bun 2024-06-17 14:39:41 -05:00
GitHub Actions f9a10c2c52 chore(release): wxt v0.18.7 2024-06-17 16:17:42 +00:00
Aaron 8206ca9164 fix(zip): Revert dotfile changes from #674 (#742) 2024-06-17 11:10:06 -05:00
Aaron 1a823cc008 chore: Extract build cache script to NPM package (#737) 2024-06-17 11:09:18 -05:00
Aaron efc011eb81 ci: Fix release 422 failure 2024-06-14 00:36:03 -05:00
GitHub Actions 35f6d28977 chore(release): @wxt-dev/module-solid v1.0.1 2024-06-14 05:29:40 +00:00
Aaron 43fcbec0eb fix: Add target: esnext by default (#733) 2024-06-14 00:25:11 -05:00
Aaron a0d56b4d27 chore: Update templates to use new modules (#732) 2024-06-14 00:19:13 -05:00
Aaron 38ccb6ace6 docs: Update frontend framework docs with new modules (#731) 2024-06-14 00:06:23 -05:00
Aaron aaf75f1de1 ci: Only mark wxt releases as latest 2024-06-14 00:02:41 -05:00
Aaron 21e8ca0a2c chore: Add changelog 2024-06-13 23:52:58 -05:00
Aaron 55444858bc ci: Add new modules as release targets 2024-06-13 23:44:28 -05:00
Aaron 771ea03069 feat: Add @wxt-dev/module-solid package (#730) 2024-06-13 23:23:32 -05:00
Aaron 38b71d1350 chore: Remove extra dependencies 2024-06-13 23:08:17 -05:00
Aaron cb781aceb5 chore: Remove extra dependencies 2024-06-13 23:05:48 -05:00
Aaron d53c792218 chore: Remove dependencies from vue module 2024-06-13 23:03:16 -05:00
Aaron 907f4d3672 feat: Add @wxt-dev/module-react package (#729) 2024-06-13 22:53:40 -05:00
Aaron 8432d73a6b fix: Generalize react-refresh preamble logic to virtualize all inline scripts (#728) 2024-06-13 19:17:23 -05:00
Aaron 94a5435089 docs: Add missing vue install step 2024-06-13 16:39:09 -05:00
Aaron edf3cf3f17 feat: Add @wxt-dev/module-vue package (#726) 2024-06-13 16:36:37 -05:00
Aaron 48783baec3 chore(deps): Upgrade and sync all dependencies (#725) 2024-06-13 15:44:00 -05:00
Aaron c70f047696 chore: Add taze to project (#724) 2024-06-13 15:17:21 -05:00
Aaron e6e0c713bc feat: Add @wxt-dev/module-vue package (#716) 2024-06-13 15:16:45 -05:00
Aaron 3691554a73 docs: Update contributor docs with release steps 2024-06-13 14:58:57 -05:00
Aaron 1a3436606b fix(dev): Fix CSP error loading HTML plugins (#723) 2024-06-13 14:49:50 -05:00
GitHub Actions 2c9e7750ec chore(release): wxt v0.18.6 2024-06-13 17:28:34 +00:00
Aaron fa67910007 fix(modules): Add modules to TS project so type augmentation works (#722) 2024-06-13 12:25:18 -05:00
Aaron c77773eb20 feat(modules): Add new addImportPreset helper function (#720) 2024-06-13 12:20:09 -05:00
Aaron 148f3d53a6 fix(modules): Re-export WxtModule type from wxt/modules to prevent TS2742 (#721) 2024-06-13 12:19:15 -05:00
Aaron c7af789173 fix(types): Module options param is optional, but types did not reflect that (#719) 2024-06-13 11:20:59 -05:00
Aaron 274d39b9a4 chore(dev-deps): Upgrade typescript to 5.4 (#718) 2024-06-13 00:38:11 -05:00
Aaron d1edb2da0e chore: Cache workspace dependency builds (#715) 2024-06-12 22:34:08 -05:00
Aaron ed860027ef ci: Fix sync-releases script 2024-06-12 00:51:11 -05:00
Aaron 488fa6e773 ci: Fix sync-releases workflow 2024-06-12 00:48:40 -05:00
Aaron ea51cc04a9 Fix changelog 2024-06-12 00:46:23 -05:00
Aaron 6d1761533f ci: Fix compare url generation 2024-06-12 00:45:39 -05:00
GitHub Actions e35b540a3b chore(release): wxt v0.18.5 2024-06-12 05:42:26 +00:00
Aaron 7080b36173 ci: Add support for managing multiple changelogs (#712) 2024-06-12 00:36:28 -05:00
Aaron 4437dc404b chore(deps): Upgrade braces to 3.0.3 (#711) 2024-06-11 19:45:07 -05:00
Aaron b3ddab7bcb feat: Module system (#672) 2024-06-11 17:45:34 -05:00
Aaron Klinker eb3b9bc334 chore: Remove log in tests 2024-06-11 17:43:21 -05:00
Dave Stewart 34ea23030b docs: Simplify menu configuration (#709)
Co-authored-by: Dave Stewart <dev@davestewart.co.uk>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-10 16:10:27 -05:00
Aaron a25721708f chore: Fix flakey TS config 2024-06-09 10:00:45 -05:00
Aaron 6427e74edb chore: Fix engine requirements for PNPM 2024-06-09 10:00:31 -05:00
TheOnlyTails 15395a8c46 fix(storage): Add storage area typing for storage keys (#708)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-09 09:20:32 -05:00
Aaron 454b076eaf docs: Add eslintrc info to auto-imports page 2024-06-08 13:40:14 -05:00
Aaron 00597f6790 docs: Fill out web-ext.config.ts docs 2024-06-08 08:20:38 -05:00
rxliuli ca29feaecf fix: To fix the issue with Entrypoint Side Effects link errors (#705) 2024-06-05 19:24:58 -07:00
Aaron f6420e5fdf docs: Fix more homepage links 2024-06-04 12:12:25 -05:00
Aaron 78e0646043 docs: Fix home page links and redirects 2024-06-04 08:41:04 -05:00
dependabot[bot] c15e75e6ab chore(deps): bump pnpm/action-setup from 3 to 4 (#689)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-03 23:06:38 -05:00
Aaron 863a006ee7 docs: Restructure wxt.dev (#701) 2024-06-03 18:43:25 -05:00
dependabot[bot] 9fd54cfa73 chore(deps-dev): bump execa from 8.0.1 to 9.1.0 (#691)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 22:35:29 -07:00
Durgaprasad 3535fb77fa docs: Add SmartEReply to homepage (#697) 2024-06-02 22:25:24 -07:00
dependabot[bot] be22fc43cb chore(deps-dev): bump prettier from 3.1.1 to 3.3.0 (#693)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 22:25:07 -07:00
dependabot[bot] 3fd09edd25 chore(deps-dev): bump tsx from 4.7.0 to 4.11.2 (#699)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 22:23:40 -07:00
dependabot[bot] d36007b453 chore(deps): bump nypm from 0.3.6 to 0.3.8 (#692)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 22:14:05 -07:00
dependabot[bot] 9eda887fd8 chore(deps): bump linkedom from 0.16.8 to 0.18.2 (#690)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 22:13:21 -07:00
John Wheeler b79220dbd1 docs: Adds demo.fun to homepage (#688)
Co-authored-by: John Wheeler <jwheeler@dinesh.local>
2024-05-31 11:31:44 -07:00
Aaron 927d29535f fix: Previous version of WXT printed on startup 2024-05-31 08:33:48 -05:00
GitHub Actions 12805e9862 chore(release): v0.18.4 2024-05-31 13:27:34 +00:00
hujiulong d889a21465 fix: Properly invalidate content script context (#683) 2024-05-31 08:24:12 -05:00
Aaron 8125d74057 docs: Update contributing guidelines 2024-05-31 01:32:16 -05:00
Aaron c39087a81a Migrate bug report to a GitHub form template 2024-05-31 01:28:45 -05:00
Aaron 7b849b80cb fix: Allow zipping hidden files in sources by listing them explicitly in includeSources (#674) 2024-05-25 16:43:51 -05:00
GitHub Actions b98a5c378e chore(release): v0.18.3 2024-05-24 10:51:34 +00:00
Aaron 215def7e15 fix(security): Upgrade tar to 6.2.1 2024-05-24 05:46:11 -05:00
Aaron 3b954cc6fb chore: Upgrade templates to v0.18 2024-05-24 05:40:07 -05:00
Aaron 20bdd8ad2f fix: Remove import * as imports from entrypoints during build (#671) 2024-05-24 05:38:53 -05:00
Edoan cb9a9c0530 docs: Add YTBlock to homepage (#666) 2024-05-21 12:01:55 -05:00
Aaron 48d8d236c0 fix: Automatically add dev server to sandbox CSP (#663) 2024-05-20 23:41:11 -05:00
Aaron 87d9511b31 chore: Add missing tests for dev mode CSP (#662) 2024-05-20 23:35:21 -05:00
Aaron 5d8efef02f ci: Fix sync-releases workflow trigger 2024-05-08 11:04:58 -05:00
Aaron c9008cd835 Update changelog 2024-05-08 11:03:17 -05:00
GitHub Actions 203a9cc234 chore(release): v0.18.2 2024-05-08 15:50:03 +00:00
Aaron 5fcaf7c68b fix: Automatically detect and add "sidePanel" permission 2024-05-08 10:42:07 -05:00
Aaron c74e530d4a docs: Fix iframe typos 2024-05-08 10:34:34 -05:00
Edoan 3f78be0c95 feat(runner): Add keepProfileChanges option (#655) 2024-05-07 22:20:51 -05:00
Linus Norton 2a3d52045d docs: Correct event handler name in handling-updates.md (#653) 2024-05-07 04:31:28 -05:00
Jeffrey Zang ea5b81d25e docs: Fix spelling mistake in remote-code.md (#652) 2024-05-06 21:58:36 -05:00
Emmanuel Ferdman cec9d7103a docs: Fix wxt-vitest-plugin reference (#650)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-05-05 08:30:40 -05:00
GitHub Actions 34dd2c1487 chore(release): v0.18.1 2024-05-04 16:42:09 +00:00
Aaron a5667f85d0 fix: _background is not defined (#649) 2024-05-04 11:39:29 -05:00
Aaron ec3dd52f83 chore: Add root README back 2024-05-04 11:03:55 -05:00
Aaron 87ebde3abb Fix sync releases workflow 2024-05-04 11:01:08 -05:00
Aaron dc5b55bffb ci: Fix sync releases workflow 2024-05-04 10:59:40 -05:00
Aaron df20993917 Fix changelog generation 2024-05-04 10:57:23 -05:00
Aaron caa0fb31e6 Add breaking change message to changelog 2024-05-04 10:53:35 -05:00
GitHub Actions 95da4fb054 chore(release): v0.18.0 2024-05-04 15:51:44 +00:00
Aaron a917f32a0e feat(experimental): Load entrypoint options with Vite Runtime API (#648) 2024-05-04 10:46:31 -05:00
Aaron 6ca376749c chore: Fix formatting after monorepo refactor 2024-05-04 09:44:07 -05:00
Aaron 792e3032b9 feat: Support returning values from scripts executed with the scripting API (#624) 2024-05-03 17:32:42 -05:00
alegal200 38a5a2c440 docs: Document required permission for storage API (#632)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-05-03 17:28:52 -05:00
yacine-bens 21106ce188 docs: Add MS Edge TTS to homepage (#647) 2024-05-03 17:26:34 -05:00
Aaron ec7263e7de chore: Refactor repo to a standard monorepo (#646) 2024-05-03 17:16:33 -05:00
dependabot[bot] e7c3fc2a5e chore(deps-dev): bump simple-git-hooks from 2.9.0 to 2.11.1 (#640)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 08:14:03 -05:00
dependabot[bot] f808cb7526 chore(deps-dev): bump publint from 0.2.6 to 0.2.7 (#639)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 08:13:15 -05:00
dependabot[bot] c4795e7479 chore(deps): bump react and @types/react (#638)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 08:13:02 -05:00
dependabot[bot] b44a96ab81 chore(deps-dev): bump lint-staged from 15.2.1 to 15.2.2 (#637)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 08:12:45 -05:00
evsamson 3bfdc5c755 FIX: Chromium window-size example format (#630)
Co-authored-by: smsnbv <active.sock7816@fastmail.com>
2024-04-22 07:12:30 -05:00
Aaron acb75543ff docs: Add docs about handling updates
Related to #620
2024-04-20 00:04:05 -05:00
Aaron 25b45b9c09 fix(dep): Upgrade @webext-core/isolated-element to v1.1.2 (#625) 2024-04-19 22:09:16 -05:00
Aaron efd5c40043 fix!: Automatically move host_permissions to permissions for MV2 (#626)
BREAKING CHANGE: Out of an abundance of caution, I've marked this as a breaking change because permission generation has changed. ***If you list `host_permissions` in your `wxt.config.ts`'s manifest and have released your extension***, double check that your `permissions` and `host_permissions` have not changed for all browsers you target. Permission changes can cause the extension to be disabled on update, and can cause a drop in users, so be sure to double check for differences compared to the previous manifest version.
2024-04-19 22:08:52 -05:00
484 changed files with 26577 additions and 10395 deletions
+3
View File
@@ -0,0 +1,3 @@
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository#about-funding-files
github: wxt-dev
-50
View File
@@ -1,50 +0,0 @@
---
name: Bug report
about: Report an issue with WXT
title: ''
labels: triage
assignees: ''
---
### Describe the bug
A clear and concise description of what the bug is.
### To Reproduce
Share minimal reproduction. Examples of a minimal reproduction:
- PR with a failing test case
- ZIP file containing a minimal WXT project (be sure to include your lockfile!!)
> **_⚠️ If you don't upload a minimal reproduction, your issue will be closed until a reproduction is added._**
>
> Why? https://antfu.me/posts/why-reproductions-are-required
Steps to reproduce the bug using the reproduction:
1. Install dependencies: `pnpm i`
2. Start dev mode: `pnpm dev`
3. Click this...
4. Do that...
5. Etc...
### Expected behavior
A clear and concise description of what you expected to happen.
### Screenshots
If applicable, add screenshots to help explain your problem.
### Environment
<!--- Run `npx envinfo --system --browsers --binaries --npmPackages wxt,vite` and paste the output below -->
```
Paste output here
```
### Additional context
Add any other context about the problem here, otherwise you can delete this section.
+74
View File
@@ -0,0 +1,74 @@
name: "\U0001F41E Bug report"
description: Report an issue with WXT
labels: [pending-triage]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
placeholder: I am doing ... What I expect is ... What actually happening is ...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: |
Please provide a minimal reproduction. This can include:
- A PR with a failing test case
- A link to a github repo
- A ZIP you upload to this issue
A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction or a partial reproduction, it will be closed immediately and labeled with as "needs-reproduction". Once a reproduction is provided, it will be re-opened.
placeholder: Reproduction URL or attach a ZIP
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Steps to reproduce
description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use.
placeholder: Run `npm install` followed by `npm run dev`
- type: textarea
id: system-info
attributes:
label: System Info
description: Output of `npx envinfo --system --browsers --binaries --npmPackages wxt,vite`
render: shell
placeholder: System, Binaries, Browsers
validations:
required: true
- type: dropdown
id: package-manager
attributes:
label: Used Package Manager
description: Select the used package manager
options:
- npm
- yarn
- pnpm
- bun
validations:
required: true
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Read the [Contributing Guidelines](https://github.com/wxt-dev/wxt/blob/main/CONTRIBUTING.md).
required: true
- label: Read the [docs](https://wxt.dev/guide/installation.html).
required: true
- label: Check that there isn't [already an issue](https://github.com/wxt-dev/wxt/issues) that reports the same bug to avoid creating a duplicate.
required: true
- label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/wxt-dev/wxt/discussions) or join our [Discord Chat Server](https://discord.gg/ZFsZqGery9).
required: true
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
required: true
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Discord Chat
url: https://discord.gg/ZFsZqGery9
about: Ask questions and discuss with other WXT users in real time.
- name: Questions & Discussions
url: https://github.com/wxt-dev/wxt/discussions
about: Use GitHub discussions for message-board style questions and discussions.
+10 -4
View File
@@ -1,17 +1,23 @@
name: Basic Setup
description: Install PNPM, Node, and dependencies
inputs:
install:
default: 'true'
description: Whether or not to run 'pnpm install'
installArgs:
default: ''
description: Additional args to append to "pnpm install"
runs:
using: composite
steps:
- name: Setup PNPM
uses: pnpm/action-setup@v3
with:
version: 8
uses: pnpm/action-setup@v4
- name: Setup NodeJS
uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- name: Install Dependencies
if: ${{ inputs.install == 'true' }}
shell: bash
run: pnpm install
run: pnpm install ${{ inputs.installArgs }}
+3 -1
View File
@@ -6,7 +6,9 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/' # Location of package manifests
directories:
- /
- packages/*
schedule:
interval: 'monthly'
- package-ecosystem: 'github-actions'
+13
View File
@@ -0,0 +1,13 @@
### Overview
<!-- Describe your changes and why you made them -->
### Manual Testing
<!-- Describe how to test your changes to make sure the PR works as intended -->
### Related Issue
<!-- If this PR is related to an issue, please link it here -->
This PR closes #<issue_number>
+17
View File
@@ -0,0 +1,17 @@
name: Continuous Publish
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm buildc all
- run: pnpx pkg-pr-new publish --compact './packages/*'
+2
View File
@@ -12,6 +12,8 @@ on:
jobs:
publish:
# Only run if it's the upstream repository, not forks
if: github.repository == 'wxt-dev/wxt'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
+21 -2
View File
@@ -1,6 +1,21 @@
name: Release
on:
workflow_dispatch:
inputs:
package:
description: Package to release
default: wxt
type: choice
options:
- auto-icons
- i18n
- module-react
- module-solid
- module-svelte
- module-vue
- storage
- unocss
- wxt
jobs:
validate:
@@ -22,14 +37,18 @@ jobs:
run: |
git config user.email "github-actions@users.noreply.github.com"
git config user.name "GitHub Actions"
pnpx changelogen@latest --release
pnpm tsx scripts/bump-package-version.ts ${{ inputs.package }}
git push
git push --tags
- name: NPM
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}" > ~/.npmrc
pnpm build
pnpm publish
working-directory: packages/${{ inputs.package }}
- name: GitHub Release
run: pnpx changelogen@latest gh release --token ${{ secrets.GITHUB_TOKEN }}
run: pnpm tsx scripts/create-github-release.ts ${{ inputs.package }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+19 -12
View File
@@ -1,22 +1,29 @@
name: Sync Releases
on:
workflow_dispatch:
push:
branches:
- main
paths:
- CHANGELOG.md
inputs:
package:
description: Package to sync
default: wxt
type: choice
options:
- auto-icons
- i18n
- module-react
- module-solid
- module-svelte
- module-vue
- storage
- wxt
jobs:
sync:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: ./.github/actions/setup
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- run: pnpm sync-releases all --token ${{ secrets.GITHUB_TOKEN }}
installArgs: --ignore-scripts
- run: pnpm tsx scripts/sync-releases.ts ${{ inputs.package }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+16 -12
View File
@@ -13,28 +13,30 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm check
validate-demo:
builds:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm buildc all
build-demo:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm tsc --noEmit
working-directory: demo
- run: pnpm build:all
working-directory: demo
working-directory: packages/wxt-demo
- run: pnpm wxt zip
working-directory: demo
- run: pnpm vitest run
working-directory: demo
working-directory: packages/wxt-demo
tests:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- name: pnpm test:coverage
run: pnpm test:coverage --reporter=default --reporter=hanging-process
- uses: codecov/codecov-action@v4
run: pnpm test:coverage -- --reporter=default --reporter=hanging-process
- uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
windows-tests:
@@ -42,10 +44,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm test run
- run: pnpm test
template:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
template:
- react
@@ -57,9 +60,10 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm pack
working-directory: packages/wxt
- run: npm i
working-directory: templates/${{ matrix.template }}
- run: npm i -D ../../wxt-*.tgz
- run: npm i -D ../../packages/wxt/wxt-*.tgz
working-directory: templates/${{ matrix.template }}
- run: pnpm compile
if: matrix.template != 'svelte'
+29
View File
@@ -0,0 +1,29 @@
name: vhs
on:
push:
paths:
- 'docs/tapes/*.tape'
workflow_dispatch:
jobs:
vhs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
with:
install: 'false'
- name: Preinstall WXT
run: |
pnpm store add wxt@latest
pnpm dlx wxt@latest --version
- uses: charmbracelet/vhs-action@v2.1.0
with:
path: 'docs/tapes/init-demo.tape'
- uses: stefanzweifel/git-auto-commit-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commit_message: 'docs: Update `wxt init` GIF'
# https://github.com/charmbracelet/vhs#output
file_pattern: 'docs/assets/*.gif'
+3 -1
View File
@@ -17,6 +17,8 @@ web-ext.config.ts
templates/*/pnpm-lock.yaml
templates/*/yarn.lock
templates/*/package-lock.json
docs/api
docs/api/reference
stats.html
.tool-versions
.cache
*-stats.txt
-1
View File
@@ -1 +0,0 @@
ignore-workspace-root-check=true
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
aaronklinker1@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+59 -5
View File
@@ -1,6 +1,6 @@
# Contributing
Everyone is welcome to contribute to WXT!
Everyone is welcome to contribute to **WXT**!
If you are changing the docs or fixing a bug, feel free to fork and open a PR.
@@ -32,18 +32,19 @@ Here are some helpful commands:
```sh
# Build WXT package
cd packages/wxt
pnpm build
```
```sh
# Build WXT package, then build demo extension
cd demo
cd packages/wxt-demo
pnpm build
```
```sh
# Build WXT package, then start the demo extension in dev mode
cd demo
cd packages/wxt-demo
pnpm dev
```
@@ -57,6 +58,21 @@ pnpm test
pnpm docs:dev
```
## Profiling
```sh
# Build the latest version
pnpm --filter wxt build
# CD to the demo directory
cd packages/wxt-demo
# 1. Generate a flamechart with 0x
pnpm dlx 0x node_modules/wxt/bin/wxt.mjs build
# 2. Inspect the process with chrome @ chrome://inspect
pnpm node --inspect node_modules/wxt/bin/wxt.mjs build
```
## Updating Docs
Documentation is written with VitePress, and is located in the `docs/` directory.
@@ -75,7 +91,7 @@ To run tests for a specific file, add the filename at the end of the test comman
pnpm test manifest-contents
```
Unit and E2E tests are ran together via [Vitest workspaces](https://vitest.dev/guide/#workspaces-support).
All test (unit and E2E) for all packages are ran together via [Vitest workspaces](https://vitest.dev/guide/#workspaces-support).
If you want to manually test a change, you can modify the demo project for your test, but please don't leave those changes committed once you open a PR.
@@ -90,7 +106,7 @@ npm run dev
npm run build
```
Note that templates are hardcoded to a specific version of `wxt` from NPM, they do not use the local version. PR checks will test your changes against the templates, but if you want to manually do it, update the package.json dependency:
Note that templates are hardcoded to a specific version of `wxt` from NPM, they do not use the local version. PR checks will test your PR's changes against the templates, but if you want to manually do it, update the package.json dependency:
```diff
"devDependencies": {
@@ -112,3 +128,41 @@ cp -r templates/vanilla templates/<new-template-name>
```
That's it. Once your template is merged, it will be available inside `wxt init` immediately. You don't need to release a new version of WXT to release a new template.
## Releasing Updates
Releases are done with GitHub actions:
- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.
## Upgrading Dependencies
Use [`taze`](https://www.npmjs.com/package/taze) to upgrade dependencies throughout the entire monorepo.
```sh
pnpm dlx taze -r
```
Configuration is in [`taze.config.ts`](./taze.config.ts).
## Install Unreleased Versions
This repo uses https://pkg.pr.new to publish versions of all it's packages for almost every commit. You can install them via:
```sh
npm i https://pkg.pr.new/[package-name]@[ref]
```
Or use one of the shorthands:
```sh
# Install the latest build of `wxt` from a PR:
npm i https://pkg.pr.new/wxt@1283
# Install the latest build of `@wxt-dev/module-react` on the `main` branch
npm i https://pkg.pr.new/@wxt-dev/module-react@main
# Install `@wxt-dev/storage` from a specific commit:
npm i https://pkg.pr.new/@wxt-dev/module-react@426f907
```
+37 -12
View File
@@ -1,16 +1,16 @@
<h1 align="center">
<img style="vertical-align:middle" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo">
<img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
<p align="center">
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="NPM" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
<a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="license | MIT" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="Codecov" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a>
<a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="coverage" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a>
</p>
<p align="center">
@@ -18,22 +18,22 @@
<br/>
<span>⚡</span>
<br/>
<q><i>It's like Nuxt, but for Chrome Extensions</i></q>
<q><i>It's like Nuxt, but for Web Extensions</i></q>
</p>
<p align="center">
<a href="https://wxt.dev" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/guide/installation.html" target="_blank">Installation</a>
<a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
&bull;
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
&bull;
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
## Demo
@@ -43,10 +43,21 @@ https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84
Bootstrap a new project:
<!-- automd:pm-x version="latest" name="wxt" args="init" -->
```sh
pnpm dlx wxt@latest init <project-name>
# npm
npx wxt@latest init
# pnpm
pnpm dlx wxt@latest init
# bun
bunx wxt@latest init
```
<!-- /automd -->
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
## Features
@@ -59,12 +70,26 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
- 🦾 Auto-imports
- 🤖 Automated publishing
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
- 📦 [Module system](https://wxt.dev/guide/essentials/wxt-modules.html#overview) for reusing code between extensions
- 🖍️ Quickly bootstrap a new project
- 📏 Bundle analysis
- ⬇️ Download and bundle remote URL imports
## Sponsors
WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
<a href="https://github.com/sponsors/wxt-dev"><img alt="WXT Sponsors" src="https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg"></a>
## Contributors
<!-- automd:contributors author="aklinker1" license="MIT" github="wxt-dev/wxt" -->
Published under the [MIT](https://github.com/wxt-dev/wxt/blob/main/LICENSE) license.
Made by [@aklinker1](https://github.com/aklinker1) and [community](https://github.com/wxt-dev/wxt/graphs/contributors) 💛
<br><br>
<a href="https://github.com/wxt-dev/wxt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
</a>
<!-- /automd -->
+15
View File
@@ -0,0 +1,15 @@
# Security Policy
While WXT is in prerelease, only the latest version will recieve security updates. The latest version is:
<img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048">
<!--
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
-->
-2
View File
@@ -1,2 +0,0 @@
#!/usr/bin/env node
import '../dist/cli.js';
-30
View File
@@ -1,30 +0,0 @@
{
"name": "demo",
"version": "1.0.0",
"description": "Demo extension for WXT",
"type": "module",
"scripts": {
"dev": "pnpm -w build && wxt",
"build": "pnpm -w build && wxt build",
"build:all": "pnpm -w build && run-s -s 'build:all:*'",
"build:all:chrome-mv3": "wxt build",
"build:all:chrome-mv2": "wxt build --mv2",
"build:all:firefox-mv3": "wxt build -b firefox --mv3",
"build:all:firefox-mv2": "wxt build -b firefox",
"test": "pnpm -w build && vitest",
"zip": "pnpm -w build && wxt zip",
"compile": "pnpm -w build && tsc --noEmit",
"postinstall": "pnpm -w build && wxt prepare"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vitest": "^1.2.2"
},
"devDependencies": {
"@types/react": "^18.2.34",
"@types/react-dom": "^18.2.14",
"sass": "^1.69.5",
"wxt": "workspace:*"
}
}
-4
View File
@@ -1,4 +0,0 @@
body {
margin: 0;
padding: 0;
}
-29
View File
@@ -1,29 +0,0 @@
{
"prompt_for_name": {
"message": "What's your name?",
"description": "Ask for the user's name"
},
"hello": {
"message": "Hello, $USER$",
"description": "Greet the user",
"placeholders": {
"user": {
"content": "$1",
"example": "Cira"
}
}
},
"bye": {
"message": "Goodbye, $USER$. Come back to $OUR_SITE$ soon!",
"description": "Say goodbye to the user",
"placeholders": {
"our_site": {
"content": "Example.com"
},
"user": {
"content": "$1",
"example": "Cira"
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

-7
View File
@@ -1,7 +0,0 @@
{
"extends": ["../tsconfig.base.json", "./.wxt/tsconfig.json"],
"compilerOptions": {
"allowImportingTsExtensions": true,
"jsx": "react-jsx"
}
}
-21
View File
@@ -1,21 +0,0 @@
import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
manifest: {
permissions: ['storage'],
default_locale: 'en',
web_accessible_resources: [
{
resources: ['/iframe-src.html'],
matches: ['*://*.google.com/*'],
},
],
},
zip: {
downloadPackages: ['sass'],
},
analysis: {
open: true,
},
});
@@ -8,14 +8,14 @@ const props = defineProps<{
<table class="no-vertical-dividers">
<thead>
<tr>
<th>Input Pattern</th>
<th style="width: 100%">Filename</th>
<th></th>
<th>Output Path</th>
</tr>
</thead>
<tbody>
<tr v-for="pattern of patterns">
<td style="white-space: nowrap">
<td style="white-space: nowrap; padding-right: 8px">
<code>entrypoints/{{ pattern[0] }}</code>
</td>
<td style="padding: 6px; opacity: 50%">
@@ -31,7 +31,7 @@ const props = defineProps<{
/>
</svg>
</td>
<td style="white-space: nowrap">
<td style="white-space: nowrap; padding-left: 8px">
<code>/{{ pattern[1] }}</code>
</td>
</tr>
@@ -1,34 +0,0 @@
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue';
const props = defineProps<{
tag?: string;
}>();
const examples = ref();
onMounted(async () => {
const res = await fetch(
'https://raw.githubusercontent.com/wxt-dev/wxt-examples/main/examples.json',
);
examples.value = await res.json();
});
const filteredExamples = computed(() => {
if (props.tag == null) return examples.value;
return examples.value.filter((example) => {
return example.tags?.includes(props.tag);
});
});
</script>
<template>
<ul>
<li v-if="examples == null">Loading...</li>
<template v-else>
<li v-for="example of filteredExamples">
<a :href="example.url" target="_blank">{{ example.name }}</a>
</li>
</template>
</ul>
</template>
@@ -0,0 +1,226 @@
<script lang="ts" setup>
import { ref, onMounted, computed, toRaw, Ref } from 'vue';
import ExampleSearchFilterByItem from './ExampleSearchFilterByItem.vue';
import ExampleSearchResult from './ExampleSearchResult.vue';
import { ExamplesMetadata, KeySelectedObject } from '../utils/types';
const props = defineProps<{
tag?: string;
}>();
const exampleMetadata = ref<ExamplesMetadata>();
onMounted(async () => {
const res = await fetch(
'https://raw.githubusercontent.com/wxt-dev/examples/main/metadata.json',
);
exampleMetadata.value = await res.json();
});
const searchText = ref('');
const selectedApis = ref<KeySelectedObject>({});
const selectedPermissions = ref<KeySelectedObject>({});
const selectedPackages = ref<KeySelectedObject>({});
function useRequiredItems(selectedItems: Ref<KeySelectedObject>) {
return computed(() =>
Array.from(
Object.entries(toRaw(selectedItems.value)).reduce(
(set, [pkg, checked]) => {
if (checked) set.add(pkg);
return set;
},
new Set<string>(),
),
),
);
}
const requiredApis = useRequiredItems(selectedApis);
const requiredPermissions = useRequiredItems(selectedPermissions);
const requiredPackages = useRequiredItems(selectedPackages);
function doesExampleMatchSelected(
exampleItems: string[],
requiredItems: Ref<string[]>,
) {
const exampleItemsSet = new Set(exampleItems);
return !requiredItems.value.find((item) => !exampleItemsSet.has(item));
}
const filteredExamples = computed(() => {
const text = searchText.value.toLowerCase();
return exampleMetadata.value.examples.filter((example) => {
const matchesText = example.searchText.toLowerCase().includes(text);
const matchesApis = doesExampleMatchSelected(example.apis, requiredApis);
const matchesPermissions = doesExampleMatchSelected(
example.permissions,
requiredPermissions,
);
const matchesPackages = doesExampleMatchSelected(
example.packages,
requiredPackages,
);
return matchesText && matchesApis && matchesPermissions && matchesPackages;
});
});
</script>
<template>
<div class="example-layout">
<div class="search">
<input v-model="searchText" placeholder="Search for an example..." />
</div>
<div class="filters">
<ExampleSearchFilterByItem
label="APIs"
:items="exampleMetadata?.allApis"
v-model="selectedApis"
/>
<ExampleSearchFilterByItem
label="Permissions"
:items="exampleMetadata?.allPermissions"
v-model="selectedPermissions"
/>
<ExampleSearchFilterByItem
label="Packages"
:items="exampleMetadata?.allPackages"
v-model="selectedPackages"
/>
</div>
<div class="results">
<p v-if="exampleMetadata == null">Loading examples...</p>
<template v-else>
<ul class="search-results">
<ExampleSearchResult
v-for="example of filteredExamples"
:key="example.name"
:example
/>
</ul>
<p v-if="filteredExamples.length === 0">No matching examples</p>
</template>
</div>
</div>
</template>
<style scoped>
.example-layout {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-areas:
'search'
'results';
gap: 16px;
}
@media only screen and (min-width: 720px) {
.example-layout {
grid-template-columns: 256px 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
'filters search'
'filters results';
}
}
.search {
grid-area: search;
background: var(--vp-c-bg-soft);
padding: 20px;
width: 100%;
display: flex;
border-radius: 16px;
}
.filters {
display: none;
grid-area: filters;
}
@media only screen and (min-width: 720px) {
.filters {
display: flex;
flex-direction: column;
gap: 2px;
border-radius: 16px;
overflow: hidden;
align-self: flex-start;
}
}
.results {
grid-area: results;
}
.box {
border-radius: 16px;
overflow: hidden;
}
.search input {
min-width: 0;
flex: 1;
font-size: 16px;
}
.checkbox-col {
flex: 1;
padding: 16px;
display: flex;
flex-direction: column;
overflow-y: auto;
max-height: 200px;
font-size: 14px;
gap: 4px;
}
.filter-btn {
color: var(--vp-c-brand-1);
}
.checkbox-col .header {
font-size: 12px;
font-weight: bold;
opacity: 50%;
}
.checkbox-col p {
display: flex;
gap: 4px;
align-items: flex-start;
text-wrap: wrap;
overflow-wrap: anywhere;
line-height: 140%;
}
span {
padding-top: 1px;
}
.checkbox-col input[type='checkbox'] {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.checkbox-col-container {
display: flex;
}
.search-results {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 16px;
}
@media only screen and (min-width: 800px) {
.search-results {
grid-template-columns: repeat(2, 1fr);
}
}
@media only screen and (min-width: 1024px) {
.search-results {
grid-template-columns: repeat(3, 1fr);
}
}
a {
background-color: red;
}
</style>
@@ -0,0 +1,100 @@
<script lang="ts" setup>
import { computed, toRaw } from 'vue';
import { KeySelectedObject } from '../utils/types';
const props = defineProps<{
label: string;
items?: string[];
}>();
const selectedItems = defineModel<KeySelectedObject>({
required: true,
});
const count = computed(() => {
return Object.values(toRaw(selectedItems.value)).filter(Boolean).length;
});
function toggleItem(pkg: string) {
selectedItems.value = {
...toRaw(selectedItems.value),
[pkg]: !selectedItems.value[pkg],
};
}
</script>
<template>
<div class="filter-container">
<p class="header">
<span>Filter by {{ label }}</span> <span v-if="count">({{ count }})</span>
</p>
<div class="scroll-container">
<ul>
<li v-for="item in items">
<label :title="item">
<input
type="checkbox"
:checked="selectedItems[item]"
@input="toggleItem(item)"
/>
<span>{{ item }}</span>
</label>
</li>
</ul>
</div>
</div>
</template>
<style scoped>
.filter-container {
height: 300px;
display: flex;
flex-direction: column;
background: var(--vp-c-bg-soft);
}
.scroll-container {
flex: 1;
overflow: hidden;
position: relative;
}
.scroll-container ul {
position: absolute;
overflow-y: auto;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
gap: 4px;
font-size: small;
padding: 8px 16px 16px 16px;
}
.header {
padding: 8px 16px;
font-size: 12px;
font-weight: bold;
opacity: 50%;
}
label {
display: flex;
gap: 4px;
align-items: flex-start;
text-wrap: wrap;
overflow-wrap: anywhere;
line-height: 140%;
cursor: pointer;
text-wrap: nowrap;
}
span {
padding-top: 1px;
}
input[type='checkbox'] {
width: 16px;
height: 16px;
flex-shrink: 0;
}
</style>
@@ -0,0 +1,72 @@
<script lang="ts" setup>
import { Example } from '../utils/types';
const props = defineProps<{
example: Example;
}>();
</script>
<template>
<li>
<a :href="example.url" target="_blank">
<p class="name">{{ example.name }}</p>
<p class="description">{{ example.description }}</p>
<p class="link">Open &rarr;</p>
</a>
</li>
</template>
<style scoped>
* {
min-width: 0;
}
a {
padding: 16px;
display: flex;
flex-direction: column;
border: 2px solid var(--vp-c-bg-soft);
border-radius: 16px;
color: var(--vp-c-text-1) !important;
gap: 8px;
}
a:hover {
outline: 2px solid var(--vp-c-brand-2);
}
.name {
font-size: 16px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.description {
height: 53px;
opacity: 70%;
font-size: 14px;
font-weight: normal;
line-height: 120%;
min-height: 0;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.link {
opacity: 0;
transition: 250ms;
color: var(--vp-c-brand-2);
font-weight: bold;
text-align: right;
}
a:hover .link {
opacity: 100%;
}
</style>
+89 -84
View File
@@ -4,11 +4,8 @@ import useListExtensionDetails, {
ChromeExtension,
} from '../composables/useListExtensionDetails';
// Add extension IDs here. Order doesn't matter, will be sorted by weekly active users
// During the transition from chrome.google.com/webstore to
// chromewebstore.google.com, queue.wxt.dev might return null for your
// extension. If it does, use "<slug>/<id>" instead of just the ID. The slug
// can be retrieved from the URL of the item on chromewebstore.google.com
// Add extension IDs here. Order doesn't matter, will be sorted by a combination of weekly active users and rating.
// Change the commit message or PR title to: "docs: Added "[extension name]" to the homepage"
const chromeExtensionIds = [
'ocfdgncpifmegplaglcnglhioflaimkd', // GitHub: Better Line Counts
'mgmdkjcljneegjfajchedjpdhbadklcf', // Anime Skip Player
@@ -16,7 +13,7 @@ const chromeExtensionIds = [
'elfaihghhjjoknimpccccmkioofjjfkf', // StayFree - Website Blocker & Web Analytics
'okifoaikfmpfcamplcfjkpdnhfodpkil', // Doozy: Ai Made Easy
'lknmjhcajhfbbglglccadlfdjbaiifig', // tl;dv - Record, Transcribe & ChatGPT for Google Meet
'youtube中文配音/oglffgiaiekgeicdgkdlnlkhliajdlja', // Youtube中文配音
'oglffgiaiekgeicdgkdlnlkhliajdlja', // Youtube中文配音
'agjnjboanicjcpenljmaaigopkgdnihi', // PreMiD
'aiakblgmlabokilgljkglggnpflljdgp', // Markdown Sticky Notes
'nomnkbngkijpffepcgbbofhcnafpkiep', // DocVersionRedirector
@@ -24,7 +21,49 @@ const chromeExtensionIds = [
'aelkipgppclpfimeamgmlonimflbhlgf', // GitHub Custom Notifier
'djnlaiohfaaifbibleebjggkghlmcpcj', // Fluent Read
'nhclljcpfmmaiojbhhnkpjcfmacfcian', // Facebook Video Controls
'mblkhbaakhbhiimkbcnmeciblfhmafna', // ElemSnap - Quick capture of webpage elements and conversion to images
'mblkhbaakhbhiimkbcnmeciblfhmafna', // ElemSnap - Quick capture of webpage elements and conversion to images,
'oajalfneblkfiejoadecnmodfpnaeblh', // MS Edge TTS (Text to Speech)
'nedcanggplmbbgmlpcjiafgjcpdimpea', // YTBlock - Block any content from YouTube™
'oadbjpccljkplmhnjekgjamejnbadlne', // demo.fun - Interactive product demos that convert
'iopdafdcollfgaoffingmahpffckmjni', // SmartEReply: Elevate Your LinkedIn™ Engagement with AI 🚀📈
'khjdmjcmpolknpccmaaipmidphjokhdf', // WorkFlowy MultiFlow
'fencadnndhdeggodopebjgdfdlhcimfk', // 香草布丁🌿🍮- https://github.com/Xdy1579883916/vanilla-pudding
'bnacincmbaknlbegecpioobkfgejlojp', // MaxFocus: Link Preview
'bcpgdpedphodjcjlminjbdeejccjbimp', // 汇率转换-中文版本
'loeilaonggnalkaiiaepbegccilkmjjp', // Currency Converter Plus
'npcnninnjghigjfiecefheeibomjpkak', // Respond Easy
'cfkdcideecefncbglkhneoflfnmhoicc', // mindful - stay focused on your goals
'lnhejcpclabmbgpiiomjbhalblnnbffg', // 1Proompt
'fonflmjnjbkigocpoommgmhljdpljain', // NiceTab - https://github.com/web-dahuyou/NiceTab
'fcffekbnfcfdemeekijbbmgmkognnmkd', // Draftly for LinkedIn
'nkndldfehcidpejfkokbeghpnlbppdmo', // YouTube Summarized - Summarize any YouTube video
'dbichmdlbjdeplpkhcejgkakobjbjalc', // 社媒助手 - https://github.com/iszhouhua/social-media-copilot
'opepfpjeogkbgeigkbepobceinnfmjdd', // Dofollow Links for SEO
'pdnenlnelpdomajfejgapbdpmjkfpjkp', // ChatGPT Writer: Use AI on Any Site (GPT-4o, Claude, Gemini, and More)
'jobnhifpphkgoelnhnopgkdhbdkiadmj', // discord message translator
'ncokhechhpjgjonhjnlaneglmdkfkcbj', // Habit Tracker app widget for daily habit tracking
'lnjaiaapbakfhlbjenjkhffcdpoompki', // Catppuccin for GitHub File Explorer Icons
'cpaedhbidlpnbdfegakhiamfpndhjpgf', // WebChat: Chat with anyone on any website
'fcphghnknhkimeagdglkljinmpbagone', // YouTube Auto HD + FPS
'lpomjgbicdemjkgmbnkjncgdebogkhlb', // MultiViewer Companion
'ggiafipgeeaaahnjamgpjcgkdpanhddg', // Sync Watch - Watch videos together on any site
'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker
'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more
'pccbghdfdnnkkbcdcibchpbffdgednkf', // Propbar - Property Data Enhancer
'lfknakglefggmdkjdfhhofkjnnolffkh', // Text Search Pro - Search by case and whole-word match!
'mbenhbocjckkbaojacmaepiameldglij', // Invoice Generator
'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker
'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI
'lhmgechokhmdekdpgkkemoeecelcaonm', // YouTube Hider - Remove Comments By Keywords, Usernames & Tools
'imgheieooppmahcgniieddodaliodeeg', // QA Compass - Record standardized bug reports easily
'npgghjedpchajflknnbngajkjkdhncdo', // aesthetic Notion, styled
'hmdcmlfkchdmnmnmheododdhjedfccka', // Eye Dropper
'eihpmapodnppeemkhkbhikmggfojdkjd', // Cursorful - Screen Recorder with Auto Zoom
'hjjkgbibknbahijglkffklflidncplkn', // Show IP Live View of Website IPs for Developers
'ilbikcehnpkmldojkcmlldkoelofnbde', // Strong Password Generator
'ocllfkhcdopiafndigclebelbecaiocp', // ZenGram: Mindful Instagram, Your Way
'odffpjnpocjfcaclnenaaaddghkgijdb', // Blync: Preview Links, Selection Search, AI Assistant
'kofbbilhmnkcmibjbioafflgmpkbnmme', // HTML to Markdown - Convert webpages to markdown
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
@@ -50,90 +89,56 @@ function getStoreUrl(extension: ChromeExtension) {
</script>
<template>
<section class="vp-doc">
<div class="container">
<h2 id="whos-using-wxt">Who's Using WXT?</h2>
<p>
Battle tested and ready for production. Explore chrome extensions made
with WXT.
</p>
<p v-if="isLoading" style="text-align: center; opacity: 50%">
Loading...
</p>
<p
v-else-if="err || sortedExtensions.length === 0"
style="text-align: center; opacity: 50%"
>
Failed to load extension details.
</p>
<ul v-else>
<li
v-for="extension of sortedExtensions"
:key="extension.id"
class="relative"
>
<img
:src="extension.iconUrl"
:alt="`${extension.name} icon`"
referrerpolicy="no-referrer"
/>
<div class="relative">
<a
:href="getStoreUrl(extension)"
target="_blank"
:title="extension.name"
class="extension-name"
>{{ extension.name }}</a
>
<p class="description" :title="extension.shortDescription">
{{ extension.shortDescription }}
</p>
</div>
<p class="user-count">
<span>{{ extension.weeklyActiveUsers.toLocaleString() }} users</span
><template v-if="extension.rating != null"
>,
<span>{{ extension.rating }} stars</span>
</template>
</p>
</li>
</ul>
<p class="centered pr">
<p v-if="isLoading" style="text-align: center; opacity: 50%">Loading...</p>
<p
v-else-if="err || sortedExtensions.length === 0"
style="text-align: center; opacity: 50%"
>
Failed to load extension details.
</p>
<ul v-else>
<li
v-for="extension of sortedExtensions"
:key="extension.id"
class="relative"
>
<img
:src="extension.iconUrl"
:alt="`${extension.name} icon`"
referrerpolicy="no-referrer"
/>
<div class="relative">
<a
href="https://github.com/wxt-dev/wxt/edit/main/docs/.vitepress/components/UsingWxtSection.vue"
:href="getStoreUrl(extension)"
target="_blank"
>Open a PR</a
:title="extension.name"
class="extension-name"
>{{ extension.name }}</a
>
to add your extension to the list!
<p class="description" :title="extension.shortDescription">
{{ extension.shortDescription }}
</p>
</div>
<p class="user-count">
<span>{{ extension.weeklyActiveUsers.toLocaleString() }} users</span
><template v-if="extension.rating != null"
>,
<span>{{ extension.rating }} stars</span>
</template>
</p>
</div>
</section>
</li>
</ul>
<p class="centered pr">
<a
href="https://github.com/wxt-dev/wxt/edit/main/docs/.vitepress/components/UsingWxtSection.vue"
target="_blank"
>Open a PR</a
>
to add your extension to the list!
</p>
</template>
<style scoped>
.vp-doc {
padding: 0 24px;
}
@media (min-width: 640px) {
.vp-doc {
padding: 0 48px;
}
}
@media (min-width: 960px) {
.vp-doc {
padding: 0 64px;
}
}
.container {
max-width: 1152px;
margin: 0 auto;
display: flex;
flex-direction: column;
}
li img {
width: 116px;
height: 116px;
+137 -111
View File
@@ -1,25 +1,24 @@
import { DefaultTheme, defineConfig } from 'vitepress';
import { generateCliDocs } from './plugins/generate-cli-docs';
import typedocSidebar from '../api/typedoc-sidebar.json';
const filteredTypedocSidebar = typedocSidebar.filter(
(item) => item.text !== 'API',
);
// Typedoc's markdown theme adds collapse: true to all our items, event ones without any children,
// so they need to be removed.
function removeCollapsedWithNoItems(items: DefaultTheme.SidebarItem[]) {
for (const item of items) {
if (item.items) removeCollapsedWithNoItems(item.items);
else delete item.collapsed;
}
}
removeCollapsedWithNoItems(filteredTypedocSidebar);
import { defineConfig } from 'vitepress';
import typedocSidebar from '../api/reference/typedoc-sidebar.json';
import {
menuGroup,
menuItem,
menuRoot,
navItem,
prepareTypedocSidebar,
} from './utils/menus';
import { meta, script } from './utils/head';
import footnote from 'markdown-it-footnote';
import { version as wxtVersion } from '../../packages/wxt/package.json';
import { version as i18nVersion } from '../../packages/i18n/package.json';
import { version as autoIconsVersion } from '../../packages/auto-icons/package.json';
import { version as unocssVersion } from '../../packages/unocss/package.json';
import { version as storageVersion } from '../../packages/storage/package.json';
const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' WXT';
const description =
"WXT provides the best developer experience, making it quick, easy, and fun to develop chrome extensions for all browsers. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
"WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
const ogTitle = `${title}${titleSuffix}`;
const ogUrl = 'https://wxt.dev';
const ogImage = 'https://wxt.dev/social-preview.png';
@@ -31,7 +30,6 @@ export default defineConfig({
description,
vite: {
clearScreen: false,
plugins: [generateCliDocs()],
},
lastUpdated: true,
sitemap: {
@@ -39,116 +37,144 @@ export default defineConfig({
},
head: [
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:title', content: ogTitle }],
['meta', { property: 'og:image', content: ogImage }],
['meta', { property: 'og:url', content: ogUrl }],
['meta', { property: 'og:description', content: description }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
[
'script',
{
async: '',
'data-website-id': 'c1840c18-a12c-4a45-a848-55ae85ef7915',
src: 'https://umami.aklinker1.io/script.js',
},
],
meta('og:type', 'website'),
meta('og:title', ogTitle),
meta('og:image', ogImage),
meta('og:url', ogUrl),
meta('og:description', description),
meta('twitter:card', 'summary_large_image', { useName: true }),
script('https://umami.aklinker1.io/script.js', {
'data-website-id': 'c1840c18-a12c-4a45-a848-55ae85ef7915',
async: '',
}),
],
markdown: {
config: (md) => {
md.use(footnote);
},
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: {
src: '/logo.svg',
alt: 'WXT logo',
},
footer: {
message: [
'<a class="light-netlify" href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-bg.svg" alt="Deploys by Netlify" style="display: inline;" /></a>',
'<a class="dark-netlify" href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" style="display: inline;" /></a>',
'Released under the <a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE">MIT License</a>.',
].join('<br/>'),
copyright:
'Copyright © 2023-present <a href="https://github.com/aklinker1">Aaron Klinker</a>',
},
editLink: {
pattern: 'https://github.com/wxt-dev/wxt/edit/main/docs/:path',
},
search: {
provider: 'local',
},
nav: [
{ text: 'Guide', link: '/guide/installation.md' },
{ text: 'Entrypoints', link: '/entrypoints/background.md' },
{ text: 'Examples', link: '/examples.md' },
{ text: 'API', link: '/api/cli.md' },
],
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Introduction', link: '/guide/introduction.md' },
{ text: 'Installation', link: '/guide/installation.md' },
{ text: 'Configuration', link: '/guide/configuration.md' },
{ text: 'Entrypoints', link: '/guide/entrypoints.md' },
{ text: 'Manifest.json', link: '/guide/manifest.md' },
{ text: 'Extension APIs', link: '/guide/extension-apis.md' },
{ text: 'Storage', link: '/guide/storage.md' },
{ text: 'Assets', link: '/guide/assets.md' },
{ text: 'Content Script UI', link: '/guide/content-script-ui.md' },
{ text: 'Multiple Browsers', link: '/guide/multiple-browsers.md' },
{ text: 'ES Modules', link: '/guide/esm.md' },
{ text: 'Auto-imports', link: '/guide/auto-imports.md' },
{ text: 'Vite', link: '/guide/vite.md' },
{ text: 'Remote Code', link: '/guide/remote-code.md' },
{ text: 'Publishing', link: '/guide/publishing.md' },
{ text: 'Development', link: '/guide/development.md' },
{ text: 'Testing', link: '/guide/testing.md' },
],
},
{
text: 'Other',
items: [
{ text: 'Migrate to WXT', link: '/guide/migrate-to-wxt.md' },
{ text: 'Compare', link: '/guide/compare.md' },
],
},
],
'/entrypoints/': [
{
text: 'Entrypoints',
items: [
{ text: 'Background', link: '/entrypoints/background.md' },
{ text: 'Bookmarks', link: '/entrypoints/bookmarks.md' },
{
text: 'Content Scripts',
link: '/entrypoints/content-scripts.md',
},
{ text: 'CSS', link: '/entrypoints/css.md' },
{ text: 'Devtools', link: '/entrypoints/devtools.md' },
{ text: 'History', link: '/entrypoints/history.md' },
{ text: 'Newtab', link: '/entrypoints/newtab.md' },
{ text: 'Options', link: '/entrypoints/options.md' },
{ text: 'Popup', link: '/entrypoints/popup.md' },
{ text: 'Sandbox', link: '/entrypoints/sandbox.md' },
{ text: 'Side Panel', link: '/entrypoints/sidepanel.md' },
{ text: 'Unlisted Pages', link: '/entrypoints/unlisted-pages.md' },
{
text: 'Unlisted Scripts',
link: '/entrypoints/unlisted-scripts.md',
},
].sort((l, r) => l.text.localeCompare(r.text)),
},
],
'/api/': [
{
items: [
{ text: 'CLI', link: '/api/cli.md' },
{
text: 'Modules',
items: filteredTypedocSidebar,
},
],
},
],
},
socialLinks: [
{ icon: 'discord', link: 'https://discord.gg/ZFsZqGery9' },
{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' },
],
nav: [
navItem('Guide', '/guide/installation'),
navItem('Examples', '/examples'),
navItem('API', '/api/reference/wxt'),
navItem(`v${wxtVersion}`, [
navItem('wxt', [
navItem(`v${wxtVersion}`, '/'),
navItem(
`Changelog`,
'https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md',
),
]),
navItem('Other Packages', [
navItem(`@wxt-dev/storage — ${storageVersion}`, '/storage'),
navItem(`@wxt-dev/auto-icons — ${autoIconsVersion}`, '/auto-icons'),
navItem(`@wxt-dev/i18n — ${i18nVersion}`, '/i18n'),
navItem(`@wxt-dev/unocss — ${unocssVersion}`, '/unocss'),
]),
]),
],
sidebar: {
'/guide/': menuRoot([
menuGroup('Get Started', '/guide/', [
menuItem('Introduction', 'introduction.md'),
menuItem('Installation', 'installation.md'),
]),
menuGroup('Essentials', '/guide/essentials/', [
menuItem('Project Structure', 'project-structure.md'),
menuItem('Entrypoints', 'entrypoints.md'),
menuGroup(
'Configuration',
'/guide/essentials/config/',
[
menuItem('Manifest', 'manifest.md'),
menuItem('Browser Startup', 'browser-startup.md'),
menuItem('Auto-imports', 'auto-imports.md'),
menuItem('Environment Variables', 'environment-variables.md'),
menuItem('Runtime Config', 'runtime.md'),
menuItem('Vite', 'vite.md'),
menuItem('Build Mode', 'build-mode.md'),
menuItem('TypeScript', 'typescript.md'),
menuItem('Hooks', 'hooks.md'),
menuItem('Entrypoint Loaders', 'entrypoint-loaders.md'),
],
true,
),
menuItem('Extension APIs', 'extension-apis.md'),
menuItem('Assets', 'assets.md'),
menuItem('Target Different Browsers', 'target-different-browsers.md'),
menuItem('Content Scripts', 'content-scripts.md'),
menuItem('Storage', 'storage.md'),
menuItem('Messaging', 'messaging.md'),
menuItem('I18n', 'i18n.md'),
menuItem('Scripting', 'scripting.md'),
menuItem('WXT Modules', 'wxt-modules.md'),
menuItem('Frontend Frameworks', 'frontend-frameworks.md'),
menuItem('ES Modules', 'es-modules.md'),
menuItem('Remote Code', 'remote-code.md'),
menuItem('Unit Testing', 'unit-testing.md'),
menuItem('E2E Testing', 'e2e-testing.md'),
menuItem('Publishing', 'publishing.md'),
menuItem('Testing Updates', 'testing-updates.md'),
]),
menuGroup('Resources', '/guide/resources/', [
menuItem('Compare', 'compare.md'),
menuItem('FAQ', 'faq.md'),
menuItem('Upgrading WXT', 'upgrading.md'),
menuItem('Migrate to WXT', 'migrate.md'),
menuItem('How WXT Works', 'how-wxt-works.md'),
]),
]),
'/api/': menuRoot([
menuGroup(
'CLI Reference',
'/api/cli/',
[
menuItem('wxt', 'wxt.md'),
menuItem('wxt build', 'wxt-build.md'),
menuItem('wxt zip', 'wxt-zip.md'),
menuItem('wxt prepare', 'wxt-prepare.md'),
menuItem('wxt clean', 'wxt-clean.md'),
menuItem('wxt init', 'wxt-init.md'),
menuItem('wxt submit', 'wxt-submit.md'),
menuItem('wxt submit init', 'wxt-submit-init.md'),
],
true,
),
menuGroup('API Reference', prepareTypedocSidebar(typedocSidebar), true),
]),
},
},
});
+61
View File
@@ -0,0 +1,61 @@
import { resolve } from 'node:path';
import consola from 'consola';
import spawn from 'nano-spawn';
const cliDir = resolve('packages/wxt/src/cli/commands');
const cliDirGlob = resolve(cliDir, '**');
export default {
watch: [cliDirGlob],
async load() {
consola.info(`Generating CLI docs`);
const [wxt, build, zip, prepare, clean, init, submit, submitInit] =
await Promise.all([
getWxtHelp(''),
getWxtHelp('build'),
getWxtHelp('zip'),
getWxtHelp('prepare'),
getWxtHelp('clean'),
getWxtHelp('init'),
getPublishExtensionHelp(''),
getPublishExtensionHelp('init'),
]);
consola.success(`Generated CLI docs`);
return {
wxt,
build,
zip,
prepare,
clean,
init,
submit,
submitInit,
};
},
};
async function getHelp(command: string): Promise<string> {
const args = command.split(' ');
const res = await spawn(args[0], [...args.slice(1), '--help'], {
cwd: 'packages/wxt',
});
return res.stdout;
}
function getWxtHelp(command: string): Promise<string> {
return getHelp(`pnpm -s wxt ${command}`.trim());
}
async function getPublishExtensionHelp(command: string): Promise<string> {
const res = await getHelp(
`./node_modules/.bin/publish-extension ${command}`.trim(),
);
return res.replace(/\$ publish-extension/g, '$ wxt submit');
}
export interface Command {
name: string;
docs: string;
}
-5
View File
@@ -1,5 +0,0 @@
# CLI Reference
> Reference generated from `wxt <command> --help`
{{ DOCS }}
@@ -1,114 +0,0 @@
import { relative, resolve } from 'node:path';
import { readFileSync, writeFileSync } from 'node:fs';
import { format } from 'prettier';
import consola from 'consola';
import { execaCommand } from 'execa';
let hasGenerated = false;
const cliDir = resolve('src/cli/commands');
const cliDirGlob = resolve(cliDir, '**');
const cliTemplatePath = resolve('docs/.vitepress/plugins/cli.tpl.md');
const cliPath = resolve('docs/api/cli.md');
const PREFACE = `<!--
DO NOT EDIT
Generated by \`wxt/docs/.vitepress/plugins/generate-config-docs.ts\`
To make changes to the config reference, update the JSDoc in \`src/core/types/external.ts\`.
-->`;
export function generateCliDocs() {
writeFileSync(cliPath, '');
const generateDocs = async () => {
consola.info(`Generating ${relative(process.cwd(), cliPath)}`);
try {
const res = await execaCommand('pnpm -s wxt --help');
const dev = splitInfo(res.stdout);
const lines: Array<string | string[]> = [
`## \`wxt\``,
'```sh',
dev.rest,
'```',
];
const commands = await Promise.allSettled(
extractCommands(dev.info).map(async (command) => {
const res = await execaCommand(`pnpm -s wxt ${command} --help`);
const { rest: docs } = splitInfo(res.stdout);
return [`## \`wxt ${command}\``, '```sh', docs, '```'];
}),
);
lines.push(
...commands
.filter((res) => res.status === 'fulfilled')
.map(
(res) => (res as unknown as PromiseFulfilledResult<string>).value,
),
);
const text = await format(
PREFACE +
'\n\n' +
readFileSync(cliTemplatePath, 'utf-8').replace(
'{{ DOCS }}',
lines.flat().join('\n'),
),
{ parser: 'markdown' },
);
writeFileSync(cliPath, text);
consola.success(`Generated ${relative(process.cwd(), cliPath)}`);
} catch (err) {
consola.fail(`Failed to generate ${relative(process.cwd(), cliPath)}`);
consola.error(err.message);
}
};
return {
name: 'docs:generate-cli-docs',
async config() {
if (!hasGenerated) {
hasGenerated = true;
await generateDocs();
}
},
configureServer(server: any) {
server.watcher.add(cliDirGlob);
},
async handleHotUpdate(ctx: { file: string }) {
if (ctx.file === cliTemplatePath || ctx.file.includes(cliDir)) {
await generateDocs();
}
},
};
}
function splitInfo(text: string): { info: string; rest: string } {
const infoStart = text.indexOf('For more info,');
const infoEnd = text.indexOf('\n\nOptions:');
if (infoStart === -1 || infoEnd === -1) {
return { info: '', rest: text };
}
const info = text.substring(infoStart, infoEnd).trim();
const rest = text.replace(info, '').trim().replace('\n\n\n\n', '\n\n');
return { info, rest };
}
function extractCommands(info: string): string[] {
const commands: string[] = [];
// Split the info by line and iterate through each line
info.split('\n').forEach((line) => {
// Use regex to capture the command after "$ wxt " and before "--help"
const match = line.match(/\$ wxt (\w+) --help/);
if (match && match[1]) {
commands.push(match[1]);
}
});
return commands;
}
+47 -56
View File
@@ -1,77 +1,68 @@
/* Colors */
:root {
--wxt-c-green: #53bc4a;
--wxt-c-green-1: #0b8a00;
--wxt-c-green-2: #096600;
--wxt-c-green-3: #096600;
--wxt-c-green-soft: rgba(11, 138, 0, 0.14);
}
.dark {
--wxt-c-green-1: #67d45e;
--wxt-c-green-2: #4fa048;
--wxt-c-green-3: #447e3f;
--wxt-c-green-2: #329929;
--wxt-c-green-3: #21651b;
--wxt-c-green-soft: rgba(103, 212, 94, 0.14);
}
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
:root {
--vp-c-brand: var(--wxt-c-green);
--vp-c-brand-1: var(--wxt-c-green-1);
--vp-c-brand-2: var(--wxt-c-green-2);
--vp-c-brand-3: var(--wxt-c-green-3);
--vp-button-brand-bg: var(--wxt-c-green);
--vp-button-brand-hover-bg: var(--wxt-c-green-2);
--vp-button-brand-active-bg: var(--wxt-c-green-3);
--vp-code-link-color: var(--wxt-c-green);
/* --vp-c-text-1: var(--wxt-c-green-1); */
--vp-button-brand-text: var(--vp-c-black);
--vp-button-brand-hover-text: var(--vp-c-black);
--vp-button-brand-active-text: var(--vp-c-black);
--vp-custom-block-tip-border: var(--wxt-c-green);
--vp-custom-block-tip-text: var(--wxt-c-green);
/* --vp-code-block-bg: #222422;
--vp-code-copy-code-bg: #313431;
--vp-code-copy-code-hover-bg: #3c403c; */
--vp-custom-block-tip-bg: var(--vp-code-block-bg);
--vp-custom-block-info-bg: var(--vp-code-block-bg);
--vp-code-color: #476582;
--vp-c-brand-soft: var(--wxt-c-green-soft);
}
.vp-doc a {
color: var(--wxt-c-green);
}
.dark {
--vp-c-bg: #131413;
--vp-c-bg-soft: #1a1b1a;
--vp-c-bg-soft-up: #1f201f;
--vp-c-bg-soft-down: #262926;
--vp-c-bg-soft-mute: #242424;
--vp-c-bg-alt: #171817;
--vp-c-default: #313136;
--vp-c-default-1: #3a3a3c;
--vp-c-default-2: #505053;
--vp-c-default-3: #2c2c30;
--vp-c-default-soft: #252529;
--vp-code-block-bg: #191a19;
--vp-code-copy-code-bg: #212321;
--vp-code-copy-code-hover-bg: #292d29;
--vp-custom-block-info-bg: #191a19;
--vp-code-color: #c9def1;
}
/* Customize Individual Components */
.vp-doc .no-vertical-dividers th,
.vp-doc .no-vertical-dividers td {
border: none;
}
.vp-doc .no-vertical-dividers tr {
border: 1px solid var(--vp-c-divider);
}
body {
overflow-y: scroll;
}
.VPSidebar {
user-select: none;
}
.VPSidebarItem .badge {
display: inline-block;
min-width: 1.6em;
padding: 0.3em 0.4em 0.2em;
border-radius: 1rem;
font-size: 0.75em;
line-height: 1;
margin-left: 0.5rem;
text-align: center;
vertical-align: middle;
background-color: var(--vp-c-default-2);
}
.light-netlify {
display: inline;
}
.dark .light-netlify {
display: none;
}
.dark-netlify {
display: none;
}
.dark .dark-netlify {
display: inline;
}
+2 -2
View File
@@ -2,7 +2,7 @@ import DefaultTheme from 'vitepress/theme';
import Icon from '../components/Icon.vue';
import EntrypointPatterns from '../components/EntrypointPatterns.vue';
import UsingWxtSection from '../components/UsingWxtSection.vue';
import ExampleList from '../components/ExampleList.vue';
import ExampleSearch from '../components/ExampleSearch.vue';
import './custom.css';
export default {
@@ -11,6 +11,6 @@ export default {
ctx.app.component('Icon', Icon);
ctx.app.component('EntrypointPatterns', EntrypointPatterns);
ctx.app.component('UsingWxtSection', UsingWxtSection);
ctx.app.component('ExampleList', ExampleList);
ctx.app.component('ExampleSearch', ExampleSearch);
},
};
+28
View File
@@ -0,0 +1,28 @@
import { HeadConfig } from 'vitepress/types/shared';
export function meta(
property: string,
content: string,
options?: { useName: boolean },
): HeadConfig {
return [
'meta',
{
[options?.useName ? 'name' : 'property']: property,
content,
},
];
}
export function script(
src: string,
props: Record<string, string> = {},
): HeadConfig {
return [
'script',
{
...props,
src,
},
];
}
+107
View File
@@ -0,0 +1,107 @@
import { DefaultTheme } from 'vitepress';
type SidebarItem = DefaultTheme.SidebarItem;
type NavItem = DefaultTheme.NavItem;
type NavItemWithLink = DefaultTheme.NavItemWithLink;
type NavItemWithChildren = DefaultTheme.NavItemWithChildren;
type NavItemChildren = DefaultTheme.NavItemChildren;
export function navItem(text: string): NavItemChildren;
export function navItem(text: string, link: string): NavItemChildren;
export function navItem(text: string, items: any[]): NavItemWithChildren;
export function navItem(text: string, arg2?: unknown): any {
if (typeof arg2 === 'string') {
return { text, link: arg2 };
} else if (Array.isArray(arg2)) {
return { text, items: arg2 };
}
return { text };
}
export function menuRoot(items: SidebarItem[]) {
return items.map((item, index) => {
// item.collapsed = false; // uncomment to expand all level-0 items
return item;
});
}
export function menuGroup(
text: string,
items: SidebarItem[],
collapsable?: boolean,
): SidebarItem;
export function menuGroup(
text: string,
base: string,
items: SidebarItem[],
collapsable?: boolean,
): SidebarItem;
export function menuGroup(
text: string,
a: string | SidebarItem[],
b?: SidebarItem[] | boolean,
c?: boolean,
): SidebarItem {
if (typeof a === 'string' && Array.isArray(b)) {
return {
text,
base: a,
items: b,
collapsed: c,
};
}
if (typeof a !== 'string' && !Array.isArray(b))
return {
text,
items: a,
collapsed: b,
};
throw Error('Unknown overload');
}
export function menuItems(items: SidebarItem[]) {
return {
items,
};
}
export function menuItem(
text: string,
link: string,
items?: SidebarItem[],
): SidebarItem {
if (items) {
return { text, link, items };
}
return { text, link };
}
/**
* Clean up and add badges to typedoc leaf sections
*/
export function prepareTypedocSidebar(items: SidebarItem[]) {
// skip contents file
const filtered = items.slice(1);
// remove Typedoc's collapse: true from text nodes
const prepareItems = (items: DefaultTheme.SidebarItem[], depth = 0) => {
for (const item of items) {
if (item.items) {
prepareItems(item.items, depth + 1);
const hasLeaves = item.items.some((item) => !item.items);
if (hasLeaves) {
item.text += ` <span class="badge">${item.items.length}</span>`;
}
} else {
delete item.collapsed;
}
}
};
// process
prepareItems(filtered);
// return
return filtered;
}
+18
View File
@@ -0,0 +1,18 @@
export interface Example {
name: string;
description?: string;
url: string;
searchText: string;
apis: string[];
permissions: string[];
packages: string[];
}
export type ExamplesMetadata = {
examples: Example[];
allApis: string[];
allPermissions: string[];
allPackages: string[];
};
export type KeySelectedObject = Record<string, boolean | undefined>;
+7
View File
@@ -0,0 +1,7 @@
# `wxt build`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.build }}</code></pre></div>
+7
View File
@@ -0,0 +1,7 @@
# `wxt clean`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.clean }}</code></pre></div>
+7
View File
@@ -0,0 +1,7 @@
# `wxt init`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.init }}</code></pre></div>
+7
View File
@@ -0,0 +1,7 @@
# `wxt prepare`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.prepare }}</code></pre></div>
+9
View File
@@ -0,0 +1,9 @@
# `wxt submit init`
> Alias for [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension)
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.submitInit }}</code></pre></div>
+9
View File
@@ -0,0 +1,9 @@
# `wxt submit`
> Alias for [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension)
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.submit }}</code></pre></div>
+7
View File
@@ -0,0 +1,7 @@
# `wxt zip`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.zip }}</code></pre></div>
+7
View File
@@ -0,0 +1,7 @@
# `wxt`
<script setup>
import { data } from '../../.vitepress/loaders/cli.data.ts'
</script>
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.wxt }}</code></pre></div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

+1
View File
@@ -0,0 +1 @@
<!--@include: ../packages/auto-icons/README.md-->
-47
View File
@@ -1,47 +0,0 @@
# Background
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/background/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background)
For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.
## Filenames
<EntrypointPatterns
:patterns="[
['background.[jt]s', 'background.js'],
['background/index.[jt]s', 'background.js'],
]"
/>
## Definition
:::warning
The main function of the background **_CANNOT BE ASYNC_**. Event listeners must be added synchronously on background startup. If your main function returns a promise, WXT will log an error.
:::
```ts
export default defineBackground(() => {
// Executed when background is loaded
});
```
or
```ts
export default defineBackground({
// Set manifest options
persistent: undefined | true | false,
type: undefined | 'module',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
// Executed when background is loaded
main() {
// ...
},
});
```
> All manifest options default to `undefined`.
-31
View File
@@ -1,31 +0,0 @@
# Bookmarks
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
## Filenames
<EntrypointPatterns
:patterns="[
['bookmarks.html', 'bookmarks.html'],
['bookmarks/index.html', 'bookmarks.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-153
View File
@@ -1,153 +0,0 @@
# Content Scripts
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/content_scripts/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts)
When creating content script entrypoints, they are automatically included in the `manifest.json` along with any CSS files they import.
## Filenames
<EntrypointPatterns
:patterns="[
['content.[jt]sx?', 'content-scripts/content.js'],
['content/index.[jt]sx?', 'content-scripts/content.js'],
['<name>.content.[jt]sx?', 'content-scripts/<name>.js'],
['<name>.content/index.[jt]sx?', 'content-scripts/<name>.js'],
]"
/>
## Definition
```ts
export default defineContentScript({
// Set manifest options
matches: string[],
excludeMatches: undefined | [],
includeGlobs: undefined | [],
excludeGlobs: undefined | [],
allFrames: undefined | [],
runAt: undefined | 'document_start' | 'document_end' | 'document_idle',
matchAboutBlank: undefined | true | false,
matchOriginAsFallback: undefined | true | false,
world: undefined | 'ISOLATED' | 'MAIN',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
// Configure how CSS is injected onto the page
cssInjectionMode: undefined | "manifest" | "manual" | "ui",
// Configure how/when content script will be registered
registration: undefined | "manifest" | "runtime",
main(ctx) {
// Executed when content script is loaded
},
});
```
> All manifest options default to `undefined`.
When defining multiple content scripts, content script entrypoints that have the same set of options will be merged into a single `content_script` item in the manifest.
## Context
Old content scripts are not automatically stopped when an extension updates and reloads. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a web extension API (ie, the `browser` or `chrome` globals).
WXT provides a utility for handling this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of your content script.
When your extension updates or is uninstalled, the context will become invalidated, and will trigger any `ctx.onInvalidated` listeners you add:
```ts
export default defineContentScript({
// ...
main(ctx: ContentScriptContext) {
// Add custom listeners for stopping work
ctx.onInvalidated(() => {
// ...
});
// Timeout utilities that are automatically cleared when invalidated
ctx.setTimeout(() => {
// ...
}, 5e3);
ctx.setInterval(() => {
// ...
}, 60e3);
// Or add event listeners that get removed when invalidated
ctx.addEventListener(document, 'visibilitychange', (event) => {
// ...
});
// You can also stop fetch requests
fetch('...url', { signal: ctx.signal });
},
});
```
The class extends [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and provides other utilities for stopping a content script's logic once it becomes invalidated.
:::warning
When working with content scripts, **you should always use the `ctx` object to stop any async or future work.**
This prevents old content scripts from interfering with new content scripts, and prevents error messages from the console in production.
If you're using a framework like React, Vue, Svelte, etc., make sure you're unmounting your UI properly in the `onRemove` option of [`createShadowRootUi`](https://wxt.dev/guide/content-script-ui.html#shadow-root).
:::
## CSS
To include CSS with your content script, import the CSS file at the top of your entrypoint.
```
<srcDir>/
└─ entrypoints/
└─ overlay.content/
├─ index.ts
└─ style.css
```
```ts
// entrypoints/overlay.content/index.ts
import './style.css';
export default defineContentScript({
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
main(ctx) {
// ...
},
});
```
Any styles imported in your content script will be added to that content script's `css` array in your `manifest.json`:
```json
// .output/chrome-mv3/manifest.json
{
"content_scripts": [
{
"matches": ["*://google.com/*", "*://duckduckgo.com/*"],
"js": ["content-scripts/overlay.js"],
"css": ["content-scripts/overlay.css"]
}
]
}
```
To disable this behavior, set `cssInjectionMode` to `"manual"` or `"ui"`.
```ts
export default defineContentScript({
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
cssInjectionMode: 'manual',
main(ctx) {
// ...
},
});
```
See [Content Script UI](/guide/content-script-ui) for more info on creating UIs and including CSS in content scripts.
-44
View File
@@ -1,44 +0,0 @@
# CSS
WXT can build CSS entrypoints individually. CSS entrypoints are always unlisted.
See [Content Script CSS](/entrypoints/content-scripts#css) documentation for the recommended approach to include CSS with a content script.
:::info
If the recommended approach doesn't work for your use case, you can use any of the filename patterns below to build the styles separate from the JS and use the [`transformManifest` hook](/api/wxt/interfaces/InlineConfig#transformmanifest) to manually add your CSS file to the manifest.
:::
## Filenames
<EntrypointPatterns
:patterns="[
['<name>.(css|scss|sass|less|styl|stylus)', '<name>.css'],
['<name>/index.(css|scss|sass|less|styl|stylus)', '<name>.css'],
['content.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
['content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
['<name>.content.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
['<name>.content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
]"
/>
## Definition
```css
body {
/* Plain CSS file */
}
```
Follow Vite's guide to setup a preprocessor: https://vitejs.dev/guide/features.html#css-pre-processors
```sh
pnpm i sass
```
```scss
body {
h1 {
/* ...*/
}
}
```
-40
View File
@@ -1,40 +0,0 @@
# Devtools
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/devtools/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/devtools_page)
## Filenames
<EntrypointPatterns
:patterns="[
['devtools.html', 'devtools.html'],
['devtools/index.html', 'devtools.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
## Adding UI Elements
Chrome extensions allow you to add panels and side panes to the devtools window.
![DevTools window showing Elements panel and Styles sidebar pane.](https://developer.chrome.com/static/docs/extensions/how-to/devtools/extend-devtools/image/devtools-window-showing-e-9051f7f0347cd_1920.png)
See the WXT's examples for a full walkthrough of extending the devtools window:
<ExampleList tag="devtools" />
-31
View File
@@ -1,31 +0,0 @@
# History
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
## Filenames
<EntrypointPatterns
:patterns="[
['history.html', 'history.html'],
['history/index.html', 'history.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-31
View File
@@ -1,31 +0,0 @@
# Newtab
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
## Filenames
<EntrypointPatterns
:patterns="[
['newtab.html', 'newtab.html'],
['newtab/index.html', 'newtab.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-36
View File
@@ -1,36 +0,0 @@
# Options
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/options/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/options_ui)
## Filenames
<EntrypointPatterns
:patterns="[
['options.html', 'options.html'],
['options/index.html', 'options.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options Title</title>
<meta name="manifest.open_in_tab" content="true|false" />
<meta name="manifest.chrome_style" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
> All manifest options default to `undefined` when the `meta` tag is not present.
-43
View File
@@ -1,43 +0,0 @@
# Popup
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/action/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action)
## Filenames
<EntrypointPatterns
:patterns="[
['popup.html', 'popup.html'],
['popup/index.html', 'popup.html'],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Default Popup Title</title>
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.type" content="page_action|browser_action" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
> All manifest options default to `undefined` when the `meta` tag is not present.
-37
View File
@@ -1,37 +0,0 @@
# Sandbox
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/)
:::tip Chromium Only
Firefox does not support sandboxed pages.
:::
## Filenames
<EntrypointPatterns
:patterns="[
['sandbox.html', 'sandbox.html'],
['sandbox/index.html', 'sandbox.html'],
['<name>.sandbox.html', '<name>.html` '],
['<name>.sandbox/index.html', '<name>.html` '],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-49
View File
@@ -1,49 +0,0 @@
# Side Panel
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars)
In Chrome, side panels use the "side_panel" API, while Firefox uses the "sidebar_action" API.
:::warning
Chrome added support for sidepanels in Manifest V3, they are not available in Manifest V2.
:::
## Filenames
<EntrypointPatterns
:patterns="[
['sidepanel.html', 'sidepanel.html'],
['sidepanel/index.html', 'sidepanel.html'],
['<name>.sidepanel.html', '<name>.html` '],
['<name>.sidepanel/index.html', '<name>.html` '],
]"
/>
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Default Side Panel Title</title>
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.open_at_install" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-49
View File
@@ -1,49 +0,0 @@
# Unlisted Pages
HTML pages that are bundled and shipped with the extension, but are not included in the manifest.
If you plan on using the page in an iframe, don't forget to add the page to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
### Examples
- Onboarding
- Dashboard
- FAQ
- Help
- Changelog
## Filenames
<EntrypointPatterns
:patterns="[
['<name>.html', '<name>.html'],
['<name>/index.html', '<name>.html'],
]"
/>
Pages are accessible at `'/<name>.html'`:
```ts
const url = browser.runtime.getURL('/<name>.html');
console.log(url); // "chrome-extension://<id>/<name>.html"
```
## Definition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
-37
View File
@@ -1,37 +0,0 @@
# Unlisted Scripts
TypeScript files that are bundled and shipped with the extension, but are not included in the manifest.
You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
## Filenames
<EntrypointPatterns
:patterns="[
['<name>.[jt]sx?', '<name>.js'],
['<name>/index.[jt]sx?', '<name>.js'],
]"
/>
## Definition
```ts
export default defineUnlistedScript(() => {
// Executed when script is loaded
});
```
or
```ts
export default defineUnlistedScript({
// Set include/exclude if the script should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
// Executed when script is loaded
main() {
// ...
},
});
```
+16 -4
View File
@@ -1,7 +1,19 @@
# Examples
---
layout: page
---
Simple walkthroughs to accomplish common tasks or patterns with WXT.
<style>
.examples-container {
padding: 32px;
}
</style>
<ExampleList />
<div class="examples-container">
<div class="vp-doc">
<h1>Examples</h1>
</div>
> Full code available at [`wxt-dev/wxt-examples`](https://github.com/wxt-dev/wxt-examples)
<br />
<ExampleSearch />
</div>
-85
View File
@@ -1,85 +0,0 @@
# Assets
WXT has two directories for storing assets like CSS, images, or fonts.
- `<srcDir>/public`: Store files that will be copied into the output directory as-is
- `<srcDir>/assets`: Store files that will be processed by Vite during the build process
## `/public` Directory
Place static files like the extension icon or `_locales/` directory here. These files will be copied over to the output directory without being transformed by Vite.
```
<srcDir>
└─ public/
├─ icon-16.png
├─ icon-32.png
├─ icon-48.png
├─ icon-96.png
└─ icon-128.png
```
### Example
You can reference these files by using absolute paths in HTML files or `browser.runtime.getURL` in content scripts.
:::code-group
```html [popup.html]
<img src="/icon-128.png" />
```
```ts [content.ts]
defineContentScript({
main() {
const image = document.createElement('img');
image.src = browser.runtime.getURL('/icon-128.png');
document.body.append(image);
},
});
```
:::
## `/assets` Directory
Files in the assets directory will be processed by Vite. They are imported in your source code, and will be transformed or renamed in the output directory.
```
<srcDir>
└─ assets/
├─ style.css
└─ illustration.svg
```
### Example
:::code-group
```html [popup.html]
<html>
<head>
<link rel="stylesheet" href="~/assets/style.css" />
<!-- ... -->
</head>
<body>
<img src="~/assets/illustration.svg" />
<!-- ... -->
</body>
</html>
```
```ts [content.ts]
import '~/assets/style.css';
import illustration from '~/assets/style.svg';
defineContentScript({
main() {
const image = document.createElement('img');
image.src = illustration;
document.body.append(image);
},
});
```
:::
-80
View File
@@ -1,80 +0,0 @@
# Auto-imports
WXT uses the same tool as Nuxt for auto-imports, [`unimport`](https://github.com/unjs/unimport).
:::info Testing
To setup your test environment for auto-imports, see [Testing](/guide/testing).
:::
## WXT Auto-imports
Some WXT APIs can be used without importing them:
- [`browser`](/api/wxt/browser/variables/browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/api/wxt/sandbox/functions/defineContentScript) from `wxt/sandbox`
- [`defineBackground`](/api/wxt/sandbox/functions/defineBackground) from `wxt/sandbox`
- [`defineUnlistedScript`](/api/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
- [`createIntegratedUi`](/api/wxt/client/functions/createIntegratedUi) from `wxt/client`
- [`createShadowRootUi`](/api/wxt/client/functions/createShadowRootUi) from `wxt/client`
- [`createIframeUi`](/api/wxt/client/functions/createIframeUi) from `wxt/client`
- [`fakeBrowser`](/api/wxt/testing/variables/fakeBrowser) from `wxt/testing`
And more!
## Project Auto-imports
In addition WXT APIs, default and named exports from inside the following directories can be used without listing them in imports.
- `<srcDir>/components/*`
- `<srcDir>/composables/*`
- `<srcDir>/hooks/*`
- `<srcDir>/utils/*`
To add auto-imports from subdirectories, like `utils/api/some-file.ts`, re-export them from the base directory:
```ts
// utils/index.ts
export * from './api/some-file.ts';
```
Alternatively, you could add the directory to the list of auto-import directories in your config file.
## TypeScript
For TypeScript to work, you need to run the `wxt prepare` command. This will ensure types are generated for auto-imports.
This should be added to your `postinstall` script:
```json
// package.json
{
"scripts": {
"postinstall": "wxt prepare" // [!code ++]
}
}
```
## Customization
You can override the default auto-import behavior in your `wxt.config.ts` file.
See [`unimport`'s documentation](https://github.com/unjs/unimport#configurations) for a complete list of options.
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
imports: {
// Add auto-imports for vue functions like createApp, ref, computed, watch, toRaw, etc...
presets: ['vue'],
},
});
```
To disable auto-imports, set `imports: false`
```ts
export default defineConfig({
imports: false,
});
```
-50
View File
@@ -1,50 +0,0 @@
# Compare
Lets compare the features of WXT vs [Plasmo](https://docs.plasmo.com/framework) (another web extension framework) and [CRXJS](https://crxjs.dev/vite-plugin) (the most popular bundler plugin).
## Overview
| Features | WXT | Plasmo | CRXJS |
| ---------------------------------------------------- | :-------------------------: | :--------------------------------------: | :---------------------------------------------------------------------: |
| Supports all browsers | ✅ | ✅ | ❌ See [#56](https://github.com/crxjs/chrome-extension-tools/issues/56) |
| MV2 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| MV3 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| Create Extension ZIPs | ✅ | ✅ | ❌ |
| Create Firefox Sources ZIP | ✅ | ❌ | ❌ |
| First-class TypeScript support | ✅ | ✅ | ✅ |
| Entrypoint discovery | File based | File based | ❌ |
| Inline entrypoint config | ✅ | ✅ | Manifest based |
| Auto-imports | ✅ | ❌ | ❌ |
| Supports all frontend frameworks | ✅ | 🟡 Only React, Vue, and Svelte | ✅ |
| Framework specific entrypoints (like `Popup.tsx`) | 🟡 `.html` `.ts` `.tsx` | ✅ `.html` `.ts` `.tsx` `.vue` `.svelte` | ❌ |
| Automated publishing | ✅ | ✅ | ❌ |
| Remote Code Bundling (Google Analytics) | ✅ | ✅ | ❌ |
| <strong style="opacity: 50%">Dev Mode</strong> | | |
| `.env` Files | ✅ | ✅ | ✅ |
| Opens browser and install extension | ✅ | ❌ | ❌ |
| HMR for UIs | ✅ | 🟡 React only | ✅ |
| Reload HTML Files on Change | ✅ | 🟡 Reloads entire extension | ✅ |
| Reload Content Scripts on Change | ✅ | 🟡 Reloads entire extension | ✅ |
| Reload Background on Change | 🟡 Reloads entire extension | 🟡 Reloads entire extension | 🟡 Reloads entire extension |
| Respects Content Script `run_at` | ✅ | ✅ | ❌ ESM-style loaders run asynchronously |
| <strong style="opacity: 50%">Built-in Utils</strong> | | | |
| Storage | ✅ | ✅ | ❌ |
| Messaging | ❌ | ✅ | ❌ |
| Content Script UI | ✅ | ✅ | ❌ |
## Why use WXT?
WXT's main goal is improving the development experience (DX) of creating web extensions, while not sacrificing support. There are two things WXT does differently:
1. Automatically opens a browser with the extension installed when starting development
2. Reload each part of the extension individually rather than reloading the entire extension
Opening a browser automatically makes it super easy to start and stop development without having to manually load the extension in your browser.
Reloading each part of the extension individually improves your iteration speed while developing UIs. This is because reloading the entire extension on every change will close the popup and any tabs open to an extension page, like options. If you save a file associated with a UI and a content script while working on the UI, it will randomly close because it needed to reload the extension when the content script changed. This interrupts your development flow and is really annoying.
WXT solves this problem by reloading HTML pages and content scripts individually (when possible) to keep your UIs open while you develop them. This is a MV3 feature, so if you're developing a MV2 extension, you'll get the same dev experience as Plasmo.
:::info
Unfortunately, there isn't an API for reloading the background page/service worker individually, so if you change a file used by the background, the entire extension will reload. See [Issue #53](https://github.com/wxt-dev/wxt/issues/53) for more details.
:::
-99
View File
@@ -1,99 +0,0 @@
# Configuration
WXT's behavior can be configured via the `wxt.config.ts` file. In this file, you can add Vite plugins, change the directory structure of your project, and set fields on your `manifest.json`.
## Config File
To configure WXT, create a `wxt.config.ts` file in your project root. It should have the following contents:
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
// My WXT config
});
```
:::info
See the [Config reference](/api/wxt/interfaces/InlineConfig) for a full list of options.
:::
## Directory Config
WXT allows you to edit several directories to your liking:
- `root` (default: `process.cwd()`) - Root of the WXT project
- `srcDir` (default: `<rootDir>`) - Location of all your source code
- `entrypointsDir` (default: `<srcDir>/entrypoints`) - Folder containing all the entrypoints.
- `publicDir` (default: `<srcDir>/public`) - Folder containing [public assets](/guide/assets)
### Example
You want a `src/` directory to contain all your source code, and you want to rename `entrypoints/` &rarr; `entries/`:
```
<rootDir>
├─ src/
│ └─ entries/
│ ├─ background.ts
│ └─ ...
└─ wxt.config.ts
```
Your config would look like this:
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
entrypointsDir: 'entries',
});
```
## Frontend Frameworks
Adding a framework like Vue, React, or Svelte is easy!
In the `wxt.config.ts` file, install and add the framework's Vite plugin to the config.
:::code-group
```ts [Vue]
import { defineConfig } from 'wxt';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
vite: () => ({
plugins: [vue()],
}),
});
```
```ts [React]
import { defineConfig } from 'wxt';
import react from '@vitejs/plugin-react';
export default defineConfig({
vite: () => ({
plugins: [react()],
}),
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
vite: () => ({
plugins: [svelte()],
}),
});
```
:::
:::info
Internally, WXT runs `vite build` multiple times during a single build. It can't share the same instance of some plugins between each build, so a function is used to return completely new instances of each plugin for each build.
:::
-408
View File
@@ -1,408 +0,0 @@
# Content Script UI
There are three ways to mount a UI inside a content script:
[[toc]]
Each has their own set of advantages and disadvantages.
| Method | Isolated Styles | Isolated Events | HMR | Use page's context |
| ----------- | :-------------: | :-----------------: | :-: | :----------------: |
| Integrated | ❌ | ❌ | ❌ | ✅ |
| Shadow Root | ✅ | ✅ (off by default) | ❌ | ✅ |
| IFrame | ✅ | ✅ | ✅ | ❌ |
## Integrated
Integrated content script UIs are injected alongside the content of a page. This means that they are affected by CSS on that page.
:::code-group
```ts [Vanilla]
// entrypoints/example-ui.content.ts
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: (container) => {
// Append children to the container
const app = document.createElement('p');
app.textContent = '...';
container.append(app);
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```ts [Vue]
// entrypoints/example-ui.content/index.ts
import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: (container) => {
// Create the app and mount it to the UI container
const app = createApp(App);
app.mount(container);
return app;
},
onRemove: (app) => {
// Unmount the app when the UI is removed
app.unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```tsx [React]
// entrypoints/example-ui.content/index.tsx
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: (container) => {
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(container);
root.render(<App />);
return root;
},
onRemove: (root) => {
// Unmount the root when the UI is removed
root.unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```ts [Svelte]
// entrypoints/example-ui.content/index.ts
import App from './App.svelte';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: (container) => {
// Create the Svelte app inside the UI container
const app = new App({
target: container,
});
return app;
},
onRemove: (app) => {
// Destroy the app when the UI is removed
app.$destroy();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```tsx [Solid]
// entrypoints/example-ui.content/index.ts
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() => <div>...</div>, container);
},
onRemove: (unmount) => {
// Unmount the app when the UI is removed
unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
:::
See the [API Reference](/api/wxt/client/functions/createIntegratedUi) for the complete list of options.
You can control how CSS is injected for an integrated content script UI with the [`cssInjectionMode`](/api/wxt/interfaces/BaseContentScriptEntrypointOptions#cssinjectionmode) property. Usually, you'll want to leave it as `"manifest"`, the default, so the UI inherits its style from the website's CSS.
## Shadow Root
Often in web extensions, you don't want your content script's CSS affecting the page, or vise-versa. The [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot) API is ideal for this.
WXT's [`createShadowRootUi`](/api/wxt/client/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs with isolated CSS. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
To use `createShadowRootUi`, follow these steps:
1. Import your CSS file at the top of your content script
2. Set [`cssInjectionMode: "ui"`](/api/wxt/interfaces/BaseContentScriptEntrypointOptions#cssinjectionmode) inside `defineContentScript`
3. Define your UI with `createShadowRootUi()`
4. Mount the UI so it is visible to users
:::code-group
```ts [Vanilla]
// 1. Import the style
import './style.css';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
onMount(container) {
// Define how your UI will be mounted inside the container
const app = document.createElement('p');
app.textContent = 'Hello world!';
container.append(app);
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```ts [Vue]
// 1. Import the style
import './style.css';
import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
onMount: (container) => {
// Define how your UI will be mounted inside the container
const app = createApp(App);
app.mount(container);
return app;
},
onRemove: (app) => {
// Unmount the app when the UI is removed
app?.unmount();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```tsx [React]
// 1. Import the style
import './style.css';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
onMount: (container) => {
// Container is a body, and React warns when creating a root on the body, so create a wrapper div
const app = document.createElement('div');
container.append(app);
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(app);
root.render(<App />);
return root;
},
onRemove: (root) => {
// Unmount the root when the UI is removed
root?.unmount();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```ts [Svelte]
// 1. Import the style
import './style.css';
import App from './App.svelte';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
onMount: (container) => {
// Create the Svelte app inside the UI container
const app = new App({
target: container,
});
return app;
},
onRemove: (app) => {
// Destroy the app when the UI is removed
app?.$destroy();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```tsx [Solid]
// 1. Import the style
import './style.css';
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() => <div>...</div>, container);
},
onRemove: (unmount) => {
// Unmount the app when the UI is removed
unmount?.();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
:::
See the [API Reference](/api/wxt/client/functions/createShadowRootUi) for the complete list of options.
:::info TailwindCSS
`createShadowRootUi` supports TailwindCSS out of the box! When importing the styles, just import the main CSS file containing the `@tailwind` directives, and everything will just work :+1:.
:::
## IFrame
If you don't need to run your UI in the same frame as the content script, you can use an IFrame to host your UI instead. Since an IFrame just hosts an HTML page, **_HMR is supported_**.
WXT provides a helper function, [`createIframeUi`](/api/wxt/client/functions/createIframeUi), which simplifies setting up the IFrame.
1. Create an HTML page that will be loaded into your IFrame
```html
<!-- entrypoints/example-iframe.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Your Content Script IFrame</title>
</head>
<body>
<!-- ... -->
</body>
</html>
```
1. Add the page to the manifest's `web_accessible_resources`
```ts
// wxt.config.ts
export default defineConfig({
manifest: {
web_accessible_resources: [
{
resources: ['example-iframe.html'],
matches: [...],
},
],
},
});
```
1. Create and mount the IFrame
```ts
export default defineContentScript({
matches: ['<all_urls>'],
async main(ctx) {
// Define the UI
const ui = await createIframeUi(ctx, {
page: '/example-iframe.html',
position: 'inline',
onMount: (wrapper, iframe) => {
// Add styles to the iframe like width
iframe.width = 123;
},
});
// Show UI to user
ui.mount();
},
});
```
See the [API Reference](/api/wxt/client/functions/createIframeUi) for the complete list of options.
-116
View File
@@ -1,116 +0,0 @@
# Development
WXT's main goal is providing the best DX it possibly can. When running your extension in dev mode, each part of your extension is reloaded separately when possible.
| | HMR | Reloaded individually | Reload extension | Restart browser |
| ------------------- | :-: | :-------------------: | :--------------: | :----------------------------------------------------: |
| HTML File | | ✅ |
| HTML Dependency | ✅ |
| MV3 Content Script | | ✅ |
| MV2 Content Script | | | ✅ |
| Background | | | ✅ |
| manifest.json | | | | 🟡 See [#16](https://github.com/wxt-dev/wxt/issues/16) |
| `wxt.config.ts` | | | | 🟡 See [#10](https://github.com/wxt-dev/wxt/issues/10) |
| `web-ext.config.ts` | | | | 🟡 See [#10](https://github.com/wxt-dev/wxt/issues/10) |
## Dev Mode vs Production Builds
There are some notable differences between the development and production versions of an extension. During development:
1. **Content scripts are not listed in the `manifest.json`** when targeting MV3. Instead, the [`scripting`](https://developer.chrome.com/docs/extensions/reference/api/scripting) permission is used to register content scripts at runtime so they can be reloaded individually.
To get the list of content scripts during development, run the following in the background's console:
```ts
await chrome.scripting.getRegisteredContentScripts();
```
2. **The CSP is modified to allow loading scripts from the dev server**. Make sure you're using Chrome v110 or above for HMR to work.
3. If you don't include a background script/service worker, one will be created to perform various tasks in dev mode, mostly related to reloading different parts of the extension on change.
For production builds, none of the above modifications will be applied, and you're extension/manifest will only include what you have defined.
## Configure Browser Startup
WXT uses [`web-ext` by Mozilla](https://github.com/mozilla/web-ext) to automatically open a browser with the extension installed. You can configure the runner's behavior via the [`runner`](/api/wxt/interfaces/InlineConfig#runner) option, or in a separate gitignored file, `web-ext.config.ts`.
:::code-group
```ts [wxt.config.ts]
import { defineConfig } from 'wxt';
export default defineConfig({
runner: {
// Runner config
},
});
```
```ts [web-ext.config.ts]
import { defineRunnerConfig } from 'wxt';
export default defineRunnerConfig({
// Runner config
});
```
:::
### Browser Binaries
`web-ext`'s browser discovery is very limited. By default, it only guesses at where Chrome and Firefox are installed. If you've customized your install locations, you may need to tell `web-ext` where the binaries/executables are located using the [`binaries` option](/api/wxt/interfaces/ExtensionRunnerConfig#binaries). For other Chromium based browsers, like Edge or Opera, you'll need to explicitly list them in the `binaries` option as well, otherwise they will open in Chrome by default.
```ts
// ~/web-ext.config.ts
import { defineRunnerConfig } from 'wxt';
export default defineRunnerConfig({
binaries: {
chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
edge: '/path/to/edge', // Open MS Edge when running "wxt -b edge"
},
});
```
:::tip
When configuring browser binaries, it's helpful to put them in `~/web-ext.config.ts` instead of the project directory's `web-ext.config.ts` file. When placed in your home directory (`~/`), this config will be used by all WXT projects, so you only need to configure the binaries once.
:::
### Other options
You can customize other options as well, like startup URLs, profiles, or additional command line arguments:
```ts
// web-ext.config.ts
import { defineRunnerConfig } from 'wxt';
export default defineRunnerConfig({
startUrls: ['https://google.com', 'https://duckduckgo.com'],
chromiumProfile: '/path/to/profile/to/use',
chromiumArgs: ['--window-size=400x300'],
});
```
For a full list of options, see the [API Reference](/api/wxt/interfaces/ExtensionRunnerConfig).
## Reload the Extension
Normally, to manually reload an extension, you have to visit `chrome://extensions` and click the reload button for your extension.
When running `wxt` command to start the dev server, WXT adds a keyboard shortcut `Alt+R`, that reloads the extension when pressed, without visiting `chrome://extensions`. This can also be customized or disabled:
```ts [wxt.config.ts]
import { defineConfig } from 'wxt';
export default defineConfig({
dev: {
reloadCommand: 'Alt+T', // false, to disable
},
});
```
:::info
This shortcut is only available during development, and is not be added to your extension when running `wxt build` or `wxt-zip`.
:::
-141
View File
@@ -1,141 +0,0 @@
# Defining Entrypoints
An "entrypoint" is any HTML, JS, or CSS file that needs to be bundled and included with the extension.
Entrypoints may or may not be listed in the extension's `manifest.json`.
## `/entrypoints` Directory
In WXT, entrypoints are defined by adding a file to the `entrypoints/` directory.
For example, a project that looks like this:
```
<rootDir>
└─ entrypoints/
├─ background.ts
├─ content.ts
├─ injected.ts
└─ popup.html
```
would result in the following `manifest.json`:
```json
{
// ...
"manifest_version": 3,
"action": {
// ...
"default_popup": "popup.html"
},
"background": {
// ...
"service_worker": "background.js"
},
"content_scripts": [
{
// ...
"js": ["content-scripts/content.js"]
}
]
}
```
If a file uses a special name recognized by WXT, it will be added to the manifest. In this case:
- `popup.html` &rarr; `action.default_popup`
- `content.ts` &rarr; `content_scripts.0.js.0`
- `background.ts` &rarr; `background.service_worker`
But not all entrypoints are added to the `manifest.json`. If the filename is not recognized by WXT, they are still built and included in the extension, but they are considered "unlisted" and are not apart of the manifest.
In this case, `injected.ts` gets output to `<outdir>/injected.js` and is accessible via `browser.runtime.getURL("/injected.js")`.
:::info
See [`/entrypoints` folder](/entrypoints/background) documentation for a full list of recognized entrypoint filenames.
:::
## Entrypoint Options
Some entrypoints, like content scripts, actions, or the background, can receive additional options.
In HTML files, options are listed as `meta` tags:
```html
<html>
<head>
<!-- Defining the popup's "default_icon" field -->
<meta name="manifest.default_icon" content="{ '16': '/icon/16.png' }" />
</head>
</html>
```
In TS files, options are apart of the file's default export:
```ts
export default defineContentScript({
matches: ['*://*.google.com/*'],
runAt: 'document_start',
main() {
// ...
},
});
```
:::info
For a full list of entrypoints and each of their options, see the [`/entrypoints` folder](/entrypoints/background) documentation.
:::
### Side Effects
You cannot use imported variables outside the `main` function in JS entrypoints. This includes options, as shown below:
```ts
// entrypoints/content.ts
import { GOOGLE_MATCHES } from '~/utils/match-patterns';
export default defineContentScript({
matches: GOOGLE_MATCHES,
main() {
// ...
},
});
```
```
$ wxt build
wxt build
WXT 0.14.1
Building chrome-mv3 for production with Vite 5.0.5
✖ Command failed after 360 ms
[8:55:54 AM] ERROR entrypoints/content.ts: Cannot use imported variable "GOOGLE_MATCHES" before main function. See https://wxt.dev/guide/entrypoints.html#side-effects
```
This throws an error because WXT needs to import each entrypoint during the build process to extract its definition (containing the `match`, `runAt`, `include`/`exclude`, etc.) to render the `manifest.json` correctly. Before loading an entrypoint, a transformation is applied to remove all imports. This prevents imported modules (local or NPM) with side-effects from running during the build process, potentially throwing an error.
:::details Why?
When importing your entrypoint to get its definition, the file is imported in a **_node environment_**, and doesn't have access to the `window`, `chrome`, or `browser` globals a web extension usually has access to. If WXT doesn't remove all the imports from the file, the imported modules could try and access one of these variables, throwing an error.
:::
:::warning
See [`wxt-dev/wxt#336`](https://github.com/wxt-dev/wxt/issues/336) to track the status of this bug.
:::
Usually, this error occurs when you try to extract options into a shared file or try to run code outside the `main` function. To fix the example from above, use literal values when defining an entrypoint instead of importing them:
```ts
import { GOOGLE_MATCHES } from '~/utils/match-patterns'; // [!code --]
export default defineContentScript({
matches: GOOGLE_MATCHES, // [!code --]
matches: ['*//*.google.com/*'], // [!code ++]
main() {
// ...
},
});
```
-38
View File
@@ -1,38 +0,0 @@
# ES Modules
Configure entrypoints to use ESM at runtime.
Currently, ESM entrypoints are opt-in, so you must configure each entrypoint with that in mind.
## HTML Pages <Badge type="warning" text="≥0.0.1" />
In general, you should always make HTML pages import ESM scripts, unless you need to support old browsers.
To make a script ESM, add `type="module"`:
<!-- prettier-ignore -->
```html
<script src="./main.ts"></script> <!-- [!code --] -->
<script src="./main.ts" type="module"></script> <!-- [!code ++] -->
```
## Background <Badge type="warning" text="≥0.16.0" />
In your background script, set `type: "module"`:
```ts
export default defineBackground({
type: 'module', // !code ++
main() {
// ...
},
});
```
:::warning
Only MV3 support ESM background scripts/service workers. When targeting MV2, the `type` option is ignored and the background is always bundled into a single file as IIFE.
:::
## Content Scripts
Coming soon. Follow [Content Script ESM Support #357](https://github.com/wxt-dev/wxt/issues/357) for updates.
+160
View File
@@ -0,0 +1,160 @@
# Assets
## `/assets` Directory
Any assets imported or referenced inside the `<srcDir>/assets/` directory will be processed by WXT's bundler.
Here's how you access them:
:::code-group
```ts [JS]
import imageUrl from '~/assets/image.png';
const img = document.createElement('img');
img.src = imageUrl;
```
```html [HTML]
<img src="~/assets/image.png" />
```
```css [CSS]
.bg-image {
background-image: url(~/assets/image.png);
}
```
:::
## `/public` Directory
Files inside `<srcDir>/public/` are copied into the output folder as-is, without being processed by WXT's bundler.
Here's how you access them:
:::code-group
```ts [JS]
import imageUrl from '/image.png';
const img = document.createElement('img');
img.src = imageUrl;
```
```html [HTML]
<img src="/image.png" />
```
```css [CSS]
.bg-image {
background-image: url(/image.png);
}
```
:::
## Inside Content Scripts
Assets inside content scripts are a little different. By default, when you import an asset, it returns just the path to the asset. This is because Vite assumes you're loading assets from the same hostname.
But, inside content scripts, the hostname is whatever the tab is set to. So if you try to fetch the asset, manually or as an `<img>`'s `src`, it will be loaded from the tab's website, not your extension.
To fix this, you need to convert the image to a full URL using `browser.runtime.getURL`:
```ts
// entrypoints/content.ts
import iconUrl from '/icon/128.png';
export default defineContentScript({
matches: ['*://*.google.com/*'],
main() {
console.log(iconUrl); // "/icon/128.png"
console.log(browser.runtime.getURL(iconUrl)); // "chrome-extension://<id>/icon/128.png"
},
});
```
## WASM
How a `.wasm` file is loaded varies greatly between packages, but most follow a basic setup: Use a JS API to load and execute the `.wasm` file.
For an extension, that means two things:
1. The `.wasm` file needs to be present in output folder so it can be loaded.
2. You must import the JS API to load and initialize the `.wasm` file, usually provided by the NPM package.
For an example, let's say you have a content script needs to parse TS code into AST. We'll use [`@oxc-parser/wasm`](https://www.npmjs.com/package/@oxc-parser/wasm) to do it!
First, we need to copy the `.wasm` file to the output directory. We'll do it with a [WXT module](/guide/essentials/wxt-modules):
```ts
// modules/oxc-parser-wasm.ts
import { resolve } from 'node:path';
export default defineWxtModule((wxt) => {
wxt.hook('build:publicAssets', (_, assets) => {
assets.push({
absoluteSrc: resolve(
'node_modules/@oxc-parser/wasm/web/oxc_parser_wasm_bg.wasm',
),
relativeDest: 'oxc_parser_wasm_bg.wasm',
});
});
});
```
Run `wxt build`, and you should see the WASM file copied into your `.output/chrome-mv3` folder!
Next, since this is in a content script and we'll be fetching the WASM file over the network to load it, we need to add the file to the `web_accessible_resources`:
```ts
// wxt.config.ts
export default defineConfig({
manifest: {
web_accessible_resources: [
{
// We'll use this matches in the cotent script as well
matches: ['*://*.github.com/*'],
// Use the same path as `relativeDest` from the WXT module
resources: ['/oxc_parser_wasm_bg.wasm'],
},
],
},
});
```
And finally, we need to load and initialize the `.wasm` file inside the content script to use it:
```ts
// entrypoints/content.ts
import initWasm, { parseSync } from '@oxc-parser/wasm';
export default defineContentScript({
matches: '*://*.github.com/*',
async main(ctx) {
if (!location.pathname.endsWith('.ts')) return;
// Get text from GitHub
const code = document.getElementById(
'read-only-cursor-text-area',
)?.textContent;
if (!code) return;
const sourceFilename = document.getElementById('file-name-id')?.textContent;
if (!sourceFilename) return;
// Load the WASM file:
await initWasm({
module_or_path: browser.runtime.getURL('/oxc_parser_wasm_bg.wasm'),
});
// Once loaded, we can use `parseSync`!
const ast = parseSync(code, { sourceFilename });
console.log(ast);
},
});
```
This code is taken directly from `@oxc-parser/wasm` docs with one exception: We manually pass in a file path. In a standard NodeJS or web project, the default path works just fine so you don't have to pass anything in. However, extensions are different. You should always explicitly pass in the full URL to the WASM file in your output directory, which is what `browser.runtime.getURL` returns.
Run your extension, and you should see OXC parse the TS file!
@@ -0,0 +1,112 @@
# Auto-imports
WXT uses [`unimport`](https://www.npmjs.com/package/unimport), the same tool as Nuxt, to setup auto-imports.
```ts
export default defineConfig({
// See https://www.npmjs.com/package/unimport#configurations
imports: {
// ...
},
});
```
By default, WXT automatically setups up auto-imports for all of it's own APIs:
- [`browser`](/api/reference/wxt/browser/variables/browser) from `wxt/browser`
- [`defineContentScript`](/api/reference/wxt/sandbox/functions/defineContentScript) from `wxt/sandbox`
- [`defineBackground`](/api/reference/wxt/sandbox/functions/defineBackground) from `wxt/sandbox`
- [`defineUnlistedScript`](/api/reference/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
- [`createIntegratedUi`](/api/reference/wxt/client/functions/createIntegratedUi) from `wxt/client`
- [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) from `wxt/client`
- [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi) from `wxt/client`
- [`fakeBrowser`](/api/reference/wxt/testing/variables/fakeBrowser) from `wxt/testing`
- And more!
WXT also adds some project directories as auto-import sources automatically:
- `<srcDir>/components/*`
- `<srcDir>/composables/*`
- `<srcDir>/hooks/*`
- `<srcDir>/utils/*`
All named and default exports from files in these directories are available everywhere else in your project without having to import them.
## TypeScript
For TypeScript and your editor to recognize auto-imported variables, you need to run the [`wxt prepare` command](/api/cli/wxt-prepare).
Add this command to your `postinstall` script so your editor has everything it needs to report type errors after installing dependencies:
```jsonc
// package.json
{
"scripts": {
"postinstall": "wxt prepare", // [!code ++]
},
}
```
## ESLint
ESLint doesn't know about the auto-imported variables unless they are explicitly defined in the ESLint's `globals`. By default, WXT will generate the config if it detects ESLint is installed in your project. If the config isn't generated automatically, you can manually tell WXT to generate it.
:::code-group
```ts [ESLint 9]
export default defineConfig({
imports: {
eslintrc: {
enabled: 9,
},
},
});
```
```ts [ESLint 8]
export default defineConfig({
imports: {
eslintrc: {
enabled: 8,
},
},
});
```
:::
Then in your ESLint config, import and use the generated file:
:::code-group
```js [ESLint 9]
// eslint.config.mjs
import autoImports from './.wxt/eslint-auto-imports.mjs';
export default [
autoImports,
{
// The rest of your config...
},
];
```
```js [ESLint 8]
// .eslintrc.mjs
export default {
extends: ['./.wxt/eslintrc-auto-import.json'],
// The rest of your config...
};
```
:::
## Disabling Auto-imports
Not all developers like auto-imports. To disable them, set `imports` to `false`.
```ts
export default defineConfig({
imports: false, // [!code ++]
});
```
@@ -0,0 +1,88 @@
---
outline: deep
---
# Browser Startup
> See the [API Reference](/api/reference/wxt/interfaces/ExtensionRunnerConfig) for a full list of config.
During development WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
## Config Files
You can configure browser startup in 3 places:
1. `<rootDir>/web-ext.config.ts`: Ignored from version control, this file lets you configure your own options for a specific project without affecting other developers
```ts
import { defineRunnerConfig } from 'wxt';
export default defineRunnerConfig({
// ...
});
```
2. `<rootDir>/wxt.config.ts`: Via the [`runner` config](/api/reference/wxt/interfaces/InlineConfig#runner), included in version control
3. `$HOME/web-ext.config.ts`: Provide default values for all WXT projects on your computer
## Recipes
### Set Browser Binaries
To set or customize the browser opened during development:
```ts
export default defineRunnerConfig({
binaries: {
chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
edge: '/path/to/edge', // Open MS Edge when running "wxt -b edge"
},
});
```
By default, WXT will try to automatically discover where Chrome/Firefox are installed. However, if you have chrome installed in a non-standard location, you need to set it manually as shown above.
### Persist Data
By default, to keep from modifying your browser's existing profiles, `web-ext` creates a brand new profile every time you run the `dev` script.
Right now, Chromium based browsers are the only browsers that support overriding this behavior and persisting data when running the `dev` script multiple times.
To persist data, set the `--user-data-dir` flag:
:::code-group
```ts [Mac/Linux]
export default defineRunnerConfig({
chromiumArgs: ['--user-data-dir=./.wxt/chrome-data'],
});
```
```ts [Windows]
import { resolve } from 'node:path';
export default defineRunnerConfig({
// On Windows, the path must be absolute
chromiumProfile: resolve('.wxt/chrome-data'),
keepProfileChanges: true,
});
```
:::
Now, next time you run the `dev` script, a persistent profile will be created in `.wxt/chrome-data/{profile-name}`. With a persistent profile, you can install devtools extensions to help with development, allow the browser to remember logins, etc, without worrying about the profile being reset the next time you run the `dev` script.
:::tip
You can use any directory you'd like for `--user-data-dir`, the examples above create a persistent profile for each WXT project. To create a profile for all WXT projects, you can put the `chrome-data` directory inside you're user's home directory.
:::
### Disable Opening Browser
If you prefer to load the extension into your browser manually, you can disable the auto-open behavior:
```ts
export default defineRunnerConfig({
disabled: true,
});
```
@@ -0,0 +1,29 @@
# Build Modes
Because WXT is powered by Vite, it supports [modes](https://vite.dev/guide/env-and-mode.html#modes) in the same way.
When running any dev or build commands, pass the `--mode` flag:
```sh
wxt --mode production
wxt build --mode development
wxt zip --mode testing
```
By default, `--mode` is `development` for the dev command and `production` for all other commands (build, zip, etc).
## Get Mode at Runtime
You can access the current mode in your extension using `import.meta.env.MODE`:
```ts
switch (import.meta.env.MODE) {
case 'development': // ...
case 'production': // ...
// Custom modes specified with --mode
case 'testing': // ...
case 'staging': // ...
// ...
}
```
@@ -0,0 +1,79 @@
# Entrypoint Loaders
To generate the manifest and other files at build-time, WXT must import each entrypoint to get their options, like content script `matches`. For HTML files, this is easy. For JS/TS entrypoints, the process is more complicated.
When loading your JS/TS entrypoints, they are imported into a NodeJS environment, not the `browser` environment that they normally run in. This can lead to issues commonly seen when running browser-only code in a NodeJS environment, like missing global variables.
WXT does several pre-processing steps to try and prevent errors during this process:
1. Use `linkedom` to make a small set of browser globals (`window`, `document`, etc) available.
2. Use `@webext-core/fake-browser` to create a fake version of the `chrome` and `browser` globals expected by extensions.
3. Pre-process the JS/TS code, stripping out the `main` function then tree-shaking unused code from the file
However, this process is not perfect. It doesn't setup all the globals found in the browser and the APIs may behave differently. As such, **_you should avoid using browser or extension APIs outside the `main` function of your entrypoints!_**
:::tip
If you're running into errors while importing entrypoints, run `wxt prepare --debug` to see more details about this process. When debugging, WXT will print out the pre-processed code to help you identify issues.
:::
Once the environment has been polyfilled and your code pre-processed, it's up the entrypoint loader to import your code, extracting the options from the default export.
There are two options for loading your entrypoints:
1. `vite-node` - default as of `v0.19.0`
2. `jiti` (**DEPRECATED, will be removed in `v0.20.0`**) - Default before `v0.19.0`
## vite-node
Since 0.19.0, WXT uses `vite-node`, the same tool that powers Vitest and Nuxt, to import your entrypoint files. It re-uses the same vite config used when building your extension, making it the most stable entrypoint loader.
## jiti
To enable `jiti`:
```ts
export default defineConfig({
entrypointLoader: 'jiti',
});
```
This is the original method WXT used to import TS files. However, because it doesn't support vite plugins like `vite-node`, it does one additional pre-processing step: It removes **_ALL_** imports from your code.
That means you cannot use imported variables outside the `main` function in JS entrypoints, like for content script `matches` or other options:
```ts
// entrypoints/content.ts
import { GOOGLE_MATCHES } from '~/utils/match-patterns';
export default defineContentScript({
matches: GOOGLE_MATCHES,
main() {
// ...
},
});
```
```
$ wxt build
wxt build
WXT 0.14.1
Building chrome-mv3 for production with Vite 5.0.5
✖ Command failed after 360 ms
[8:55:54 AM] ERROR entrypoints/content.ts: Cannot use imported variable "GOOGLE_MATCHES" before main function.
```
Usually, this error occurs when you try to extract options into a shared file or when running code outside the `main` function. To fix the example from above, use literal values when defining an entrypoint instead of importing them:
```ts
import { GOOGLE_MATCHES } from '~/utils/match-patterns'; // [!code --]
export default defineContentScript({
matches: GOOGLE_MATCHES, // [!code --]
matches: ['*//*.google.com/*'], // [!code ++]
main() {
// ...
},
});
```
@@ -0,0 +1,81 @@
# Environment Variables
## Dotenv Files
WXT supports [dotenv files the same way as Vite](https://vite.dev/guide/env-and-mode.html#env-files). Create any of the following files:
```
.env
.env.local
.env.[mode]
.env.[mode].local
.env.[browser]
.env.[browser].local
.env.[mode].[browser]
.env.[mode].[browser].local
```
And any environment variables listed inside them will be available at runtime:
```sh
# .env
WXT_API_KEY=...
```
```ts
await fetch(`/some-api?apiKey=${import.meta.env.WXT_API_KEY}`);
```
Remember to prefix any environment variables with `WXT_` or `VITE_`, otherwise they won't be available at runtime, as per [Vite's convention](https://vite.dev/guide/env-and-mode.html#env-files).
## Built-in Environment Variables
WXT provides some custom environment variables based on the current command:
| Usage | Type | Description |
| ---------------------------------- | --------- | ----------------------------------------------------- |
| `import.meta.env.MANIFEST_VERSION` | `2 │ 3` | The target manifest version |
| `import.meta.env.BROWSER` | `string` | The target browser |
| `import.meta.env.CHROME` | `boolean` | Equivalent to `import.meta.env.BROWSER === "chrome"` |
| `import.meta.env.FIREFOX` | `boolean` | Equivalent to `import.meta.env.BROWSER === "firefox"` |
| `import.meta.env.SAFARI` | `boolean` | Equivalent to `import.meta.env.BROWSER === "safari"` |
| `import.meta.env.EDGE` | `boolean` | Equivalent to `import.meta.env.BROWSER === "edge"` |
| `import.meta.env.OPERA` | `boolean` | Equivalent to `import.meta.env.BROWSER === "opera"` |
You can also access all of [Vite's environment variables](https://vite.dev/guide/env-and-mode.html#env-variables):
| Usage | Type | Description |
| ---------------------- | --------- | --------------------------------------------------------------------------- |
| `import.meta.env.MODE` | `string` | The [mode](/guide/essentials/config/build-mode) the extension is running in |
| `import.meta.env.PROD` | `boolean` | When `NODE_ENV='production'` |
| `import.meta.env.DEV` | `boolean` | Opposite of `import.meta.env.PROD` |
:::details Other Vite Environment Variables
Vite provides two other environment variables, but they aren't useful in WXT projects:
- `import.meta.env.BASE_URL`: Use `browser.runtime.getURL` instead.
- `import.meta.env.SSR`: Always `false`.
:::
## Manifest
To use environment variables in the manifest, you need to use the function syntax:
```ts
export default defineConfig({
extensionApi: 'chrome',
modules: ['@wxt-dev/module-vue'],
manifest: { // [!code --]
oauth2: { // [!code --]
client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code --]
} // [!code --]
} // [!code --]
manifest: () => ({ // [!code ++]
oauth2: { // [!code ++]
client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code ++]
} // [!code ++]
}), // [!code ++]
});
```
WXT can't load your `.env` files until after the config file has been loaded. So by using the function syntax for `manifest`, it defers creating the object until after the `.env` files are loaded into the process.
+58
View File
@@ -0,0 +1,58 @@
# Hooks
WXT includes a system that lets you hook into the build process and make changes.
## Adding Hooks
The easiest way to add a hook is via the `wxt.config.ts`. Here's an example hook that modifies the `manifest.json` file before it is written to the output directory:
```ts
// wxt.config.ts
export default defineConfig({
hooks: {
'build:manifestGenerated': (wxt, manifest) => {
if (wxt.config.mode === 'development') {
manifest.title += ' (DEV)';
}
},
},
});
```
Most hooks provide the `wxt` object as the first argument. It contains the resolved config and other info about the current build. The other arguments can be modified by reference to change different parts of the build system.
Putting one-off hooks like this in your config file is simple, but if you find yourself writing lots of hooks, you should extract them into [WXT Modules](/guide/essentials/wxt-modules) instead.
## Execution Order
Because hooks can be defined in multiple places, including [WXT Modules](/guide/essentials/wxt-modules), the order which they're executed can matter. Hooks are executed in the following order:
1. NPM modules in the order listed in the [`modules` config](/api/reference/wxt/interfaces/InlineConfig#modules)
2. User modules in [`/modules` folder](/guide/essentials/project-structure), loaded alphabetically
3. Hooks listed in your `wxt.config.ts`
To see the order for your project, run `wxt prepare --debug` flag and search for the "Hook execution order":
```
⚙ Hook execution order:
⚙ 1. wxt:built-in:unimport
⚙ 2. src/modules/auto-icons.ts
⚙ 3. src/modules/example.ts
⚙ 4. src/modules/i18n.ts
⚙ 5. wxt.config.ts > hooks
```
Changing execution order is simple:
- Prefix your user modules with a number (lower numbers are loaded first):
<!-- prettier-ignore -->
```html
📁 modules/
📄 0.my-module.ts
📄 1.another-module.ts
```
- If you need to run an NPM module after user modules, just make it a user module and prefix the filename with a number!
```ts
// modules/2.i18n.ts
export { default } from '@wxt-dev/i18n/module';
```
+255
View File
@@ -0,0 +1,255 @@
# Manifest
In WXT, there is no `manifest.json` file in your source code. Instead, WXT generates the manifest from multiple sources:
- Global options [defined in your `wxt.config.ts` file](#global-options)
- Entrypoint-specific options [defined in your entrypoints](/guide/essentials/entrypoints#defining-manifest-options)
- [WXT Modules](/guide/essentials/wxt-modules) added to your project can modify your manifest
- [Hooks](/guide/essentials/config/hooks) defined in your project can modify your manifest
Your extension's `manifest.json` will be output to `.output/{target}/manifest.json` when running `wxt build`.
## Global Options
To add a property to your manifest, use the `manifest` config inside your `wxt.config.ts`:
```ts
export default defineConfig({
manifest: {
// Put manual changes here
},
});
```
You can also define the manifest as a function, and use JS to generate it based on the target browser, mode, and more.
```ts
export default defineConfig({
manifest: ({ browser, manifestVersion, mode, command }) => {
return {
// ...
};
},
});
```
### MV2 and MV3 Compatibility
When adding properties to the manifest, always define the property in it's MV3 format when possible. When targeting MV2, WXT will automatically convert these properties to their MV2 format.
For example, for this config:
```ts
export default defineConfig({
manifest: {
action: {
default_title: 'Some Title',
},
web_accessible_resources: [
{
matches: ['*://*.google.com/*'],
resources: ['icon/*.png'],
},
],
},
});
```
WXT will generate the following manifests:
:::code-group
```json [MV2]
{
"manifest_version": 2,
// ...
"browser_action": {
"default_title": "Some Title"
},
"web_accessible_resources": ["icon/*.png"]
}
```
```json [MV3]
{
"manifest_version": 3,
// ...
"action": {
"default_title": "Some Title"
},
"web_accessible_resources": [
{
"matches": ["*://*.google.com/*"],
"resources": ["icon/*.png"]
}
]
}
```
:::
You can also specify properties specific to a single manifest version, and they will be stripped out when targeting the other manifest version.
## Name
> [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/name/)
If not provided via the `manifest` config, the manifest's `name` property defaults to your `package.json`'s `name` property.
## Version and Version Name
> [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/version/)
Your extension's `version` and `version_name` is based on the `version` from your `package.json`.
- `version_name` is the exact string listed
- `version` is the string cleaned up, with any invalid suffixes removed
Example:
```json
// package.json
{
"version": "1.3.0-alpha2"
}
```
```json
// .output/<target>/manifest.json
{
"version": "1.3.0",
"version_name": "1.3.0-alpha2"
}
```
If a version is not present in your `package.json`, it defaults to `"0.0.0"`.
## Icons
WXT automatically discovers your extension's icon by looking at files in the `public/` directory:
```
public/
├─ icon-16.png
├─ icon-24.png
├─ icon-48.png
├─ icon-96.png
└─ icon-128.png
```
Specifically, an icon must match one of these regex to be discovered:
<<< @/../packages/wxt/src/core/utils/manifest.ts#snippet
If you don't like these filename or you're migrating to WXT and don't want to rename the files, you can manually specify an `icon` in your manifest:
```ts
export default defineConfig({
manifest: {
icons: {
16: '/extension-icon-16.png',
24: '/extension-icon-24.png',
48: '/extension-icon-48.png',
96: '/extension-icon-96.png',
128: '/extension-icon-128.png',
},
},
});
```
Alternatively, you can use [`@wxt-dev/auto-icons`](https://www.npmjs.com/package/@wxt-dev/auto-icons) to let WXT generate your icon at the required sizes.
## Permissions
> [Chrome docs](https://developer.chrome.com/docs/extensions/reference/permissions/)
Most of the time, you need to manually add permissions to your manifest. Only in a few specific situations are permissions added automatically:
- During development: the `tabs` and `scripting` permissions will be added to enable hot reloading.
- When a `sidepanel` entrypoint is present: The `sidepanel` permission is added.
```ts
export default defineConfig({
manifest: {
permissions: ['storage', 'tabs'],
},
});
```
## Host Permissions
> [Chrome docs](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions)
```ts
export default defineConfig({
manifest: {
permissions: ['storage', 'tabs'],
},
});
```
:::warning
If you use host permissions and target both MV2 and MV3, make sure to only include the required host permissions for each version:
```ts
export default defineConfig({
manifest: ({ manifestVersion }) => ({
host_permissions: manifestVersion === 2 ? [...] : [...],
}),
});
```
:::
## Default Locale
```ts
export default defineConfig({
manifest: {
name: '__MSG_extName__',
description: '__MSG_extDescription__',
default_locale: 'en',
},
});
```
> See [I18n docs](/guide/essentials/i18n) for a full guide on internationalizing your extension.
## Actions
In MV2, you have two options: [`browser_action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action) and [`page_action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/page_action). In MV3, they were merged into a single [`action`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action) API.
By default, whenever an `action` is generated, WXT falls back to `browser_action` when targeting MV2.
### Action With Popup
To generate a manifest where a UI appears after clicking the icon, just create a [Popup entrypoint](/guide/essentials/entrypoints#popup). If you want to use a `page_action` for MV2, add the following meta tag to the HTML document's head:
```html
<meta name="manifest.type" content="page_action" />
```
### Action Without Popup
If you want to use the `activeTab` permission or the `browser.action.onClicked` event, but don't want to show a popup:
1. Delete the [Popup entrypoint](/guide/essentials/entrypoints#popup) if it exists
2. Add the `action` key to your manifest:
```ts
export default defineConfig({
manifest: {
action: {},
},
});
```
Same as an action with a popup, WXT will fallback on using `browser_action` for MV2. To use a `page_action` instead, add that key as well:
```ts
export default defineConfig({
manifest: {
action: {},
page_action: {},
},
});
```
+56
View File
@@ -0,0 +1,56 @@
# Runtime Config
> This API is still a WIP, with more features coming soon!
Define runtime configuration in a single place, `<srcDir>/app.config.ts`:
```ts
import { defineAppConfig } from 'wxt/sandbox';
// Define types for your config
declare module 'wxt/sandbox' {
export interface WxtAppConfig {
theme?: 'light' | 'dark';
}
}
export default defineAppConfig({
theme: 'dark',
});
```
:::warning
This file is committed to the repo, so don't put any secrets here. Instead, use [Environment Variables](#environment-variables)
:::
To access runtime config, WXT provides the `useAppConfig` function:
```ts
import { useAppConfig } from 'wxt/sandbox';
console.log(useAppConfig()); // { theme: "dark" }
```
## Environment Variables in App Config
You can use environment variables in the `app.config.ts` file.
```ts
declare module 'wxt/sandbox' {
export interface WxtAppConfig {
apiKey?: string;
skipWelcome: boolean;
}
}
export default defineAppConfig({
apiKey: import.meta.env.WXT_API_KEY,
skipWelcome: import.meta.env.WXT_SKIP_WELCOME === 'true',
});
```
This has several advantages:
- Define all expected environment variables in a single file
- Convert strings to other types, like booleans or arrays
- Provide default values if an environment variable is not provided
@@ -0,0 +1,65 @@
# TypeScript Configuration
When you run [`wxt prepare`](/api/cli/wxt-prepare), WXT generates a base TSConfig file for your project at `<rootDir>/.wxt/tsconfig.json`.
At a minimum, you need to create a TSConfig in your root directory that looks like this:
```jsonc
// <rootDir>/tsconfig.json
{
"extends": ".wxt/tsconfig.json",
}
```
Or if you're in a monorepo, you may not want to extend the config. If you don't extend it, you need to add `.wxt/wxt.d.ts` to the TypeScript project:
```ts
/// <reference types="./.wxt/wxt.d.ts" />
```
## Compiler Options
To specify custom compiler options, add them in `<rootDir>/tsconfig.json`:
```jsonc
// <rootDir>/tsconfig.json
{
"extends": ".wxt/tsconfig.json",
"compilerOptions": {
"jsx": "preserve",
},
}
```
## TSConfig Paths
WXT provides a default set of path aliases.
| Alias | To | Example |
| ----- | ------------- | ----------------------------------------------- |
| `~~` | `<rootDir>/*` | `import "~~/scripts"` |
| `@@` | `<rootDir>/*` | `import "@@/scripts"` |
| `~` | `<srcDir>/*` | `import { toLowerCase } from "~/utils/strings"` |
| `@` | `<srcDir>/*` | `import { toLowerCase } from "@/utils/strings"` |
To add your own, DO NOT add them to your `tsconfig.json`! Instead, use the [`alias` option](/api/reference/wxt/interfaces/InlineConfig#alias) in `wxt.config.ts`.
This will add your custom aliases to `<rootDir>/.wxt/tsconfig.json` next time you run `wxt prepare`. It also adds your alias to the bundler so it can resolve imports.
```ts
import { resolve } from 'node:path';
export default defineConfig({
alias: {
// Directory:
testing: resolve('utils/testing'),
// File:
strings: resolve('utils/strings.ts'),
},
});
```
```ts
import { fakeTab } from 'testing/fake-objects';
import { toLowerCase } from 'strings';
```
+68
View File
@@ -0,0 +1,68 @@
# Vite
WXT uses [Vite](https://vitejs.dev/) under the hood to bundle your extension.
This page explains how to customize your project's Vite config. Refer to [Vite's documentation](https://vite.dev/config/) to learn more about configuring the bundler.
:::tip
In most cases, you shouldn't change Vite's build settings. WXT provides sensible defaults that output a valid extension accepted by all stores when publishing.
:::
## Change Vite Config
You can change Vite's config via the `wxt.config.ts` file:
```ts
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
vite: () => ({
// Override config here, same as `defineConfig({ ... })`
// inside vite.config.ts files
}),
});
```
## Add Vite Plugins
To add a plugin, install the NPM package and add it to the Vite config:
```ts
// wxt.config.ts
import { defineConfig } from 'wxt';
import VueRouter from 'unplugin-vue-router/vite';
export default defineConfig({
vite: () => ({
plugins: [
VueRouter({
/* ... */
}),
],
}),
});
```
:::warning
Due to the way WXT orchestrates Vite builds, some plugins may not work as expected. For example, `vite-plugin-remove-console` normally only runs when you build for production (`vite build`). However, WXT uses a combination of dev server and builds during development, so you need to manually tell it when to run:
```ts
// wxt.config.ts
import { defineConfig } from 'wxt';
import removeConsole from 'vite-plugin-remove-console';
export default defineConfig({
vite: (configEnv) => ({
plugins:
configEnv.mode === 'production'
? [removeConsole({ includes: ['log'] })]
: [],
}),
});
```
Search [GitHub issues](https://github.com/wxt-dev/wxt/issues?q=is%3Aissue+label%3A%22vite+plugin%22) if you run into issues with a specific plugin.
If an issue doesn't exist for your plugin, [open a new one](https://github.com/wxt-dev/wxt/issues/new/choose).
:::
+668
View File
@@ -0,0 +1,668 @@
---
outline: deep
---
# Content Scripts
> To create a content script, see [Entrypoint Types](/guide/essentials/entrypoints#content-scripts).
## Context
The first argument to a content script's `main` function is it's "context".
```ts
// entrypoints/example.content.ts
export default defineContentScript({
main(ctx) {},
});
```
This object is responsible for tracking whether or not the content script's context is "invalidated". Most browsers, by default, do not stop content scripts if the extension is uninstalled, updated, or disabled. When this happens, content scripts start reporting this error:
```
Error: Extension context invalidated.
```
The `ctx` object provides several helpers to stop asynchronous code from running once the context is invalidated:
```ts
ctx.addEventListener(...);
ctx.setTimeout(...);
ctx.setInterval(...);
ctx.requestAnimationFrame(...);
// and more
```
You can also check if the context is invalidated manually:
```ts
if (ctx.isValid) {
// do something
}
// OR
if (ctx.isInvalid) {
// do something
}
```
## CSS
In regular web extensions, CSS for content scripts is usually a separate CSS file, that is added to a CSS array in the manifest:
```json
{
"content_scripts": [
{
"css": ["content/style.css"],
"js": ["content/index.js"],
"matches": ["*://*/*"]
}
]
}
```
In WXT, to add CSS to a content script, simply import the CSS file into your JS entrypoint, and WXT will automatically add the bundled CSS output to the `css` array.
```ts
// entrypoints/example.content/index.ts
import './style.css';
export default defineContentScript({
// ...
});
```
To create a standalone content script that only includes a CSS file:
1. Create the CSS file: `entrypoints/example.content.css`
2. Use the `build:manifestGenerated` hook to add the content script to the manifest:
```ts
// wxt.config.ts
export default defineConfig({
hooks: {
'build:manifestGenerated': (wxt, manifest) => {
manifest.content_scripts ??= [];
manifest.content_scripts.push({
// Build extension once to see where your CSS get's written to
css: ['content-scripts/example.css'],
matches: ['*://*/*'],
});
},
},
});
```
## UI
WXT provides 3 built-in utilities for adding UIs to a page from a content script:
- [Integrated](#integrated) - `createIntegratedUi`
- [Shadow Root](#shadow-root) -`createShadowRootUi`
- [IFrame](#iframe) - `createIframeUi`
Each has their own set of advantages and disadvantages.
| Method | Isolated Styles | Isolated Events | HMR | Use page's context |
| ----------- | :-------------: | :-----------------: | :-: | :----------------: |
| Integrated | ❌ | ❌ | ❌ | ✅ |
| Shadow Root | ✅ | ✅ (off by default) | ❌ | ✅ |
| IFrame | ✅ | ✅ | ✅ | ❌ |
### Integrated
Integrated content script UIs are injected alongside the content of a page. This means that they are affected by CSS on that page.
:::code-group
```ts [Vanilla]
// entrypoints/example-ui.content.ts
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Append children to the container
const app = document.createElement('p');
app.textContent = '...';
container.append(app);
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```ts [Vue]
// entrypoints/example-ui.content/index.ts
import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Create the app and mount it to the UI container
const app = createApp(App);
app.mount(container);
return app;
},
onRemove: (app) => {
// Unmount the app when the UI is removed
app.unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```tsx [React]
// entrypoints/example-ui.content/index.tsx
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(container);
root.render(<App />);
return root;
},
onRemove: (root) => {
// Unmount the root when the UI is removed
root.unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```ts [Svelte]
// entrypoints/example-ui.content/index.ts
import App from './App.svelte';
import { mount, unmount } from 'svelte';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
mount(App, {
target: container,
});
},
onRemove: (app) => {
// Destroy the app when the UI is removed
unmount(app);
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
```tsx [Solid]
// entrypoints/example-ui.content/index.ts
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() => <div>...</div>, container);
return unmount;
},
onRemove: (unmount) => {
// Unmount the app when the UI is removed
unmount();
},
});
// Call mount to add the UI to the DOM
ui.mount();
},
});
```
:::
See the [API Reference](/api/reference/wxt/client/functions/createIntegratedUi) for the complete list of options.
### Shadow Root
Often in web extensions, you don't want your content script's CSS affecting the page, or vise-versa. The [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot) API is ideal for this.
WXT's [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs whose styles are isolated from the page. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
To use `createShadowRootUi`, follow these steps:
1. Import your CSS file at the top of your content script
2. Set [`cssInjectionMode: "ui"`](/api/reference/wxt/interfaces/BaseContentScriptEntrypointOptions#cssinjectionmode) inside `defineContentScript`
3. Define your UI with `createShadowRootUi()`
4. Mount the UI so it is visible to users
:::code-group
```ts [Vanilla]
// 1. Import the style
import './style.css';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: 'body',
onMount(container) {
// Define how your UI will be mounted inside the container
const app = document.createElement('p');
app.textContent = 'Hello world!';
container.append(app);
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```ts [Vue]
// 1. Import the style
import './style.css';
import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Define how your UI will be mounted inside the container
const app = createApp(App);
app.mount(container);
return app;
},
onRemove: (app) => {
// Unmount the app when the UI is removed
app?.unmount();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```tsx [React]
// 1. Import the style
import './style.css';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Container is a body, and React warns when creating a root on the body, so create a wrapper div
const app = document.createElement('div');
container.append(app);
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(app);
root.render(<App />);
return root;
},
onRemove: (root) => {
// Unmount the root when the UI is removed
root?.unmount();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```ts [Svelte]
// 1. Import the style
import './style.css';
import App from './App.svelte';
import { mount, unmount } from 'svelte';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
mount(App, {
target: container,
});
},
onRemove: () => {
// Destroy the app when the UI is removed
unmount(app);
},
});
// 4. Mount the UI
ui.mount();
},
});
```
```tsx [Solid]
// 1. Import the style
import './style.css';
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: 'body',
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() => <div>...</div>, container);
},
onRemove: (unmount) => {
// Unmount the app when the UI is removed
unmount?.();
},
});
// 4. Mount the UI
ui.mount();
},
});
```
:::
See the [API Reference](/api/reference/wxt/client/functions/createShadowRootUi) for the complete list of options.
Full examples:
- [react-content-script-ui](https://github.com/wxt-dev/examples/tree/main/examples/react-content-script-ui)
- [tailwindcss](https://github.com/wxt-dev/examples/tree/main/examples/tailwindcss)
### IFrame
If you don't need to run your UI in the same frame as the content script, you can use an IFrame to host your UI instead. Since an IFrame just hosts an HTML page, **_HMR is supported_**.
WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi), which simplifies setting up the IFrame.
1. Create an HTML page that will be loaded into your IFrame:
```html
<!-- entrypoints/example-iframe.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Your Content Script IFrame</title>
</head>
<body>
<!-- ... -->
</body>
</html>
```
1. Add the page to the manifest's `web_accessible_resources`:
```ts
// wxt.config.ts
export default defineConfig({
manifest: {
web_accessible_resources: [
{
resources: ['example-iframe.html'],
matches: [...],
},
],
},
});
```
1. Create and mount the IFrame:
```ts
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
// Define the UI
const ui = createIframeUi(ctx, {
page: '/example-iframe.html',
position: 'inline',
anchor: 'body',
onMount: (wrapper, iframe) => {
// Add styles to the iframe like width
iframe.width = '123';
},
});
// Show UI to user
ui.mount();
},
});
```
See the [API Reference](/api/reference/wxt/client/functions/createIframeUi) for the complete list of options.
## Isolated World vs Main World
By default, all content scripts run in an isolated context where only the DOM is shared with the webpage it is running on - an "isolated world". In MV3, Chromium introduced the ability to run content scripts in the "main" world - where everything, not just the DOM, is available to the content script, just like if the script were loaded by the webpage.
You can enable this for a content script by setting the `world` option:
```ts
export default defineContentScript({
world: 'MAIN',
});
```
However, this approach has several notable drawbacks:
- Doesn't support MV2
- `world: "MAIN"` is only supported by Chromium browsers
- Main world content scripts don't have access to the extension API
Instead, WXT recommends injecting a script into the main world manually using it's `injectScript` function. This will address the drawbacks mentioned before.
- `injectScript` supports both MV2 and MV3
- `injectScript` supports all browsers
- Having a "parent" content script means you can send messages back and forth, making it possible to access the extension API
To use `injectScript`, we need two entrypoints, one content script and one unlisted script:
<!-- prettier-ignore -->
```html
📂 entrypoints/
📄 example.content.ts
📄 example-main-world.ts
```
```ts
// entrypoints/example-main-world.ts
export default defineUnlistedScript(() => {
console.log('Hello from the main world');
});
```
```ts
// entrypoints/example.content.ts
export default defineContentScript({
matches: ['*://*/*'],
async main() {
console.log('Injecting script...');
await injectScript('/example-main-world.js', {
keepInDom: true,
});
console.log('Done!');
},
});
```
```json
export default defineConfig({
manifest: {
// ...
web_accessible_resources: [
{
resources: ["example-main-world.js"],
matches: ["*://*/*"],
}
]
}
});
```
`injectScript` works by creating a `script` element on the page pointing to your script. This loads the script into the page's context so it runs in the main world.
`injectScript` returns a promise, that when resolved, means the script has been evaluated by the browser and you can start communicating with it.
:::warning Warning: `run_at` Caveat
For MV3, `injectScript` is synchronous and the injected script will be evaluated at the same time as your the content script's `run_at`.
However for MV2, `injectScript` has to `fetch` the script's text content and create an inline `<script>` block. This means for MV2, your script is injected asynchronously and it will not be evaluated at the same time as your content script's `run_at`.
:::
## Mounting UI to dynamic element
In many cases, you may need to mount a UI to a DOM element that does not exist at the time the web page is initially loaded. To handle this, use the `autoMount` API to automatically mount the UI when the target element appears dynamically and unmount it when the element disappears. In WXT, the `anchor` option is used to target the element, enabling automatic mounting and unmounting based on its appearance and removal.
```ts
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
// It observes the anchor
anchor: '#your-target-dynamic-element',
onMount: (container) => {
// Append children to the container
const app = document.createElement('p');
app.textContent = '...';
container.append(app);
},
});
// Call autoMount to observe anchor element for add/remove.
ui.autoMount();
},
});
```
:::tip
When the `ui.remove` is called, `autoMount` also stops.
:::
See the [API Reference](/api/reference/wxt/client/interfaces/ContentScriptUi.html#automount) for the complete list of options.
## Dealing with SPAs
It is difficult to write content scripts for SPAs (single page applications) and websites using HTML5 history mode for navigation because content scripts are only ran on full page reloads. SPAs and websites that take advantage of HTML5 history mode **_do not perform a full reload when changing paths_**, and thus your content script isn't going to be ran when you expect it to be.
Let's look at an example. Say you want to add a UI to YouTube when watching a video:
```ts
export default defineContentScript({
matches: ['*://*.youtube.com/watch*'],
main(ctx) {
console.log('YouTube content script loaded');
mountUi(ctx);
},
});
function mountUi(ctx: ContentScriptContext): void {
// ...
}
```
You're only going to see "YouTube content script loaded" when reloading the watch page or when navigating directly to it from another website.
To get around this, you'll need to manually listen for the path to change and run your content script when the URL matches what you expect it to match.
```ts
const watchPattern = new MatchPattern('*://*.youtube.com/watch*');
export default defineContentScript({
matches: ['*://*.youtube.com/*'],
main(ctx) {
ctx.addEventListener(window, 'wxt:locationchange', ({ newUrl }) => {
if (watchPattern.includes(newUrl)) mainWatch(ctx);
});
},
});
function mainWatch(ctx: ContentScriptContext) {
mountUi(ctx);
}
```
+9
View File
@@ -0,0 +1,9 @@
# E2E Testing
## Playwright
[Playwright](https://playwright.dev) is the only good option for writing Chrome Extension end-to-end tests.
To add E2E tests to your project, follow Playwright's [Chrome Extension docs](https://playwright.dev/docs/chrome-extensions). When you have to pass the path to your extension, pass the output directory, `/path/to/project/.output/chrome-mv3`.
For a complete example, see the [WXT's Playwright Example](https://github.com/wxt-dev/examples/tree/main/examples/playwright-e2e-testing).
+556
View File
@@ -0,0 +1,556 @@
---
outline: deep
---
# Entrypoints
WXT uses the files inside the `entrypoints/` directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (Pug, TS, JSX, SCSS, etc).
Here's an example set of entrypoints:
<!-- prettier-ignore -->
```html
📂 entrypoints/
📂 popup/
📄 index.html
📄 main.ts
📄 style.css
📄 background.ts
📄 content.ts
```
[[toc]]
## Listed vs Unlisted
For web extensions, there are two types of entrypoints:
- **Listed**: Referenced in the `manifest.json`
- **Unlisted**: Not referenced in the `manifest.json`
Throughout the rest of WXT's documentation, listed entrypoints are referred to by name. For example:
- Popup
- Options
- Background
- Content Scripts
- Etc.
Some examples of "unlisted" entrypoints:
- A welcome page shown when the extension is installed
- JS files injected by content scripts into the page's main world
:::tip
Regardless of whether an entrypoint is listed or unlisted, it will still be bundled into your extension and be available at runtime.
:::
## Adding Entrypoints
An entrypoint can be defined as a single file or directory with an `index` file inside it.
:::code-group
<!-- prettier-ignore -->
```html [Single File]
📂 entrypoints/
📄 background.ts
```
<!-- prettier-ignore -->
```html [Directory]
📂 entrypoints/
📂 background/
📄 index.ts
```
:::
The entrypoint's name dictates the type of entrypoint, listed vs unlisted. In this example, "background" is the name of the ["Background" entrypoint](#background).
Refer to the [Entrypoint Types](#entrypoint-types) section for the full list of listed entrypoints and their filename patterns.
## Defining Manifest Options
Most listed entrypoints have options that need to be added to the `manifest.json`. However with WXT, instead of defining the options in a separate file, _you define these options inside the entrypoint file itself_.
For example, here's how to define `matches` for content scripts:
```ts
// entrypoints/content.ts
export default defineContentScript({
matches: ['*://*.wxt.dev/*'],
main() {
// ...
},
});
```
For HTML entrypoints, options are configured as `<meta>` tags. For example, to use a `page_action` for your MV2 popup:
```html
<!doctype html>
<html lang="en">
<head>
<meta name="manifest.type" content="page_action" />
</head>
</html>
```
> Refer to the [Entrypoint Types](#entrypoint-types) sections for a list of options configurable inside each entrypoint, and how to define them.
When building your extension, WXT will look at the options defined in your entrypoints, and generate the manifest accordingly.
## Entrypoint Types
### Background
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/background/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background)
For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.
<EntrypointPatterns
:patterns="[
['background.[jt]s', 'background.js'],
['background/index.[jt]s', 'background.js'],
]"
/>
:::code-group
```ts [Minimal]
export default defineBackground(() => {
// Executed when background is loaded
});
```
```ts [With Manifest Options]
export default defineBackground({
// Set manifest options
persistent: undefined | true | false,
type: undefined | 'module',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
main() {
// Executed when background is loaded, CANNOT BE ASYNC
},
});
```
:::
### Bookmarks
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
<EntrypointPatterns
:patterns="[
['bookmarks.html', 'bookmarks.html'],
['bookmarks/index.html', 'bookmarks.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Content Scripts
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/content_scripts/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts)
See [Content Script UI](/guide/essentials/content-scripts) for more info on creating UIs and including CSS in content scripts.
<EntrypointPatterns
:patterns="[
['content.[jt]sx?', 'content-scripts/content.js'],
['content/index.[jt]sx?', 'content-scripts/content.js'],
['<name>.content.[jt]sx?', 'content-scripts/<name>.js'],
['<name>.content/index.[jt]sx?', 'content-scripts/<name>.js'],
]"
/>
```ts
export default defineContentScript({
// Set manifest options
matches: string[],
excludeMatches: undefined | [],
includeGlobs: undefined | [],
excludeGlobs: undefined | [],
allFrames: undefined | true | false,
runAt: undefined | 'document_start' | 'document_end' | 'document_idle',
matchAboutBlank: undefined | true | false,
matchOriginAsFallback: undefined | true | false,
world: undefined | 'ISOLATED' | 'MAIN',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
// Configure how CSS is injected onto the page
cssInjectionMode: undefined | "manifest" | "manual" | "ui",
// Configure how/when content script will be registered
registration: undefined | "manifest" | "runtime",
main(ctx: ContentScriptContext) {
// Executed when content script is loaded, can be async
},
});
```
### Devtools
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/devtools/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/devtools_page)
Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/examples/devtools-extension#readme) to add different panels and panes.
<EntrypointPatterns
:patterns="[
['devtools.html', 'devtools.html'],
['devtools/index.html', 'devtools.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### History
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
<EntrypointPatterns
:patterns="[
['history.html', 'history.html'],
['history/index.html', 'history.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Newtab
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
<EntrypointPatterns
:patterns="[
['newtab.html', 'newtab.html'],
['newtab/index.html', 'newtab.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Options
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/options/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/options_ui)
<EntrypointPatterns
:patterns="[
['options.html', 'options.html'],
['options/index.html', 'options.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options Title</title>
<!-- Customize the manifest options -->
<meta name="manifest.open_in_tab" content="true|false" />
<meta name="manifest.chrome_style" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Popup
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/action/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action)
<EntrypointPatterns
:patterns="[
['popup.html', 'popup.html'],
['popup/index.html', 'popup.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Set the `action.default_title` in the manifest -->
<title>Default Popup Title</title>
<!-- Customize the manifest options -->
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.type" content="page_action|browser_action" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Sandbox
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/)
:::warning Chromium Only
Firefox does not support sandboxed pages.
:::
<EntrypointPatterns
:patterns="[
['sandbox.html', 'sandbox.html'],
['sandbox/index.html', 'sandbox.html'],
['<name>.sandbox.html', '<name>.html'],
['<name>.sandbox/index.html', '<name>.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Side Panel
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars)
In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar_action` API.
<EntrypointPatterns
:patterns="[
['sidepanel.html', 'sidepanel.html'],
['sidepanel/index.html', 'sidepanel.html'],
['<name>.sidepanel.html', '<name>.html` '],
['<name>.sidepanel/index.html', '<name>.html` '],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Default Side Panel Title</title>
<!-- Customize the manifest options -->
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
...
}"
/>
<meta name="manifest.open_at_install" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
### Unlisted CSS
Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors
CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs.
<EntrypointPatterns
:patterns="[
['<name>.(css|scss|sass|less|styl|stylus)', '<name>.css'],
['<name>/index.(css|scss|sass|less|styl|stylus)', '<name>.css'],
['content.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
['content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
['<name>.content.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
['<name>.content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
]"
/>
```css
body {
/* ... */
}
```
### Unlisted Pages
<EntrypointPatterns
:patterns="[
['<name>.html', '<name>.html'],
['<name>/index.html', '<name>.html'],
]"
/>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
</body>
</html>
```
Pages are accessible at `/<name>.html`:
```ts
const url = browser.runtime.getURL('/<name>.html');
console.log(url); // "chrome-extension://<id>/<name>.html"
```
### Unlisted Scripts
<EntrypointPatterns
:patterns="[
['<name>.[jt]sx?', '<name>.js'],
['<name>/index.[jt]sx?', '<name>.js'],
]"
/>
:::code-group
```ts [Minimal]
export default defineUnlistedScript(() => {
// Executed when script is loaded
});
```
```ts [With Options]
export default defineUnlistedScript({
// Set include/exclude if the script should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
main() {
// Executed when script is loaded
},
});
```
:::
Scripts are accessible from `/<name>.js`:
```ts
const url = browser.runtime.getURL('/<name>.js');
console.log(url); // "chrome-extension://<id>/<name>.js"
```
You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
+38
View File
@@ -0,0 +1,38 @@
# ES Modules
Currently, ESM entrypoints are opt-in, so you must configure each entrypoint with that in mind.
## HTML Pages <Badge type="warning" text="≥0.0.1" />
In general, you should always make HTML pages import ESM scripts, unless you need to support old browsers.
To make a script ESM, add `type="module"`:
<!-- prettier-ignore -->
```html
<script src="./main.ts"></script> <!-- [!code --] -->
<script src="./main.ts" type="module"></script> <!-- [!code ++] -->
```
## Background <Badge type="warning" text="≥0.16.0" />
In your background script, set `type: "module"`:
```ts
export default defineBackground({
type: 'module', // [!code ++]
main() {
// ...
},
});
```
:::warning
Only MV3 supports ESM background scripts/service workers. When targeting MV2, the `type` option is ignored and the background is always bundled into a single file as IIFE.
:::
## Content Scripts
WXT does not yet include built-in support for ESM content scripts. The plan is to add support for chunking to reduce bundle size, but not support HMR for now. There are several technical issues that make implementing a generic solution for HMR impossible. See [Content Script ESM Support #357](https://github.com/wxt-dev/wxt/issues/357) for details.
If you can't wait, and need ESM support right now, you can implement ESM support manually. See the [ESM Content Script UI](https://github.com/wxt-dev/examples/tree/main/examples/esm-content-script-ui) example to get started.
+92
View File
@@ -0,0 +1,92 @@
# Extension APIs
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/api) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Browser_support_for_JavaScript_APIs)
Different browsers provide different global variables for accessing the extension APIs (chrome provides `chrome`, firefox provides `browser`, etc).
WXT simplifies this - always use `browser`:
```ts
browser.action.onClicked.addListener(() => {
// ...
});
```
Other than that, refer to Chrome and Mozilla's documentation for how to use specific APIs. Everything a normal extension can do, WXT can do as well, just via `browser` instead of `chrome`.
## Webextension Polyfill
> Since `v0.1.0`
By default, WXT uses the [`webextension-polyfill` by Mozilla](https://www.npmjs.com/package/webextension-polyfill) to make the extension API consistent between browsers.
To access types, you should import the relevant namespace from `wxt/browser`:
```ts
import { Runtime } from 'wxt/browser';
function handleMessage(message: any, sender: Runtime.Sender) {
// ...
}
```
### Disabling the polyfill
> Since `v0.19.0`
After the release of MV3 and Chrome's official deprecation of MV2 in June 2024, the polyfill isn't really doing anything useful anymore.
You can disable it with a single line:
```ts
// wxt.config.ts
export default defineConfig({
extensionApi: 'chrome',
});
```
This will change `wxt/browser` to simply export the `browser` or `chrome` globals based on browser at runtime:
<<< @/../packages/wxt/src/browser/chrome.ts#snippet
Accessing types is a little different with the polyfill disabled. They do not need to be imported; they're available on the `browser` object itself:
```ts
function handleMessage(message: any, sender: browser.runtime.Sender) {
// ...
}
```
## Feature Detection
Depending on the manifest version and browser, some APIs are not available at runtime. If an API is not available, it will be `undefined`.
:::warning
Types will not help you here. The types WXT provides for `browser` assume all APIs exist. You are responsible for knowing whether an API is available or not.
:::
To check if an API is available, use feature detection:
```ts
if (browser.runtime.onSuspend != null) {
browser.runtime.onSuspend.addListener(() => {
// ...
});
}
```
Here, [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) is your best friend:
```ts
browser.runtime.onSuspend?.addListener(() => {
// ...
});
```
Alternatively, if you're trying to use similar APIs under different names (to support MV2 and MV3), you can do something like this:
```ts
(browser.action ?? browser.browser_action).onClicked.addListener(() => {
//
});
```
@@ -0,0 +1,103 @@
# Frontend Frameworks
## Built-in Modules
WXT has preconfigured modules for the most popular frontend frameworks:
- [`@wxt-dev/module-react`](https://github.com/wxt-dev/wxt/tree/main/packages/module-react)
- [`@wxt-dev/module-vue`](https://github.com/wxt-dev/wxt/tree/main/packages/module-vue)
- [`@wxt-dev/module-svelte`](https://github.com/wxt-dev/wxt/tree/main/packages/module-svelte)
- [`@wxt-dev/module-solid`](https://github.com/wxt-dev/wxt/tree/main/packages/module-solid)
Install the module for your framework, then add it to your config:
:::code-group
```ts [React]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-react'],
});
```
```ts [Vue]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-vue'],
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-svelte'],
});
```
```ts [Solid]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-solid'],
});
```
:::
## Adding Vite Plugins
If your framework doesn't have an official WXT module, no worries! WXT supports any framework with a Vite plugin.
Just add the Vite plugin to your config and you're good to go! Use the framework in HTML pages or content scripts and it will just work 👍
```ts
import { defineConfig } from 'wxt';
import react from '@vitejs/plugin-react';
export default defineConfig({
vite: () => ({
plugins: [react()],
}),
});
```
> The WXT modules just simplify the configuration and add auto-imports. They're not much different than the above.
## Multiple Apps
Since web extensions usually contain multiple UIs across multiple entrypoints (popup, options, changelog, side panel, content scripts, etc), you'll need to create individual app instances, one per entrypoint.
Usually, this means each entrypoint should be a directory with it's own files inside it. Here's the recommended folder structure:
<!-- prettier-ignore -->
```html
📂 {srcDir}/
📂 assets/ <---------- Put shared assets here
📄 tailwind.css
📂 components/
📄 Button.tsx
📂 entrypoints/
📂 options/ <--------- Use a folder with an index.html file in it
📁 pages/ <--------- A good place to put your router pages if you have them
📄 index.html
📄 App.tsx
📄 main.tsx <--------- Create and mount your app here
📄 style.css <--------- Entrypoint-specific styles
📄 router.ts
```
## Configuring Routers
All frameworks come with routers for building a multi-page app using the URL's path... But web extensions don't work like this. Since HTML files are static, `chrome-extension://{id}/popup.html`, there's no way to change the entire path for routing.
Instead, you need to configure the router to run in "hash" mode, where the routing information is a part of the URL's hash, not the path (ie: `popup.html#/` and `popup.html#/account/settings`).
Refer to your router's docs for information about hash mode and how to enable it. Here's a non-extensive list of a few popular routers:
- [`react-router`](https://reactrouter.com/en/main/routers/create-hash-router)
- [`vue-router`](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [`svelte-spa-router`](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [`solid-router`](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
+90
View File
@@ -0,0 +1,90 @@
# I18n
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/api/i18n) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n)
This page discusses how to setup internationalization using the vanilla `browser.i18n` APIs and mentions some alternatives if you want to use something else.
[[toc]]
## Usage
1. Add `default_locale` to your manifest:
```ts
export default defineConfig({
manifest: {
default_locale: 'en',
},
});
```
2. Create `messages.json` files in the `public/` directory:
<!-- prettier-ignore -->
```html
📂 {srcDir}/
📂 public/
📂 _locales/
📂 en/
📄 messages.json
📂 de/
📄 messages.json
📂 ko/
📄 messages.json
```
```jsonc
// public/_locales/en/messages.json
{
"helloWorld": {
"message": "Hello world!",
},
}
```
3. Get the translation:
```ts
browser.i18n.getMessage('helloWorld');
```
4. _Optional_: Add translations for extension name and description:
```json
{
"extName": {
"message": "..."
},
"extDescription": {
"message": "..."
},
"helloWorld": {
"message": "Hello world!"
}
}
```
```ts
export default defineConfig({
manifest: {
name: '__MSG_extName__',
description: '__MSG_extDescription__',
default_locale: 'en',
},
});
```
## Alternatives
The vanilla API has very few features, which is why you may want to consider using third-party NPM packages like `i18next`, `react-i18n`, `vue-i18n`, etc.
However, it is recommended you stick with the vanilla API (or a package based on top of the vanilla API, like [`@wxt-dev/i18n`](/i18n)), because:
- They can localize text in your manifest and CSS files
- Translations are loaded synchronously
- Translations are not bundled multiple times, keeping your extension small
- Zero configuration
However, there is one major downside to the vanilla API and any packages built on top of it:
- Language cannot be changed without changing your browser/system language
Here are some examples of how to setup a third party i18n library:
- [vue-i18n](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vue-i18n)
+16
View File
@@ -0,0 +1,16 @@
# Messaging
[Chrome Docs](https://developer.chrome.com/docs/extensions/develop/concepts/messaging) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#communicating_with_background_scripts)
Read the docs linked above to learn more about using the vanilla messaging APIs.
## Alternatives
The vanilla APIs are difficult to use and are a pain point to many new extension developers. For this reason, WXT recommends installing an NPM package that wraps around the vanilla APIs.
Here are some popular messaging libraries that support all browsers and work with WXT:
- [`trpc-chrome`](https://www.npmjs.com/package/trpc-chrome) - [tRPC](https://trpc.io/) adapter for Web Extensions.
- [`webext-bridge`](https://www.npmjs.com/package/webext-bridge) - Messaging in WebExtensions made super easy. Out of the box.
- [`@webext-core/messaging`](https://www.npmjs.com/package/@webext-core/messaging) - Light weight, type-safe wrapper around the web extension messaging APIs
- [`@webext-core/proxy-service`](https://www.npmjs.com/package/@webext-core/proxy-service) - A type-safe wrapper around the web extension messaging APIs that lets you call a function from anywhere, but execute it in the background.
+101
View File
@@ -0,0 +1,101 @@
# Project Structure
WXT follows a strict project structure. By default, it's a flat folder structure that looks like this:
<!-- prettier-ignore -->
```html
📂 {rootDir}/
📁 .output/
📁 .wxt/
📁 assets/
📁 components/
📁 composables/
📁 entrypoints/
📁 hooks/
📁 modules/
📁 public/
📁 utils/
📄 .env
📄 .env.publish
📄 app.config.ts
📄 package.json
📄 tsconfig.json
📄 web-ext.config.ts
📄 wxt.config.ts
```
Here's a brief summary of each of these files and directories:
- `.output/`: All build artifacts will go here
- `.wxt/`: Generated by WXT, it contains TS config
- `assets/`: Contains all CSS, images, and other assets that should be processed by WXT
- `components/`: Auto-imported by default, contains UI components
- `composables/`: Auto-imported by default, contains composable functions for Vue
- `entrypoints/`: Contains all the entrypoints that get bundled into your extension
- `hooks/`: Auto-imported by default, contains hooks for React and Solid
- `public/`: Contains any files you want to copy into the output folder as-is, without being processed by WXT
- `utils/`: Auto-imported by default, contains generic utilities used throughout your project
- `.env`: Contains [Environment Variables](/guide/essentials/config/environment-variables)
- `.env.publish`: Contains Environment Variables for [publishing](/guide/essentials/publishing)
- `app.config.ts`: Contains [Runtime Config](/guide/essentials/config/runtime)
- `package.json`: The standard file used by your package manager
- `tsconfig.json`: Config telling TypeScript how to behave
- `web-ext.config.ts`: Configure [Browser Startup](/guide/essentials/config/browser-startup)
- `wxt.config.ts`: The main config file for WXT projects
## Adding a `src/` Directory
Many developers like having a `src/` directory to separate source code from configuration files. You can enable it inside the `wxt.config.ts` file:
```ts
// wxt.config.ts
export default defineConfig({
srcDir: 'src',
});
```
After enabling it, your project structure should look like this:
<!-- prettier-ignore -->
```html
📂 {rootDir}/
📁 .output/
📁 .wxt/
📂 src/
📁 assets/
📁 components/
📁 composables/
📁 entrypoints/
📁 hooks/
📁 modules/
📁 public/
📁 utils/
📄 app.config.ts
📄 .env
📄 .env.publish
📄 package.json
📄 tsconfig.json
📄 web-ext.config.ts
📄 wxt.config.ts
```
## Customizing Other Directories
You can configure the following directories:
<!-- prettier-ignore -->
```ts
// wxt.config.ts
export default defineConfig({
// Relative to project root
srcDir: "src", // default: "."
outDir: "dist", // default: ".output"
// Relative to srcDir
entrypointsDir: "entries", // default: "entrypoints"
modulesDir: "wxt-modules", // default: "modules"
publicDir: "static", // default: "public"
})
```
You can use absolute or relative paths.
@@ -4,7 +4,7 @@ outline: deep
# Publishing
WXT will help you ZIP your extensions and submit them to the stores for review.
WXT can ZIP your extension and submit it to various stores for review or for self-hosting.
## First Time Publishing
@@ -18,16 +18,16 @@ For specific details about each store, see the stores sections below.
## Automation
WXT provides two commands to help automate the release process:
WXT provides two commands to help automate submitting a new version for review and publishing:
- `wxt submit init`: Setup all the required secrets and options for the `wxt submit` command
- `wxt submit`: Submit new versions of your extension for review (and publish them automatically once approved)
- `wxt submit init`: Help setup all the required secrets and options for the `wxt submit` command
To get started, run `wxt submit init` and follow the prompts. Once finished, you should have a `.env.submit` file! WXT will use this file to submit your updates.
> In CI, make sure you add all the environment variables to the submit step.
To release an update, build all the ZIPs you plan on releasing:
To submit a new version for publishing, build all the ZIPs you plan on releasing:
```sh
wxt zip
@@ -36,29 +36,25 @@ wxt zip -b firefox
Then run the `wxt submit` command, passing in all the ZIP files you want to release. In this case, we'll do a release for all 3 major stores: Chrome Web Store, Edge Addons, and Firefox Addons Store.
If it's your first time running the command, you'll want to test your secrets by passing the `--dry-run` flag:
If it's your first time running the command or you recently made changes to the release process, you'll want to test your secrets by passing the `--dry-run` flag.
```sh
wxt submit --dry-run \
--chrome-zip .output/<your-extension>-<version>-chrome.zip \
--firefox-zip .output/<your-extension>-<version>-firefox.zip --firefox-sources-zip .output/<your-extension>-<version>-sources.zip \
--edge-zip .output/<your-extension>-<version>-chrome.zip
--chrome-zip .output/{your-extension}-{version}-chrome.zip \
--firefox-zip .output/{your-extension}-{version}-firefox.zip --firefox-sources-zip .output/{your-extension}-{version}-sources.zip \
--edge-zip .output/{your-extension}-{version}-chrome.zip
```
If the dry run passes, remove the flag and do the actual release:
```sh
wxt submit \
--chrome-zip .output/<your-extension>-<version>-chrome.zip \
--firefox-zip .output/<your-extension>-<version>-firefox.zip --firefox-sources-zip .output/<your-extension>-<version>-sources.zip \
--edge-zip .output/<your-extension>-<version>-chrome.zip
--chrome-zip .output/{your-extension}-{version}-chrome.zip \
--firefox-zip .output/{your-extension}-{version}-firefox.zip --firefox-sources-zip .output/{your-extension}-{version}-sources.zip \
--edge-zip .output/{your-extension}-{version}-chrome.zip
```
:::tip
If you only need to release to a single store, only pass that store's ZIP flag.
:::
:::tip
:::warning
See the [Firefox Addon Store](#firefox-addon-store) section for more details about the `--firefox-sources-zip` option.
:::
@@ -78,9 +74,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 'latest'
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
@@ -135,9 +129,7 @@ wxt zip
Firefox requires you to upload a ZIP of your source code. This allows them to rebuild your extension and review the code in a readable way. More details can be found in [Firefox's docs](https://extensionworkshop.com/documentation/publish/source-code-submission/).
WXT fully supports generating and automatically submitting a source code ZIP.
When you run `wxt zip -b firefox`, your sources are zipped into the `.output` directory alongside the extension. WXT will automatically exclude certain files such as config files, hidden files, and tests. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension.
When running `wxt zip -b firefox`, WXT will zip both your extension and sources. Certain files (such as config files, hidden files, tests, and excluded entrypoints) are automatically excluded from your sources. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension.
To customize which files are zipped, add the `zip` option to your config file.
@@ -178,16 +170,16 @@ bun zip:firefox
:::
Ensure that you have a `README.md` or `SOURCE_CODE_REVIEW.md` file with the above commands so that the Firefox team knows how to build your extension.
Make sure the build output is the exact same when running `wxt build -b firefox` in your main project and inside the zipped sources.
:::warning
If you use a `.env` files, they can effect the chunk hashes in the output directory. Either delete the .env file before running `wxt zip -b firefox`, or include it in your sources zip with the [`zip.includeSources`](/api/wxt/interfaces/InlineConfig#includesources) option. Be careful to not include any secrets in your `.env` files.
If you use a `.env` files, they can affect the chunk hashes in the output directory. Either delete the .env file before running `wxt zip -b firefox`, or include it in your sources zip with the [`zip.includeSources`](/api/reference/wxt/interfaces/InlineConfig#includesources) option. Be careful to not include any secrets in your `.env` files.
See Issue [#377](https://github.com/wxt-dev/wxt/issues/377) for more details.
:::
Ensure that you have a `README.md` or `SOURCE_CODE_REVIEW.md` file with the above commands so that the Firefox team knows how to build your extension.
#### Private Packages
If you use private packages and you don't want to provide your auth token to the Firefox team during the review process, you can use `zip.downloadPackages` to download any private packages and include them in the zip.
@@ -207,7 +199,7 @@ export default defineConfig({
Depending on your package manager, the `package.json` in the sources zip will be modified to use the downloaded dependencies via the `overrides` or `resolutions` field.
:::warning
WXT uses the command `npm pack <package-name>` to download the package. That means regardless of your package manager, you need to properly setup a `.npmrc` file. NPM and PNPM both respect `.npmrc` files, but Yarn and Bun have their own ways of authorizing private registries, so you'll need to add an `.npmrc` file.
WXT uses the command `npm pack <package-name>` to download the package. That means regardless of your package manager, you need to properly setup a `.npmrc` file. NPM and PNPM both respect `.npmrc` files, but Yarn and Bun have their own ways of authorizing private registries, so you'll need to add a `.npmrc` file.
:::
### Safari
@@ -1,10 +1,10 @@
# Remote Code
WXT will automatically download and bundle imports with the `url:` prefix so the extension does not depend of remote code, [a requirement from Google for MV3](https://developer.chrome.com/docs/extensions/migrating/improve-security/#remove-remote-code).
WXT will automatically download and bundle imports with the `url:` prefix so the extension does not depend on remote code, [a requirement from Google for MV3](https://developer.chrome.com/docs/extensions/migrating/improve-security/#remove-remote-code).
## Google Analytics
For example, you can import google analytics:
For example, you can import Google Analytics:
```ts
// utils/google-analytics.ts
+29
View File
@@ -0,0 +1,29 @@
# Scripting
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/api/scripting) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting)
Refer to the browser docs above for basics on how the API works.
## Execute Script Return Values
When using `browser.scripting.executeScript`, you can execute content scripts or unlisted scripts. To return a value, just return a value from the script's `main` function.
```ts
// entrypoints/background.ts
const res = await browser.scripting.executeScript({
target: { tabId },
files: ['content-scripts/example.js'],
});
console.log(res); // "Hello John!"
```
```ts
// entrypoints/example.content.ts
export default defineContentScript({
registration: 'runtime',
main(ctx) {
console.log('Script was executed!');
return 'Hello John!';
},
});
```
+15
View File
@@ -0,0 +1,15 @@
# Storage
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/api/storage) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage)
You can use the vanilla APIs (see docs above), use [WXT's built-in storage API](/storage), or install a package from NPM.
## Alternatives
1. [`wxt/storage`](/storage) (recommended): WXT ships with its own wrapper around the vanilla storage APIs that simplifies common use cases
2. DIY: If you're migrating to WXT and already have a storage wrapper, keep using it. In the future, if you want to delete that code, you can use one of these alternatives, but there's no reason to replace working code during a migration.
3. Any other NPM package: [There are lots of wrappers around the storage API](https://www.npmjs.com/search?q=chrome%20storage), you can find one you like. Here's some popular ones:
- [`webext-storage`](https://www.npmjs.com/package/webext-storage) - A more usable typed storage API for Web Extensions
- [`@webext-core/storage`](https://www.npmjs.com/package/@webext-core/storage) - A type-safe, localStorage-esque wrapper around the web extension storage APIs

Some files were not shown because too many files have changed in this diff Show More