Compare commits

..

173 Commits

Author SHA1 Message Date
GitHub Actions aea137a3a1 chore(release): v0.12.3 2023-12-23 04:26:47 +00:00
Aaron ad0e8e4144 fix: Correctly mock webextension-polyfill for Vitest (#294) 2023-12-22 22:21:09 -06:00
GitHub Actions 7fd5752ef1 chore(release): v0.12.2 2023-12-19 17:48:24 +00:00
Aaron 1a8b7ff635 feat: Support PNPM without hoisting dependencies (#291) 2023-12-19 11:42:20 -06:00
GitHub Actions fe3ca0fcbd chore(release): v0.12.1 2023-12-16 17:24:16 +00:00
Aaron f49ee9f005 fix: Fix package.json lint errors (#290) 2023-12-16 11:18:16 -06:00
Aaron 961440c0ac fix: Upgrade @webext-core/match-patterns to 1.0.3 (#289) 2023-12-16 11:07:18 -06:00
Aaron 9b562b0ca8 chore: Upgrade templates to wxt@^0.12.0 (#285) 2023-12-13 16:21:52 -06:00
Aaron Klinker 7b2563b2dc Update changelog with breaking changes 2023-12-13 16:02:24 -06:00
GitHub Actions 63f507ecf5 chore(release): v0.12.0 2023-12-13 21:57:34 +00:00
Aaron 0a88955394 fix: Throw error when no entrypoints are found (#283) 2023-12-13 15:50:28 -06:00
Aaron Klinker 291d25b675 docs: Fix dead links 2023-12-13 15:50:09 -06:00
Aaron 6f902cc598 docs: Improve content script UI guide (#272) 2023-12-13 15:43:40 -06:00
Aaron fde488ac82 feat!: Add support for "main world" content scripts (#284)
BREAKING CHANGE: `defineContentScript` and `defineBackground` are now exported from `wxt/sandbox` instead of `wxt/client`. If you use auto-imports, no changes are required. If you have disabled auto-imports, you'll need to manually update your import statements.
2023-12-13 15:41:39 -06:00
Aaron 0873c24ad8 chore: Convert WXT CLI to an ESM binary (#279) 2023-12-12 14:44:04 -06:00
Aaron 7c02497148 fix: Only use type imports for Vite (#278) 2023-12-12 14:24:42 -06:00
GitHub Actions 25441a3b97 chore(release): v0.11.2 2023-12-12 19:24:47 +00:00
Aaron f1cf866fe1 fix: Improve duplicate entrypoint name detection and catch the error before loading their config (#276) 2023-12-12 13:22:09 -06:00
Aaron d92a126489 fix: Discover .js, .jsx, and .tsx unlisted scripts correctly (#274) 2023-12-12 12:24:38 -06:00
Aaron Klinker bcb93afe4e chore: Add missing navigation item 2023-12-09 23:37:29 -06:00
Aaron 21cf3642cb docs: Improve content script UI docs (#268) 2023-12-09 23:36:35 -06:00
Andrew Walsh 2ef28ec0c4 Update content-scripts.md (#266) 2023-12-09 23:33:59 -06:00
Aaron 59094c9af3 chore: Update sSolid template to vite 5 (#265) 2023-12-07 23:05:08 -06:00
GitHub Actions ba41691296 chore(release): v0.11.1 2023-12-07 21:41:45 +00:00
Aaron 8786aa91b3 feat: Add util for detecting URL changes in content scripts (#264) 2023-12-07 13:29:33 -06:00
Aaron 0623d10653 chore: Upgrade templates to wxt@^0.11.0 (#263) 2023-12-06 11:39:03 -06:00
Aaron Klinker 85174d4994 Fix changelog 2023-12-06 11:33:49 -06:00
GitHub Actions 4e2dd5d618 chore(release): v0.11.0 2023-12-06 17:24:11 +00:00
Aaron 1a39a0d153 feat!: Vite 5 support (#261) 2023-12-06 11:18:52 -06:00
Aaron f922648dd7 chore: Abstract vite from WXT's core logic (#242) 2023-12-05 16:55:22 -06:00
Ítalo Brasil e6142e3608 docs: Adds tl;dv to homepage (#260) 2023-12-05 12:30:57 -06:00
Aaron ed73451fd0 chore: Speed up CI using pnpm instead of npm (#259) 2023-12-04 14:14:44 -06:00
GitHub Actions 8a25a2f60f chore(release): v0.10.4 2023-12-04 15:54:42 +00:00
Aaron 397e9a8b45 feat: Add config to customize outDir (#258) 2023-12-04 09:50:24 -06:00
dependabot[bot] 7cef7680de chore(deps): bump esbuild from 0.19.5 to 0.19.8 (#253)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 15:23:31 -06:00
dependabot[bot] 0c57375758 chore(deps-dev): bump tsx from 3.14.0 to 4.6.1 (#252)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 15:09:19 -06:00
dependabot[bot] 2c6af84165 chore(deps): bump rollup-plugin-visualizer from 5.9.2 to 5.9.3 (#256)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 15:08:06 -06:00
dependabot[bot] 58e1371701 chore(deps-dev): bump @types/lodash.merge from 4.6.8 to 4.6.9 (#255)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 15:06:58 -06:00
dependabot[bot] 0d8e7463b8 chore(deps-dev): bump prettier from 3.0.3 to 3.1.0 (#254)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 15:06:24 -06:00
Aaron 77eeacaf0f docs: Update sidepanel availability (#250) 2023-11-29 17:12:37 -06:00
冯不游 e80c134150 docs: Add Doozy to homepage (#249) 2023-11-29 07:21:46 -06:00
GitHub Actions 03c8ab4d6f chore(release): v0.10.3 2023-11-28 18:18:59 +00:00
Aaron df13b9705c fix(auto-imports): Don't add imports to node_module dependencies (#247) 2023-11-28 12:15:05 -06:00
Aaron 762ba0080d chore(deps): Upgrade to typescript 5.3 (#245) 2023-11-27 10:03:03 -06:00
Aaron Klinker 317b1b6dcc docs: Fix typo 2023-11-27 09:57:09 -06:00
Aaron 93175a6477 chore: Use defu for merging some config objects (#243) 2023-11-26 12:23:10 -06:00
Aaron 60d6707b11 chore: Use normalize-path instead of vite.normalizePath (#244) 2023-11-26 11:36:42 -06:00
Aaron Klinker 97cbda3dab ci: only print response headers from docs webhook 2023-11-25 14:57:04 -06:00
Aaron Klinker 1611c1dba6 ci: Publish docs on push to main 2023-11-25 14:51:30 -06:00
Aaron Klinker b59252284a Print verbose webhook 2023-11-25 14:48:05 -06:00
Aaron Klinker 742b99657a chore: Trigger docs upgrade via webhook 2023-11-25 14:45:41 -06:00
Aaron Klinker e2997a43e0 Fix docker push command 2023-11-24 14:18:27 -06:00
Aaron Klinker 21dead60fd Self-host docs 2023-11-24 14:16:18 -06:00
GitHub Actions 3d1bc0a12c chore(release): v0.10.2 2023-11-20 19:52:55 +00:00
Aaron Klinker 4b24bee1fb Update demo app logs 2023-11-20 13:47:31 -06:00
Aaron Klinker 82ed821eb5 fix: Apply mode option to build steps correctly 2023-11-20 13:47:18 -06:00
Aaron 60625280c2 chore: Upgrade templates to v0.10 (#239) 2023-11-16 16:50:30 -06:00
GitHub Actions 083792b5f0 chore(release): v0.10.1 2023-11-16 17:46:36 +00:00
Aaron Klinker 3da3e07bda fix: Remove WXT global to remove unused modules from production builds 2023-11-16 11:35:20 -06:00
Aaron Klinker 64d61eb3f1 Update changelog 2023-11-14 23:26:27 -06:00
GitHub Actions aa6009e476 chore(release): v0.10.0 2023-11-15 05:21:53 +00:00
Aaron d2cb8f9416 feat: Add wxt/storage API (#234) 2023-11-14 23:07:45 -06:00
Nenad Novaković 046a4809d3 feat!: Allow plural directory and only png's for manifest icons (#237)
BREAKING CHANGE: No longer discover icons with extensions other than `.png`. If you previously used `.jpg`, `.jpeg`, `.bmp`, or `.svg`, you'll need to convert your icons to `.png` files or manually add them to the manifest inside your `wxt.config.ts` file.
2023-11-14 23:07:09 -06:00
Aaron Klinker 207b750d4e docs: Update main README links 2023-11-12 10:37:56 -06:00
Aaron 077fa9939e feat: List bun as an experimental option in wxt init (#233) 2023-11-11 15:45:29 -06:00
Aaron 44d715a29e fix: Don't use bun to load entrypoint config (#232) 2023-11-11 14:46:13 -06:00
GitHub Actions 3ab9fe4ae3 chore(release): v0.9.2 2023-11-11 19:57:25 +00:00
Aaron 91b28c2cb9 feat: Experimental option to exclude webextension-polyfill (#231) 2023-11-11 13:53:47 -06:00
Aaron Klinker d1b523061f ci: Fix sync-release workflow 2023-11-11 12:49:40 -06:00
Aaron Klinker c69ea3967f Fix changelog 2023-11-11 12:47:32 -06:00
GitHub Actions 41527a2200 chore(release): v0.9.1 2023-11-11 18:27:09 +00:00
Aaron 281f28192d fix: Generate valid manifest for Firefox MV3 (#229) 2023-11-11 12:22:46 -06:00
Aaron 67ffa44f9c docs: Update feature comparison 2023-11-11 08:53:59 -06:00
Aaron Klinker 421c0e412d docs: Add StayFree to homepage (#221) 2023-11-08 10:51:42 -06:00
Aaron d21ee08eb6 docs: Add UltraWideo to homepage (#193) 2023-11-08 09:26:51 -06:00
Aaron 7fa150dfae docs: Improve the "Used By" section on homepage (#220) 2023-11-08 08:27:58 -06:00
Aaron 7ba52b18fd ci: Automatically sync GitHub releases with CHANGELOG.md on push (#218) 2023-11-07 13:08:46 -06:00
Aaron 66e5079b84 feat: Add alias config for customizing path aliases (#216) 2023-11-07 11:38:48 -06:00
Aaron Klinker f1e8084be8 chore: Update Vite dependency range to ^4.0.0 || ^5.0.0-0 2023-11-07 10:42:36 -06:00
Aaron Klinker 609ae2ae37 fix: Move webextension-polyfill from peer to regular dependencies
This closes https://github.com/wxt-dev/wxt/issues/215
2023-11-07 10:38:24 -06:00
Aaron Klinker c81dfff37c docs: Add examples 2023-11-05 11:35:59 -06:00
Aaron 9c27820add chore: Upgrade templates to wxt@^0.9.0 (#214) 2023-11-04 13:01:05 -05:00
Aaron 22f8e10918 chore: Remove whitespace from genearted .wxt files (#211) 2023-11-04 12:28:31 -05:00
Aaron Klinker 96d41f8b7c Update changelog 2023-11-04 11:17:32 -05:00
GitHub Actions c4ce44e001 chore(release): v0.9.0 2023-11-04 15:56:43 +00:00
yyyanghj 6641ffaeed fix!: Remove lib from .wxt/tsconfig.json (#209)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2023-11-04 10:50:52 -05:00
Aaron 450dc0975f Add CLI screenshot back to README 2023-11-04 09:12:06 -05:00
Aaron d343555f41 Update MP4 title 2023-11-04 01:34:25 -05:00
Aaron 7993908c6c docs: Add demo video (#208) 2023-11-04 01:32:19 -05:00
yyyanghj 94e1dbce24 chore: Fix Svelte and React template READMEs (#207) 2023-11-03 12:59:35 -05:00
Aaron d14215946a Cleanup README badge links 2023-11-03 11:46:14 -05:00
Aaron Klinker 345406f02a docs: Fix heading 2023-11-03 09:39:09 -05:00
GitHub Actions 6b689bab23 chore(release): v0.8.7 2023-11-03 14:36:36 +00:00
Aaron fcdf0dcbe9 feat: createContentScriptIframe utility (#206) 2023-11-03 09:31:16 -05:00
Aaron bd35acdae1 chore: Run E2E tests in parallel (#204) 2023-11-02 11:15:07 -05:00
Aaron 21c35ee593 ci: Separate validation into multiple jobs (#203) 2023-11-02 11:08:19 -05:00
Aaron Klinker 0f09cbee0e chore: Improve prettier git hook 2023-11-02 10:31:54 -05:00
Aaron Klinker 183bb02e29 chore(deps-dev): Update sass to 1.69.5 2023-11-02 10:26:14 -05:00
Aaron 0bf4ea0897 chore(deps): Upgrade vite to 4.5.0 (#202) 2023-11-02 10:24:50 -05:00
Aaron Klinker cd4d00e23e chore(deps-dev): Update @type/* packages for demo 2023-11-02 10:16:52 -05:00
Aaron Klinker 5de18e5371 chore(deps-dev): Upgrade vitepress to 1.0.0-rc.24 2023-11-02 10:04:33 -05:00
Aaron af823418ee chore(deps): Upgrade giget to ^1.1.3 (#199) 2023-11-02 09:11:17 -05:00
Aaron Klinker 0b8d101c33 chore(deps-dev): Upgrade vue to 3.3.7 2023-11-02 08:58:25 -05:00
Aaron b587849126 chore(deps-dev): Upgrade typedoc to ^0.25.3 (#201) 2023-11-02 08:57:16 -05:00
Aaron 2a35ce0a1e chore(deps-dev): Upgrade execa to ^8.0.1 (#200) 2023-11-02 08:57:06 -05:00
Aaron Klinker c390b70f76 chore(deps): Upgrade unimport to 3.4.0 2023-11-02 08:54:24 -05:00
Aaron Klinker c9fd739128 chore(deps): Upgrade linkedom to 0.16.1 2023-11-02 08:52:22 -05:00
Aaron Klinker 0806c06042 chore(deps): Upgrade jiti to 1.21.0 2023-11-02 08:51:14 -05:00
Aaron Klinker 5f74a544a4 chore(deps-dev): Upgrade lint-staged to ^15.0.2 2023-11-02 08:47:53 -05:00
Aaron Klinker 121778894c chore(deps): Upgrade immer to ^10.0.3 2023-11-02 08:46:25 -05:00
Aaron Klinker 102c72a03c chore(deps): Upgrade esbuild to ^0.19.5 2023-11-02 08:42:16 -05:00
Aaron Klinker a0507866db chore(deps): Upgrade c12 to ^1.5.1 2023-11-02 08:41:05 -05:00
Aaron Klinker f3874da780 chore: Upgrade types 2023-11-02 08:39:47 -05:00
dependabot[bot] cc5d24ec92 chore(deps): bump @types/webextension-polyfill from 0.10.0 to 0.10.5 (#196)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 08:05:50 -05:00
dependabot[bot] 08760015da chore(deps-dev): bump tsx from 3.12.8 to 3.14.0 (#198)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 08:02:53 -05:00
dependabot[bot] 5368371020 chore(deps): bump filesize from 10.0.12 to 10.1.0 (#197)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 08:01:50 -05:00
dependabot[bot] 08d62a437a chore(deps): bump is-wsl from 3.0.0 to 3.1.0 (#195)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 08:00:52 -05:00
dependabot[bot] 37e2348d79 chore(deps-dev): bump happy-dom from 12.4.0 to 12.10.3 (#194)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 07:59:55 -05:00
GitHub Actions 6a30dc46fa chore(release): v0.8.6 2023-10-30 15:41:46 +00:00
Aaron Klinker b75c553e51 fix: Inline WXT modules inside WxtVitest plugin 2023-10-30 10:25:22 -05:00
GitHub Actions a56face03d chore(release): v0.8.5 2023-10-30 14:54:59 +00:00
Aaron Klinker 19756c61ce docs: Add test.server.deps.inline to Vitest guide 2023-10-30 09:48:52 -05:00
Aaron 5f54b4de17 chore: Reduce package size by 70%, 1.92 MB to 590 kB (#190) 2023-10-29 23:00:10 -05:00
Aaron Klinker ea8935c47c Hide code coverage badge while it's broken 2023-10-29 21:24:21 -05:00
Aaron e37f73880d feat: Refactor project structure to export initialize, prepare, and zip functions (#182) 2023-10-29 21:19:31 -05:00
Aaron Klinker 2e24b9e18a chore: Update template docs 2023-10-29 20:24:38 -05:00
Aaron Klinker f8a0fb37a2 fix: Enable Vue SFC auto-imports in vue template 2023-10-29 20:24:27 -05:00
Nenad Novaković dd26b99027 docs: Update auto-imports.md (#186) 2023-10-29 08:29:54 -05:00
Aaron Klinker d9e9b43f8d docs: Improve runner.binaries documentation 2023-10-25 03:14:53 -05:00
GitHub Actions d580083727 chore(release): v0.8.4 2023-10-20 15:26:38 +00:00
Aaron 5a70d9e57d fix: Allow actions without a popup (#181) 2023-10-20 10:20:40 -05:00
GitHub Actions 1b1af245bd chore(release): v0.8.3 2023-10-18 21:38:14 +00:00
Aaron 874a531a62 feat: Add testing utils under wxt/testing (#178) 2023-10-18 16:28:26 -05:00
GitHub Actions a3d409f250 chore(release): v0.8.2 2023-10-17 20:39:45 +00:00
Aaron b6758ca9fa fix(firefox): Stop extending AbortController to fix crash in content scripts (#176) 2023-10-17 15:33:55 -05:00
Aaron 2672308946 chore: Improve output consistency (#175) 2023-10-16 11:00:33 -05:00
GitHub Actions 7ac171ed3b chore(release): v0.8.1 2023-10-16 14:33:06 +00:00
Aaron Klinker 6a93f20bb3 fix: Output action.browser_style correctly 2023-10-16 09:20:40 -05:00
Aaron Klinker 921af6a5a1 Rename jobs 2023-10-15 15:08:34 -05:00
Aaron Klinker 21ebeacd20 chore(deps): Revert node 20 upgrade due to OOM errors in CI 2023-10-15 14:51:36 -05:00
Aaron 527600031f docs: Generate full API docs with typedoc (#174) 2023-10-14 22:03:23 -05:00
Aaron Klinker 91a804c492 Update changelog 2023-10-14 10:40:36 -05:00
Aaron Klinker 54f3785063 Upgrade templates to use v0.8 2023-10-14 10:40:10 -05:00
GitHub Actions 44e4bc5295 chore(release): v0.8.0 2023-10-14 15:38:11 +00:00
Aaron f464d7d33f chore(deps): Use NodeJS v20 for development (#170) 2023-10-14 10:28:34 -05:00
Aaron d0672739f0 chore(deps): Upgrade vite to 4.4.11 (#169) 2023-10-14 10:19:00 -05:00
Aaron 44464f914f feat!: Use defineUnlistedScript to define unlisted scripts (#167)
BREAKING CHANGE: Unlisted scripts must now `export default defineUnlistedScript(...)`
2023-10-14 10:06:52 -05:00
Aaron 8940c41bdb chore(deps): Upgrade vitest to 0.34.6 (#168) 2023-10-14 10:06:20 -05:00
Aaron Klinker 446f265b6c types!: Rename BackgroundScriptDefintition to BackgroundDefinition 2023-10-14 09:41:07 -05:00
渣渣120 7a465684c0 docs: Fix wrong links (#166) 2023-10-14 08:09:46 -05:00
GitHub Actions 0369316463 chore(release): v0.7.5 2023-10-13 23:13:01 +00:00
Aaron 8b74291d18 fix: Add browser_style to popup options (#165) 2023-10-13 18:06:40 -05:00
Aaron 739b738100 fix: Ignore non-manifest fields when merging content script entries (#164) 2023-10-13 18:01:13 -05:00
Aaron 95442dd39b fix: More consistent version_name generation between browsers (#163) 2023-10-13 17:37:08 -05:00
GitHub Actions 4f82645c08 chore(release): v0.7.4 2023-10-11 15:59:17 +00:00
Aaron d200c376f8 fix: Support react-refresh when pre-rendering HTML pages in dev mode (#158) 2023-10-11 10:52:48 -05:00
Aaron Klinker b58fb02016 docs: Add migration guides 2023-10-07 10:49:31 -05:00
Aaron e1bab6c746 chore: Upgrade templates to v0.7 (#156) 2023-10-07 10:00:36 -05:00
GitHub Actions d10c22fbd9 chore(release): v0.7.3 2023-10-07 14:59:25 +00:00
Aaron da1f7f3ea5 feat: Support JS entrypoints (#155) 2023-10-07 09:53:47 -05:00
GitHub Actions 2659272f8a chore(release): v0.7.2 2023-10-05 22:17:59 +00:00
Aaron 868fd27804 feat: Allow customizing entrypoint options per browser (#154) 2023-10-05 17:12:20 -05:00
Aaron Klinker d9fdcb5b33 fix: Add missing persistent type to defineBackgroundScript 2023-10-05 16:08:03 -05:00
Aaron Klinker 580793158f fix: Default safari to MV2 2023-10-05 16:07:20 -05:00
Aaron Klinker f58d69dc5f docs: Add docs for development and testing 2023-10-02 15:05:47 -05:00
Aaron c9028dd335 docs: Restructure website to improve UX (#149) 2023-10-02 09:42:46 -05:00
dependabot[bot] 6342f59c5e chore(deps-dev): bump typescript from 5.1 to 5.2 (#148)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2023-10-01 17:58:31 -05:00
dependabot[bot] 2d91898452 chore(deps-dev): bump vitest from 0.34.3 to 0.34.6 (#145)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 17:36:48 -05:00
dependabot[bot] 4acefd601c chore(deps-dev): bump vitest-mock-extended from 1.2.1 to 1.3.0 (#147)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 17:27:00 -05:00
dependabot[bot] 9a2e71b481 chore(deps-dev): bump @faker-js/faker from 8.0.2 to 8.1.0 (#146)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 17:26:20 -05:00
dependabot[bot] 6e578f3156 chore(deps-dev): bump @types/fs-extra from 11.0.1 to 11.0.2 (#144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 17:25:42 -05:00
GitHub Actions 474de83e28 chore(release): v0.7.1 2023-10-01 04:18:14 +00:00
Aaron Klinker 65fcfc0064 docs: Add docs for createContentScriptUi 2023-09-30 23:05:07 -05:00
257 changed files with 8175 additions and 4027 deletions
+4 -2
View File
@@ -2,5 +2,7 @@ coverage:
status:
project:
default:
target: 80%
threshold: 1%
informational: true
patch:
default:
informational: true
+17
View File
@@ -0,0 +1,17 @@
name: Basic Setup
description: Install PNPM, Node, and dependencies
runs:
using: composite
steps:
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup NodeJS
uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- name: Install Dependencies
shell: bash
run: pnpm install
+27
View File
@@ -0,0 +1,27 @@
name: Publish Docs
on:
push:
branches:
- main
workflow_dispatch:
inputs:
tag:
description: Docker Image Tag
required: true
default: latest
jobs:
publish:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: docker/login-action@v3
with:
registry: https://${{ secrets.DOCKER_REGISTRY_HOSTNAME }}
username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- run: pnpm docs:build
- run: docker build docs/.vitepress -t ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
- run: docker push ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
- run: curl -X POST -i ${{ secrets.UPDATE_DOCS_WEBHOOK }}
+2 -15
View File
@@ -11,24 +11,11 @@ jobs:
needs:
- validate
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- uses: ./.github/actions/setup
- name: Bump and Tag
run: |
+22
View File
@@ -0,0 +1,22 @@
name: Sync Releases
on:
workflow_dispatch:
push:
branches:
- main
paths:
- CHANGELOG.md
jobs:
sync:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- run: pnpm sync-releases all --token ${{ secrets.GITHUB_TOKEN }}
+74 -115
View File
@@ -7,120 +7,79 @@ on:
- main
jobs:
wxt:
name: WXT
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Formatting
run: pnpm format:check
- name: Type Check
run: pnpm compile
- name: Build Demo
run: |
pnpm build:all:chrome-mv2
pnpm build:all:chrome-mv3
pnpm build:all:firefox-mv2
pnpm build:all:firefox-mv3
pnpm tsc --noEmit
pnpm wxt zip
working-directory: demo
- name: Tests
run: pnpm test:coverage --reporter=default --reporter=hanging-process
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
project-templates:
name: Project Templates
formatting:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build Local Tarball
run: pnpm pack
- name: Validate Vanilla
working-directory: templates/vanilla
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
- name: Validate Vue
working-directory: templates/vue
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
- name: Validate React
working-directory: templates/react
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
- name: Validate Svelte
working-directory: templates/svelte
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run check
- name: Validate Solid
working-directory: templates/solid
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm format:check
lint:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm lint
type-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm compile
validate-demo:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm build:all
working-directory: demo
- run: pnpm tsc --noEmit
working-directory: demo
- run: pnpm wxt zip
working-directory: demo
- run: pnpm vitest run
working-directory: demo
tests:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- name: pnpm test:coverage
run: pnpm test:coverage --reporter=default --reporter=hanging-process
- uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
windows-tests:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm test run
template:
runs-on: ubuntu-22.04
strategy:
matrix:
template:
- react
- solid
- svelte
- vanilla
- vue
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: pnpm pack
- run: npm i
working-directory: templates/${{ matrix.template }}
- run: npm i -D ../../wxt-*.tgz
working-directory: templates/${{ matrix.template }}
- run: pnpm compile
if: matrix.template != 'svelte'
working-directory: templates/${{ matrix.template }}
- run: pnpm check
if: matrix.template == 'svelte'
working-directory: templates/${{ matrix.template }}
- run: pnpm build
working-directory: templates/${{ matrix.template }}
env:
# Debug Vite 5's deprecated CJS support
VITE_CJS_TRACE: true
+2 -1
View File
@@ -17,5 +17,6 @@ web-ext.config.ts
templates/*/pnpm-lock.yaml
templates/*/yarn.lock
templates/*/package-lock.json
docs/config.md
docs/api
stats.html
.tool-versions
-7
View File
@@ -5,10 +5,3 @@ dist
docs/.vitepress/cache
pnpm-lock.yaml
CHANGELOG.md
.gitattributes
.gitignore
.prettierignore
*.png
*.svg
*.txt
_gitignore
+492
View File
@@ -1,5 +1,497 @@
# Changelog
## v0.12.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.12.2...v0.12.3)
### 🩹 Fixes
- Correctly mock `webextension-polyfill` for Vitest ([#294](https://github.com/wxt-dev/wxt/pull/294))
## v0.12.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.12.1...v0.12.2)
### 🚀 Enhancements
- Support PNPM without hoisting dependencies ([#291](https://github.com/wxt-dev/wxt/pull/291))
## v0.12.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.12.0...v0.12.1)
### 🩹 Fixes
- Upgrade `@webext-core/match-patterns` to `1.0.3` ([#289](https://github.com/wxt-dev/wxt/pull/289))
- Fix `package.json` lint errors ([#290](https://github.com/wxt-dev/wxt/pull/290))
### 🏡 Chore
- Upgrade templates to `wxt@^0.12.0` ([#285](https://github.com/wxt-dev/wxt/pull/285))
## v0.12.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.11.2...v0.12.0)
### 🚀 Enhancements
- ⚠️ Add support for "main world" content scripts ([#284](https://github.com/wxt-dev/wxt/pull/284))
### 🩹 Fixes
- Only use type imports for Vite ([#278](https://github.com/wxt-dev/wxt/pull/278))
- Throw error when no entrypoints are found ([#283](https://github.com/wxt-dev/wxt/pull/283))
### 📖 Documentation
- Improve content script UI guide ([#272](https://github.com/wxt-dev/wxt/pull/272))
- Fix dead links ([291d25b](https://github.com/wxt-dev/wxt/commit/291d25b))
### 🏡 Chore
- Convert WXT CLI to an ESM binary ([#279](https://github.com/wxt-dev/wxt/pull/279))
#### ⚠️ Breaking Changes
`defineContentScript` and `defineBackground` are now exported from `wxt/sandbox` instead of `wxt/client`. ([#284](https://github.com/wxt-dev/wxt/pull/284))
- If you use auto-imports, no changes are required.
- If you have disabled auto-imports, you'll need to manually update your import statements:
```diff
- import { defineBackground, defineContentScript } from 'wxt/client';
+ import { defineBackground, defineContentScript } from 'wxt/sandbox';
```
## v0.11.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.11.1...v0.11.2)
### 🩹 Fixes
- Discover `.js`, `.jsx`, and `.tsx` unlisted scripts correctly ([#274](https://github.com/wxt-dev/wxt/pull/274))
- Improve duplicate entrypoint name detection and catch the error before loading their config ([#276](https://github.com/wxt-dev/wxt/pull/276))
### 📖 Documentation
- Improve content script UI docs ([#268](https://github.com/wxt-dev/wxt/pull/268))
### 🏡 Chore
- Update sSolid template to vite 5 ([#265](https://github.com/wxt-dev/wxt/pull/265))
- Add missing navigation item ([bcb93af](https://github.com/wxt-dev/wxt/commit/bcb93af))
## v0.11.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.11.0...v0.11.1)
### 🚀 Enhancements
- Add util for detecting URL changes in content scripts ([#264](https://github.com/wxt-dev/wxt/pull/264))
### 🏡 Chore
- Upgrade templates to `wxt@^0.11.0` ([#263](https://github.com/wxt-dev/wxt/pull/263))
## v0.11.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.10.4...v0.11.0)
### 🚀 Enhancements
- ⚠️ Vite 5 support ([#261](https://github.com/wxt-dev/wxt/pull/261))
### 📖 Documentation
- Adds tl;dv to homepage ([#260](https://github.com/wxt-dev/wxt/pull/260))
### 🏡 Chore
- Speed up CI using `pnpm` instead of `npm` ([#259](https://github.com/wxt-dev/wxt/pull/259))
- Abstract vite from WXT's core logic ([#242](https://github.com/wxt-dev/wxt/pull/242))
#### ⚠️ Breaking Changes
- You will need to update any other Vite plugins to a version that supports Vite 5 ([#261](https://github.com/wxt-dev/wxt/pull/261))
### ❤️ Contributors
- Ítalo Brasil ([@italodeverdade](http://github.com/italodeverdade))
## v0.10.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.10.3...v0.10.4)
### 🚀 Enhancements
- Add config to customize `outDir` ([#258](https://github.com/wxt-dev/wxt/pull/258))
### 📖 Documentation
- Add Doozy to homepage ([#249](https://github.com/wxt-dev/wxt/pull/249))
- Update sidepanel availability ([#250](https://github.com/wxt-dev/wxt/pull/250))
### 🏡 Chore
- **deps-dev:** Bump prettier from 3.0.3 to 3.1.0 ([#254](https://github.com/wxt-dev/wxt/pull/254))
- **deps-dev:** Bump @types/lodash.merge from 4.6.8 to 4.6.9 ([#255](https://github.com/wxt-dev/wxt/pull/255))
- **deps-dev:** Bump tsx from 3.14.0 to 4.6.1 ([#252](https://github.com/wxt-dev/wxt/pull/252))
### ❤️ Contributors
- 冯不游
## v0.10.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.10.2...v0.10.3)
### 🩹 Fixes
- **auto-imports:** Don't add imports to `node_module` dependencies ([#247](https://github.com/wxt-dev/wxt/pull/247))
### 📖 Documentation
- Fix typo ([317b1b6](https://github.com/wxt-dev/wxt/commit/317b1b6))
### 🏡 Chore
- Trigger docs upgrade via webhook ([742b996](https://github.com/wxt-dev/wxt/commit/742b996))
- Use `normalize-path` instead of `vite.normalizePath` ([#244](https://github.com/wxt-dev/wxt/pull/244))
- Use `defu` for merging some config objects ([#243](https://github.com/wxt-dev/wxt/pull/243))
### 🤖 CI
- Publish docs on push to main ([1611c1d](https://github.com/wxt-dev/wxt/commit/1611c1d))
- Only print response headers from docs webhook ([97cbda3](https://github.com/wxt-dev/wxt/commit/97cbda3))
## v0.10.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.10.1...v0.10.2)
### 🩹 Fixes
- Apply `mode` option to build steps correctly ([82ed821](https://github.com/wxt-dev/wxt/commit/82ed821))
### 🏡 Chore
- Upgrade templates to v0.10 ([#239](https://github.com/wxt-dev/wxt/pull/239))
## v0.10.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.10.0...v0.10.1)
### 🩹 Fixes
- Remove WXT global to remove unused modules from production builds ([3da3e07](https://github.com/wxt-dev/wxt/commit/3da3e07))
## v0.10.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.9.2...v0.10.0)
### 🚀 Enhancements
- List `bun` as an experimental option in `wxt init` ([#233](https://github.com/wxt-dev/wxt/pull/233))
- ⚠️ Allow plural directory and only png's for manifest icons ([#237](https://github.com/wxt-dev/wxt/pull/237))
- Add `wxt/storage` API ([#234](https://github.com/wxt-dev/wxt/pull/234))
### 🩹 Fixes
- Don't use `bun` to load entrypoint config ([#232](https://github.com/wxt-dev/wxt/pull/232))
### 📖 Documentation
- Update main README links ([207b750](https://github.com/wxt-dev/wxt/commit/207b750))
#### ⚠️ Breaking Changes
- ⚠️ No longer discover icons with extensions other than `.png`. If you previously used `.jpg`, `.jpeg`, `.bmp`, or `.svg`, you'll need to convert your icons to `.png` files or manually add them to the manifest inside your `wxt.config.ts` file ([#237](https://github.com/wxt-dev/wxt/pull/237))
### ❤️ Contributors
- Nenad Novaković ([@dvlden](https://github.com/dvlden))
## v0.9.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.9.1...v0.9.2)
### 🚀 Enhancements
- Experimental option to exclude `webextension-polyfill` ([#231](https://github.com/wxt-dev/wxt/pull/231))
### 🤖 CI
- Fix sync-release workflow ([d1b5230](https://github.com/wxt-dev/wxt/commit/d1b5230))
## v0.9.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.9.0...v0.9.1)
### 🚀 Enhancements
- Add `alias` config for customizing path aliases ([#216](https://github.com/wxt-dev/wxt/pull/216))
### 🩹 Fixes
- Move `webextension-polyfill` from peer to regular dependencies ([609ae2a](https://github.com/wxt-dev/wxt/commit/609ae2a))
- Generate valid manifest for Firefox MV3 ([#229](https://github.com/wxt-dev/wxt/pull/229))
### 📖 Documentation
- Add examples ([c81dfff](https://github.com/wxt-dev/wxt/commit/c81dfff))
- Improve the "Used By" section on homepage ([#220](https://github.com/wxt-dev/wxt/pull/220))
- Add UltraWideo to homepage ([#193](https://github.com/wxt-dev/wxt/pull/193))
- Add StayFree to homepage ([#221](https://github.com/wxt-dev/wxt/pull/221))
- Update feature comparison ([67ffa44](https://github.com/wxt-dev/wxt/commit/67ffa44))
### 🏡 Chore
- Remove whitespace from genearted `.wxt` files ([#211](https://github.com/wxt-dev/wxt/pull/211))
- Upgrade templates to `wxt@^0.9.0` ([#214](https://github.com/wxt-dev/wxt/pull/214))
- Update Vite dependency range to `^4.0.0 || ^5.0.0-0` ([f1e8084](https://github.com/wxt-dev/wxt/commit/f1e8084be89e512dde441b9197a99183c497f67d))
### 🤖 CI
- Automatically sync GitHub releases with `CHANGELOG.md` on push ([#218](https://github.com/wxt-dev/wxt/pull/218))
### ❤️ Contributors
- Aaron Klinker ([@aaronklinker-st](http://github.com/aaronklinker-st))
## v0.9.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.7...v0.9.0)
### 🩹 Fixes
- ⚠️ Remove `lib` from `.wxt/tsconfig.json` ([#209](https://github.com/wxt-dev/wxt/pull/209))
### 📖 Documentation
- Fix heading ([345406f](https://github.com/wxt-dev/wxt/commit/345406f))
- Add demo video ([#208](https://github.com/wxt-dev/wxt/pull/208))
### 🏡 Chore
- Fix Svelte and React template READMEs ([#207](https://github.com/wxt-dev/wxt/pull/207))
#### ⚠️ Breaking Changes
- ⚠️ Removed [`"WebWorker"` types](https://www.typescriptlang.org/tsconfig/lib.html) from `.wxt/tsconfig.json` ([#209](https://github.com/wxt-dev/wxt/pull/209)). These types are useful for MV3 projects using a service worker. To add them back to your project, add the following to your project's TSConfig:
```diff
{
"extends": "./.wxt/tsconfig.json",
+ "compilerOptions": {
+ "lib": ["ESNext", "DOM", "WebWorker"]
+ }
}
```
### ❤️ Contributors
- yyyanghj ([@yyyanghj](https://github.com/yyyanghj))
## v0.8.7
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.6...v0.8.7)
### 🚀 Enhancements
- `createContentScriptIframe` utility ([#206](https://github.com/wxt-dev/wxt/pull/206))
### 🏡 Chore
- **deps-dev:** Bump happy-dom from 12.4.0 to 12.10.3 ([#194](https://github.com/wxt-dev/wxt/pull/194))
- **deps-dev:** Bump tsx from 3.12.8 to 3.14.0 ([#198](https://github.com/wxt-dev/wxt/pull/198))
- Upgrade types ([f3874da](https://github.com/wxt-dev/wxt/commit/f3874da))
- **deps-dev:** Upgrade `lint-staged` to `^15.0.2` ([5f74a54](https://github.com/wxt-dev/wxt/commit/5f74a54))
- **deps-dev:** Upgrade `execa` to `^8.0.1` ([#200](https://github.com/wxt-dev/wxt/pull/200))
- **deps-dev:** Upgrade `typedoc` to `^0.25.3` ([#201](https://github.com/wxt-dev/wxt/pull/201))
- **deps-dev:** Upgrade `vue` to `3.3.7` ([0b8d101](https://github.com/wxt-dev/wxt/commit/0b8d101))
- **deps-dev:** Upgrade `vitepress` to `1.0.0-rc.24` ([5de18e5](https://github.com/wxt-dev/wxt/commit/5de18e5))
- **deps-dev:** Update `@type/*` packages for demo ([cd4d00e](https://github.com/wxt-dev/wxt/commit/cd4d00e))
- **deps-dev:** Update `sass` to `1.69.5` ([183bb02](https://github.com/wxt-dev/wxt/commit/183bb02))
- Improve prettier git hook ([0f09cbe](https://github.com/wxt-dev/wxt/commit/0f09cbe))
- Run E2E tests in parallel ([#204](https://github.com/wxt-dev/wxt/pull/204))
### 🤖 CI
- Separate validation into multiple jobs ([#203](https://github.com/wxt-dev/wxt/pull/203))
## v0.8.6
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.5...v0.8.6)
### 🩹 Fixes
- Inline WXT modules inside `WxtVitest` plugin ([b75c553](https://github.com/wxt-dev/wxt/commit/b75c553))
## v0.8.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.4...v0.8.5)
### 🚀 Enhancements
- Refactor project structure to export `initialize`, `prepare`, and `zip` functions ([#182](https://github.com/wxt-dev/wxt/pull/182))
### 🩹 Fixes
- Enable Vue SFC auto-imports in `vue` template ([f8a0fb3](https://github.com/wxt-dev/wxt/commit/f8a0fb3))
### 📖 Documentation
- Improve `runner.binaries` documentation ([d9e9b43](https://github.com/wxt-dev/wxt/commit/d9e9b43))
- Update auto-imports.md ([#186](https://github.com/wxt-dev/wxt/pull/186))
- Add `test.server.deps.inline` to Vitest guide ([19756c6](https://github.com/wxt-dev/wxt/commit/19756c6))
### 🏡 Chore
- Update template docs ([2e24b9e](https://github.com/wxt-dev/wxt/commit/2e24b9e))
- Reduce package size by 70%, 1.92 MB to 590 kB ([#190](https://github.com/wxt-dev/wxt/pull/190))
### ❤️ Contributors
- Nenad Novaković ([@dvlden](https://github.com/dvlden))
## v0.8.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.3...v0.8.4)
### 🩹 Fixes
- Allow actions without a popup ([#181](https://github.com/wxt-dev/wxt/pull/181))
## v0.8.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.2...v0.8.3)
### 🚀 Enhancements
- Add testing utils under `wxt/testing` ([#178](https://github.com/wxt-dev/wxt/pull/178))
## v0.8.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.1...v0.8.2)
### 🩹 Fixes
- **firefox:** Stop extending `AbortController` to fix crash in content scripts ([#176](https://github.com/wxt-dev/wxt/pull/176))
### 🏡 Chore
- Improve output consistency ([#175](https://github.com/wxt-dev/wxt/pull/175))
## v0.8.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.0...v0.8.1)
### 🩹 Fixes
- Output `action.browser_style` correctly ([6a93f20](https://github.com/wxt-dev/wxt/commit/6a93f20))
### 📖 Documentation
- Generate full API docs with typedoc ([#174](https://github.com/wxt-dev/wxt/pull/174))
## v0.8.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.5...v0.8.0)
### 🚀 Enhancements
- ⚠️ Use `defineUnlistedScript` to define unlisted scripts ([#167](https://github.com/wxt-dev/wxt/pull/167))
### 📖 Documentation
- Fix wrong links ([#166](https://github.com/wxt-dev/wxt/pull/166))
### 🌊 Types
- ⚠️ Rename `BackgroundScriptDefintition` to `BackgroundDefinition` ([446f265](https://github.com/wxt-dev/wxt/commit/446f265))
#### ⚠️ Breaking Changes
- ⚠️ Unlisted scripts must now `export default defineUnlistedScript(...)` ([#167](https://github.com/wxt-dev/wxt/pull/167))
- ⚠️ Rename `BackgroundScriptDefintition` to `BackgroundDefinition` ([446f265](https://github.com/wxt-dev/wxt/commit/446f265))
### ❤️ Contributors
- 渣渣120 [@WOSHIZHAZHA120](https://github.com/WOSHIZHAZHA120)
## v0.7.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.4...v0.7.5)
### 🩹 Fixes
- More consistent `version_name` generation between browsers ([#163](https://github.com/wxt-dev/wxt/pull/163))
- Ignore non-manifest fields when merging content script entries ([#164](https://github.com/wxt-dev/wxt/pull/164))
- Add `browser_style` to popup options ([#165](https://github.com/wxt-dev/wxt/pull/165))
## v0.7.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.3...v0.7.4)
### 🩹 Fixes
- Support `react-refresh` when pre-rendering HTML pages in dev mode ([#158](https://github.com/wxt-dev/wxt/pull/158))
### 📖 Documentation
- Add migration guides ([b58fb02](https://github.com/wxt-dev/wxt/commit/b58fb02))
### 🏡 Chore
- Upgrade templates to v0.7 ([#156](https://github.com/wxt-dev/wxt/pull/156))
## v0.7.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.2...v0.7.3)
### 🚀 Enhancements
- Support JS entrypoints ([#155](https://github.com/wxt-dev/wxt/pull/155))
## v0.7.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.1...v0.7.2)
### 🚀 Enhancements
- Allow customizing entrypoint options per browser ([#154](https://github.com/wxt-dev/wxt/pull/154))
### 🩹 Fixes
- Default safari to MV2 ([5807931](https://github.com/wxt-dev/wxt/commit/5807931))
- Add missing `persistent` type to `defineBackgroundScript` ([d9fdcb5](https://github.com/wxt-dev/wxt/commit/d9fdcb5))
### 📖 Documentation
- Restructure website to improve UX ([#149](https://github.com/wxt-dev/wxt/pull/149))
- Add docs for development and testing ([f58d69d](https://github.com/wxt-dev/wxt/commit/f58d69d))
### 🏡 Chore
- **deps-dev:** Bump @types/fs-extra from 11.0.1 to 11.0.2 ([#144](https://github.com/wxt-dev/wxt/pull/144))
- **deps-dev:** Bump @faker-js/faker from 8.0.2 to 8.1.0 ([#146](https://github.com/wxt-dev/wxt/pull/146))
- **deps-dev:** Bump vitest-mock-extended from 1.2.1 to 1.3.0 ([#147](https://github.com/wxt-dev/wxt/pull/147))
- **deps-dev:** Bump vitest from 0.34.3 to 0.34.6 ([#145](https://github.com/wxt-dev/wxt/pull/145))
- **deps-dev:** Bump typescript from 5.1 to 5.2 ([#148](https://github.com/wxt-dev/wxt/pull/148))
## v0.7.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.0...v0.7.1)
### 🚀 Enhancements
- `createContentScriptUi` helper ([#143](https://github.com/wxt-dev/wxt/pull/143))
### 📖 Documentation
- Add docs for `createContentScriptUi` ([65fcfc0](https://github.com/wxt-dev/wxt/commit/65fcfc0))
### 🏡 Chore
- **release:** V0.7.1-alpha1 ([2d4983e](https://github.com/wxt-dev/wxt/commit/2d4983e))
## v0.7.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.6.6...v0.7.0)
+34
View File
@@ -70,3 +70,37 @@ pnpm test manifest-contents
Unit and E2E tests 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.
## Templates
Each directory inside `templates/` is it's own standalone project. Simply `cd` into the directory you're updating, install dependencies with `npm` (NOT `pnpm`), and run the relevant commands
```sh
cd templates/vue
npm i
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:
```diff
"devDependencies": {
"typescript": "^5.3.2",
"vite-plugin-solid": "^2.7.0",
- "wxt": "^0.8.0"
+ "wxt": "../.."
}
```
Then run `npm i` again.
### Adding Templates
To add a template, copy the vanilla template and give it a new name.
```sh
cp -r templates/vailla 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.
+14 -13
View File
@@ -4,17 +4,14 @@
</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" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<img alt="npm" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048">
<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>
<span> </span>
<img alt="NPM" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048">
<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://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>
<!-- Hide code coverage while it's broken -->
<!-- <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> -->
</p>
<p align="center">
@@ -28,15 +25,19 @@
<p align="center">
<a href="https://wxt.dev" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/get-started/installation.html" target="_blank">Installation</a>
<a href="https://wxt.dev/guide/installation.html" target="_blank">Installation</a>
&bull;
<a href="https://wxt.dev/guide/background.html" target="_blank">Entrypoints</a>
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/config.html" target="_blank">Configuration</a>
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
## Demo
https://github.com/wxt-dev/wxt/assets/10101283/07359e53-f491-43b6-8e8f-fae94aec8063
## Quick Start
Bootstrap a new project:
@@ -45,7 +46,7 @@ Bootstrap a new project:
pnpx wxt@latest init <project-name>
```
Or see the [installation guide](https://wxt.dev/get-started/installation.html) to get started with WXT.
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
## Features
-2
View File
@@ -1,2 +0,0 @@
#!/usr/bin/env node
require('../dist/cli.cjs');
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env node
import '../dist/cli.js';
+8 -8
View File
@@ -1,30 +1,30 @@
{
"name": "WXT Demo",
"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": "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": {
"webextension-polyfill": "^0.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"vitest": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@types/webextension-polyfill": "^0.10.0",
"sass": "^1.64.0",
"@types/react": "^18.2.34",
"@types/react-dom": "^18.2.14",
"sass": "^1.69.5",
"wxt": "workspace:*"
}
}
@@ -0,0 +1,29 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import background from '../background';
browser.i18n.getMessage = () => 'fake-message';
const logMock = vi.fn();
console.log = logMock;
describe('Background Entrypoint', () => {
beforeEach(() => {
fakeBrowser.reset();
});
it("should log the extenion's runtime ID", () => {
const id = 'some-id';
fakeBrowser.runtime.id = id;
background.main();
expect(logMock).toBeCalledWith(id);
});
it('should set the start time in storage', async () => {
background.main();
await new Promise((res) => setTimeout(res));
expect(await storage.getItem('session:startTime')).toBeDefined();
});
});
+10 -1
View File
@@ -1,3 +1,5 @@
import messages from 'public/_locales/en/messages.json';
export default defineBackground(() => {
console.log(browser.runtime.id);
logId();
@@ -6,6 +8,7 @@ export default defineBackground(() => {
chrome: __IS_CHROME__,
firefox: __IS_FIREFOX__,
manifestVersion: __MANIFEST_VERSION__,
messages,
});
// @ts-expect-error: should only accept entrypoints or public assets
@@ -20,5 +23,11 @@ export default defineBackground(() => {
browser.i18n.getMessage('bye', ['Aaron']);
browser.i18n.getMessage('@@extension_id');
console.log('WXT MODE:', import.meta.env.MODE);
console.log('WXT MODE:', {
MODE: import.meta.env.MODE,
DEV: import.meta.env.DEV,
PROD: import.meta.env.PROD,
});
storage.setItem('session:startTime', Date.now());
});
+5 -1
View File
@@ -6,7 +6,11 @@ export default defineContentScript({
console.log(browser.runtime.id);
logId();
console.log('WXT MODE:', import.meta.env.MODE);
console.log('WXT MODE:', {
MODE: import.meta.env.MODE,
DEV: import.meta.env.DEV,
PROD: import.meta.env.PROD,
});
const n = (Math.random() * 100).toFixed(1);
ctx.setInterval(() => {
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Iframe Example</title>
</head>
<body>
<p>Hello iframe page!</p>
<script type="module" src="./main.ts"></script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
console.log('iframe 2');
+13
View File
@@ -0,0 +1,13 @@
export default defineContentScript({
matches: ['*://*.google.com/*'],
main(ctx) {
const ui = createContentScriptIframe(ctx, {
page: '/iframe-src.html',
type: 'overlay',
anchor: 'form[action="/search"]',
});
ui.mount();
console.log('Mounted iframe');
},
});
@@ -0,0 +1,11 @@
export default defineContentScript({
// Site that uses HTML5 history
matches: ['*://*.crunchyroll.com/*'],
main(ctx) {
ctx.addEventListener(window, 'wxt:locationchange', ({ newUrl, oldUrl }) => {
console.log('Location changed:', newUrl.href, oldUrl.href);
});
console.log('Watching for location change...');
},
});
@@ -0,0 +1,8 @@
export default defineContentScript({
matches: ['*://*/*'],
world: 'MAIN',
main() {
console.log(`Hello from ${location.hostname}!`);
},
});
+5 -1
View File
@@ -4,4 +4,8 @@ console.log(browser.runtime.id);
logId();
console.log(2);
console.log('WXT MODE:', import.meta.env.MODE);
console.log('WXT MODE:', {
MODE: import.meta.env.MODE,
DEV: import.meta.env.DEV,
PROD: import.meta.env.PROD,
});
+11
View File
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sidebar</title>
</head>
<body>
<p>Example</p>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
export default defineUnlistedScript(() => {
console.log('injected');
});
+10
View File
@@ -0,0 +1,10 @@
import { defineConfig } from 'vitest/config';
import { WxtVitest } from 'wxt/testing';
export default defineConfig({
test: {
mockReset: true,
restoreMocks: true,
},
plugins: [WxtVitest()],
});
+10
View File
@@ -3,6 +3,16 @@ 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/*'],
},
],
},
alias: {
public: 'src/public',
},
});
+2
View File
@@ -0,0 +1,2 @@
FROM lipanski/docker-static-website:latest
COPY dist .
+44 -17
View File
@@ -8,14 +8,24 @@ import useListExtensionDetails, {
const chromeExtensionIds = [
'ocfdgncpifmegplaglcnglhioflaimkd', // GitHub: Better Line Counts
'mgmdkjcljneegjfajchedjpdhbadklcf', // Anime Skip Player
'bfbnagnphiehemkdgmmficmjfddgfhpl', // UltraWideo
'elfaihghhjjoknimpccccmkioofjjfkf', // StayFree - Website Blocker & Web Analytics
'okifoaikfmpfcamplcfjkpdnhfodpkil', // Doozy: Ai Made Easy
'lknmjhcajhfbbglglccadlfdjbaiifig', // tl;dv - Record, Transcribe & ChatGPT for Google Meet
];
const { data } = useListExtensionDetails(chromeExtensionIds);
const sortedExtensions = computed(() =>
!data.value
? undefined
: [...data.value].sort((l, r) => r.weeklyActiveUsers - l.weeklyActiveUsers),
);
const sortedExtensions = computed(() => {
if (!data.value?.length) return [];
return [...data.value]
.map((item) => ({
...item,
// Sort based on the user count weighted by the rating
sortKey: ((item.rating ?? 5) / 5) * item.weeklyActiveUsers,
}))
.sort((l, r) => r.sortKey - l.sortKey);
});
function getStoreUrl(extension: ChromeExtension) {
const url = new URL(extension.storeUrl);
@@ -27,32 +37,41 @@ function getStoreUrl(extension: ChromeExtension) {
<template>
<section class="vp-doc">
<div class="container">
<h2>Who's Using WXT?</h2>
<h2 id="whos-using-wxt">Who's Using WXT?</h2>
<p>
Battle tested and ready for production. Explore chrome extensions made
with WXT.
</p>
<ul>
<li v-for="extension of sortedExtensions">
<li
v-for="extension of sortedExtensions"
:key="extension.id"
class="relative"
>
<img
:src="extension.iconUrl"
:alt="`${extension.name} icon`"
referrerpolicy="no-referrer"
/>
<div>
<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>
<p class="user-count">
{{ extension.weeklyActiveUsers.toLocaleString() }} users
</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">
@@ -121,7 +140,8 @@ li {
background-color: var(--vp-c-bg-soft);
border-radius: 12px;
flex: 1;
gap: 16px;
gap: 24px;
align-items: center;
}
.centered {
@@ -136,9 +156,10 @@ li .description {
}
li .user-count {
opacity: 70%;
text-align: right;
width: 100%;
font-size: small;
position: absolute;
bottom: 12px;
right: 16px;
}
li a {
@@ -157,8 +178,6 @@ li a:hover {
li div {
flex: 1;
display: flex;
flex-direction: column;
}
li .description {
@@ -167,10 +186,18 @@ li .description {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
flex-grow: 1;
margin-bottom: 16px;
}
li .extension-name {
font-size: large;
}
.pr {
opacity: 70%;
}
.relative {
position: relative;
}
</style>
@@ -7,6 +7,7 @@ export interface ChromeExtension {
weeklyActiveUsers: number;
shortDescription: string;
storeUrl: string;
rating: number | undefined;
}
const operationName = 'WxtDocsUsedBy';
@@ -18,6 +19,7 @@ const query = `query ${operationName}($ids:[String!]!) {
weeklyActiveUsers
shortDescription
storeUrl
rating
}
}`;
+71 -36
View File
@@ -1,5 +1,19 @@
import { defineConfig } from 'vitepress';
import { generateConfigDocs } from './plugins/generate-config-docs';
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);
const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' WXT';
@@ -17,7 +31,7 @@ export default defineConfig({
description,
vite: {
clearScreen: false,
plugins: [generateConfigDocs()],
plugins: [generateCliDocs()],
},
lastUpdated: true,
sitemap: {
@@ -55,56 +69,77 @@ export default defineConfig({
},
nav: [
{ text: 'Get Started', link: '/get-started/installation.md' },
{ text: 'Guide', link: '/guide/auto-imports.md' },
{ text: 'Config', link: '/config.md' },
{ text: 'API', link: '/api.md' },
{ text: 'Guide', link: '/guide/installation.md' },
{ text: 'Entrypoints', link: '/entrypoints/background.md' },
{ text: 'Examples', link: '/examples.md' },
{ text: 'API', link: '/api/cli.md' },
],
sidebar: {
'/get-started/': [
{
text: 'Get Started',
items: [
{ text: 'Introduction', link: '/get-started/introduction.md' },
{ text: 'Installation', link: '/get-started/installation.md' },
{ text: 'Configuration', link: '/get-started/configuration.md' },
{ text: 'Entrypoints', link: '/get-started/entrypoints.md' },
{ text: 'Assets', link: '/get-started/assets.md' },
{ text: 'Build Targets', link: '/get-started/build-targets.md' },
{ text: 'Publishing', link: '/get-started/publishing.md' },
{ text: 'Testing', link: '/get-started/testing.md' },
{ text: 'Compare', link: '/get-started/compare.md' },
],
},
],
'/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: 'Assets', link: '/guide/assets.md' },
{ text: 'Multiple Browsers', link: '/guide/multiple-browsers.md' },
{ text: 'Publishing', link: '/guide/publishing.md' },
{ text: 'Auto-imports', link: '/guide/auto-imports.md' },
{ text: 'Manifest.json', link: '/guide/manifest.md' },
{ text: 'Extension APIs', link: '/guide/extension-apis.md' },
{ text: 'Storage', link: '/guide/storage.md' },
{ text: 'Content Script UI', link: '/guide/content-script-ui.md' },
{ text: 'Remote Code', link: '/guide/remote-code.md' },
{ text: 'Development', link: '/guide/development.md' },
{ text: 'Testing', link: '/guide/testing.md' },
{ text: 'Vite', link: '/guide/vite.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: '/guide/background.md' },
{ text: 'Bookmarks', link: '/guide/bookmarks.md' },
{ text: 'Content Scripts', link: '/guide/content-scripts.md' },
{ text: 'CSS', link: '/guide/css.md' },
{ text: 'Devtools', link: '/guide/devtools.md' },
{ text: 'History', link: '/guide/history.md' },
{ text: 'Newtab', link: '/guide/newtab.md' },
{ text: 'Options', link: '/guide/options.md' },
{ text: 'Popup', link: '/guide/popup.md' },
{ text: 'Sandbox', link: '/guide/sandbox.md' },
{ text: 'Sidepanel', link: '/guide/sidepanel.md' },
{ text: 'Unlisted Pages', link: '/guide/unlisted-pages.md' },
{ text: 'Unlisted Scripts', link: '/guide/unlisted-scripts.md' },
{ 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: 'Sidepanel', 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,
},
],
},
],
+5
View File
@@ -0,0 +1,5 @@
# CLI Reference
> Reference generated from `wxt <command> --help`
{{ DOCS }}
@@ -0,0 +1,108 @@
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.all(
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);
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;
}
@@ -1,145 +0,0 @@
import { resolve } from 'node:path';
import { Project, ts, Type, Node, JSDocableNode } from 'ts-morph';
import { readFileSync, writeFileSync } from 'node:fs';
import { format } from 'prettier';
import consola from 'consola';
let hasGenerated = false;
const externalTypesPath = resolve('src/core/types/external.ts');
const configTemplatePath = resolve('docs/config.tpl.md');
const configPath = resolve('docs/config.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\`.
-->`;
/**
* Custom property paths that should not be recursively inspected. Usually 3rd party types.
*/
const LEAF_PATHS = ['imports', 'vite', 'server'];
/**
* Override any types that resolve to `import(...)` instead of their type names when calling
* `type.getText()`.
*
* This also stops any further type inspection for objects, meaning the object will be documented,
* not it's properties.
*/
const CUSTOM_TYPES = {
manifest:
'Manifest | Promise<Manifest> | () => Manifest | () => Promise<Manifest>',
imports: "false | Partial<import('unimport').UnimportOptions>",
vite: "Omit<import('vite').UserConfig, 'root' | 'configFile' | 'mode'>",
};
export function generateConfigDocs() {
writeFileSync(configPath, '');
const generateDocs = async () => {
consola.info('Generating /config.md');
try {
const project = new Project({
tsConfigFilePath: resolve('tsconfig.json'),
});
// Load file containing "UserConfig"
const externalTypesFile = project.addSourceFileAtPath(externalTypesPath);
project.resolveSourceFileDependencies();
const inlineConfigInterface =
externalTypesFile.getInterfaceOrThrow('InlineConfig');
const getDocsFor = (path: string[], node: Node<ts.Node>): string[] => {
const pathStr = path.join('.');
let type: Type<ts.Type>;
if (node.isKind(ts.SyntaxKind.InterfaceDeclaration)) {
type = node.getType();
} else if (node.isKind(ts.SyntaxKind.PropertySignature)) {
type = node.getTypeNodeOrThrow()?.getType();
} else if (node.isKind(ts.SyntaxKind.MethodSignature)) {
type = node.getType();
} else {
throw Error('Unsupported type node: ' + node.getKindName());
}
if (type.isObject() && !type.isArray() && !CUSTOM_TYPES[pathStr]) {
return type.getProperties().flatMap((property) => {
const childPath = [...path, property.getName()];
return getDocsFor(childPath, property.getDeclarations()[0]);
});
}
if ('getJsDocs' in node) {
const lines: string[] = [];
const docs = (node as unknown as JSDocableNode).getJsDocs();
let typeText: string;
if (CUSTOM_TYPES[pathStr]) {
typeText = CUSTOM_TYPES[pathStr];
} else if (type.isUnion() && !type.isBoolean()) {
typeText = type
.getUnionTypes()
.map((type) => type.getText())
.join(' | ');
} else {
typeText = type.getText();
}
const defaultValue = docs
.flatMap((doc) => doc.getTags())
.find((tag) => tag.getTagName() === 'default')
?.getCommentText();
lines.push(
'',
`## ${pathStr}`,
'',
`- **Type**: \`${typeText}\``,
`- **Default**: \`${defaultValue}\``,
...docs.flatMap((doc) => doc.getDescription()),
);
return lines;
}
return [];
};
const lines = getDocsFor([], inlineConfigInterface);
const text = await format(
PREFACE +
'\n\n' +
readFileSync(configTemplatePath, 'utf-8').replace(
'{{ DOCS }}',
lines.join('\n'),
),
{ parser: 'markdown' },
);
writeFileSync(configPath, text);
consola.success('Generated /config.md');
} catch (err) {
consola.fail('Failed to generate /config.md');
consola.error(err.message);
}
};
return {
name: 'docs:generate-config-docs',
async config() {
if (!hasGenerated) {
hasGenerated = true;
await generateDocs();
}
},
configureServer(server: any) {
server.watcher.add(externalTypesPath);
},
async handleHotUpdate(ctx: { file: string }) {
if ([externalTypesPath, configTemplatePath].includes(ctx.file)) {
await generateDocs();
}
},
};
}
-5
View File
@@ -1,5 +0,0 @@
# API Reference
:::warning 🚧&ensp;Under construction
This documentation does not exist yet.
:::
-5
View File
@@ -1,5 +0,0 @@
# Config Reference
Discover all the options you can use in your `wxt.config.ts` file.
{{ DOCS }}
@@ -8,8 +8,8 @@ For MV2, the background is added as a script to the background page. For MV3, th
<EntrypointPatterns
:patterns="[
['background.ts', 'background.js'],
['background/index.ts', 'background.js'],
['background.[jt]s', 'background.js'],
['background/index.[jt]s', 'background.js'],
]"
/>
@@ -8,10 +8,10 @@ When creating content script entrypoints, they are automatically included in the
<EntrypointPatterns
:patterns="[
['content.(ts|tsx)', 'content-scripts/content.js'],
['content/index.(ts|tsx)', 'content-scripts/content.js'],
['<name>.content.(ts|tsx)', 'content-scripts/<name>.js'],
['<name>.content/index.(ts|tsx)', 'content-scripts/<name>.js'],
['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'],
]"
/>
@@ -47,49 +47,9 @@ export default defineContentScript({
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.
## 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"]
}
]
}
```
## Context
Old content scripts are not automatically stopped when an extension updates and restarts. 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. Since it's not connected to the latest version of your extension, the browser decides to throw an error.
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 extension API.
WXT provides a utility for managing this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of your content script.
@@ -123,3 +83,57 @@ When working with content scripts, **you should always use the `ctx` object to s
This prevents old content scripts from interfering with new content scripts, and prevents error messages from the console in production.
:::
## 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) {
// ...
},
});
```
@@ -2,10 +2,10 @@
WXT can build CSS entrypoints individually. CSS entrypoints are always unlisted.
See [Content Script CSS](/guide/content-scripts.md#css) documentation for the recomended approach to include CSS with a content script.
See [Content Script CSS](/entrypoints/content-scripts#css) documentation for the recomended 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](/config.md#transformmanifest) to manually add your CSS file to the manifest.
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
@@ -29,6 +29,7 @@
}"
/>
<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', ...]" />
@@ -1,9 +1,9 @@
# Side Panel
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/)
[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)
:::tip Chromium Only
Firefox does not support sidepanel pages.
:::warning
Chrome added support for sidepanels in Manifest V3, they are not available in Manfiest V2.
:::
## Filenames
+37
View File
@@ -0,0 +1,37 @@
# Unlisted Scripts
TypeScript files that are built, but are not included in the manifest.
You are responsible for loading/running these scripts where needed.
## 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() {
// ...
},
});
```
+27
View File
@@ -0,0 +1,27 @@
# Examples
Simple walkthroughs to accomplish common tasks or patterns with WXT.
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
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();
})
</script>
<ul>
<li v-if="examples == null">
Loading...
</li>
<template v-else>
<li v-for="example of examples">
<a :href="example.url" target="_blank">{{ example.name }}</a>
</li>
</template>
</ul>
> Full code available at [`wxt-dev/wxt-examples`](https://github.com/wxt-dev/wxt-examples)
-46
View File
@@ -1,46 +0,0 @@
# Build Targets
You can build an extension for any combination of browser and manifest verison. Different browsers and manifest versions support different APIs and entrypoints, so be sure to check that your extension functions as expected for each target.
Separate build targets are written to their own output directories:
```
<rootDir>
└─ .output
├─ chrome-mv3
├─ firefox-mv2
├─ edge-mv3
└─ ...
```
## Target Browser
To build for a specific browser, pass the `-b --browser` flag from the CLI:
```
wxt --browser firefox
wxt build --browser firefox
```
By default, it will build for `chrome`. When excluding the [`--mv2` or `--mv3` flags](#target-manifest-version), it will default to the commonly accepted manifest version used with that browser.
| Browser | Default Manifest Version |
| ---------------- | :----------------------: |
| `chrome` | 3 |
| `firefox` | 2 |
| `safari` | 2 |
| `edge` | 3 |
| Any other string | 3 |
## Target Manifest Version
To build for a specific manifest version, pass either the `--mv2` flag or `--mv3` flag from the CLI.
```sh
wxt --mv2
wxt build --mv2
```
When the `-b --browser` flag is not passed, it defaults to `chrome`. So here, we're targetting MV2 for Chrome.
-5
View File
@@ -1,5 +0,0 @@
# Testing
:::warning 🚧&ensp;Testing utils are not implemented yet!
Eventually, the plan is to have an integration with Vitest.
:::
+9 -6
View File
@@ -3,18 +3,21 @@
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](/get-started/testing.md).
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`](/config.md#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/config.md#defiencontentscript) from `wxt/client`
- [`defineBackground`](/config.md#definebackgroundscript) from `wxt/client`
- [`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`
- [`createContentScriptUi`](/api/wxt/client/functions/createContentScriptUi) from `wxt/client`
- [`fakeBrowser`](/api/wxt/testing/variables/fakeBrowser) from `wxt/testing`
And more. All [`wxt/client`](/config.md#wxtclient) APIs can be used without imports.
And more!
## Project Auto-imports
@@ -61,7 +64,7 @@ import { defineConfig } from 'wxt';
export default defineConfig({
imports: {
// Add auto-imports for vue fuctions like createApp, ref, computed, watch, toRaw, etc...
preset: ['vue'],
presets: ['vue'],
},
});
```
@@ -27,9 +27,9 @@ Lets compare the features of WXT vs [Plasmo](https://docs.plasmo.com/framework),
| Reload Content Scripts on Change | ✅ | 🟡 Reloads entire extension |
| Reload Background on Change | 🟡 Reloads entire extension | 🟡 Reloads entire extension |
| <strong style="opacity: 50%">Built-in Utils</strong> | | |
| Storage | | ✅ |
| Messaging | | ✅ |
| Content Script UI | | ✅ |
| Storage | | ✅ |
| Messaging | 🟡 Coming soon | ✅ |
| Content Script UI | | ✅ |
## Dev Mode
@@ -15,7 +15,7 @@ export default defineConfig({
```
:::info
See the [Config reference](/config.md) for a full list of options.
See the [Config reference](/api/wxt/interfaces/InlineConfig) for a full list of options.
:::
## Directory Config
@@ -25,7 +25,7 @@ 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](/get-started/assets.md)
- `publicDir` (default: `<srcDir>/public`) - Folder containing [public assets](/guide/assets)
### Example
+326
View File
@@ -0,0 +1,326 @@
# 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 | HMR | Use page's context |
| ---------- | :-------------: | :-: | :----------------: |
| Integrated | ❌ | ❌ | ✅ |
| ShadowRoot | ✅ | ❌ | ✅ |
| 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.
You can control how CSS is injected for an integrated content script UI with the [`cssInjectionMode`](/api/wxt/interfaces/ContentScriptBaseDefinition#cssinjectionmode) property.
:::code-group
```ts [Vanilla]
// entrypoints/example-ui.content.ts
export default defineContentScript({
main(ctx) {
// Create the UI container
const container = document.createElement('div');
// Add UI container to the page
const anchor = document.querySelector('#anchor');
anchore.append(container);
// Remove UI container when invalidated
ctx.onInvalidated(() => {
container.remove();
});
},
});
```
```ts [Vue]
// entrypoints/example-ui.content/index.ts
import { createApp } from 'vue';
export default defineContentScript({
main(ctx) {
// Create the UI container
const container = document.createElement('div');
// Create the app and mount it to the UI container
const app = createApp(...);
app.mount(container);
// Add UI container to the page
const anchor = document.querySelector('#anchor');
anchore.append(container);
// Unmount the app and remove UI container when invalidated
ctx.onInvalidated(() => {
app.unmount();
container.remove();
});
},
});
```
```tsx [React]
// entrypoints/example-ui.content/index.tsx
import ReactDOM from 'react-dom/client';
export default defineContentScript({
main(ctx) {
// Create the UI container
const container = document.createElement('div');
// Create a root on the UI container and render a component
const root = ReactDOM.createRoot(container);
root.render(...);
// Add UI container to the page
const anchor = document.querySelector('#anchor');
anchore.append(container);
// Unmount the root and remove UI container when invalidated
ctx.onInvalidated(() => {
root.unmount();
container.remove();
});
},
});
```
```ts [Svelete]
// entrypoints/example-ui.content/index.ts
import App from './App.svelte';
export default defineContentScript({
main(ctx) {
// Create the UI container
const container = document.createElement('div');
// Create the Svelte app inside the UI container
const app = new App({
target: ui,
});
// Add UI container to the page
const anchor = document.querySelector('#anchor');
anchore.append(container);
// Destroy the app and remove UI container when invalidated
ctx.onInvalidated(() => {
app.$destroy();
container.remove();
});
},
});
```
```tsx [Solid]
// entrypoints/example-ui.content/index.ts
import { render } from 'solid-js/web';
export default defineContentScript({
main(ctx) {
// Create the UI container
const container = document.createElement('div');
// Render your app to the UI container
const unmount = render(() => ..., container)
// Add UI container to the page
const anchor = document.querySelector('#anchor');
anchore.append(container);
// Unmount the app and remove UI container when invalidated
ctx.onInvalidated(() => {
unmount();
container.remove();
});
},
});
```
:::
## ShadowRoot
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 provides a helper function, [`createContentScriptUi`](/api/wxt/client/functions/createContentScriptUi), that abstracts all the `ShadowRoot` setup away, making it easy to create UIs with isolated CSS.
To use `createContentScriptUi`, follow these steps:
1. Import your CSS file at the top of your content script
2. Set [`cssInjectionMode: "ui"`](/api/wxt/interfaces/ContentScriptBaseDefinition#cssinjectionmode) inside `defineContentScript`
3. Define your UI with `createContentScriptUi()`
4. Mount the UI so it is visible to users
```ts
// 1. Import the style
import './style.css';
export default defineContentScript({
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
async main(ctx) {
// 3. Define your UI
const ui = await createContentScriptUi(ctx, {
name: 'example-ui',
anchor: '#anchor',
type: 'inline',
mount(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();
},
});
```
> `createContentScriptUi` will automatically remove the UI from the page when the content script is invalidated.
See the [API Reference](/api/wxt/client/functions/createContentScriptUi) for the complete list of options.
:::info TailwindCSS
`createContentScriptUi` 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:.
:::
When using a frontend framework for your UI, you'll need to unmount the app when the UI is removed. This is accomplished by returning an app reference from the `mount` option and by passing in a custom `onRemoved` option:
:::code-group
```ts [Vue]
import { createApp } from 'vue';
const ui = createContentScriptUi(ctx, {
// ...
mount(container) {
const app = createApp(App);
app.mount(container);
return app;
},
onRemove(app) {
app.unmount();
},
});
```
```tsx [React]
import ReactDOM from 'react-dom/client';
const ui = createContentScriptUi(ctx, {
// ...
mount(container) {
const root = ReactDOM.createRoot(container);
root.render(...);
return root;
},
onRemove(root) {
root.unmount();
},
});
```
```ts [Svelte]
import App from './App.svelte';
const ui = createContentScriptUi(ctx, {
// ...
mount(container) {
return new App({ target: container });
},
onRemove(app) {
app.$destry();
},
});
```
```tsx [Solid]
import { render } from 'solid-js/web';
const ui = createContentScriptUi(ctx, {
// ...
mount(container) {
return render(() => ..., container);
},
onRemove(unmount) {
unmount();
},
});
```
:::
:::warning
The `mount(container)` and `onRemove(app)` options passed into `createContentScriptUi` **_are different from_** the `ui.mount()` and `ui.remove()` functions available on the returned UI object.
You don't need to pass anything into `ui.mount()` and `ui.remove()` because **_you already defined how and where the UI will be mounted_** in the options passed into `createContentScriptUi`.
:::
## 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, [`createContentScriptIframe`](/api/wxt/client/functions/createContentScriptUi), 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_resouces`
```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({
// ...
async main(ctx) {
// Define the UI
const ui = await createContentScriptIframe(ctx, {
page: '/example-iframe.html',
anchor: '#anchor',
type: 'inline',
});
// Add styles to the iframe like width
ui.iframe.width = 123;
// Show UI to user
ui.mount();
},
});
```
See the [API Reference](/api/wxt/client/functions/createContentScriptUi) for the complete list of options.
+59
View File
@@ -0,0 +1,59 @@
# 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) |
## 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
});
```
:::
`web-ext`'s browser discovery is very limitted. 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.
:::
@@ -42,7 +42,7 @@ would result in the following `manifest.json`:
}
```
If a file uses a [special name recognized by WXT](/get-started/entrypoints.md), it will be added to the manifest. In this case:
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`
@@ -53,7 +53,7 @@ But not all entrypoints are added to the `manifest.json`. If the filename is not
In this case, `injected.ts` gets output to `<outdir>/injected.js` and is accessible via `browser.runtime.getURL("/injected.js")`.
:::info
See [`/entrypoints` folder](/guide/background.md) documentation for a full list of recognized entrypoint filenames.
See [`/entrypoints` folder](/entrypoints/background) documentation for a full list of recognized entrypoint filenames.
:::
## Entrypoint Options
@@ -84,5 +84,5 @@ export default defineContentScript({
```
:::info
For a full list of entrypoints and each of their options, see the [`/entrypoints` folder](/guide/background.md) documentation.
For a full list of entrypoints and each of their options, see the [`/entrypoints` folder](/entrypoints/background) documentation.
:::
+3 -3
View File
@@ -11,7 +11,7 @@ And that's it! Your extension now supports Chrome, Firefox, Safari, Edge, and ot
## Basic Usage
The `browser` variable is available globally via [auto-imports](/guide/auto-imports.md), or it can be imported manually.
The `browser` variable is available globally via [auto-imports](/guide/auto-imports), or it can be imported manually.
```ts
import { browser } from 'wxt/browser';
@@ -79,8 +79,8 @@ There are a number of message passing libraries you can use to improve the messa
Here are some that are compatible with WXT (because they are based off `webextension-polyfill` as well):
- [`@webext-core/messaging`](https://webext-core.aklinker1.io/guide/proxy-service/) - "A light-weight, type-safe wrapper around the `browser.runtime` messaging APIs"
- [`@webext-core/proxy-service`](https://webext-core.aklinker1.io/guide/messaging/) - "Create TRPC-like services that can be called from anywhere but run in the background"
- [`@webext-core/messaging`](https://webext-core.aklinker1.io/guide/messaging/) - "A light-weight, type-safe wrapper around the `browser.runtime` messaging APIs"
- [`@webext-core/proxy-service`](https://webext-core.aklinker1.io/guide/proxy-service/) - "Create TRPC-like services that can be called from anywhere but run in the background"
- [`webext-bridge`](https://github.com/zikaari/webext-bridge) - "Messaging in Web Extensions made super easy. Out of the box."
## Browser Differences
@@ -1,6 +1,6 @@
# Installation
Bootstrap a new project, start from scratch, or migrate an existing project.
Bootstrap a new project, start from scratch, or [migrate an existing project](/guide/migrate-to-wxt).
## Bootstrap Project
@@ -26,7 +26,9 @@ There are several starting templates available.
| <Icon name="Svelte" /> [`svelte`](https://github.com/wxt-dev/wxt/tree/main/templates/svelte) |
| <Icon name="Solid" icon="https://www.solidjs.com/img/favicons/favicon-32x32.png" /> [`solid`](https://github.com/wxt-dev/wxt/tree/main/templates/solid) |
> All templates are in TypeScript. WXT does not support JS at this time.
:::info
All templates default to TypeScript. Rename the file extensions to `.js` to use JavaScript instead.
:::
## From Scratch
@@ -142,6 +144,6 @@ If you're an experienced web extension developer and think the dev manifest look
You're ready to build your web extension!
- Learn how to [add entrypoints](./entrypoints.md) like the popup, options page, or content scripts
- [Configure WXT](./configuration.md) by creating a `wxt.config.ts` file
- Learn how to [add entrypoints](./entrypoints) like the popup, options page, or content scripts
- [Configure WXT](./configuration) by creating a `wxt.config.ts` file
- Checkout [example projects](https://github.com/wxt-dev/wxt-examples) to see how to perfom common tasks with WXT
@@ -35,5 +35,5 @@ Production builds are optimized for store review, changing as few files as possi
In addition, WXT fully supports Firefox's source code requirements when using a bundler. It will automatically create and upload a ZIP file of your source code.
:::info
See [Publishing](./publishing.md) for more info around production builds.
See [Publishing](./publishing) for more info around production builds.
:::
+3 -3
View File
@@ -2,7 +2,7 @@
The manifest.json is generated at build-time based on files in your `entrypoints` directory and your `wxt.config.ts`.
## Confiuration
## Configuration
While entrypoints are generated and added to the manifest at build-time, you can customize or add to your `manifest.json` in the config file.
@@ -49,7 +49,7 @@ The [manifest's `version` and `version_name`](https://developer.chrome.com/docs/
## `icons`
By default, WXT will discover icons in your [`public` directory](/get-started/assets#public-directory) and use them for the [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/).
By default, WXT will discover icons in your [`public` directory](/guide/assets#public-directory) and use them for the [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/).
```
public/
@@ -94,7 +94,7 @@ export default defineConfig({
## Localization
Similar to the icon, the [`_locales` directory](https://developer.chrome.com/docs/extensions/reference/i18n/) should be placed inside the the WXT's [`public` directory](/get-started/assets#public-directory).
Similar to the icon, the [`_locales` directory](https://developer.chrome.com/docs/extensions/reference/i18n/) should be placed inside the the WXT's [`public` directory](/guide/assets#public-directory).
```
public/
+57
View File
@@ -0,0 +1,57 @@
---
outline: deep
---
# Migrate to WXT
> If you have problems migrating to WXT, feel free to ask for help in GitHub by [starting a discussion](https://github.com/wxt-dev/wxt/discussions/new?category=q-a)!
## Overview
Always start by generating a new vanilla project and merging it into your project one file at a time.
```sh
cd path/to/your/project
pnpx wxt@latest init example-wxt --template vanilla
```
In general, you'll need to:
<input type="checkbox" /> Install `wxt`<br />
<input type="checkbox" /> Update/create `package.json` scripts to use `wxt` (don't forget about `postinstall`)<br />
<input type="checkbox" /> Move entrypoints into `entrypoints/` directory<br />
<input type="checkbox" /> Move assets into either the `assets/` or `public/` directories<br />
<input type="checkbox" /> Move manifest.json content into `wxt.config.ts`<br />
<input type="checkbox" /> Convert custom import syntax to be compatible with Vite<br />
<input type="checkbox" /> Add a default export to JS entrypoints<br />
<input type="checkbox" /> Use the `browser` global instead of `chrome`<br />
<input type="checkbox" /> Compare final `manifest.json` files, making sure permissions and host permissions are unchanged<br />
<input type="checkbox" /> Extension output by `wxt build` works the same way as before the migration<br />
Every project is different, so there's no one-solution-fits-all to migrating your project. Just make sure `wxt dev` runs, `wxt build` results in a working extension, and the list of permissions in the `manifest.json` hasn't changed. If all that looks good, you've finished migrating your extension!
## Popular Tools/Frameworks
Here's specific steps for other popuplar frameworks/build tools.
### `vite-plugin-web-extension`
Since you're already using Vite, it's a simple refactor.
1. Install `wxt`
2. Move and refactor your entrypoints to WXT's style (with a default export)
3. Update package.json scripts to use `wxt`
4. Add `"postinstall": "wxt prepare"` script
5. Move the `manifest.json` into `wxt.config.ts`
6. Move any custom settings from `vite.config.ts` into `wxt.config.ts`'s
7. Compare `dist/manifest.json` to `.output/*/manifest.json`, they should have the same content as before. If not, tweak your entrypoints and config to get as close as possible.
### `plasmo`
1. Install `wxt`
2. Move entrypoints into `entrypoints/` directory, merging the named exports used to configure your JS entrypoints into WXT's default export
3. Move public `assets/*` into the `public/` directory
4. If you use CSUI, migrate to WXT's `createContentScriptUi`
5. Convert Plasmo's custom import resolutions to Vite's
6. If importing remote code via a URL, add a `url:` prefix so it works with WXT
7. Compare your output `manifest.json` files from before the migration to after the migration. They should have the same content. If not, tweak your entrypoints and config to get as close as possible.
+114
View File
@@ -0,0 +1,114 @@
# Multiple Browsers
You can build an extension for any combination of browser and manifest verison. Different browsers and manifest versions support different APIs and entrypoints, so be sure to check that your extension functions as expected for each target.
Separate build targets are written to their own output directories:
```
<rootDir>
└─ .output
├─ chrome-mv3
├─ firefox-mv2
├─ edge-mv3
└─ ...
```
## Target Browser
To build for a specific browser, pass the `-b --browser` flag from the CLI:
```
wxt --browser firefox
wxt build --browser firefox
```
By default, it will build for `chrome`. When excluding the [manifest version flags](#target-manifest-version), it will default to the commonly accepted manifest version for that browser.
| Browser | Default Manifest Version |
| ---------------- | :----------------------: |
| `chrome` | 3 |
| `firefox` | 2 |
| `safari` | 2 |
| `edge` | 3 |
| Any other string | 3 |
:::tip
To configure which browser is opened when running dev mode via `wxt -b <browser>`, see the [Development docs](/guide/development#configure-browser-startup) docs.
:::
## Target Manifest Version
To build for a specific manifest version, pass either the `--mv2` flag or `--mv3` flag from the CLI.
```sh
wxt --mv2
wxt build --mv2
```
When the `-b --browser` flag is not passed, it defaults to `chrome`. So here, we're targetting MV2 for Chrome.
## Customizing Entrypoints
There are several ways to customize entrypoint definitions per browser.
First, you can use either the `include` or `exclude` option to include or exclude the entrypoint from specific browsers. Here are some examples
:::code-group
```ts [Background]
export default defineBackground({
// Only include a background script when targeting chrome
include: ['chrome'],
});
```
```ts [Content Script]
export default defineContentScript({
// Do not add this content script to the manifest when targeting firefox
exclude: ['firefox'],
});
```
```html [HTML page]
<!-- entrypoints/options.html -->
<html>
<head>
<!-- Don't include the options page for safari -->
<meta name="manifest.exclude" content="['safari']" />
</head>
</html>
```
:::
Second, you can change individual options per-browser:
:::code-group
```ts [Background]
export default defineBackground({
persistent: {
// Use a non-persistent background script for just safari
safari: false,
},
});
```
```ts [Content Script]
export default defineContentScript({
matches: {
// Run the content script on different pages for each browser
chrome: ['*://*.google.com/*'],
firefox: ['*://*.duckduckgo.com/*'],
edge: ['*://*.bing.com/*'],
},
});
```
:::
:::warning
Only `defineBackground` and `defineContentScript` support per-browser options right now.
:::
+36
View File
@@ -0,0 +1,36 @@
# Storage API
WXT's storage API is powered by `unstorage`. See [their docs](https://unstorage.unjs.io/usage#usage-1) for more details.
## Overview
:::code-group
```ts [native]
const { installDate } = await browser.storage.local.get('installDate');
await browser.storage.local.set({ key: 'value' });
```
```ts [wxt/browser]
const installDate = await storage.get('local:installDate');
await storage.setItem('key', 'value');
```
:::
Use the `"local:"`, `"session:"`, `"sync:"`, and `"managed:"` prefixes to specify which storage area to use.
## Customization
WXT also provides a driver for `unstorage`. To customize the `storage` object's setup, like removing the prefixes and using a single storage area, you can create your own storage:
```ts
// storage.ts
export default createStorage({
driver: webExtensionDriver({ storageArea: 'local' }),
});
```
:::note
`wxt/browser` re-exports all of `unstorage`, which is where `createStorage` comes from.
:::
+27
View File
@@ -0,0 +1,27 @@
# Testing
## Official Frameworks
WXT officially supports [Vitest](https://vitest.dev/) for unit tests and either [Playwright](https://playwright.dev/) or [Puppeteer](https://pptr.dev/) for E2E tests against Chromium browsers.
For details setting up each testing framework, see the official examples:
- [Vitest example](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-vitest#readme)
- [Playwright example](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-playwright#readme)
- [Puppeteer example](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-puppeteer#readme)
### Unofficial Frameworks
Puppeteer and Playwright are the only E2E test runners that support Chrome Extensions. There are no other options at the time of writing.
There are other options for unit tests however, like [Jest](https://jestjs.io/), [Mocha](https://mochajs.org/), or [`node:test`](https://nodejs.org/api/test.html). **_WXT does not claim to support any of them_** because none of them support all of WXT's features, like TypeScript or auto-imports.
If you want to try to use a different framework for unit tests, you will need to configure the environment manually:
- **Auto-imports**: Add `unimport` to your test environment or disable them by setting `imports: false` in your `wxt.config.ts` file
- **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.js`
- **[Remote Code Bundling](/guide/remote-code)**: If you use it, configure your environment to handle the `url:` module prefix
- **Global Variables**: If you consume them, manually define globals provided by WXT (like `__BROWSER__`) by adding them to the global scope before accessing them (`globalThis.__BROWSER__ = "chrome"`)
- **Import paths**: If you use the `@/` or `~/` path aliases, add them to your test environment
[Here's how Vitest is configured](https://github.com/wxt-dev/wxt/blob/main/src/testing/wxt-vitest-plugin.ts) for reference.
-20
View File
@@ -1,20 +0,0 @@
# Unlisted Scripts
TypeScript files that are built, but are not included in the manifest.
## Filenames
<EntrypointPatterns
:patterns="[
['<name>.(ts|tsx)', '<name>.js'],
['<name>/index.(ts|tsx)', '<name>.js'],
]"
/>
## Definition
Unlike the background or content scripts, you can define this script's logic in the top level scope.
```ts
// Code goes here
```
+12 -11
View File
@@ -13,31 +13,31 @@ hero:
actions:
- theme: brand
text: Get Started
link: /get-started/installation
link: /guide/installation
- theme: alt
text: Learn More
link: /get-started/introduction
link: /guide/introduction
features:
- icon: 🌐
title: Supported Browsers
details: WXT will build extensions for Chrome, Firefox, Edge, Safari, and any Chromium based browser.
link: /get-started/build-targets
link: /guide/multiple-browsers
linkText: Read docs
- icon: ✅
title: MV2 and MV3
details: Build Manifest V2 or V3 extensions for any browser using the same codebase.
link: /get-started/build-targets#target-manifest-version
link: /guide/multiple-browsers#target-manifest-version
linkText: Read docs
- icon: ⚡
title: Fast Dev Mode
details: Lighting fast HMR for UI development and fast reloads for content/background scripts enables faster iterations.
link: /get-started/introduction.html#development
link: /guide/development.html
linkText: Learn more
- icon: 📂
title: File Based Entrypoints
details: Manifest is generated based on files in the project with inline configuration.
link: /get-started/entrypoints
link: /guide/entrypoints
linkText: See project structure
- icon: 🚔
title: TypeScript
@@ -50,17 +50,17 @@ features:
- icon: ⬇️
title: Bundle Remote Code
details: Downloads and bundles remote code imported from URLs.
link: guide/remote-code
link: /guide/remote-code
linkText: Read docs
- icon: 🎨
title: Frontend Framework Agnostic
details: Works with any front-end framework with a Vite plugin.
link: /get-started/configuration#frontend-frameworks
link: /guide/configuration#frontend-frameworks
linkText: Add a framework
- icon: 🖍️
title: Bootstrap a New Project
details: Get started quickly with several awesome project templates.
link: /get-started/installation#bootstrap-project
link: /guide/installation#bootstrap-project
linkText: See templates
- icon: 📏
title: Bundle Analysis
@@ -76,8 +76,9 @@ features:
<p>
WXT's simplifies the chrome extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
</p>
<div style="margin: auto; width: 100%; max-width: 700px; text-align: center">
<img src="./assets/cli-output.png" alt="Screenshot of WXT's build output" />
<div style="margin: auto; width: 100%; max-width: 900px; text-align: center">
<video src="https://github.com/wxt-dev/wxt/assets/10101283/b32e6766-ec11-45a4-9677-226ee4718e1c" controls></video>
<br />
<small>
And who doesn't appreciate a beautiful CLI?
</small>
+31
View File
@@ -0,0 +1,31 @@
# Netlify Redirects File
# https://docs.netlify.com/routing/redirects/
# Old URLs -> New URLs
/config.html /api/wxt/interfaces/InlineConfig.html
/api/config.html /api/wxt/interfaces/InlineConfig.html
/entrypoints /entrypoints/background.html
/get-started/assets.html /guide/assets.html
/get-started/build-targets.html /guide/multiple-browsers.html
/get-started/compare.html /guide/compare.html
/get-started/configuration.html /guide/configuration.html
/get-started/entrypoints.html /guide/entrypoints.html
/get-started/installation.html /guide/installation.html
/get-started/introduction.html /guide/introduction.html
/get-started/publishing.html /guide/publishing.html
/get-started/testing.html /guide/testing.html
/guide/background.html /entrypoints/background.html
/guide/bookmarks.html /entrypoints/bookmarks.html
/guide/content-scripts.html /entrypoints/content-scripts.html
/guide/css.html /entrypoints/css.html
/guide/devtools.html /entrypoints/devtools.html
/guide/history.html /entrypoints/history.html
/guide/manifest.html /entrypoints/manifest.html
/guide/newtab.html /entrypoints/newtab.html
/guide/options.html /entrypoints/options.html
/guide/popup.html /entrypoints/popup.html
/guide/sandbox.html /entrypoints/sandbox.html
/guide/sidepanel.html /entrypoints/sidepanel.html
/guide/unlisted-pages.html /entrypoints/unlisted-pages.html
/guide/unlisted-scripts.html /entrypoints/unlisted-scripts.html
/guide/build-targets.html /guide/multiple-browsers.html
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+17
View File
@@ -0,0 +1,17 @@
{
"entryPoints": [
"../src",
"../src/client",
"../src/browser.ts",
"../src/sandbox",
"../src/storage.ts",
"../src/testing"
],
"plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"],
"out": "./api",
"githubPages": false,
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"readme": "none"
}
+32 -16
View File
@@ -17,11 +17,27 @@ describe('Auto Imports', () => {
export {}
declare global {
const ContentScriptContext: typeof import('wxt/client')['ContentScriptContext']
const InvalidMatchPattern: typeof import('wxt/sandbox')['InvalidMatchPattern']
const MatchPattern: typeof import('wxt/sandbox')['MatchPattern']
const browser: typeof import('wxt/browser')['browser']
const builtinDrivers: typeof import('wxt/storage')['builtinDrivers']
const createContentScriptIframe: typeof import('wxt/client')['createContentScriptIframe']
const createContentScriptUi: typeof import('wxt/client')['createContentScriptUi']
const defineBackground: typeof import('wxt/client')['defineBackground']
const createStorage: typeof import('wxt/storage')['createStorage']
const defineBackground: typeof import('wxt/sandbox')['defineBackground']
const defineConfig: typeof import('wxt')['defineConfig']
const defineContentScript: typeof import('wxt/client')['defineContentScript']
const defineContentScript: typeof import('wxt/sandbox')['defineContentScript']
const defineDriver: typeof import('wxt/storage')['defineDriver']
const defineUnlistedScript: typeof import('wxt/sandbox')['defineUnlistedScript']
const fakeBrowser: typeof import('wxt/testing')['fakeBrowser']
const joinKeys: typeof import('wxt/storage')['joinKeys']
const normalizeBaseKey: typeof import('wxt/storage')['normalizeBaseKey']
const normalizeKey: typeof import('wxt/storage')['normalizeKey']
const prefixStorage: typeof import('wxt/storage')['prefixStorage']
const restoreSnapshot: typeof import('wxt/storage')['restoreSnapshot']
const snapshot: typeof import('wxt/storage')['snapshot']
const storage: typeof import('wxt/storage')['storage']
const webExtensionDriver: typeof import('wxt/storage')['webExtensionDriver']
}
"
`);
@@ -35,16 +51,16 @@ describe('Auto Imports', () => {
expect(await project.serializeFile('.wxt/wxt.d.ts'))
.toMatchInlineSnapshot(`
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
/// <reference types=\\"vite/client\\" />
/// <reference types=\\"./types/imports.d.ts\\" />
/// <reference types=\\"./types/paths.d.ts\\" />
/// <reference types=\\"./types/i18n.d.ts\\" />
/// <reference types=\\"./types/globals.d.ts\\" />
"
`);
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
/// <reference types="vite/client" />
/// <reference types="./types/imports.d.ts" />
/// <reference types="./types/paths.d.ts" />
/// <reference types="./types/i18n.d.ts" />
/// <reference types="./types/globals.d.ts" />
"
`);
});
});
@@ -77,10 +93,10 @@ describe('Auto Imports', () => {
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
/// <reference types=\\"vite/client\\" />
/// <reference types=\\"./types/paths.d.ts\\" />
/// <reference types=\\"./types/i18n.d.ts\\" />
/// <reference types=\\"./types/globals.d.ts\\" />
/// <reference types="vite/client" />
/// <reference types="./types/paths.d.ts" />
/// <reference types="./types/i18n.d.ts" />
/// <reference types="./types/globals.d.ts" />
"
`,
);
+221 -59
View File
@@ -104,26 +104,64 @@ describe('Manifest Content', () => {
})
`;
it('should include a background script for mv2', async () => {
it.each(['chrome', 'safari'])(
'should include scripts and persistent for %s mv2',
async (browser) => {
const project = new TestProject();
project.addFile('entrypoints/background.ts', backgroundContent);
await project.build({ browser, manifestVersion: 2 });
const manifest = await project.getOutputManifest(
`.output/${browser}-mv2/manifest.json`,
);
expect(manifest.background).toEqual({
persistent: true,
scripts: ['background.js'],
});
},
);
it.each(['chrome', 'safari'])(
'should include a service worker and type for %s mv3',
async (browser) => {
const project = new TestProject();
project.addFile('entrypoints/background.ts', backgroundContent);
await project.build({ browser, manifestVersion: 3 });
const manifest = await project.getOutputManifest(
`.output/${browser}-mv3/manifest.json`,
);
expect(manifest.background).toEqual({
type: 'module',
service_worker: 'background.js',
});
},
);
it('should include a background script and type for firefox mv3', async () => {
const project = new TestProject();
project.addFile('entrypoints/background.ts', backgroundContent);
await project.build();
const manifest = await project.getOutputManifest();
await project.build({ browser: 'firefox', manifestVersion: 3 });
const manifest = await project.getOutputManifest(
'.output/firefox-mv3/manifest.json',
);
expect(manifest.background).toEqual({
type: 'module',
service_worker: 'background.js',
scripts: ['background.js'],
});
});
it('should include a options_ui and browser_style for firefox', async () => {
it('should include a background script and persistent for firefox mv2', async () => {
const project = new TestProject();
project.addFile('entrypoints/background.ts', backgroundContent);
await project.build({ manifestVersion: 2 });
await project.build({ browser: 'firefox', manifestVersion: 2 });
const manifest = await project.getOutputManifest(
'.output/chrome-mv2/manifest.json',
'.output/firefox-mv2/manifest.json',
);
expect(manifest.background).toEqual({
@@ -136,28 +174,30 @@ describe('Manifest Content', () => {
describe('icons', () => {
it('should auto-discover icons with the correct name', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile('public/icon-16.png');
project.addFile('public/icon/32.jpeg');
project.addFile('public/icon@48w.jpg');
project.addFile('public/icon-64x64.gif');
project.addFile('public/icon@96.bmp');
project.addFile('public/icon/128x128.ico');
project.addFile('public/icon/32.png');
project.addFile('public/icon@48w.png');
project.addFile('public/icon-64x64.png');
project.addFile('public/icon@96.png');
project.addFile('public/icons/128x128.png');
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.icons).toEqual({
'16': 'icon-16.png',
'32': 'icon/32.jpeg',
'48': 'icon@48w.jpg',
'64': 'icon-64x64.gif',
'96': 'icon@96.bmp',
'128': 'icon/128x128.ico',
'32': 'icon/32.png',
'48': 'icon@48w.png',
'64': 'icon-64x64.png',
'96': 'icon@96.png',
'128': 'icons/128x128.png',
});
});
it('should return undefined when no icons are found', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile('public/logo.png');
project.addFile('public/icon.jpeg');
@@ -169,6 +209,7 @@ describe('Manifest Content', () => {
it('should allow icons to be overwritten from the wxt.config.ts file', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile('public/icon-16.png');
project.addFile('public/icon-32.png');
project.addFile('public/logo-16.png');
@@ -194,15 +235,15 @@ describe('Manifest Content', () => {
});
describe('content_scripts', () => {
it('should group content scripts and styles together based on their matches and run_at', async () => {
it('should group content scripts and styles together based on their manifest properties', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
);
project.addFile(
'entrypoints/one.content/style.css',
@@ -211,11 +252,11 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/two.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/two.content/style.css',
@@ -224,11 +265,11 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/three.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/three.content/style.css',
@@ -237,11 +278,11 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/four.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://duckduckgo.com/*"],
runAt: "document_end",
main: () => {},
})`,
export default defineContentScript({
matches: ["*://duckduckgo.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/four.content/style.css',
@@ -276,15 +317,15 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/one.content/index.ts',
`export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
matches: ["*://google.com/*"],
main: () => {},
})`,
);
project.addFile(
'entrypoints/two.content/style.css',
`body {
background-color: red;
}`,
background-color: red;
}`,
);
project.setConfigFileConfig({
manifest: {
@@ -320,7 +361,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
@@ -333,7 +374,7 @@ describe('Manifest Content', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"css\\":[\\"content-scripts/content.css\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["https://*.google.com/*"],"css":["content-scripts/content.css"],"js":["content-scripts/content.js"]}]}"
`);
});
@@ -346,7 +387,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "manifest",
@@ -360,7 +401,7 @@ describe('Manifest Content', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"css\\":[\\"content-scripts/content.css\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["https://*.google.com/*"],"css":["content-scripts/content.css"],"js":["content-scripts/content.js"]}]}"
`);
});
@@ -373,7 +414,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "manual",
@@ -385,10 +426,10 @@ describe('Manifest Content', () => {
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
`);
".output/chrome-mv3/manifest.json
----------------------------------------
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["https://*.google.com/*"],"js":["content-scripts/content.js"]}]}"
`);
});
it('should not add a content script entry for CSS when cssInjectionMode is "ui", but add a web_accessible_resources entry for MV2', async () => {
@@ -400,7 +441,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "ui",
@@ -416,7 +457,7 @@ describe('Manifest Content', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv2/manifest.json
----------------------------------------
{\\"manifest_version\\":2,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"js\\":[\\"content-scripts/content.js\\"]}],\\"web_accessible_resources\\":[\\"content-scripts/content.css\\"]}"
{"manifest_version":2,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["https://*.google.com/*"],"js":["content-scripts/content.js"]}],"web_accessible_resources":["content-scripts/content.css"]}"
`);
});
@@ -429,7 +470,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "ui",
@@ -445,7 +486,7 @@ describe('Manifest Content', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"js\\":[\\"content-scripts/content.js\\"]}],\\"web_accessible_resources\\":[{\\"resources\\":[\\"content-scripts/content.css\\"],\\"matches\\":[\\"https://*.google.com/*\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["https://*.google.com/*"],"js":["content-scripts/content.js"]}],"web_accessible_resources":[{"resources":["content-scripts/content.css"],"matches":["https://*.google.com/*"]}]}"
`);
});
});
@@ -459,7 +500,7 @@ describe('Manifest Content', () => {
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "ui",
@@ -480,16 +521,17 @@ describe('Manifest Content', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"web_accessible_resources\\":[{\\"resources\\":[\\"one.png\\"],\\"matches\\":[\\"https://one.com/*\\"]},{\\"resources\\":[\\"content-scripts/content.css\\"],\\"matches\\":[\\"https://*.google.com/*\\"]}],\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","web_accessible_resources":[{"resources":["one.png"],"matches":["https://one.com/*"]},{"resources":["content-scripts/content.css"],"matches":["https://*.google.com/*"]}],"content_scripts":[{"matches":["https://*.google.com/*"],"js":["content-scripts/content.js"]}]}"
`);
});
it('should respect the transformManifest option', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile(
'wxt.config.ts',
`import { defineConfig } from 'wxt';
export default defineConfig({
transformManifest(manifest) {
manifest.author = "Custom Author"
@@ -505,7 +547,127 @@ describe('Manifest Content', () => {
expect(output).toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"author\\":\\"Custom Author\\"}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","author":"Custom Author"}"
`);
});
it.each([
{ browser: undefined, outDir: 'chrome-mv3', expected: undefined },
{ browser: 'chrome', outDir: 'chrome-mv3', expected: undefined },
{ browser: 'firefox', outDir: 'firefox-mv2', expected: true },
{ browser: 'safari', outDir: 'safari-mv2', expected: false },
])(
'should respect the per-browser entrypoint option with %j',
async ({ browser, expected, outDir }) => {
const project = new TestProject();
project.addFile(
'entrypoints/background.ts',
`export default defineBackground({
persistent: {
firefox: true,
safari: false,
},
main: () => {},
})`,
);
await project.build({ browser });
const safariManifest = await project.getOutputManifest(
`.output/${outDir}/manifest.json`,
);
expect(safariManifest.background.persistent).toBe(expected);
},
);
describe('versions', () => {
it.each([
['chrome', 3] as const,
['safari', 2] as const,
['edge', 3] as const,
])(
'should include version_name on %s when it needs simplified',
async (browser, manifestVersion) => {
const project = new TestProject({
version: '1.0.0-alpha1',
});
project.addFile('entrypoints/unlisted.html');
await project.build({ browser, manifestVersion });
const manifest = await project.getOutputManifest(
`.output/${browser}-mv${manifestVersion}/manifest.json`,
);
expect(manifest.version).toBe('1.0.0');
expect(manifest.version_name).toBe('1.0.0-alpha1');
},
);
it.each([['firefox', 2] as const])(
"should not include a version_name on %s because the browser doesn't support it",
async (browser, manifestVersion) => {
const project = new TestProject({
version: '1.0.0-alpha1',
});
project.addFile('entrypoints/unlisted.html');
await project.build({ browser, manifestVersion });
const manifest = await project.getOutputManifest(
`.output/${browser}-mv${manifestVersion}/manifest.json`,
);
expect(manifest.version).toBe('1.0.0');
expect(manifest.version_name).toBeUndefined();
},
);
it.each([
['chrome', 3] as const,
['firefox', 2] as const,
['safari', 3] as const,
['edge', 3] as const,
])(
'should not include the version_name if it is equal to version',
async (browser, manifestVersion) => {
const project = new TestProject({
version: '1.0.0.1',
});
project.addFile('entrypoints/unlisted.html');
await project.build({ browser, manifestVersion });
const manifest = await project.getOutputManifest(
`.output/${browser}-mv${manifestVersion}/manifest.json`,
);
expect(manifest.version).toBe('1.0.0.1');
expect(manifest.version_name).toBeUndefined();
},
);
});
it('should respect the action field in the manifest without a popup', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.setConfigFileConfig({
manifest: {
action: {
default_title: 'Hello world',
},
},
});
await project.build();
expect(await project.getOutputManifest()).toMatchInlineSnapshot(`
{
"action": {
"default_title": "Hello world",
},
"description": "Example description",
"manifest_version": 3,
"name": "E2E Extension",
"version": "0.0.0",
}
`);
});
});
+65 -3
View File
@@ -7,13 +7,18 @@ describe('Output Directory Structure', () => {
project.addFile('entrypoints/.DS_Store');
project.addFile('entrypoints/.hidden1/index.html');
project.addFile('entrypoints/.hidden2.html');
project.addFile('entrypoints/unlisted.html');
await project.build();
expect(await project.serializeOutput()).toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\"}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0"}
================================================================================
.output/chrome-mv3/unlisted.html
----------------------------------------
"
`);
});
@@ -69,7 +74,7 @@ describe('Output Directory Structure', () => {
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"css\\":[\\"content-scripts/one.css\\",\\"content-scripts/two.css\\"],\\"js\\":[\\"content-scripts/one.js\\",\\"content-scripts/two.js\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["*://*/*"],"css":["content-scripts/one.css","content-scripts/two.css"],"js":["content-scripts/one.js","content-scripts/two.js"]}]}"
`);
});
@@ -96,7 +101,7 @@ describe('Output Directory Structure', () => {
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"js\\":[\\"content-scripts/overlay-one.js\\"]}]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["*://*/*"],"js":["content-scripts/overlay-one.js"]}]}"
`);
});
@@ -165,4 +170,61 @@ describe('Output Directory Structure', () => {
expect(await project.fileExists('stats.html')).toBe(true);
});
it('should support JavaScript entrypoints', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/background.js',
`export default defineBackground(() => {});`,
);
project.addFile(
'entrypoints/unlisted.js',
`export default defineUnlistedScript(() => {})`,
);
project.addFile(
'entrypoints/content.js',
`export default defineContentScript({
matches: ["*://*.google.com/*"],
main() {},
})`,
);
project.addFile(
'entrypoints/named.content.jsx',
`export default defineContentScript({
matches: ["*://*.duckduckgo.com/*"],
main() {},
})`,
);
await project.build();
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","background":{"service_worker":"background.js"},"content_scripts":[{"matches":["*://*.google.com/*"],"js":["content-scripts/content.js"]},{"matches":["*://*.duckduckgo.com/*"],"js":["content-scripts/named.js"]}]}"
`);
expect(await project.fileExists('.output/chrome-mv3/background.js'));
expect(
await project.fileExists('.output/chrome-mv3/content-scripts/content.js'),
);
expect(
await project.fileExists('.output/chrome-mv3/content-scripts/named.js'),
);
expect(await project.fileExists('.output/chrome-mv3/unlisted.js'));
});
it("should output to a custom directory when overriding 'outDir'", async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.setConfigFileConfig({
outDir: 'dist',
});
await project.build();
expect(await project.fileExists('dist/chrome-mv3/manifest.json')).toBe(
true,
);
});
});
+4 -4
View File
@@ -35,9 +35,9 @@ describe('React', () => {
).toBe(true);
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":\\"<all_urls>\\",\\"js\\":[\\"content-scripts/demo.js\\"]}]}"
`);
".output/chrome-mv3/manifest.json
----------------------------------------
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":"<all_urls>","js":["content-scripts/demo.js"]}]}"
`);
});
});
+5 -1
View File
@@ -5,7 +5,11 @@ describe('Remote Code', () => {
it('should download "url:*" modules and include them in the final bundle', async () => {
const url = 'https://code.jquery.com/jquery-3.7.1.slim.min.js';
const project = new TestProject();
project.addFile('entrypoints/popup.ts', `import "url:${url}"`);
project.addFile(
'entrypoints/popup.ts',
`import "url:${url}"
export default defineUnlistedScript(() => {})`,
);
await project.build();
+146 -93
View File
@@ -4,6 +4,7 @@ import { TestProject } from '../utils';
describe('TypeScript Project', () => {
it('should generate defined constants correctly', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
await project.build();
@@ -21,7 +22,7 @@ describe('TypeScript Project', () => {
const __IS_SAFARI__: boolean;
const __IS_EDGE__: boolean;
const __IS_OPERA__: boolean;
const __COMMAND__: \\"build\\" | \\"serve\\";
const __COMMAND__: "build" | "serve";
const __ENTRYPOINT__: string;
}
"
@@ -41,13 +42,13 @@ describe('TypeScript Project', () => {
".wxt/types/paths.d.ts
----------------------------------------
// Generated by wxt
import \\"wxt/browser\\";
import "wxt/browser";
declare module \\"wxt/browser\\" {
type PublicPath =
| \\"/options.html\\"
| \\"/popup.html\\"
| \\"/sandbox.html\\"
declare module "wxt/browser" {
export type PublicPath =
| "/options.html"
| "/popup.html"
| "/sandbox.html"
export interface WxtRuntime extends Runtime.Static {
getURL(path: PublicPath): string;
}
@@ -58,6 +59,7 @@ describe('TypeScript Project', () => {
it('should augment the types for browser.i18n.getMessage', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile(
'public/_locales/en/messages.json',
JSON.stringify({
@@ -103,9 +105,9 @@ describe('TypeScript Project', () => {
".wxt/types/i18n.d.ts
----------------------------------------
// Generated by wxt
import \\"wxt/browser\\";
import "wxt/browser";
declare module \\"wxt/browser\\" {
declare module "wxt/browser" {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
@@ -120,91 +122,91 @@ describe('TypeScript Project', () => {
/**
* The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.
Note: You can't use this message in a manifest file.
*
* \\"<browser.runtime.id>\\"
*
* "<browser.runtime.id>"
*/
getMessage(
messageName: \\"@@extension_id\\",
messageName: "@@extension_id",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
*
*
* \\"<browser.i18n.getUiLocale()>\\"
*
* "<browser.i18n.getUiLocale()>"
*/
getMessage(
messageName: \\"@@ui_locale\\",
messageName: "@@ui_locale",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* The text direction for the current locale, either \\"ltr\\" for left-to-right languages such as English or \\"rtl\\" for right-to-left languages such as Japanese.
*
* \\"<ltr|rtl>\\"
* The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.
*
* "<ltr|rtl>"
*/
getMessage(
messageName: \\"@@bidi_dir\\",
messageName: "@@bidi_dir",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"rtl\\"; otherwise, it's \\"ltr\\".
*
* \\"<rtl|ltr>\\"
* If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".
*
* "<rtl|ltr>"
*/
getMessage(
messageName: \\"@@bidi_reversed_dir\\",
messageName: "@@bidi_reversed_dir",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"left\\"; otherwise, it's \\"right\\".
*
* \\"<left|right>\\"
* If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".
*
* "<left|right>"
*/
getMessage(
messageName: \\"@@bidi_start_edge\\",
messageName: "@@bidi_start_edge",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"right\\"; otherwise, it's \\"left\\".
*
* \\"<right|left>\\"
* If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".
*
* "<right|left>"
*/
getMessage(
messageName: \\"@@bidi_end_edge\\",
messageName: "@@bidi_end_edge",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Ask for the user's name
*
* \\"What's your name?\\"
*
* "What's your name?"
*/
getMessage(
messageName: \\"prompt_for_name\\",
messageName: "prompt_for_name",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Greet the user
*
* \\"Hello, $USER$\\"
*
* "Hello, $USER$"
*/
getMessage(
messageName: \\"hello\\",
messageName: "hello",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Say goodbye to the user
*
* \\"Goodbye, $USER$. Come back to $OUR_SITE$ soon!\\"
*
* "Goodbye, $USER$. Come back to $OUR_SITE$ soon!"
*/
getMessage(
messageName: \\"bye\\",
messageName: "bye",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
@@ -216,6 +218,7 @@ describe('TypeScript Project', () => {
it('should reference all the required types in a single declaration file', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
await project.build();
@@ -224,17 +227,18 @@ describe('TypeScript Project', () => {
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
/// <reference types=\\"vite/client\\" />
/// <reference types=\\"./types/imports.d.ts\\" />
/// <reference types=\\"./types/paths.d.ts\\" />
/// <reference types=\\"./types/i18n.d.ts\\" />
/// <reference types=\\"./types/globals.d.ts\\" />
/// <reference types="vite/client" />
/// <reference types="./types/imports.d.ts" />
/// <reference types="./types/paths.d.ts" />
/// <reference types="./types/i18n.d.ts" />
/// <reference types="./types/globals.d.ts" />
"
`);
});
it('should generate a TSConfig file for the project', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
await project.build();
@@ -243,39 +247,39 @@ describe('TypeScript Project', () => {
".wxt/tsconfig.json
----------------------------------------
{
\\"compilerOptions\\": {
\\"target\\": \\"ESNext\\",
\\"module\\": \\"ESNext\\",
\\"moduleResolution\\": \\"Bundler\\",
\\"noEmit\\": true,
\\"esModuleInterop\\": true,
\\"forceConsistentCasingInFileNames\\": true,
\\"resolveJsonModule\\": true,
\\"strict\\": true,
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@\\": [\\"..\\"],
\\"@/*\\": [\\"../*\\"],
\\"~\\": [\\"..\\"],
\\"~/*\\": [\\"../*\\"],
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"]
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@": [".."],
"@/*": ["../*"],
"~": [".."],
"~/*": ["../*"],
"@@": [".."],
"@@/*": ["../*"],
"~~": [".."],
"~~/*": ["../*"]
}
},
\\"include\\": [
\\"../**/*\\",
\\"./wxt.d.ts\\"
"include": [
"../**/*",
"./wxt.d.ts"
],
\\"exclude\\": [\\"../.output\\"]
"exclude": ["../.output"]
}"
`);
});
it('should generate correct path aliases for a custom srcDir', async () => {
const project = new TestProject();
project.addFile('src/entrypoints/unlisted.html');
project.setConfigFileConfig({
srcDir: 'src',
});
@@ -287,33 +291,82 @@ describe('TypeScript Project', () => {
".wxt/tsconfig.json
----------------------------------------
{
\\"compilerOptions\\": {
\\"target\\": \\"ESNext\\",
\\"module\\": \\"ESNext\\",
\\"moduleResolution\\": \\"Bundler\\",
\\"noEmit\\": true,
\\"esModuleInterop\\": true,
\\"forceConsistentCasingInFileNames\\": true,
\\"resolveJsonModule\\": true,
\\"strict\\": true,
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@\\": [\\"../src\\"],
\\"@/*\\": [\\"../src/*\\"],
\\"~\\": [\\"../src\\"],
\\"~/*\\": [\\"../src/*\\"],
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"]
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@": ["../src"],
"@/*": ["../src/*"],
"~": ["../src"],
"~/*": ["../src/*"],
"@@": [".."],
"@@/*": ["../*"],
"~~": [".."],
"~~/*": ["../*"]
}
},
\\"include\\": [
\\"../**/*\\",
\\"./wxt.d.ts\\"
"include": [
"../**/*",
"./wxt.d.ts"
],
\\"exclude\\": [\\"../.output\\"]
"exclude": ["../.output"]
}"
`);
});
it('should add additional path aliases listed in the alias config, preventing defaults from being overridden', async () => {
const project = new TestProject();
project.addFile('src/entrypoints/unlisted.html');
project.setConfigFileConfig({
srcDir: 'src',
alias: {
example: 'example',
'@': 'ignored-path',
},
});
await project.build();
const output = await project.serializeFile('.wxt/tsconfig.json');
expect(output).toMatchInlineSnapshot(`
".wxt/tsconfig.json
----------------------------------------
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"example": ["../example"],
"example/*": ["../example/*"],
"@": ["../src"],
"@/*": ["../src/*"],
"~": ["../src"],
"~/*": ["../src/*"],
"@@": [".."],
"@@/*": ["../*"],
"~~": [".."],
"~~/*": ["../*"]
}
},
"include": [
"../**/*",
"./wxt.d.ts"
],
"exclude": ["../.output"]
}"
`);
});
+27 -5
View File
@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { InlineConfig } from '~/types';
describe('User Config', () => {
// Root directory is tested with all tests.
@@ -28,7 +29,7 @@ describe('User Config', () => {
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"background\\":{\\"service_worker\\":\\"background.js\\"}}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","background":{"service_worker":"background.js"}}"
`);
});
@@ -54,12 +55,13 @@ describe('User Config', () => {
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"background\\":{\\"service_worker\\":\\"background.js\\"}}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","background":{"service_worker":"background.js"}}"
`);
});
it('should merge inline and user config based manifests', async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html');
project.addFile(
'wxt.config.ts',
`import { defineConfig } from 'wxt';
@@ -78,11 +80,31 @@ describe('User Config', () => {
}),
});
const output = await project.serializeOutput();
expect(output).toMatchInlineSnapshot(`
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"example_customization\\":[\\"production\\",\\"chrome\\",\\"3\\",\\"build\\"]}"
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","example_customization":["3","build","production","chrome"]}"
`);
});
it('should exclude the polyfill when the experimental setting is set to false', async () => {
const buildBackground = async (config?: InlineConfig) => {
const background = `export default defineBackground(() => console.log(browser.runtime.id));`;
const projectWithPolyfill = new TestProject();
projectWithPolyfill.addFile('entrypoints/background.ts', background);
await projectWithPolyfill.build(config);
return await projectWithPolyfill.serializeFile(
'.output/chrome-mv3/background.js',
);
};
const withPolyfill = await buildBackground();
const withoutPolyfill = await buildBackground({
experimental: {
includeBrowserPolyfill: false,
},
});
expect(withoutPolyfill).not.toBe(withPolyfill);
});
});
+4 -2
View File
@@ -24,7 +24,7 @@ export class TestProject {
{
name: 'E2E Extension',
description: 'Example description',
version: '0.0.0-test',
version: '0.0.0',
dependencies: {
wxt: '../../..',
},
@@ -77,7 +77,9 @@ export class TestProject {
await fs.writeFile(filePath, content ?? '', 'utf-8');
}
await execaCommand('npm i --ignore-scripts', { cwd: this.root });
await execaCommand('pnpm --ignore-workspace i --ignore-scripts', {
cwd: this.root,
});
await build({ ...config, root: this.root });
}
+91 -51
View File
@@ -1,10 +1,10 @@
{
"name": "wxt",
"type": "module",
"version": "0.7.1-alpha1",
"version": "0.12.3",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18.16.0",
"node": ">=18",
"pnpm": ">=8"
},
"repository": {
@@ -30,104 +30,144 @@
"bin",
"dist"
],
"bin": "./bin/wxt.cjs",
"bin": "./bin/wxt.mjs",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./client": {
"import": "./dist/client.js",
"types": "./dist/client.d.ts"
"types": "./dist/client.d.ts",
"import": "./dist/client.js"
},
"./sandbox": {
"types": "./dist/sandbox.d.ts",
"import": "./dist/sandbox.js"
},
"./browser": {
"import": "./dist/browser.js",
"types": "./dist/browser.d.ts"
"types": "./dist/browser.d.ts",
"import": "./dist/browser.js"
},
"./testing": {
"import": {
"types": "./dist/testing.d.ts",
"default": "./dist/testing.js"
},
"require": {
"types": "./dist/testing.d.cts",
"default": "./dist/testing.cjs"
}
},
"./storage": {
"import": {
"types": "./dist/storage.d.ts",
"default": "./dist/storage.js"
},
"require": {
"types": "./dist/storage.d.cts",
"default": "./dist/storage.cjs"
}
}
},
"scripts": {
"wxt": "tsx src/cli/index.ts",
"wxt": "tsx src/cli.ts",
"build": "tsx scripts/build.ts",
"format": "prettier --write .",
"format:check": "prettier --write .",
"compile": "tsc --noEmit",
"format:check": "prettier --check .",
"lint": "run-p -c -s lint:*",
"lint:eslint": "echo 'ESLint: TODO'",
"lint:package": "publint",
"compile": "run-s -c compile:*",
"compile:wxt": "tsc --noEmit",
"compile:virtual": "tsc --noEmit -p src/virtual",
"test": "vitest",
"test:coverage": "vitest run --coverage.enabled --coverage.exclude=e2e --coverage.exclude=src/testing",
"test:coverage": "vitest run --coverage",
"prepare": "simple-git-hooks",
"prepublish": "pnpm -s build",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs",
"docs:gen": "typedoc --options docs/typedoc.json",
"docs:dev": "pnpm -s docs:gen && vitepress dev docs",
"docs:build": "pnpm -s docs:gen && vitepress build docs",
"docs:preview": "pnpm -s docs:gen && vitepress preview docs",
"sync-releases": "pnpx changelogen@latest gh release"
},
"dependencies": {
"@types/webextension-polyfill": "^0.10.1",
"@webext-core/fake-browser": "^1.2.2",
"@types/webextension-polyfill": "^0.10.5",
"@webext-core/fake-browser": "^1.3.1",
"@webext-core/isolated-element": "^1.0.4",
"@webext-core/match-patterns": "^1.0.2",
"@webext-core/match-patterns": "^1.0.3",
"async-mutex": "^0.4.0",
"c12": "^1.4.2",
"c12": "^1.5.1",
"cac": "^6.7.14",
"chokidar": "^3.5.3",
"consola": "^3.2.3",
"esbuild": "^0.19.4",
"defu": "^6.1.3",
"esbuild": "^0.19.5",
"fast-glob": "^3.3.1",
"filesize": "^10.0.8",
"fs-extra": "^11.1.1",
"get-port": "^7.0.0",
"giget": "^1.1.2",
"immer": "^10.0.2",
"giget": "^1.1.3",
"immer": "^10.0.3",
"is-wsl": "^3.0.0",
"jiti": "^1.19.1",
"jiti": "^1.21.0",
"json5": "^2.2.3",
"linkedom": "^0.15.1",
"linkedom": "^0.16.1",
"minimatch": "^9.0.3",
"normalize-path": "^3.0.0",
"ora": "^7.0.1",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"rollup-plugin-visualizer": "^5.9.2",
"unimport": "^3.1.0",
"vite": "^4.4.7",
"unimport": "^3.4.0",
"unstorage": "^1.9.0",
"vite": "^5.0.0",
"web-ext-run": "^0.1.0",
"webextension-polyfill": "^0.10.0",
"zip-dir": "^2.0.0"
},
"devDependencies": {
"@faker-js/faker": "^8.0.2",
"@types/fs-extra": "^11.0.1",
"@types/lodash.merge": "^4.6.7",
"@types/node": "^20.4.5",
"@types/prompts": "^2.4.4",
"@vitest/coverage-v8": "^0.34.1",
"execa": "^7.2.0",
"jsdom": "^22.1.0",
"lint-staged": "^14.0.0",
"@faker-js/faker": "^8.3.1",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.10.3",
"@types/normalize-path": "^3.0.2",
"@types/prompts": "^2.4.9",
"@vitest/coverage-v8": "^1.0.1",
"execa": "^8.0.1",
"happy-dom": "^12.10.3",
"lint-staged": "^15.2.0",
"lodash.merge": "^4.6.2",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.1",
"p-map": "^7.0.0",
"prettier": "^3.1.0",
"publint": "^0.2.6",
"simple-git-hooks": "^2.9.0",
"ts-morph": "^19.0.0",
"tsup": "^7.1.0",
"tsx": "^3.12.7",
"typescript": "^5.1.6",
"vitepress": "1.0.0-rc.10",
"vitest": "^0.34.1",
"vitest-mock-extended": "^1.1.4",
"vue": "^3.3.4",
"webextension-polyfill": "^0.10.0"
},
"peerDependencies": {
"webextension-polyfill": ">=0.10.0"
"tsup": "^8.0.1",
"tsx": "^4.6.2",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.3.2",
"vitepress": "1.0.0-rc.31",
"vitest": "^1.1.0",
"vitest-mock-extended": "^1.3.1",
"vue": "^3.3.10"
},
"packageManager": "pnpm@8.6.3",
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "prettier --write"
"*": "prettier --ignore-unknown --write"
},
"changelog": {
"excludeAuthors": [
+1705 -1035
View File
File diff suppressed because it is too large Load Diff
+83 -59
View File
@@ -1,82 +1,106 @@
import tsup from 'tsup';
import glob from 'fast-glob';
import { printFileList } from '../src/core/log/printFileList';
import { formatDuration } from '../src/core/utils/formatDuration';
import { printFileList } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import ora from 'ora';
import fs from 'fs-extra';
import { consola } from 'consola';
import pMap from 'p-map';
import os from 'node:os';
import path from 'node:path';
const spinner = ora('Building WXT').start();
const spinnerText = 'Building WXT';
const spinner = ora(spinnerText).start();
const startTime = Date.now();
const outDir = 'dist';
const virtualEntrypoints = ['background', 'content-script'];
await fs.rm(path.join(outDir, '*'), { recursive: true, force: true });
await fs.rm(outDir, { recursive: true, force: true });
const preset = {
dts: true,
silent: true,
sourcemap: false,
external: [
'virtual:user-unlisted-script',
'virtual:user-content-script-isolated-world',
'virtual:user-content-script-main-world',
'virtual:user-background',
],
} satisfies tsup.Options;
await Promise.all([
tsup.build({
function spinnerPMap(configs: tsup.Options[]) {
let completed = 0;
const updateSpinner = () => {
spinner.text = `${spinnerText} [${completed}/${configs.length}]`;
};
updateSpinner();
return pMap(
config,
async (config) => {
const res = await tsup.build(config);
completed++;
updateSpinner();
return res;
},
{
stopOnError: true,
concurrency: process.env.CI === 'true' ? os.cpus().length : Infinity,
},
);
}
const config: tsup.Options[] = [
// CJS/ESM
{
...preset,
entry: {
index: 'src/index.ts',
testing: 'src/testing/index.ts',
storage: 'src/storage.ts',
},
format: ['cjs', 'esm'],
sourcemap: true,
dts: true,
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { cli: 'src/cli/index.ts' },
format: ['cjs'],
sourcemap: 'inline',
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { client: 'src/client/index.ts' },
format: ['esm'],
sourcemap: 'inline',
dts: true,
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { browser: 'src/client/browser.ts' },
format: ['esm'],
sourcemap: 'inline',
dts: true,
silent: true,
external: ['vite'],
}),
...virtualEntrypoints.map((entryName) =>
tsup.build({
entry: {
[`virtual-modules/${entryName}-entrypoint`]: `src/client/virtual-modules/${entryName}-entrypoint.ts`,
},
format: ['esm'],
sourcemap: true,
silent: true,
external: [`virtual:user-${entryName}`, 'vite'],
}),
),
tsup.build({
clean: true,
},
// ESM-only
{
...preset,
entry: {
'virtual-modules/reload-html': `src/client/virtual-modules/reload-html.ts`,
browser: 'src/browser.ts',
sandbox: 'src/sandbox/index.ts',
client: 'src/client/index.ts',
},
format: ['esm'],
sourcemap: true,
silent: true,
external: ['vite'],
}),
tsup.build({
},
{
...preset,
entry: {
'virtual-modules/fake-browser': `src/client/virtual-modules/fake-browser.ts`,
'virtual/background-entrypoint': 'src/virtual/background-entrypoint.ts',
'virtual/content-script-isolated-world-entrypoint':
'src/virtual/content-script-isolated-world-entrypoint.ts',
'virtual/content-script-main-world-entrypoint':
'src/virtual/content-script-main-world-entrypoint.ts',
'virtual/mock-browser': 'src/virtual/mock-browser.ts',
'virtual/reload-html': 'src/virtual/reload-html.ts',
'virtual/unlisted-script-entrypoint':
'src/virtual/unlisted-script-entrypoint.ts',
},
format: ['esm', 'cjs'],
silent: true,
external: ['vite'],
}),
]).catch((err) => {
format: ['esm'],
splitting: false,
dts: false,
external: [...preset.external, 'wxt'],
},
// CJS-only
{
...preset,
entry: {
cli: 'src/cli.ts',
},
format: ['esm'],
},
];
await spinnerPMap(config).catch((err) => {
spinner.fail();
console.error(err);
process.exit(1);
-16
View File
@@ -1,16 +0,0 @@
declare module 'zip-dir' {
// Represents the options object for zipdir function
interface ZipDirOptions {
saveTo?: string;
filter?: (path: string, stat: import('fs').Stats) => boolean;
each?: (path: string) => void;
}
function zipdir(
dirPath: string,
options?: ZipDirOptions,
callback?: (error: Error | null, buffer: Buffer) => void,
): Promise<Buffer>;
export = zipdir;
}
+70
View File
@@ -0,0 +1,70 @@
import { fakeBrowser } from '@webext-core/fake-browser';
import { describe, it, expect, beforeEach } from 'vitest';
import { browser } from '~/browser';
import { storage } from '~/storage';
describe('Storage Utils', () => {
beforeEach(() => {
fakeBrowser.reset();
});
describe.each(['local', 'sync', 'managed', 'session'] as const)(
'storage - %s',
(storageArea) => {
describe('getItem', () => {
it('should return the value from the correct storage area', async () => {
const expected = 123;
await fakeBrowser.storage[storageArea].set({ count: expected });
expect(await storage.getItem(`${storageArea}:count`)).toBe(expected);
});
it("should return null if the value doesn't exist", async () => {
expect(await storage.getItem(`${storageArea}:count`)).toBeNull();
});
});
describe('getItems', () => {
it('should return an array of values in the same order as the keys passed in', async () => {
const expected = [
{ key: `${storageArea}:count`, value: 234 },
{ key: `${storageArea}:installDate`, value: null },
];
const keys = expected.map((item) => item.key);
await fakeBrowser.storage[storageArea].set({
count: expected[0].value,
});
const actual = await storage.getItems(keys);
expect(actual).toEqual(expected);
});
});
describe('setItem', () => {
it('should set the value in the correct storage area', () => {});
it.each([undefined, null])(
'should remove the item from storage when setting the value to %s',
async (value) => {
await fakeBrowser.storage[storageArea].set({ count: 345 });
await storage.setItem(`${storageArea}:count`, value as null);
// For some reason storage sets the value to "null" instead of deleting it. So using
// fakeBrowser during the expect fails. Using storage works. I've confirmed that this
// doesn't happen in a real extension environment.
expect(await storage.getItem(`${storageArea}:count`)).toBeNull();
},
);
});
describe('removeItem', () => {
it('should remove the key from storage', async () => {
await fakeBrowser.storage[storageArea].set({ count: 456 });
await storage.removeItem(`${storageArea}:count`);
expect(await browser.storage[storageArea].get()).toEqual({});
});
});
},
);
});
+81
View File
@@ -0,0 +1,81 @@
/**
* @module wxt/browser
*/
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
runtime: WxtRuntime;
i18n: WxtI18n;
}
export interface WxtRuntime extends Runtime.Static {
// Overriden per-project
}
export interface WxtI18n extends I18n.Static {
// Overriden per-project
}
export const browser: AugmentedBrowser = originalBrowser;
// re-export all the types from webextension-polyfill
// Because webextension-polyfill uses a weird namespace with "import export", there isn't a good way
// to get these types without re-listing them.
/** @ignore */
export type {
ActivityLog,
Alarms,
Bookmarks,
Action,
BrowserAction,
BrowserSettings,
BrowsingData,
CaptivePortal,
Clipboard,
Commands,
ContentScripts,
ContextualIdentities,
Cookies,
DeclarativeNetRequest,
Devtools,
Dns,
Downloads,
Events,
Experiments,
Extension,
ExtensionTypes,
Find,
GeckoProfiler,
History,
I18n,
Identity,
Idle,
Management,
Manifest,
ContextMenus,
Menus,
NetworkStatus,
NormandyAddonStudy,
Notifications,
Omnibox,
PageAction,
Permissions,
Pkcs11,
Privacy,
Proxy,
Runtime,
Scripting,
Search,
Sessions,
SidebarAction,
Storage,
Tabs,
Theme,
TopSites,
Types,
Urlbar,
UserScripts,
WebNavigation,
WebRequest,
Windows,
} from 'webextension-polyfill';
+164
View File
@@ -0,0 +1,164 @@
import cac from 'cac';
import { version } from '~/version';
import { build, clean, createServer, initialize, prepare, zip } from '~/core';
import consola, { LogLevels } from 'consola';
import { printHeader } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
const cli = cac('wxt');
cli.help();
cli.version(version);
cli.option('--debug', 'enable debug mode');
// DEV
cli
.command('[root]', 'start dev server')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.action(
wrapAction(async (root, flags) => {
const server = await createServer({
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
});
await server.start();
return { isOngoing: true };
}),
);
// BUILD
cli
.command('build [root]', 'build for production')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.option('--analyze', 'visualize extension bundle')
.action(
wrapAction(async (root, flags) => {
await build({
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
analysis: {
enabled: flags.analyze,
},
});
}),
);
// ZIP
cli
.command('zip [root]', 'build for production and zip output')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.action(
wrapAction(async (root, flags) => {
await zip({
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
});
}),
);
// PREPARE
cli
.command('prepare [root]', 'prepare typescript project')
.option('-c, --config <file>', 'use specified config file')
.action(
wrapAction(async (root, flags) => {
await prepare({
root,
configFile: flags.config,
debug: flags.debug,
});
}),
);
// CLEAN
cli
.command('clean [root]', 'clean generated files and caches')
.alias('cleanup')
.action(
wrapAction(async (root, flags) => {
await clean(root);
}),
);
// INIT
cli
.command('init [directory]', 'initialize a new project')
.option('-t, --template <template>', 'template to use')
.option('--pm <packageManager>', 'which package manager to use')
.action(
wrapAction(
async (directory, flags) => {
await initialize({
directory,
template: flags.template,
packageManager: flags.pm,
});
},
{ disableFinishedLog: true },
),
);
cli.parse();
/**
* Wrap an action handler to add a timer, error handling, and maybe enable debug mode.
*/
function wrapAction(
cb: (
...args: any[]
) => void | { isOngoing?: boolean } | Promise<void | { isOngoing?: boolean }>,
options?: {
disableFinishedLog?: boolean;
},
) {
return async (...args: any[]) => {
// Enable consola's debug mode globally at the start of all commands when the `--debug` flag is
// passed
const isDebug = !!args.find((arg) => arg?.debug);
if (isDebug) {
consola.level = LogLevels.debug;
}
const startTime = Date.now();
try {
printHeader();
const status = await cb(...args);
if (!status?.isOngoing && !options?.disableFinishedLog)
consola.success(
`Finished in ${formatDuration(Date.now() - startTime)}`,
);
} catch (err) {
consola.fail(
`Command failed after ${formatDuration(Date.now() - startTime)}`,
);
consola.error(err);
process.exit(1);
}
};
}
-31
View File
@@ -1,31 +0,0 @@
import * as wxt from '../..';
import { defineCommand } from '../utils/defineCommand';
export const build = defineCommand<
[
root: string | undefined,
flags: {
mode?: string;
config?: string;
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
analyze?: boolean;
},
]
>(async (root, flags) => {
const cliConfig: wxt.InlineConfig = {
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
analysis: {
enabled: flags.analyze,
},
};
await wxt.build(cliConfig);
});
-11
View File
@@ -1,11 +0,0 @@
import { defineCommand } from '../utils/defineCommand';
import * as wxt from '../..';
export const clean = defineCommand<
[
root: string | undefined,
flags: {
debug?: boolean;
},
]
>(wxt.clean);
-30
View File
@@ -1,30 +0,0 @@
import * as wxt from '../..';
import { defineCommand } from '../utils/defineCommand';
export const dev = defineCommand<
[
root: string | undefined,
flags: {
mode?: string;
config?: string;
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
const cliConfig: wxt.InlineConfig = {
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const server = await wxt.createServer(cliConfig);
await server.start();
return true;
});
-7
View File
@@ -1,7 +0,0 @@
export * from './build';
export * from './dev';
export * from './init';
export * from './prepare';
export * from './publish';
export * from './zip';
export * from './clean';
-27
View File
@@ -1,27 +0,0 @@
import { getInternalConfig } from '../../core/utils/getInternalConfig';
import { findEntrypoints } from '../../core/build/findEntrypoints';
import { generateTypesDir } from '../../core/build/generateTypesDir';
import { defineCommand } from '../utils/defineCommand';
import * as wxt from '../..';
export const prepare = defineCommand<
[
root: string | undefined,
flags: {
config?: string;
debug?: boolean;
},
]
>(async (root, flags) => {
const cliConfig: wxt.InlineConfig = {
root,
configFile: flags.config,
debug: flags.debug,
};
const config = await getInternalConfig(cliConfig, 'build');
config.logger.info('Generating types...');
const entrypoints = await findEntrypoints(config);
await generateTypesDir(entrypoints, config);
});
-11
View File
@@ -1,11 +0,0 @@
import { consola } from 'consola';
import { defineCommand } from '../utils/defineCommand';
export const publish = defineCommand(
async (
root: any,
{ config: configFile, debug }: { config?: string; debug?: string },
) => {
consola.warn('wxt publish: Not implemented');
},
);
-32
View File
@@ -1,32 +0,0 @@
import * as wxt from '../..';
import { buildInternal } from '../../core/build';
import { getInternalConfig } from '../../core/utils/getInternalConfig';
import { zipExtension } from '../../core/zip';
import { defineCommand } from '../utils/defineCommand';
export const zip = defineCommand<
[
root: string | undefined,
flags: {
mode?: string;
config?: string;
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
const cliConfig: wxt.InlineConfig = {
root,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const config = await getInternalConfig(cliConfig, 'build');
const output = await buildInternal(config);
await zipExtension(config, output);
});
-64
View File
@@ -1,64 +0,0 @@
import cac from 'cac';
import { version } from '../../package.json';
import * as commands from './commands';
const cli = cac('wxt');
cli.help();
cli.version(version);
cli.option('--debug', 'enable debug mode');
// DEV
cli
.command('[root]', 'start dev server')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.action(commands.dev);
// BUILD
cli
.command('build [root]', 'build for production')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.option('--analyze', 'visualize extension bundle')
.action(commands.build);
// ZIP
cli
.command('zip [root]', 'build for production and zip output')
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.action(commands.zip);
// PREPARE
cli
.command('prepare [root]', 'prepare typescript project')
.option('-c, --config <file>', 'use specified config file')
.action(commands.prepare);
// CLEAN
cli
.command('clean [root]', 'clean generated files and caches')
.alias('cleanup')
.action(commands.clean);
// PUBLISH
cli.command('publish [root]', 'publish to stores').action(commands.publish);
// INIT
cli
.command('init [directory]', 'initialize a new project')
.option('-t, --template <template>', 'template to use')
.option('--pm <packageManager>', 'which package manager to use')
.action(commands.init);
cli.parse();
-16
View File
@@ -1,16 +0,0 @@
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
runtime: WxtRuntime;
i18n: WxtI18n;
}
export interface WxtRuntime extends Runtime.Static {
// Overriden per-project
}
export interface WxtI18n extends I18n.Static {
// Overriden per-project
}
export const browser: AugmentedBrowser = originalBrowser;

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