Compare commits

..

86 Commits

Author SHA1 Message Date
github-actions[bot] 1aecdbb71c chore(release): @wxt-dev/module-solid v1.1.4
📼 VHS / Create VHS (push) Waiting to run
2025-09-03 22:26:05 +00:00
github-actions[bot] d8e517cb7b chore(release): @wxt-dev/module-react v1.1.4
📼 VHS / Create VHS (push) Waiting to run
2025-09-03 22:17:14 +00:00
github-actions[bot] 9da5b4cacc chore(release): @wxt-dev/analytics v0.5.1
📼 VHS / Create VHS (push) Waiting to run
2025-09-03 22:10:36 +00:00
Aaron 84af32242b ci: Create script to list unreleased commits for each package (#1878) 2025-09-03 17:05:27 -05:00
Aaron 7a81472407 chore(deps): Upgrade all dev dependencies (#1876) 2025-09-03 16:41:00 -05:00
Aaron 9399d8df42 chore: Create script for managing dependency upgrades (#1875) 2025-09-03 16:09:14 -05:00
Aaron 459f73fd5c fix: Add support for @sveltejs/vite-plugin-svelte@6 2025-09-03 10:04:52 -05:00
Aaron c1c3d3b7c5 chore: Upgrade happy-dom to v18.0.1 2025-09-03 09:34:53 -05:00
Aaron 380a9630e5 chore: Upgrade dotenv to v17.2.2 2025-09-03 09:34:09 -05:00
Aaron 984568e00c chore: Upgrade faker to v10 2025-09-03 09:32:07 -05:00
Aaron 5fefd8e083 chore: Upgrade nano-spawn to v1 2025-09-03 09:28:17 -05:00
Aaron a6eef643f6 chore(deps): Upgrade typescript from 5.8.3 to 5.9.2 2025-09-03 09:14:54 -05:00
Aaron a01928e010 chore(deps): Upgrade oxlint from 0.16.8 to 1.14.0 2025-09-03 09:10:47 -05:00
Aaron d901db2316 chore(deps): Upgrade dev dependencies in workspace root (#1874) 2025-09-03 09:05:46 -05:00
Kim Gyeongjae fdda812078 fix: Prevent Unlisted CSS from being excluded in the build when using CSS preprocessor (#1590)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:49:07 -05:00
Atomie CHEN 7c59e9d5b5 fix(types): use TType for DocumentEventMap key in ctx.addEventListener (#1863)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:42:40 -05:00
tanishq-aggarwal c9b0c4da34 feat: Integrate latest measurement protocol changes (#1767)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:22:06 -05:00
TomvdPeet 9f214141b4 docs: Add "Browsely" to homepage (#1850)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:21:39 -05:00
kakuiho 4638e90f02 docs: Added Filmbudd Pro to the homepage (#1853)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:20:27 -05:00
Snow d1c50ab6f8 docs: Add "MultiField CopyCat" to homepage (#1856)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-09-03 08:19:22 -05:00
Abijith Vasanthakumar 489aa0ffd8 docs: Add "ChatSight" to Using WXT section (#1862) 2025-09-03 08:16:54 -05:00
Aaron ebcc605961 chore(deps): Update github actions (#1873) 2025-09-03 08:14:36 -05:00
Aaron bb630049d5 ci: Use node 20 in CI 2025-09-03 07:40:26 -05:00
Aaron 39321adfe1 ci: Use deploy key to bypass branch protection rules (#1861) 2025-08-26 10:48:51 -06:00
aklinker1 ea2b8df906 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-08-15 00:32:30 +00:00
aklinker1 74a9b1aef4 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-08-11 23:42:47 +00:00
github-actions[bot] 8b3e35de94 chore(release): @wxt-dev/auto-icons v1.1.0
📼 VHS / Create VHS (push) Waiting to run
2025-08-11 23:41:58 +00:00
github-actions[bot] 9d4724fa99 chore(release): wxt v0.20.8
📼 VHS / Create VHS (push) Waiting to run
2025-08-11 23:37:18 +00:00
Typed SIGTERM a8379cbd80 docs: Add SVG compatibility note (#1830)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 09:39:45 -05:00
Sam Carlton bdfb6c5a84 fix(paths): add browser paths for Arc & Dia on macos (#1814)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 09:24:39 -05:00
sylva 87414d7137 fix: adding missing chromiumPort parameter in web-ext.ts to enable fixed port debugging (#1818)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 09:16:23 -05:00
Kuba f9d0b64d7a feat: Add overlay option for dev icons (#1825)
Signed-off-by: Kuba Serafinowski <kuba.serafinowski@gmail.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 09:15:59 -05:00
Aaron 97918710bd docs: Add section on extension API entrypoint limitations (#1743) 2025-08-03 09:14:56 -05:00
btea ac340b3f1e fix: init project should allow the .git folder to exist (#1731)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 09:14:17 -05:00
Alexander Kachkaev a2097e148b fix: improve Chrome path search (#1823)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 08:44:06 -05:00
Patryk Kuniczak 190be7f732 fix(rolldown-compat): Only apply footer to content and unlisted scripts via plugin (#1793)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-08-03 08:40:27 -05:00
Aaron 0333ce5c46 chore: Fix type error 2025-08-03 08:02:52 -05:00
Aaron 47df623fd4 docs: Update using WXT section 2025-08-03 07:59:37 -05:00
dependabot[bot] c609efbd9a chore(deps): bump stefanzweifel/git-auto-commit-action from 5 to 6
Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 5 to 6.
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 18:26:56 -05:00
Johnny Fekete c8cd41a833 fix: add missing comma in extension IDs (#1827) 2025-07-28 18:02:03 +02:00
Yacine Benslimane 02a0110091 docs: add "Epic Games Library Extension" to docs (#1728)
Co-authored-by: Florian Metz <me@timeraa.dev>
2025-07-24 18:37:07 +02:00
Sam Carlton 6c572c56a3 feat(config): add browser path for Zen via Homebrew (#1813)
Co-authored-by: Florian Metz <me@timeraa.dev>
2025-07-24 18:36:29 +02:00
Greg Zajac 2db516ef3b docs: add 2FAS Pass extension (#1790)
Co-authored-by: Florian Metz <me@timeraa.dev>
2025-07-24 18:33:39 +02:00
Michał Warać 9f3787cd95 docs: add "Piwik PRO Tracking Helper" to the home page (#1822) 2025-07-24 18:05:46 +02:00
Nguyen Canh Toan 5511943237 docs: Update UsingWxtSection.vue (#1740)
Co-authored-by: Florian Metz <me@timeraa.dev>
2025-07-24 18:03:10 +02:00
John Valai 626838eba6 fix: correct typos in docs (#1817) 2025-07-18 03:44:33 -05:00
aklinker1 587328f54c fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-17 00:32:55 +00:00
aklinker1 1aa88b1fb6 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-16 00:32:35 +00:00
ayu e7db119545 chore: wxt & @wxt-dev/module-vue support Vite 7 (#1771) 2025-07-15 19:21:51 -05:00
Thomas Howlett da79536456 docs: correct content script example code (#1811) 2025-07-15 18:56:58 -05:00
aklinker1 aaaff43731 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-12 00:32:04 +00:00
Nishu 5d77142758 docs: adding youtube-video-scheduler in docs. (#1736) 2025-07-12 00:18:36 +02:00
wenyuan 586ea0002b docs: add "Quick Prompt" to the homepage (#1785)
Co-authored-by: Florian Metz <me@timeraa.dev>
2025-07-12 00:18:22 +02:00
流浪大法师 e642d6fe76 docs: add "Add QR Code Generator Icon Back To Address Bar" to the home page (#1800) 2025-07-12 00:15:26 +02:00
Achraf M. Bouhamid 7148f288d5 docs: add "PIPX" to the home page (#1798) 2025-07-12 00:15:14 +02:00
aklinker1 40abf3fb5b fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-10 00:31:55 +00:00
aklinker1 adea6c7177 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-09 00:32:25 +00:00
Aaron 96a307e99f docs: Use proper content type when loading extensions on homepage 2025-07-08 02:22:19 -05:00
aklinker1 f0201f72ef fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-07-02 00:31:40 +00:00
aklinker1 0b1b677b3d fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-06-29 00:35:32 +00:00
aklinker1 b1dcec86cd fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2025-06-28 00:30:09 +00:00
Mark 7c783fe565 fix: rootDir, not srdDir for public/_locales (#1751) 2025-06-16 16:56:53 -05:00
요술토끼(wizardrabbit) 9a0207d7cf docs: Added "토탐정" to the homepage (#1750) 2025-06-16 07:51:48 -05:00
Honwhy Wang 1b0e047d6b docs: adding wechat reader enhancer in docs. (#1745) 2025-06-15 18:23:56 -05:00
0xHPZ 7ab3c7e5d9 docs: Added "Chatslator" to the homepage (#1739) 2025-06-14 19:51:04 -05:00
Nishu 9ece49d226 fix: Support negation patterns when including/excluding files from ZIP files (#1517)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-06-14 18:08:04 -05:00
Aaron 1272e2ec74 chore: Fix markdown lint errors and add upgrade @aklinker1/check to
include markdownlint
2025-06-10 16:14:58 -05:00
Aaron db16602018 docs: Add missing breaking change to 0.20 migration (#1636) 2025-06-06 11:03:21 -05:00
Aaron 5c202805b9 fix: Fix bundle variable assignment error with rolldown-vite (#1715) 2025-06-06 10:56:06 -05:00
Aaron bfc8866b69 fix: Fix bad regex from #1707 (#1712) 2025-06-05 01:02:21 -05:00
spookyuser 9aedc29289 docs: Update environment-variables.md with info about env.DEV (#1630)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-06-05 01:01:42 -05:00
Hyunbin Seo e1f3d75ac4 docs: fix svelte app unmount code (#1673) 2025-06-05 00:48:37 -05:00
Aaron 030f23d2e9 docs: Update JSDoc with ways to cancel timeouts and intervals set by content script context 2025-06-05 00:38:57 -05:00
Nishu 6be9a4e71b fix: recreating keyboardShortcuts interface on file changes. (#1465)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2025-06-05 00:35:13 -05:00
Aaron eb0bffb5dd docs: Fix API reference sidebar by naming new module 2025-06-05 00:33:52 -05:00
BrowserLens 240bbb3676 docs: Added "BrowserLens" to the homepage (#1691) 2025-06-05 00:27:14 -05:00
Aaron 7d25110a6e chore: Remove automd 2025-06-05 00:18:43 -05:00
Aaron 598eea703b docs: Simplify README HTML elements 2025-06-04 23:32:24 -05:00
Aaron 8793f7bff4 chore: Manually fix markdownlint errors (#1711) 2025-06-04 18:30:54 -05:00
Aaron c1b3911622 fix: Support http server websocket inside containers (#1707) 2025-06-04 17:40:48 -05:00
Aaron b0bf05109d ci: Use matrix for test jobs (#1708) 2025-06-04 17:40:28 -05:00
Aaron d9cb0ebcc8 chore: Fix auto-fixable markdownlint errors (#1710) 2025-06-04 17:40:00 -05:00
Aaron 1eda21f63f chore: Add markdownlint to repo (#1709) 2025-06-04 17:34:42 -05:00
github-actions[bot] f2dbcde124 chore(release): wxt v0.20.7
📼 VHS / Create VHS (push) Waiting to run
2025-06-02 21:40:42 +00:00
Richard Lee 60f00db2c5 feat: Add DisableLoadExtensionCommandLineSwitch flag to Chromium runner (#1698) 2025-06-02 16:36:24 -05:00
github-actions[bot] a9332453fc chore(release): @wxt-dev/runner v0.1.1
📼 VHS / Create VHS (push) Waiting to run
2025-06-02 20:09:59 +00:00
97 changed files with 5057 additions and 4137 deletions
+3 -5
View File
@@ -4,12 +4,10 @@ description: Install PNPM, Node, and dependencies
inputs:
install:
default: 'true'
type: boolean
description: Whether or not to run 'pnpm install'
installArgs:
default: ''
type: string
description: Additional args to append to "pnpm install"
runs:
@@ -17,12 +15,12 @@ runs:
steps:
- name: 🛠️ Setup PNPM
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@f2b2b233b538f500472c7274c7012f57857d8ce0 # v4.1.0
- name: 🛠️ Setup NodeJS
uses: actions/setup-node@v4
uses: actions/setup-node@b7234cc9fe124f0f4932554b4e5284543083ae7b # v4.4.0
with:
node-version: 18
node-version: 20
cache: pnpm
- name: 📦 Install Dependencies
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: ${{ github.repository == 'wxt-dev/wxt' }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
with:
# Only fetch the config file from the repository
sparse-checkout-cone-mode: false
+2 -2
View File
@@ -23,13 +23,13 @@ jobs:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
- name: Login to Docker Registry
uses: docker/login-action@v3
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: https://${{ secrets.DOCKER_REGISTRY_HOSTNAME }}
username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
+2 -1
View File
@@ -38,9 +38,10 @@ jobs:
- validate
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
with:
fetch-depth: 0
ssh-key: ${{ secrets.DEPLOY_KEY }} # https://github.com/sbellone/release-workflow-example
- name: Setup
uses: ./.github/actions/setup
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
@@ -32,7 +32,7 @@ jobs:
- name: Commit Changes
id: commit
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@01d77ca6cb089da1360e540865f7d035c95aa199 # v6.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+25 -23
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
@@ -41,7 +41,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
@@ -55,39 +55,41 @@ jobs:
working-directory: packages/wxt-demo
tests:
name: Tests
runs-on: ubuntu-22.04
name: Tests (${{ matrix.title }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- title: 'Linux'
os: ubuntu-22.04
coverage: true
- title: 'Windows'
os: windows-latest
coverage: false
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
- name: Setup Bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@22457c87c1b161cf7dde222c3e82b2b5f8d2bed2 # v2.0.2
- name: Run Tests
run: pnpm test:coverage -- --reporter=default --reporter=hanging-process
if: ${{ ! matrix.coverage }}
run: pnpm test
- name: Run Tests (Coverage)
if: matrix.coverage
run: pnpm test:coverage --reporter=default --reporter=hanging-process
- name: Upload Coverage
uses: codecov/codecov-action@v5
if: matrix.coverage
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
windows-tests:
name: Windows Tests
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
- name: Run Tests
run: pnpm test
template:
name: Template
runs-on: ubuntu-22.04
@@ -102,7 +104,7 @@ jobs:
- vue
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup
uses: ./.github/actions/setup
@@ -31,12 +31,12 @@ jobs:
pnpm dlx wxt@latest --version
- name: Record VHS
uses: charmbracelet/vhs-action@v2.1.0
uses: charmbracelet/vhs-action@59641cdc7fadf3978db65eb8c6937ea2752f4ec3 # v2.1.0
with:
path: 'docs/tapes/init-demo.tape'
- name: Save recorded GIF
uses: stefanzweifel/git-auto-commit-action@v5
uses: stefanzweifel/git-auto-commit-action@01d77ca6cb089da1360e540865f7d035c95aa199 # v6.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/refs/heads/main/schema/markdownlint-config-schema.json",
"line-length": false,
"no-inline-html": false,
"first-line-heading": false
}
+9
View File
@@ -0,0 +1,9 @@
node_modules
.git
.output
dist
# Generated files
packages/wxt/README.md
packages/*/CHANGELOG.md
docs/api
+4 -4
View File
@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
aaronklinker1@gmail.com.
<aaronklinker1@gmail.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -116,7 +116,7 @@ the community.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
@@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
<https://www.contributor-covenant.org/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>.
+10 -6
View File
@@ -146,17 +146,21 @@ Releases are done with GitHub actions:
## Upgrading Dependencies
Use [`taze`](https://www.npmjs.com/package/taze) to upgrade dependencies throughout the entire monorepo.
WXT has custom rules around what dependencies can be upgraded. Use the `scripts/upgrade-deps.ts` script to upgrade dependencies and follow these rules.
```sh
pnpm dlx taze -r
pnpm tsx scripts/upgrade-deps.ts
```
Configuration is in [`taze.config.ts`](./taze.config.ts).
To see all the options, run:
```sh
pnpm tsx scripts/upgrade-deps.ts --help
```
## Install Unreleased Versions
This repo uses https://pkg.pr.new to publish versions of all it's packages for almost every commit. You can install them via:
This repo uses <https://pkg.pr.new> to publish versions of all it's packages for almost every commit. You can install them via:
```sh
npm i https://pkg.pr.new/[package-name]@[ref]
@@ -177,10 +181,10 @@ npm i https://pkg.pr.new/@wxt-dev/module-react@426f907
## Blog Posts
Anyone is welcome to submit a blog post on https://wxt.dev/blog!
Anyone is welcome to submit a blog post on <https://wxt.dev/blog>!
> [!NOTE]
> Before starting on a blog post, please message Aaron on Discord or start a discussion on GitHub to get permission to write about a topic, but most topics are welcome: Major version updates, tutorials, etc.
- **English only**: Blog posts should be written in English. Unfortunately, our maintainers doesn't have the bandwidth right now to translate our docs, let alone blog posts. Sorry 😓
- **English only**: Blog posts should be written in English. Unfortunately, our maintainers don't have the bandwidth right now to translate our docs, let alone blog posts. Sorry 😓
- **AI**: Please only use AI to translate or proof-read your blog post. Don't generate the whole thing... We don't want to publish that.
+18 -44
View File
@@ -1,50 +1,32 @@
<h1 align="center">
<img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
<div align="center">
<p align="center">
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="license | MIT" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="coverage" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a>
</p>
# <img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo"> WXT
<p align="center">
<span>Next-gen framework for developing web extensions.</span>
<br/>
<span>⚡</span>
<br/>
<q><i>It's like Nuxt, but for Web Extensions</i></q>
</p>
[![npm version](https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
[![downloads](https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
[![license | MIT](https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048)](https://github.com/wxt-dev/wxt/blob/main/LICENSE)
[![coverage](https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048)](https://codecov.io/github/wxt-dev/wxt)
<p align="center">
<a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
&bull;
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
&bull;
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
Next-gen framework for developing web extensions.<br/>⚡<br/><q><i>It's like Nuxt, but for Web Extensions</i></q>
[Get Started](https://wxt.dev/guide/installation.html) •
[Configuration](https://wxt.dev/api/config.html) •
[Examples](https://wxt.dev/examples.html) •
[Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md) •
[Discord](https://discord.gg/ZFsZqGery9)
</div>
![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
## Demo
https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94
<https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94>
## Quick Start
Bootstrap a new project:
<!-- automd:pm-x version="latest" name="wxt" args="init" -->
```sh
# npm
npx wxt@latest init
@@ -56,8 +38,6 @@ pnpm dlx wxt@latest init
bunx wxt@latest init
```
<!-- /automd -->
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
## Features
@@ -79,17 +59,11 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
<a href="https://github.com/sponsors/wxt-dev"><img alt="WXT Sponsors" src="https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg"></a>
[![WXT Sponsors](https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg)](https://github.com/sponsors/wxt-dev)
## Contributors
<!-- automd:contributors author="aklinker1" license="MIT" github="wxt-dev/wxt" -->
Published under the [MIT](https://github.com/wxt-dev/wxt/blob/main/LICENSE) license.
Made by [@aklinker1](https://github.com/aklinker1) and [community](https://github.com/wxt-dev/wxt/graphs/contributors) 💛
<br><br>
<a href="https://github.com/wxt-dev/wxt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
</a>
<!-- /automd -->
[![WXT contributors](https://contrib.rocks/image?repo=wxt-dev/wxt)](https://github.com/wxt-dev/wxt/graphs/contributors)
+18 -1
View File
@@ -4,7 +4,7 @@ import useListExtensionDetails, {
ChromeExtension,
} from '../composables/useListExtensionDetails';
// Add extension IDs here. Order doesn't matter, will be sorted by a combination of weekly active users and rating.
// Add extension IDs to end of the list. On the website, extensions will be sorted by a combination of weekly active users and rating.
// Change the commit message or PR title to: "docs: Added "[extension name]" to the homepage"
const chromeExtensionIds = [
'ocfdgncpifmegplaglcnglhioflaimkd', // GitHub: Better Line Counts
@@ -85,6 +85,23 @@ const chromeExtensionIds = [
'edkhpdceeinkcacjdgebjehipmnbomce', // NZBDonkey - The ultimate NZB file download tool
'cckggnbnimdbbpmdinkkgbbncopbloob', // WeChat Markdown Editor(微信 Markdown 编辑器)
'jcblcjolcojmfopefcighfmkkefbaofg', // Tab Grab
'eehmoikadcijkapfjocnhjclpbaindlb', // BrowserLens - https://browserlens.com/
'hfhellofkjebbchcdffmicekjdomkcmc', // Epic Games Library Extension
'gknigcbhlammoakmmdddkblknanpjiac', // Zen Analytics Pixel Tracker - zapt.web.app
'cnklededohhcbmjjdlbjdkkihkgoggol', // Crypto Pulse - Compose your newtab with nature images, widgets & realtime Crypto Price & Bitcoin RSS.
'miponnamafdenpgjemkknimgjfibicdc', // Youtube Video Scheduler
'nhmbcmalgpkjbomhlhgdicanmkkaajmg', // Chatslator: Livestream Chat Translator
'mbamjfdjbcdgpopfnkkmlohadbbnplhm', // 公众号阅读增强器 - https://wxreader.honwhy.wang
'hannhecbnjnnbbafffmogdlnajpcomek', // 토탐정
'ehboaofjncodknjkngdggmpdinhdoijp', // 2FAS Pass - https://2fas.com/
'hnjamiaoicaepbkhdoknhhcedjdocpkd', // Quick Prompt - https://github.com/wenyuanw/quick-prompt
'kacblhilkacgfnkjfodalohcnllcgmjd', // Add QR Code Generator Icon Back To Address Bar
'fkbdlogfdjmpfepbbbjcgcfbgbcfcnne', // Piwik PRO Tracking Helper
'nkbikckldmljjiiajklecmgmajgapbfl', // PIPX - Take Control of Picture-in-Picture, Automatically
'hgppdobcpkfkmiegekaglonjajeojmdd', // Browsely - AI-powered browser extension
'ehmoihnjgkdimihkhokkmfjdgomohjgm', // Filmbudd Pro - Simple, private and synced ratings and watch notes across all your devices
'alglchohmdikgdjhafiicilegegieafa', // MultiField CopyCat - Copy, Paste & Autofill Web Forms Instantly
'aamihahiiogceidpbnfgehacgiecephe', // ChatSight - Add Table of Contents to ChatGPT
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
@@ -35,6 +35,9 @@ export default function (ids: string[]) {
query,
variables: { ids },
}),
headers: {
'Content-Type': 'application/json',
},
})
.then(async (res) => {
isLoading.value = false;
@@ -8,16 +8,6 @@ outline: deep
During development, WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
:::danger
Chrome 137 removed support for the `--load-extension` CLI flag, which WXT relied on to open the browser with an extension installed. So this feature will not work for Chrome.
You have two options:
1. Install [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (which still supports the `--load-extension` flag) and [point the `chrome` binary to it](#set-browser-binaries), or
2. [Disable this feature](#disable-opening-browser) and manually load your extension
:::
## Config Files
You can configure browser startup in 3 places:
@@ -10,7 +10,7 @@ WXT does several pre-processing steps to try and prevent errors during this proc
2. Use `@webext-core/fake-browser` to create a fake version of the `chrome` and `browser` globals expected by extensions.
3. Pre-process the JS/TS code, stripping out the `main` function then tree-shaking unused code from the file
However, this process is not perfect. It doesn't setup all the globals found in the browser and the APIs may behave differently. As such, **_you should avoid using browser or extension APIs outside the `main` function of your entrypoints!_**
However, this process is not perfect. It doesn't setup all the globals found in the browser and the APIs may behave differently. As such, **_you should avoid using browser or extension APIs outside the `main` function of your entrypoints!_** See [Entrypoint Limitations](/guide/essentials/extension-apis#entrypoint-limitations) for more details.
:::tip
If you're running into errors while importing entrypoints, run `wxt prepare --debug` to see more details about this process. When debugging, WXT will print out the pre-processed code to help you identify issues.
@@ -4,7 +4,7 @@
WXT supports [dotenv files the same way as Vite](https://vite.dev/guide/env-and-mode.html#env-files). Create any of the following files:
```
```plaintext
.env
.env.local
.env.[mode]
@@ -65,7 +65,6 @@ To use environment variables in the manifest, you need to use the function synta
```ts
export default defineConfig({
extensionApi: 'chrome',
modules: ['@wxt-dev/module-vue'],
manifest: { // [!code --]
oauth2: { // [!code --]
@@ -81,3 +80,16 @@ export default defineConfig({
```
WXT can't load your `.env` files until after the config file has been loaded. So by using the function syntax for `manifest`, it defers creating the object until after the `.env` files are loaded into the process.
Note that Vite's runtime environment variables, like `import.meta.env.DEV`, will not be defined. Instead, access the `mode` like this:
```ts
export default defineConfig({
manifest: ({ mode }) => {
const isDev = mode === 'development';
console.log('Is development mode:', isDev);
// ...
},
});
```
+3 -1
View File
@@ -32,7 +32,7 @@ Because hooks can be defined in multiple places, including [WXT Modules](/guide/
To see the order for your project, run `wxt prepare --debug` flag and search for the "Hook execution order":
```
```plaintext
⚙ Hook execution order:
⚙ 1. wxt:built-in:unimport
⚙ 2. src/modules/auto-icons.ts
@@ -50,7 +50,9 @@ Changing execution order is simple:
📄 0.my-module.ts
📄 1.another-module.ts
```
- If you need to run an NPM module after user modules, just make it a user module and prefix the filename with a number!
```ts
// modules/2.i18n.ts
export { default } from '@wxt-dev/i18n/module';
+2 -1
View File
@@ -128,7 +128,7 @@ If a version is not present in your `package.json`, it defaults to `"0.0.0"`.
WXT automatically discovers your extension's icon by looking at files in the `public/` directory:
```
```plaintext
public/
├─ icon-16.png
├─ icon-24.png
@@ -235,6 +235,7 @@ If you want to use the `activeTab` permission or the `browser.action.onClicked`
1. Delete the [Popup entrypoint](/guide/essentials/entrypoints#popup) if it exists
2. Add the `action` key to your manifest:
```ts
export default defineConfig({
manifest: {
+1 -1
View File
@@ -20,7 +20,7 @@ export default defineAppConfig({
```
:::warning
This file is committed to the repo, so don't put any secrets here. Instead, use [Environment Variables](#environment-variables)
This file is committed to the repo, so don't put any secrets here. Instead, use [Environment Variables](/guide/essentials/config/environment-variables)
:::
To access runtime config, WXT provides the `useAppConfig` function:
+9 -8
View File
@@ -19,7 +19,7 @@ export default defineContentScript({
This object is responsible for tracking whether or not the content script's context is "invalidated". Most browsers, by default, do not stop content scripts if the extension is uninstalled, updated, or disabled. When this happens, content scripts start reporting this error:
```
```plaintext
Error: Extension context invalidated.
```
@@ -76,6 +76,7 @@ To create a standalone content script that only includes a CSS file:
1. Create the CSS file: `entrypoints/example.content.css`
2. Use the `build:manifestGenerated` hook to add the content script to the manifest:
```ts [wxt.config.ts]
export default defineConfig({
hooks: {
@@ -210,9 +211,7 @@ export default defineContentScript({
anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
mount(App, {
target: container,
});
return mount(App, { target: container });
},
onRemove: (app) => {
// Destroy the app when the UI is removed
@@ -395,11 +394,9 @@ export default defineContentScript({
anchor: 'body',
onMount: (container) => {
// Create the Svelte app inside the UI container
mount(App, {
target: container,
});
return mount(App, { target: container });
},
onRemove: () => {
onRemove: (app) => {
// Destroy the app when the UI is removed
unmount(app);
},
@@ -459,6 +456,7 @@ If you don't need to run your UI in the same frame as the content script, you ca
WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/utils/content-script-ui/iframe/functions/createIframeUi), which simplifies setting up the IFrame.
1. Create an HTML page that will be loaded into your IFrame:
```html
<!-- entrypoints/example-iframe.html -->
<!doctype html>
@@ -473,7 +471,9 @@ WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/utils/cont
</body>
</html>
```
1. Add the page to the manifest's `web_accessible_resources`:
```ts [wxt.config.ts]
export default defineConfig({
manifest: {
@@ -486,6 +486,7 @@ WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/utils/cont
},
});
```
1. Create and mount the IFrame:
```ts
+8 -8
View File
@@ -300,14 +300,14 @@ When defining content script entrypoints, keep in mind that WXT will import this
<!-- prettier-ignore -->
```ts
browser.runtime.onMessage.addListener((message) => { // [!code --]
// ... // [!code --]
}); // [!code --]
const container = document.createElement('div'); // [!code --]
document.body.append(container); // [!code --]
export default defineBackground(() => {
browser.runtime.onMessage.addListener((message) => { // [!code ++]
// ... // [!code ++]
}); // [!code ++]
export default defineContentScript({
main: function () {
const container = document.createElement('div'); // [!code ++]
document.body.append(container); // [!code ++]
},
});
```
@@ -580,7 +580,7 @@ body {
}
```
Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors
Follow Vite's guide to setup your preprocessor of choice: <https://vitejs.dev/guide/features.html#css-pre-processors>
CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs.
+1 -1
View File
@@ -1,6 +1,6 @@
# ES Modules
You source code should always be written as ESM. However, you have some control whether an entrypoint is bundled as ESM.
Your source code should always be written as ESM. However, you have some control whether an entrypoint is bundled as ESM.
## HTML Pages <Badge type="warning" text="≥0.0.1" />
+60
View File
@@ -75,3 +75,63 @@ Alternatively, if you're trying to use similar APIs under different names (to su
//
});
```
## Entrypoint Limitations
Because WXT imports your entrypoint files into a NodeJS, non-extension environment, the `chrome`/`browser` variables provided to extensions by the browser **will not be available**.
To prevent some basic errors, WXT polyfills these globals with the same in-memory, fake implementation it uses for testing: [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/fake-browser/installation/). However, not all the APIs have been implemented.
So it is extremely important to NEVER use `browser.*` extension APIs outside the main function of any JS/TS entrypoints (background, content scripts, and unlisted scripts). If you do, you'll see an error like this:
```plaintext
✖ Command failed after 440 ms
ERROR Browser.action.onClicked.addListener not implemented.
```
The fix is simple, just move your API usage into the entrypoint's main function:
:::code-group
```ts [background.ts]
browser.action.onClicked.addListener(() => {
/* ... */
}); // [!code --]
export default defineBackground(() => {
browser.action.onClicked.addListener(() => {
/* ... */
}); // [!code ++]
});
```
```ts [content.ts]
browser.runtime.onMessage.addListener(() => {
/* ... */
}); // [!code --]
export default defineContentScript({
main() {
browser.runtime.onMessage.addListener(() => {
/* ... */
}); // [!code ++]
},
});
```
```ts [unlisted.ts]
browser.runtime.onMessage.addListener(() => {
/* ... */
}); // [!code --]
export default defineUnlistedScript(() => {
browser.runtime.onMessage.addListener(() => {
/* ... */
}); // [!code ++]
});
```
:::
Read [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) for more technical details about this limitation.
+5 -1
View File
@@ -9,6 +9,7 @@ This page discusses how to setup internationalization using the vanilla `browser
## Usage
1. Add `default_locale` to your manifest:
```ts
export default defineConfig({
manifest: {
@@ -16,11 +17,12 @@ This page discusses how to setup internationalization using the vanilla `browser
},
});
```
2. Create `messages.json` files in the `public/` directory:
<!-- prettier-ignore -->
```html
📂 {srcDir}/
📂 {rootDir}/
📂 public/
📂 _locales/
📂 en/
@@ -41,9 +43,11 @@ This page discusses how to setup internationalization using the vanilla `browser
```
3. Get the translation:
```ts
browser.i18n.getMessage('helloWorld');
```
4. _Optional_: Add translations for extension name and description:
```json
+2 -2
View File
@@ -72,13 +72,13 @@ jobs:
submit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: 'pnpm'
- name: Install dependencies
@@ -62,6 +62,7 @@ Here are some examples:
```
- HTML file only built for all targets other than `chrome`:
```html
<!doctype html>
<html lang="en">
+1 -1
View File
@@ -105,4 +105,4 @@ To use a different framework, you will likely have to disable auto-imports, setu
It is possible to do, but will require a bit more setup. Refer to Vitest's setup for an example of how to setup a test environment:
https://github.com/wxt-dev/wxt/blob/main/packages/wxt/src/testing/wxt-vitest-plugin.ts
<https://github.com/wxt-dev/wxt/blob/main/packages/wxt/src/testing/wxt-vitest-plugin.ts>
+6 -1
View File
@@ -9,18 +9,23 @@ WXT provides a "module system" that let's you run code at different steps in the
There are two ways to add a module to your project:
1. **NPM**: install an NPM package, like [`@wxt-dev/auto-icons`](https://www.npmjs.com/package/@wxt-dev/auto-icons) and add it to your config:
```ts [wxt.config.ts]
export default defineConfig({
modules: ['@wxt-dev/auto-icons'],
});
```
> Searching for ["wxt module"](https://www.npmjs.com/search?q=wxt%20module) on NPM is a good way to find published WXT modules.
2. **Local**: add a file to your project's `modules/` directory:
```
```plaintext
<rootDir>/
modules/
my-module.ts
```
> To learn more about writing your own modules, read the [Writing Modules](/guide/essentials/wxt-modules) docs.
## Module Options
+22
View File
@@ -45,47 +45,63 @@ Once you've run the `dev` command, continue to [Next Steps](#next-steps)!
1. Create a new project
:::code-group
```sh [PNPM]
cd my-project
pnpm init
```
```sh [Bun]
cd my-project
bun init
```
```sh [NPM]
cd my-project
npm init
```
```sh [Yarn]
cd my-project
yarn init
```
:::
2. Install WXT:
:::code-group
```sh [PNPM]
pnpm i -D wxt
```
```sh [Bun]
bun i -D wxt
```
```sh [NPM]
npm i -D wxt
```
```sh [Yarn]
yarn add --dev wxt
```
:::
3. Add an entrypoint, `my-project/entrypoints/background.ts`:
:::code-group
```ts
export default defineBackground(() => {
console.log('Hello world!');
});
```
:::
4. Add scripts to your `package.json`:
```json [package.json]
{
"scripts": {
@@ -99,20 +115,26 @@ Once you've run the `dev` command, continue to [Next Steps](#next-steps)!
}
}
```
5. Run your extension in dev mode
:::code-group
```sh [PNPM]
pnpm dev
```
```sh [Bun]
bun run dev
```
```sh [NPM]
npm run dev
```
```sh [Yarn]
yarn dev
```
:::
WXT will automatically open a browser window with your extension installed.
+1 -1
View File
@@ -1,4 +1,4 @@
# Welcome to WXT!
# Welcome to WXT
WXT is a modern, open-source framework for building web extensions. Inspired by Nuxt, its goals are to:
+9 -10
View File
@@ -20,13 +20,13 @@ await chrome.scripting.getRegisteredContentScripts();
## How do I disable opening the browser automatically during development?
See https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser
See <https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser>
## How do I stay logged into a website during development?
See https://wxt.dev/guide/essentials/config/browser-startup.html#persist-data
See <https://wxt.dev/guide/essentials/config/browser-startup.html#persist-data>
## My component library doesn't work in content scripts!
## My component library doesn't work in content scripts
This is usually caused by one of two things (or both) when using `createShadowRootUi`:
@@ -36,7 +36,6 @@ This is usually caused by one of two things (or both) when using `createShadowRo
Some component libraries manually add CSS to the page by adding a `<style>` or `<link>` element. They place this element in the document's `<head>` by default. This causes your styles to be placed outside the `ShadowRoot` and it's isolation blocks the styles from being applied to your UI.
When a library does this, **you need to tell the library where to put its styles**. Here's the documentation for a few popular component libraries:
- Ant Design: [`StyleProvider`](https://ant.design/docs/react/compatible-style#shadow-dom-usage)
- Mantine: [`MantineProvider#getRootElement` and `MantineProvider#cssVariablesSelector`](https://mantine.dev/theming/mantine-provider/)
@@ -167,11 +166,11 @@ Both issues have the same fix: tell the library to put elements inside the `Shad
## Is there an LLM trained on WXT's docs that I chat with?
Yes! There's a "Ask AI" button in the bottom right of the page, try it out! Or visit https://knowledge.wxt.dev/ for a fullscreen experience.
Yes! There's a "Ask AI" button in the bottom right of the page, try it out! Or visit <https://knowledge.wxt.dev/> for a fullscreen experience.
Additionally, if you want to train your own model or provide context to your editor, you can use the LLM knowledge files hosted by the site:
https://wxt.dev/knowledge/index.json
<https://wxt.dev/knowledge/index.json>
You don't need to crawl the entire website, these files already contain all the relevant docs for training a LLM on WXT. But feel free to crawl it and generate your own files if you want!
@@ -179,11 +178,11 @@ You don't need to crawl the entire website, these files already contain all the
To run the WXT dev server in a devcontainer, but load the dev build of your extension in your browser:
1. **Bind-mount your project directory to your host**
1. **Bind-mount your project directory to your host**
If you're using VS Code, you can open your project folder with the `Dev Containers: Open Folder in Container...` command. This keeps the folder synchronized between your host and the devcontainer, ensuring that the extension `dist` directory remains accessible from the host.
2. **Disable auto-opening the browser**
WXT automatically opens your browser during development, but since you're running inside a container, it won't be able to access it. Follow the instructions [here](https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser) to disable browser auto-opening in your `wxt.config.ts`.
2. **Disable auto-opening the browser**
WXT automatically opens your browser during development, but since you're running inside a container, it won't be able to access it. Follow [the guide here](https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser) to disable browser auto-opening in your `wxt.config.ts`.
3. **Tell WXT to listen on all network interfaces**
3. **Tell WXT to listen on all network interfaces**
To enable hot-reloading, your extension has to connect to the WXT dev server running inside your container. WXT will only listen on `localhost` by default, which prevents connections from outside the devcontainer. To fix this you can instruct WXT to listen on all interfaces with `wxt --host 0.0.0.0`.
+50 -18
View File
@@ -9,14 +9,18 @@ outline: deep
To upgrade WXT to the latest major version:
1. Install it, skipping scripts so `wxt prepare` doesn't run - it will probably throw an error after a major version change (we'll run it later).
```sh
pnpm i wxt@latest --ignore-scripts
```
2. Follow the upgrade steps below to fix any breaking changes.
3. Run `wxt prepare`. It should succeed and type errors will go away afterwords.
```sh
pnpm wxt prepare
```
4. Manually test to make sure both dev mode and production builds work.
For minor or patch version updates, there are no special steps. Just update it with your package manager:
@@ -44,19 +48,36 @@ Read through all the changes once before updating your code.
WXT's `browser` no longer uses the `webextension-polyfill`!
:::details Why?
See https://github.com/wxt-dev/wxt/issues/784
See <https://github.com/wxt-dev/wxt/issues/784>
:::
To upgrade, you have two options:
1. **Stop using the polyfill** - No changes necessary, though you may want to do some manual testing to make sure everything continues to work. None of the early testers of this feature reported any runtime issues once they stopped using the polyfill.
- If you're already using `extensionApi: "chrome"`, then you don't need to test anything! You're already using the same `browser` object v0.20 provides by default.
1. **Stop using the polyfill**
- If you're already using `extensionApi: "chrome"`, then you're not using the polyfill and there is nothing to change!
- Otherwise there is only one change: `browser.runtime.onMessage` no longer supports using promises to return a response:
```ts
browser.runtime.onMessage.addListener(async () => { // [!code --]
const res = await someAsyncWork(); // [!code --]
return res; // [!code --]
browser.runtime.onMessage.addListener(async (_message, _sender, sendResponse) => { // [!code ++]
someAsyncWork().then((res) => { // [!code ++]
sendResponse(res); // [!code ++]
}); // [!code ++]
return true; // [!code ++]
});
```
2. **Continue using the polyfill** - If you want to keep using the polyfill, you can! One less thing to worry about during this upgrade.
- Install `webextension-polyfill` and WXT's [new polyfill module](https://www.npmjs.com/package/@wxt-dev/webextension-polyfill):
```sh
pnpm i webextension-polyfill @wxt-dev/webextension-polyfill
```
- Add the WXT module to your config:
```ts [wxt.config.ts]
export default defineConfig({
modules: ['@wxt-dev/webextension-polyfill'],
@@ -106,9 +127,9 @@ The default location for the `public/` and `modules/` directories have changed t
- If you follow the default folder structure, you don't need to make any changes.
- If you set a custom `srcDir`, you have two options:
1. Move the your `public/` and `modules/` directories to the project root:
<!-- prettier-ignore -->
```html
1. Move the your `public/` and `modules/` directories to the project root:
<!-- prettier-ignore -->
```html
📂 {rootDir}/
📁 modules/ <!-- [!code ++] -->
📁 public/ <!-- [!code ++] -->
@@ -121,14 +142,16 @@ The default location for the `public/` and `modules/` directories have changed t
📄 app.config.ts
📄 wxt.config.ts
```
2. Keep the folders in the same place and update your project config:
```ts [wxt.config.ts]
export default defineConfig({
srcDir: 'src',
publicDir: 'src/public', // [!code ++]
modulesDir: 'src/modules', // [!code ++]
});
```
2. Keep the folders in the same place and update your project config:
```ts [wxt.config.ts]
export default defineConfig({
srcDir: 'src',
publicDir: 'src/public', // [!code ++]
modulesDir: 'src/modules', // [!code ++]
});
```
### Import Path Changes and `#imports`
@@ -230,6 +253,7 @@ If you've previously loaded the extension into your browser manually for develop
### Deprecated APIs Removed
- `entrypointLoader` option: WXT now uses `vite-node` for importing entrypoints during the build process.
<!-- markdownlint-disable-next-line MD051 -->
> This was deprecated in v0.19.0, see the [v0.19 section](#v0-18-5-rarr-v0-19-0) for migration steps.
- `transformManifest` option: Use the `build:manifestGenerated` hook to transform the manifest instead:
<!-- prettier-ignore -->
@@ -251,6 +275,7 @@ If you've previously loaded the extension into your browser manually for develop
To improve consistency with the `web-ext.config.ts` filename, the "runner" API and config options have been renamed. You can continue using the old names, but they have been deprecated and will be removed in a future version:
1. The `runner` option has been renamed to `webExt`:
```ts [wxt.config.ts]
export default defineConfig({
runner: { // [!code --]
@@ -259,12 +284,16 @@ To improve consistency with the `web-ext.config.ts` filename, the "runner" API a
},
});
```
2. `defineRunnerConfig` has been renamed to `defineWebExtConfig`:
```ts [web-ext.config.ts]
import { defineRunnerConfig } from 'wxt'; // [!code --]
import { defineWebExtConfig } from 'wxt'; // [!code ++]
```
3. The `ExtensionRunnerConfig` type has been renamed to `WebExtConfig`
```ts
import type { ExtensionRunnerConfig } from 'wxt'; // [!code --]
import type { WebExtConfig } from 'wxt'; // [!code ++]
@@ -342,7 +371,7 @@ WXT no longer ships with Common JS support. If you're using CJS, here's your mig
1. Add [`"type": "module"`](https://nodejs.org/api/packages.html#type) to your `package.json`.
2. Change the file extension of any `.js` files that use CJS syntax to `.cjs`, or update them to use EMS syntax.
Vite also provides steps for migrating to ESM. Check them out for more details: https://vitejs.dev/guide/migration#deprecate-cjs-node-api
Vite also provides steps for migrating to ESM. Check them out for more details: <https://vitejs.dev/guide/migration#deprecate-cjs-node-api>
## v0.18.0 &rarr; v0.18.5
@@ -357,11 +386,13 @@ If you already have `<srcDir>/modules` or `<srcDir>/Modules` directory, `wxt pre
You have two options:
1. [Recommended] Keep your files where they are and tell WXT to look in a different folder:
```ts [wxt.config.ts]
export default defineConfig({
modulesDir: 'wxt-modules', // defaults to "modules"
});
```
2. Rename your `modules` directory to something else.
## v0.17.0 &rarr; v0.18.0
@@ -415,7 +446,7 @@ item.watch((newValue: number, oldValue: number) => { // [!code ++]
JS entrypoints in the output directory have been moved. Unless you're doing some kind of post-build work referencing files, you don't have to make any changes.
```
```plaintext
.output/
<target>/
chunks/
@@ -445,7 +476,7 @@ export default defineConfig({
### Renamed Undocumented Constants
Renamed undocumented constants for detecting the build config at runtime in [#380](https://github.com/wxt-dev/wxt/pull/380). Now documented here: https://wxt.dev/guide/multiple-browsers.html#runtime
Renamed undocumented constants for detecting the build config at runtime in [#380](https://github.com/wxt-dev/wxt/pull/380). Now documented here: <https://wxt.dev/guide/multiple-browsers.html#runtime>
- `__BROWSER__` → `import.meta.env.BROWSER`
- `__COMMAND__` → `import.meta.env.COMMAND`
@@ -472,7 +503,7 @@ Renamed undocumented constants for detecting the build config at runtime in [#38
### New `wxt/storage` APIs
`wxt/storage` no longer relies on [`unstorage`](https://www.npmjs.com/package/unstorage). Some `unstorage` APIs, like `prefixStorage`, have been removed, while others, like `snapshot`, are methods on the new `storage` object. Most of the standard usage remains the same. See https://wxt.dev/guide/storage and https://wxt.dev/api/reference/wxt/storage/ for more details ([#300](https://github.com/wxt-dev/wxt/pull/300))
`wxt/storage` no longer relies on [`unstorage`](https://www.npmjs.com/package/unstorage). Some `unstorage` APIs, like `prefixStorage`, have been removed, while others, like `snapshot`, are methods on the new `storage` object. Most of the standard usage remains the same. See <https://wxt.dev/guide/storage> and <https://wxt.dev/api/reference/wxt/storage/> for more details ([#300](https://github.com/wxt-dev/wxt/pull/300))
## v0.11.0 &rarr; v0.12.0
@@ -482,6 +513,7 @@ Renamed undocumented constants for detecting the build config at runtime in [#38
- If you use auto-imports, no changes are required.
- If you have disabled auto-imports, you'll need to manually update your import statements:
```ts
import { defineBackground, defineContentScript } from 'wxt/client'; // [!code --]
import { defineBackground, defineContentScript } from 'wxt/sandbox'; // [!code ++]
+21 -18
View File
@@ -4,7 +4,7 @@
"engines": {
"node": ">=18.20.3"
},
"packageManager": "pnpm@10.10.0",
"packageManager": "pnpm@10.15.1",
"scripts": {
"check": "check && pnpm -r --sequential run check",
"test": "pnpm -r --sequential run test run",
@@ -17,33 +17,36 @@
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.4",
"@aklinker1/check": "2.0.0",
"@commitlint/config-conventional": "^19.8.0",
"@commitlint/types": "^19.8.0",
"@aklinker1/buildc": "^1.1.5",
"@aklinker1/check": "^2.1.0",
"@commitlint/config-conventional": "^19.8.1",
"@commitlint/types": "^19.8.1",
"@types/fs-extra": "^11.0.4",
"@vitest/coverage-v8": "^3.1.2",
"changelogen": "^0.6.1",
"@types/semver": "^7.7.1",
"@vitest/coverage-v8": "^3.2.4",
"changelogen": "^0.6.2",
"consola": "^3.4.2",
"fast-glob": "^3.3.3",
"feed": "^4.2.2",
"fs-extra": "^11.3.0",
"lint-staged": "^15.5.1",
"feed": "^5.1.0",
"fs-extra": "^11.3.1",
"lint-staged": "^16.1.6",
"markdown-it-footnote": "^4.0.0",
"nano-spawn": "^0.2.0",
"prettier": "^3.5.3",
"simple-git-hooks": "^2.13.0",
"tsx": "4.19.4",
"markdownlint-cli": "^0.45.0",
"nano-spawn": "^1.0.2",
"prettier": "^3.6.2",
"semver": "^7.7.2",
"simple-git-hooks": "^2.13.1",
"tsx": "4.20.5",
"typedoc": "^0.25.4",
"typedoc-plugin-frontmatter": "^1.3.0",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.8.3",
"vitepress": "^1.6.3",
"typescript": "^5.9.2",
"vitepress": "^1.6.4",
"vitepress-knowledge": "^0.4.1",
"vitepress-plugin-group-icons": "^1.5.2",
"vitepress-plugin-group-icons": "^1.6.3",
"vitest-mock-extended": "^3.1.0",
"vue": "^3.5.13",
"vue": "^3.5.21",
"wxt": "workspace:*"
},
"simple-git-hooks": {
+29 -1
View File
@@ -1,6 +1,34 @@
# Changelog
## v0.5.1
[compare changes](https://github.com/wxt-dev/wxt/compare/analytics-v0.5.0...analytics-v0.5.1)
### 🚀 Enhancements
- Integrate latest measurement protocol changes ([#1767](https://github.com/wxt-dev/wxt/pull/1767))
### 🩹 Fixes
- Use `@wxt-dev/browser` instead of `@types/chrome` ([#1645](https://github.com/wxt-dev/wxt/pull/1645))
### 🏡 Chore
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
- Change browser workspace dependency to `^` ([c7335add](https://github.com/wxt-dev/wxt/commit/c7335add))
- Fix auto-fixable `markdownlint` errors ([#1710](https://github.com/wxt-dev/wxt/pull/1710))
- **deps:** Upgrade typescript from 5.8.3 to 5.9.2 ([a6eef643](https://github.com/wxt-dev/wxt/commit/a6eef643))
- Create script for managing dependency upgrades ([#1875](https://github.com/wxt-dev/wxt/pull/1875))
- **deps:** Upgrade all dev dependencies ([#1876](https://github.com/wxt-dev/wxt/pull/1876))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Tanishq-aggarwal ([@tanishq-aggarwal](https://github.com/tanishq-aggarwal))
## v0.5.0
[⚠️ breaking changes](https://wxt.dev/guide/resources/upgrading.html) &bull; [compare changes](https://github.com/wxt-dev/wxt/compare/analytics-v0.4.1...analytics-v0.5.0)
### 🩹 Fixes
@@ -9,4 +37,4 @@
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Aaron ([@aklinker1](https://github.com/aklinker1))
+8
View File
@@ -10,15 +10,19 @@ Report analytics events from your web extension extension.
## Install With WXT
1. Install the NPM package:
```bash
pnpm i @wxt-dev/analytics
```
2. In your `wxt.config.ts`, add the WXT module:
```ts
export default defineConfig({
modules: ['@wxt-dev/analytics/module'],
});
```
3. In your `<srcDir>/app.config.ts`, add a provider:
```ts
@@ -49,9 +53,11 @@ Report analytics events from your web extension extension.
## Install Without WXT
1. Install the NPM package:
```bash
pnpm i @wxt-dev/analytics
```
2. Create an `analytics` instance:
```ts
@@ -66,10 +72,12 @@ Report analytics events from your web extension extension.
```
3. Import your analytics module in the background to initialize the message listener:
```ts
// background.ts
import './utils/analytics';
```
4. Then use your `analytics` instance to report events:
```ts
@@ -46,6 +46,7 @@ export const googleAnalytics4 =
ad_personalization: 'DENIED',
},
user_properties: mappedUserProperties,
user_agent: navigator.userAgent,
events: [
{
name: eventName,
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@wxt-dev/analytics",
"version": "0.5.0",
"version": "0.5.1",
"description": "Add analytics to your web extension",
"repository": {
"type": "git",
@@ -51,11 +51,11 @@
"wxt": ">=0.20.0"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"@types/ua-parser-js": "^0.7.39",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
},
"dependencies": {
+32
View File
@@ -1,5 +1,37 @@
# Changelog
## v1.1.0
[compare changes](https://github.com/wxt-dev/wxt/compare/auto-icons-v1.0.2...auto-icons-v1.1.0)
### 🚀 Enhancements
- Add overlay option for dev icons ([#1825](https://github.com/wxt-dev/wxt/pull/1825))
### 📖 Documentation
- Rewrite and restructure the documentation website ([#933](https://github.com/wxt-dev/wxt/pull/933))
- Use full URLs in README so they work on the docs site ([d20793d5](https://github.com/wxt-dev/wxt/commit/d20793d5))
- Add SVG compatibility note ([#1830](https://github.com/wxt-dev/wxt/pull/1830))
### 🏡 Chore
- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947))
- **deps:** Bump sharp from 0.33.4 to 0.33.5 ([#959](https://github.com/wxt-dev/wxt/pull/959))
- Upgrade all non-major dependencies ([#1040](https://github.com/wxt-dev/wxt/pull/1040))
- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164))
- **deps:** Bump dev and non-breaking major dependencies ([#1167](https://github.com/wxt-dev/wxt/pull/1167))
- Use PNPM 10's new catelog feature ([#1493](https://github.com/wxt-dev/wxt/pull/1493))
- Move production dependencies to PNPM 10 catelog ([#1494](https://github.com/wxt-dev/wxt/pull/1494))
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
### ❤️ Contributors
- Typed SIGTERM ([@typed-sigterm](https://github.com/typed-sigterm))
- Kuba ([@zizzfizzix](https://github.com/zizzfizzix))
- Aaron ([@aklinker1](https://github.com/aklinker1))
## v1.0.2
[compare changes](https://github.com/wxt-dev/wxt/compare/auto-icons-v1.0.1...auto-icons-v1.0.2)
+2 -1
View File
@@ -5,7 +5,8 @@
## Features
- Generate extension icons with the correct sizes
- Make the icon greyscale during development
- Make the icon greyscale or include a visible overlay during development
- SVG is supported
## Usage
+6 -6
View File
@@ -24,7 +24,7 @@
}
],
"license": "MIT",
"version": "1.0.2",
"version": "1.1.0",
"type": "module",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
@@ -46,15 +46,15 @@
},
"dependencies": {
"defu": "^6.1.4",
"fs-extra": "^11.3.0",
"fs-extra": "^11.3.1",
"sharp": "^0.34.1"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"oxlint": "^0.16.8",
"@aklinker1/check": "^2.1.0",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
}
}
+59 -12
View File
@@ -14,11 +14,24 @@ export default defineWxtModule<AutoIconsOptions>({
{
enabled: true,
baseIconPath: resolve(wxt.config.srcDir, 'assets/icon.png'),
grayscaleOnDevelopment: true,
developmentIndicator: 'grayscale',
sizes: [128, 48, 32, 16],
},
);
// Backward compatibility for the deprecated option
if (options?.grayscaleOnDevelopment !== undefined) {
wxt.logger.warn(
'`[auto-icons]` "grayscaleOnDevelopment" is deprecated. Use "developmentIndicator" instead.',
);
if (options?.developmentIndicator === undefined) {
parsedOptions.developmentIndicator = options!.grayscaleOnDevelopment
? 'grayscale'
: false;
}
}
const resolvedPath = resolve(wxt.config.srcDir, parsedOptions.baseIconPath);
if (!parsedOptions.enabled)
@@ -42,21 +55,44 @@ export default defineWxtModule<AutoIconsOptions>({
});
wxt.hooks.hook('build:done', async (wxt, output) => {
const image = sharp(resolvedPath).png();
if (
wxt.config.mode === 'development' &&
parsedOptions.grayscaleOnDevelopment
) {
image.grayscale();
}
const outputFolder = wxt.config.outDir;
for (const size of parsedOptions.sizes) {
const resized = image.resize(size);
const resizedImage = sharp(resolvedPath).resize(size).png();
if (wxt.config.mode === 'development') {
if (parsedOptions.developmentIndicator === 'grayscale') {
resizedImage.grayscale();
} else if (parsedOptions.developmentIndicator === 'overlay') {
// Helper to build an overlay that places a yellow rectangle at the bottom
// of the icon with the text "DEV" in black. The overlay has the same
// dimensions as the icon so we can composite it with default gravity.
const buildDevOverlay = (size: number) => {
const rectHeight = Math.round(size * 0.5);
const fontSize = Math.round(size * 0.35);
return Buffer.from(`<?xml version="1.0" encoding="UTF-8"?>
<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="${size - rectHeight}" width="${size}" height="${rectHeight}" fill="#ffff00" />
<text x="${size / 2}" y="${size - rectHeight / 2}" font-family="Arial, Helvetica, sans-serif" font-size="${fontSize}" font-weight="bold" fill="black" text-anchor="middle" dominant-baseline="middle">DEV</text>
</svg>`);
};
const overlayBuffer = await sharp(buildDevOverlay(size))
.png()
.toBuffer();
resizedImage.composite([
{
input: overlayBuffer,
left: 0,
top: 0,
},
]);
}
}
ensureDir(resolve(outputFolder, 'icons'));
await resized.toFile(resolve(outputFolder, `icons/${size}.png`));
await resizedImage.toFile(resolve(outputFolder, `icons/${size}.png`));
output.publicAssets.push({
type: 'asset',
@@ -89,9 +125,20 @@ export interface AutoIconsOptions {
* @default "<srcDir>/assets/icon.png"
*/
baseIconPath?: string;
/**
* Apply a visual indicator to the icon when running in development mode.
*
* "grayscale" converts the icon to grayscale.
* "overlay" covers the bottom half with a yellow rectangle and writes "DEV" in black text.
* Set to `false` to disable any indicator.
*
* @default "grayscale"
*/
developmentIndicator?: 'grayscale' | 'overlay' | false;
/**
* Grayscale the image when in development mode to indicate development
* @default true
* @deprecated Use `developmentIndicator` instead
*/
grayscaleOnDevelopment?: boolean;
/**
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@wxt-dev/browser",
"description": "Provides a cross-browser API for using extension APIs and types based on @types/chrome",
"version": "0.0.326",
"version": "0.1.4",
"type": "module",
"main": "src/index.mjs",
"types": "src/index.d.ts",
@@ -23,12 +23,12 @@
"src"
],
"devDependencies": {
"@types/chrome": "0.0.326",
"fs-extra": "^11.3.0",
"nano-spawn": "^0.2.0",
"tsx": "4.19.4",
"typescript": "^5.8.3",
"vitest": "^3.1.2"
"@types/chrome": "0.1.6",
"fs-extra": "^11.3.1",
"nano-spawn": "^1.0.2",
"tsx": "4.20.5",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
},
"dependencies": {
"@types/filesystem": "*",
+1742 -2673
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -24,10 +24,10 @@
],
"devDependencies": {
"@types/chrome": "{{chromeTypesVersion}}",
"fs-extra": "^11.3.0",
"nano-spawn": "^0.2.0",
"fs-extra": "^11.3.1",
"nano-spawn": "^1.0.2",
"tsx": "4.19.4",
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"vitest": "^3.1.2"
}
}
+2 -2
View File
@@ -6,7 +6,7 @@
1. Simple messages file format
2. Plural form support
3. Integrates with the [I18n Ally VS Code extension](#vscode)
3. Integrates with the [I18n Ally VS Code extension](#vs-code)
It also provides several benefits over other non-web extension specific i18n packages:
@@ -81,7 +81,7 @@ And you're done! Using WXT, you get type-safety out of the box.
> [!NOTE]
> For the best DX, you should to integrate `@wxt-dev/i18n` into your build process. This enables:
>
> 1. Plural forms
> 2. Simple messages file format
> 3. Type safety
+5 -5
View File
@@ -40,13 +40,13 @@
}
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"@types/node": "^20.17.6",
"oxlint": "^0.16.8",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"vitest": "^3.1.2",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"vitest": "^3.2.4",
"vitest-plugin-random-seed": "^1.1.1",
"wxt": "workspace:*"
},
+26
View File
@@ -1,5 +1,31 @@
# Changelog
## v1.1.4
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.1.3...module-react-v1.1.4)
### 🩹 Fixes
- Add support for WXT v0.20.0 ([c9dca022](https://github.com/wxt-dev/wxt/commit/c9dca022))
### 🏡 Chore
- Add funding links to `package.json` files ([#1446](https://github.com/wxt-dev/wxt/pull/1446))
- Use PNPM 10's new catelog feature ([#1493](https://github.com/wxt-dev/wxt/pull/1493))
- Move production dependencies to PNPM 10 catelog ([#1494](https://github.com/wxt-dev/wxt/pull/1494))
- **deps:** Upgrade to Vite 6 and related dependencies ([#1496](https://github.com/wxt-dev/wxt/pull/1496))
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
- **deps:** Update all dependencies ([#1648](https://github.com/wxt-dev/wxt/pull/1648))
- **deps:** Upgrade typescript from 5.8.3 to 5.9.2 ([a6eef643](https://github.com/wxt-dev/wxt/commit/a6eef643))
- Create script for managing dependency upgrades ([#1875](https://github.com/wxt-dev/wxt/pull/1875))
- **deps:** Upgrade all dev dependencies ([#1876](https://github.com/wxt-dev/wxt/pull/1876))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Okinea Dev <hi@okinea.dev>
## v1.1.3
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.1.2...module-react-v1.1.3)
+8 -8
View File
@@ -18,7 +18,7 @@
},
"license": "MIT",
"funding": "https://github.com/sponsors/wxt-dev",
"version": "1.1.3",
"version": "1.1.4",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -51,14 +51,14 @@
"@vitejs/plugin-react": "^4.4.1"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@aklinker1/check": "^2.1.0",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.9",
"publint": "^0.3.12",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
}
}
+28
View File
@@ -1,5 +1,33 @@
# Changelog
## v1.1.4
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.1.3...module-solid-v1.1.4)
### 🩹 Fixes
- Add support for WXT v0.20.0 ([c9dca022](https://github.com/wxt-dev/wxt/commit/c9dca022))
### 🏡 Chore
- **deps-dev:** Bump solid-js from 1.9.3 to 1.9.4 ([#1391](https://github.com/wxt-dev/wxt/pull/1391))
- Add funding links to `package.json` files ([#1446](https://github.com/wxt-dev/wxt/pull/1446))
- **deps:** Bump vite-plugin-solid from 2.10.2 to 2.11.6 ([#1491](https://github.com/wxt-dev/wxt/pull/1491))
- Use PNPM 10's new catelog feature ([#1493](https://github.com/wxt-dev/wxt/pull/1493))
- Move production dependencies to PNPM 10 catelog ([#1494](https://github.com/wxt-dev/wxt/pull/1494))
- **deps:** Upgrade to Vite 6 and related dependencies ([#1496](https://github.com/wxt-dev/wxt/pull/1496))
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
- **deps:** Update all dependencies ([#1648](https://github.com/wxt-dev/wxt/pull/1648))
- **deps:** Upgrade typescript from 5.8.3 to 5.9.2 ([a6eef643](https://github.com/wxt-dev/wxt/commit/a6eef643))
- Create script for managing dependency upgrades ([#1875](https://github.com/wxt-dev/wxt/pull/1875))
- **deps:** Upgrade all dev dependencies ([#1876](https://github.com/wxt-dev/wxt/pull/1876))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Okinea Dev <hi@okinea.dev>
## v1.1.3
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.1.2...module-solid-v1.1.3)
+5 -5
View File
@@ -18,7 +18,7 @@
},
"license": "MIT",
"funding": "https://github.com/sponsors/wxt-dev",
"version": "1.1.3",
"version": "1.1.4",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -51,11 +51,11 @@
"vite-plugin-solid": "^2.11.6"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"publint": "^0.3.12",
"solid-js": "^1.9.6",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"solid-js": "^1.9.9",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
}
}
+4 -4
View File
@@ -47,13 +47,13 @@
"svelte": ">=5"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0"
"@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
}
}
+4 -4
View File
@@ -46,13 +46,13 @@
"wxt": ">=0.19.16"
},
"dependencies": {
"@vitejs/plugin-vue": "^5.2.3"
"@vitejs/plugin-vue": "^5.2.3 || ^6.0.0"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"wxt": "workspace:*"
}
}
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v0.1.1
[compare changes](https://github.com/wxt-dev/wxt/compare/runner-v0.1.0...runner-v0.1.1)
+6 -4
View File
@@ -2,7 +2,9 @@
Programmatically open a browser and install a web extension from a local directory.
###### With WXT
## Usage
### With WXT
> [!WARNING]
> This package is intended to replace [`web-ext`](https://github.com/mozilla/web-ext) in the future, but it is not ready at the moment. Once it's ready for testing in WXT, more details will be added here.
@@ -16,7 +18,7 @@ export default defineRunnerConfig({
});
```
###### JS API
### JS API
```ts
import { run } from '@wxt-dev/runner';
@@ -92,7 +94,7 @@ await run({
These presets configure different flags for different operating systems when spawning the browser process.
If you want to customize your data persistence beyond what these presets define, [you can override the browser flags yourself](#Arguments) to configure persistence.
If you want to customize your data persistence beyond what these presets define, [you can override the browser flags yourself](#arguments) to configure persistence.
### Browser Binaries
@@ -153,7 +155,7 @@ To see debug logs, set the `DEBUG` env var to `"@wxt-dev/runner"`. This will pri
<details>
<summary>Example debug output</summary>
```
```plaintext
@wxt-dev/runner:options User options: { extensionDir: 'demo-extension', target: undefined }
@wxt-dev/runner:options Resolved options: {
browserBinary: '/usr/bin/chromium',
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@wxt-dev/runner",
"description": "Launch Chrome and Firefox with a web extension installed",
"version": "0.1.0",
"version": "0.1.1",
"type": "module",
"repository": {
"type": "git",
@@ -29,13 +29,13 @@
},
"dependencies": {},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"oxlint": "^0.16.8",
"@aklinker1/check": "^2.1.0",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"tsx": "4.19.4",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"vitest": "^3.1.2"
"tsx": "4.20.5",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"vitest": "^3.2.4"
},
"main": "dist/index.mjs",
"types": "dist/index.d.ts",
+25 -2
View File
@@ -1,11 +1,13 @@
export type BrowserPlatform = 'windows' | 'mac' | 'linux';
export type KnownTarget =
| 'arc'
| 'chromium'
| 'chrome'
| 'chrome-beta'
| 'chrome-dev'
| 'chrome-canary'
| 'dia'
| 'edge'
| 'edge-beta'
| 'edge-dev'
@@ -21,6 +23,11 @@ export const KNOWN_BROWSER_PATHS: Record<
> = {
// Chromium based targets
arc: {
mac: ['/Applications/Arc.app/Contents/MacOS/Arc'],
linux: [],
windows: [],
},
chromium: {
mac: [],
linux: [
@@ -30,7 +37,10 @@ export const KNOWN_BROWSER_PATHS: Record<
windows: [],
},
chrome: {
mac: ['/Applications/Chrome.app/Contents/MacOS/Google Chrome'],
mac: [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chrome.app/Contents/MacOS/Google Chrome',
],
linux: [],
windows: ['C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'],
},
@@ -42,6 +52,7 @@ export const KNOWN_BROWSER_PATHS: Record<
'chrome-canary': {
mac: [
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
'/Applications/Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
],
linux: [],
windows: [],
@@ -51,6 +62,11 @@ export const KNOWN_BROWSER_PATHS: Record<
linux: [],
windows: [],
},
dia: {
mac: ['/Applications/Dia.app/Contents/MacOS/Dia'],
linux: [],
windows: [],
},
edge: {
mac: [],
linux: [],
@@ -96,7 +112,12 @@ export const KNOWN_BROWSER_PATHS: Record<
windows: [],
},
zen: {
mac: ['/Applications/Zen Browser.app/Contents/MacOS/zen'],
mac: [
'/Applications/Zen Browser.app/Contents/MacOS/zen',
// Homebrew Cask
// https://github.com/Homebrew/homebrew-cask/blob/main/Casks/z/zen.rb#L23C13-L23C19
'/Applications/Zen.app/Contents/MacOS/zen',
],
linux: [],
windows: [],
},
@@ -107,10 +128,12 @@ export const KNOWN_BROWSER_PATHS: Record<
*/
export const FALLBACK_TARGETS: Partial<Record<KnownTarget, KnownTarget[]>> = {
chrome: [
'arc',
'chromium',
'chrome-canary',
'chrome-beta',
'chrome-dev',
'dia',
'edge',
'edge-canary',
'edge-beta',
+5 -5
View File
@@ -35,13 +35,13 @@
"dequal": "^2.0.3"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"@webext-core/fake-browser": "^1.3.2",
"oxlint": "^0.16.8",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"vitest": "^3.1.2"
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"vitest": "^3.2.4"
},
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
+4 -4
View File
@@ -40,11 +40,11 @@
"wxt": ">=0.19.0"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"oxlint": "^0.16.8",
"@aklinker1/check": "^2.1.0",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
"wxt": "workspace:*"
},
+3 -3
View File
@@ -45,11 +45,11 @@
"wxt": ">=0.20.0"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@aklinker1/check": "^2.1.0",
"@types/webextension-polyfill": "^0.12.3",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"webextension-polyfill": "^0.12.0",
"wxt": "workspace:*"
}
+7 -7
View File
@@ -18,18 +18,18 @@
},
"dependencies": {
"@wxt-dev/i18n": "workspace:*",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.9",
"@wxt-dev/auto-icons": "workspace:*",
"@wxt-dev/unocss": "workspace:*",
"sass": "^1.87.0",
"typescript": "^5.8.3",
"sass": "^1.92.0",
"typescript": "^5.9.2",
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
"vitest": "^3.1.2",
"vitest": "^3.2.4",
"vitest-plugin-random-seed": "^1.1.1",
"wxt": "workspace:*"
},
+66
View File
@@ -1,5 +1,71 @@
# Changelog
## v0.20.8
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.7...wxt-v0.20.8)
### 🩹 Fixes
- Support http server websocket inside containers ([#1707](https://github.com/wxt-dev/wxt/pull/1707))
- Recreating keyboardShortcuts interface on file changes. ([#1465](https://github.com/wxt-dev/wxt/pull/1465))
- Fix bad regex from #1707 ([#1712](https://github.com/wxt-dev/wxt/pull/1712), [#1707](https://github.com/wxt-dev/wxt/issues/1707))
- Fix bundle variable assignment error with `rolldown-vite` ([#1715](https://github.com/wxt-dev/wxt/pull/1715))
- Support negation patterns when including/excluding files from ZIP files ([#1517](https://github.com/wxt-dev/wxt/pull/1517))
- **rolldown-compat:** Only apply footer to content and unlisted scripts via plugin ([#1793](https://github.com/wxt-dev/wxt/pull/1793))
- Init project should allow the .git folder to exist ([#1731](https://github.com/wxt-dev/wxt/pull/1731))
- Adding missing chromiumPort parameter in web-ext.ts to enable fixed port debugging ([#1818](https://github.com/wxt-dev/wxt/pull/1818))
### 📖 Documentation
- Fix API reference sidebar by naming new module ([eb0bffb5](https://github.com/wxt-dev/wxt/commit/eb0bffb5))
- Update JSDoc with ways to cancel timeouts and intervals set by content script context ([030f23d2](https://github.com/wxt-dev/wxt/commit/030f23d2))
### 🏡 Chore
- Fix auto-fixable `markdownlint` errors ([#1710](https://github.com/wxt-dev/wxt/pull/1710))
- Remove automd ([7d25110a](https://github.com/wxt-dev/wxt/commit/7d25110a))
- Wxt & @wxt-dev/module-vue support Vite 7 ([#1771](https://github.com/wxt-dev/wxt/pull/1771))
- Fix type error ([0333ce5c](https://github.com/wxt-dev/wxt/commit/0333ce5c))
### 🤖 CI
- Use matrix for test jobs ([#1708](https://github.com/wxt-dev/wxt/pull/1708))
### ❤️ Contributors
- Sylva <sylvain.lavabre@live.fr>
- Btea ([@btea](https://github.com/btea))
- Patryk Kuniczak ([@PatrykKuniczak](https://github.com/PatrykKuniczak))
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Ayu ([@ayu-exorcist](https://github.com/ayu-exorcist))
- Nishu ([@nishu-murmu](https://github.com/nishu-murmu))
## v0.20.7
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.6...wxt-v0.20.7)
### 🚀 Enhancements
- Add `@font-face` to be processed by `splitShadowRootCss` ([#1635](https://github.com/wxt-dev/wxt/pull/1635))
- Add DisableLoadExtensionCommandLineSwitch flag to Chromium runner ([#1698](https://github.com/wxt-dev/wxt/pull/1698))
### 🩹 Fixes
- Improve CSS reset inside shadow roots ([da5cd325](https://github.com/wxt-dev/wxt/commit/da5cd325))
### 🏡 Chore
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
- **deps:** Update all dependencies ([#1648](https://github.com/wxt-dev/wxt/pull/1648))
- Change browser workspace dependency to `^` ([c7335add](https://github.com/wxt-dev/wxt/commit/c7335add))
### ❤️ Contributors
- Richard Lee ([@dlackty](https://github.com/dlackty))
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Seaders ([@seaders](https://github.com/seaders))
## v0.20.6
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.5...wxt-v0.20.6)
+18 -44
View File
@@ -1,51 +1,33 @@
<!-- DO NOT EDIT, THIS FILE WAS GENERATED BY '../../scripts/generate-readmes.sh' -->
<h1 align="center">
<img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
<div align="center">
<p align="center">
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="license | MIT" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
<span> </span>
<a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="coverage" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a>
</p>
# <img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo"> WXT
<p align="center">
<span>Next-gen framework for developing web extensions.</span>
<br/>
<span>⚡</span>
<br/>
<q><i>It's like Nuxt, but for Web Extensions</i></q>
</p>
[![npm version](https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
[![downloads](https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
[![license | MIT](https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048)](https://github.com/wxt-dev/wxt/blob/main/LICENSE)
[![coverage](https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048)](https://codecov.io/github/wxt-dev/wxt)
<p align="center">
<a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
&bull;
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
&bull;
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
Next-gen framework for developing web extensions.<br/>⚡<br/><q><i>It's like Nuxt, but for Web Extensions</i></q>
[Get Started](https://wxt.dev/guide/installation.html) •
[Configuration](https://wxt.dev/api/config.html) •
[Examples](https://wxt.dev/examples.html) •
[Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md) •
[Discord](https://discord.gg/ZFsZqGery9)
</div>
![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
## Demo
https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94
<https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94>
## Quick Start
Bootstrap a new project:
<!-- automd:pm-x version="latest" name="wxt" args="init" -->
```sh
# npm
npx wxt@latest init
@@ -57,8 +39,6 @@ pnpm dlx wxt@latest init
bunx wxt@latest init
```
<!-- /automd -->
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
## Features
@@ -80,17 +60,11 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
<a href="https://github.com/sponsors/wxt-dev"><img alt="WXT Sponsors" src="https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg"></a>
[![WXT Sponsors](https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg)](https://github.com/sponsors/wxt-dev)
## Contributors
<!-- automd:contributors author="aklinker1" license="MIT" github="wxt-dev/wxt" -->
Published under the [MIT](https://github.com/wxt-dev/wxt/blob/main/LICENSE) license.
Made by [@aklinker1](https://github.com/aklinker1) and [community](https://github.com/wxt-dev/wxt/graphs/contributors) 💛
<br><br>
<a href="https://github.com/wxt-dev/wxt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
</a>
<!-- /automd -->
[![WXT contributors](https://contrib.rocks/image?repo=wxt-dev/wxt)](https://github.com/wxt-dev/wxt/graphs/contributors)
@@ -1,4 +1,4 @@
import { describe, it, expect } from 'vitest';
import { describe, expect, it } from 'vitest';
import { TestProject } from '../utils';
describe('Output Directory Structure', () => {
@@ -220,6 +220,77 @@ describe('Output Directory Structure', () => {
expect(await project.fileExists('.output/chrome-mv3/unlisted.js'));
});
it('should support CSS entrypoints', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/plain-one.css',
`body {
font: 100% Helvetica, sans-serif;
color: #333;
}`,
);
project.addFile(
'entrypoints/plain-two.content.css',
`body {
font: 100% Helvetica, sans-serif;
color: #333;
}`,
);
project.addFile(
'entrypoints/sass-one.scss',
`$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}`,
);
project.addFile(
'entrypoints/sass-two.content.scss',
`$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}`,
);
await project.build();
expect(await project.serializeOutput(['.output/chrome-mv3/manifest.json']))
.toMatchInlineSnapshot(`
".output/chrome-mv3/assets/plain-one.css
----------------------------------------
body{font:100% Helvetica,sans-serif;color:#333}
================================================================================
.output/chrome-mv3/assets/sass-one.css
----------------------------------------
body{font:100% Helvetica,sans-serif;color:#333}
================================================================================
.output/chrome-mv3/content-scripts/plain-two.css
----------------------------------------
body{font:100% Helvetica,sans-serif;color:#333}
================================================================================
.output/chrome-mv3/content-scripts/sass-two.css
----------------------------------------
body{font:100% Helvetica,sans-serif;color:#333}
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
<contents-ignored>"
`);
});
it("should output to a custom directory when overriding 'outDir'", async () => {
const project = new TestProject();
project.addFile('entrypoints/unlisted.html', '<html></html>');
@@ -362,13 +433,11 @@ describe('Output Directory Structure', () => {
function logHello(name) {
console.log(\`Hello \${name}!\`);
}
background;
const definition = defineBackground({
main() {
logHello("background");
}
});
background;
function initPlugins() {
}
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) ? globalThis.browser : globalThis.chrome;
@@ -397,7 +466,6 @@ describe('Output Directory Structure', () => {
const result$1 = result;
return result$1;
}();
background;
"
`);
});
+22
View File
@@ -285,4 +285,26 @@ describe('Zipping', () => {
expect(await project.fileExists(sourcesZip)).toBe(false);
},
);
it('should include files in the zip when negated in zip.exclude', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
});
project.addFile(
'entrypoints/background.ts',
'export default defineBackground(() => {});',
);
const unzipDir = project.resolvePath('.output/test-1.0.0-chrome');
const sourcesZip = project.resolvePath('.output/test-1.0.0-chrome.zip');
await project.zip({
zip: {
exclude: ['**/*.json', '!manifest.json'],
},
});
await extract(sourcesZip, { dir: unzipDir });
expect(await project.fileExists(unzipDir, 'manifest.json')).toBe(true);
});
});
+13 -13
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.20.6",
"version": "0.20.8",
"description": "⚡ Next-gen Web Extension Framework",
"license": "MIT",
"scripts": {
@@ -11,7 +11,7 @@
"check:default": "check",
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
"test": "buildc --deps-only -- vitest",
"test:coverage": "pnpm test -- run --coverage",
"test:coverage": "pnpm test run --coverage",
"sync-releases": "pnpx changelogen@latest gh release",
"prepack": "pnpm build"
},
@@ -30,12 +30,12 @@
"ci-info": "^4.2.0",
"consola": "^3.4.2",
"defu": "^6.1.4",
"dotenv": "^16.5.0",
"dotenv": "^17.2.2",
"dotenv-expand": "^12.0.2",
"esbuild": "^0.25.0",
"fast-glob": "^3.3.3",
"filesize": "^10.1.6",
"fs-extra": "^11.3.0",
"fs-extra": "^11.3.1",
"get-port-please": "^3.1.2",
"giget": "^1.2.3 || ^2.0.0",
"hookable": "^5.5.3",
@@ -46,7 +46,7 @@
"linkedom": "^0.18.10",
"magicast": "^0.3.5",
"minimatch": "^10.0.1",
"nano-spawn": "^0.2.0",
"nano-spawn": "^1.0.2",
"normalize-path": "^3.0.0",
"nypm": "^0.6.0",
"ohash": "^2.0.11",
@@ -58,26 +58,26 @@
"publish-browser-extension": "^2.3.0 || ^3.0.0",
"scule": "^1.3.0",
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0",
"vite": "^5.4.19 || ^6.3.4",
"vite": "^5.4.19 || ^6.3.4 || ^7.0.0",
"vite-node": "^2.1.4 || ^3.1.2",
"web-ext-run": "^0.2.3"
},
"devDependencies": {
"@aklinker1/check": "2.0.0",
"@faker-js/faker": "^9.7.0",
"@aklinker1/check": "^2.1.0",
"@faker-js/faker": "^10.0.0",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.17.6",
"@types/normalize-path": "^3.0.2",
"@types/prompts": "^2.4.9",
"extract-zip": "^2.0.1",
"happy-dom": "^17.4.6",
"happy-dom": "^18.0.1",
"lodash.merge": "^4.6.2",
"oxlint": "^0.16.8",
"oxlint": "^1.14.0",
"publint": "^0.3.12",
"typescript": "^5.8.3",
"unbuild": "^3.5.0",
"vitest": "^3.1.2",
"typescript": "^5.9.2",
"unbuild": "^3.6.1",
"vitest": "^3.2.4",
"vitest-plugin-random-seed": "^1.1.1"
},
"peerDependenciesMeta": {},
+95 -18
View File
@@ -2,6 +2,7 @@ import type * as vite from 'vite';
import {
BuildStepOutput,
Entrypoint,
EntrypointGroup,
ResolvedConfig,
WxtBuilder,
WxtBuilderServer,
@@ -24,7 +25,9 @@ import { ViteNodeServer } from 'vite-node/server';
import { ViteNodeRunner } from 'vite-node/client';
import { installSourcemapsSupport } from 'vite-node/source-map';
import { createExtensionEnvironment } from '../../utils/environments';
import { relative } from 'node:path';
import { dirname, extname, join, relative } from 'node:path';
import fs from 'fs-extra';
import { normalizePath } from '../../utils/paths';
export async function createViteBuilder(
wxtConfig: ResolvedConfig,
@@ -106,6 +109,8 @@ export async function createViteBuilder(
const plugins: NonNullable<vite.UserConfig['plugins']> = [
wxtPlugins.entrypointGroupGlobals(entrypoint),
];
const iifeReturnValueName = safeVarName(entrypoint.name);
if (
entrypoint.type === 'content-script-style' ||
entrypoint.type === 'unlisted-style'
@@ -113,17 +118,16 @@ export async function createViteBuilder(
plugins.push(wxtPlugins.cssEntrypoints(entrypoint, wxtConfig));
}
const iifeReturnValueName = safeVarName(entrypoint.name);
const libMode: vite.UserConfig = {
if (
entrypoint.type === 'content-script' ||
entrypoint.type === 'unlisted-script'
) {
plugins.push(wxtPlugins.iifeFooter(iifeReturnValueName));
}
return {
mode: wxtConfig.mode,
plugins,
esbuild: {
// Add a footer with the returned value so it can return values to `scripting.executeScript`
// Footer is added a part of esbuild to make sure it's not minified. It
// get's removed if added to `build.rollupOptions.output.footer`
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
footer: iifeReturnValueName + ';',
},
build: {
lib: {
entry,
@@ -159,8 +163,7 @@ export async function createViteBuilder(
// See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
'process.env.NODE_ENV': JSON.stringify(wxtConfig.mode),
},
};
return libMode;
} satisfies vite.UserConfig;
};
/**
@@ -172,10 +175,7 @@ export async function createViteBuilder(
);
return {
mode: wxtConfig.mode,
plugins: [
wxtPlugins.multipageMove(entrypoints, wxtConfig),
wxtPlugins.entrypointGroupGlobals(entrypoints),
],
plugins: [wxtPlugins.entrypointGroupGlobals(entrypoints)],
build: {
rollupOptions: {
input: entrypoints.reduce<Record<string, string>>((input, entry) => {
@@ -308,7 +308,10 @@ export async function createViteBuilder(
async build(group) {
let entryConfig;
if (Array.isArray(group)) entryConfig = getMultiPageConfig(group);
else if (group.inputPath.endsWith('.css'))
else if (
group.type === 'content-script-style' ||
group.type === 'unlisted-style'
)
entryConfig = getCssConfig(group);
else entryConfig = getLibModeConfig(group);
@@ -319,9 +322,10 @@ export async function createViteBuilder(
buildConfig,
);
const result = await vite.build(buildConfig);
const chunks = getBuildOutputChunks(result);
return {
entrypoints: group,
chunks: getBuildOutputChunks(result),
chunks: await moveHtmlFiles(wxtConfig, group, chunks),
};
},
async createServer(info) {
@@ -400,3 +404,76 @@ function getRollupEntry(entrypoint: Entrypoint): string {
}
return entrypoint.inputPath;
}
/**
* Ensures the HTML files output by a multipage build are in the correct location. This does two
* things:
*
* 1. Moves the HTML files to their final location at `<outDir>/<entrypoint.name>.html`.
* 2. Updates the bundle so it summarizes the files correctly in the returned build output.
*
* Assets (JS and CSS) are output to the `<outDir>/assets` directory, and don't need to be modified.
* HTML files access them via absolute URLs, so we don't need to update any import paths in the HTML
* files either.
*/
async function moveHtmlFiles(
config: ResolvedConfig,
group: EntrypointGroup,
chunks: BuildStepOutput['chunks'],
): Promise<BuildStepOutput['chunks']> {
if (!Array.isArray(group)) return chunks;
const entryMap = group.reduce<Record<string, Entrypoint>>((map, entry) => {
const a = normalizePath(relative(config.root, entry.inputPath));
map[a] = entry;
return map;
}, {});
const movedChunks = await Promise.all(
chunks.map(async (chunk) => {
if (!chunk.fileName.endsWith('.html')) return chunk;
const entry = entryMap[chunk.fileName];
const oldBundlePath = chunk.fileName;
const newBundlePath = getEntrypointBundlePath(
entry,
config.outDir,
extname(chunk.fileName),
);
const oldAbsPath = join(config.outDir, oldBundlePath);
const newAbsPath = join(config.outDir, newBundlePath);
await fs.ensureDir(dirname(newAbsPath));
await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
return {
...chunk,
fileName: newBundlePath,
};
}),
);
// TODO: Optimize and only delete old path directories
removeEmptyDirs(config.outDir);
return movedChunks;
}
/**
* Recursively remove all directories that are empty/
*/
export async function removeEmptyDirs(dir: string): Promise<void> {
const files = await fs.readdir(dir);
for (const file of files) {
const filePath = join(dir, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await removeEmptyDirs(filePath);
}
}
try {
await fs.rmdir(dir);
} catch {
// noop on failure - this means the directory was not empty.
}
}
@@ -12,7 +12,7 @@ export function bundleAnalysis(config: ResolvedConfig): vite.Plugin {
config.analysis.outputDir,
`${config.analysis.outputName}-${increment++}.json`,
),
});
}) as vite.Plugin;
}
/**
@@ -16,7 +16,7 @@ export function devServerGlobals(
return {
define: {
__DEV_SERVER_ORIGIN__: JSON.stringify(
server.origin.replace(/^http(s):/, 'ws$1:'),
server.origin.replace(/^http(s?):/, 'ws$1:'),
),
},
};
@@ -0,0 +1,20 @@
import type { Plugin } from 'vite';
/**
* Add a footer with the returned value so it can return values to `scripting.executeScript`
* Footer is added a part of esbuild to make sure it's not minified. It
* get's removed if added to `build.rollupOptions.output.footer`
* See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
*/
export function iifeFooter(iifeReturnValueName: string): Plugin {
return {
name: 'wxt:iife-footer',
generateBundle(_, bundle) {
for (const chunk of Object.values(bundle)) {
if (chunk.type === 'chunk' && chunk.isEntry) {
chunk.code += `${iifeReturnValueName};`;
}
}
},
};
}
@@ -1,7 +1,6 @@
export * from './devHtmlPrerender';
export * from './devServerGlobals';
export * from './download';
export * from './multipageMove';
export * from './resolveVirtualModules';
export * from './tsconfigPaths';
export * from './noopBackground';
@@ -14,3 +13,4 @@ export * from './defineImportMeta';
export * from './removeEntrypointMainFunction';
export * from './wxtPluginLoader';
export * from './resolveAppConfig';
export * from './iifeFooter';
@@ -1,96 +0,0 @@
import type * as vite from 'vite';
import { Entrypoint, ResolvedConfig } from '../../../../types';
import { dirname, extname, resolve, join } from 'node:path';
import { getEntrypointBundlePath } from '../../../utils/entrypoints';
import fs, { ensureDir } from 'fs-extra';
import { normalizePath } from '../../../utils/paths';
/**
* Ensures the HTML files output by a multipage build are in the correct location. This does two
* things:
*
* 1. Moves the HMTL files to their final location at `<outDir>/<entrypoint.name>.html`.
* 2. Updates the bundle so it summarizes the files correctly in the returned build output.
*
* Assets (JS and CSS) are output to the `<outDir>/assets` directory, and don't need to be modified.
* HTML files access them via absolute URLs, so we don't need to update any import paths in the HTML
* files either.
*
* THIS PLUGIN SHOULD ONLY BE APPLIED TO MULTIPAGE BUILDS. It should not be added to every build.
*/
export function multipageMove(
entrypoints: Entrypoint[],
config: ResolvedConfig,
): vite.Plugin {
return {
name: 'wxt:multipage-move',
async writeBundle(_, bundle) {
for (const oldBundlePath in bundle) {
// oldBundlePath = 'entrypoints/popup.html' or 'entrypoints/options/index.html'
// Find a matching entrypoint - oldBundlePath is the same as end end of the input path.
const entrypoint = entrypoints.find(
(entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath),
);
if (entrypoint == null) {
config.logger.debug(
`No entrypoint found for ${oldBundlePath}, leaving in chunks directory`,
);
continue;
}
// Get the new bundle path
const newBundlePath = getEntrypointBundlePath(
entrypoint,
config.outDir,
extname(oldBundlePath),
);
if (newBundlePath === oldBundlePath) {
config.logger.debug(
'HTML file is already in the correct location',
oldBundlePath,
);
continue;
}
// Move file and update bundle
// Do this inside a mutex lock so it only runs one at a time for concurrent multipage builds
const oldAbsPath = resolve(config.outDir, oldBundlePath);
const newAbsPath = resolve(config.outDir, newBundlePath);
await ensureDir(dirname(newAbsPath));
await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
const renamedChunk = {
...bundle[oldBundlePath],
fileName: newBundlePath,
};
delete bundle[oldBundlePath];
bundle[newBundlePath] = renamedChunk;
}
// Remove directories that were created
// TODO: Optimize and only delete old path directories
removeEmptyDirs(config.outDir);
},
};
}
/**
* Recursively remove all directories that are empty/
*/
export async function removeEmptyDirs(dir: string): Promise<void> {
const files = await fs.readdir(dir);
for (const file of files) {
const filePath = join(dir, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await removeEmptyDirs(filePath);
}
}
try {
await fs.rmdir(dir);
} catch {
// noop on failure - this means the directory was not empty.
}
}
+7 -2
View File
@@ -110,8 +110,13 @@ async function createServerInternal(): Promise<WxtDevServer> {
// Listen for file changes and reload different parts of the extension accordingly
const reloadOnChange = createFileReloader(server);
server.watcher.on('all', reloadOnChange);
keyboardShortcuts.start();
server.watcher.on('all', async (...args) => {
await reloadOnChange(args[0], args[1]);
// Restart keyboard shortcuts after file is changed - for some reason they stop working.
keyboardShortcuts.start();
});
keyboardShortcuts.printHelp({
canReopenBrowser:
!wxt.config.runnerConfig.config.disabled && !!runner.canOpen?.(),
+3 -1
View File
@@ -58,7 +58,9 @@ export async function initialize(options: {
const isExists = await fs.pathExists(input.directory);
if (isExists) {
const isEmpty = (await fs.readdir(input.directory)).length === 0;
const isEmpty =
(await fs.readdir(input.directory)).filter((dir) => dir !== '.git')
.length === 0;
if (!isEmpty) {
consola.error(
`The directory ${path.resolve(input.directory)} is not empty. Aborted.`,
+2 -5
View File
@@ -26,9 +26,7 @@ export function createKeyboardShortcuts(
return {
start() {
if (rl) return;
rl = readline.createInterface({
rl ??= readline.createInterface({
input: process.stdin,
terminal: false, // Don't intercept ctrl+C, ctrl+Z, etc
});
@@ -37,8 +35,7 @@ export function createKeyboardShortcuts(
},
stop() {
rl?.close();
rl = undefined;
rl?.removeListener('line', handleInput);
},
printHelp(flags) {
+2
View File
@@ -39,6 +39,7 @@ export function createWebExtRunner(): ExtensionRunner {
devtools: wxtUserConfig?.openDevtools,
startUrl: wxtUserConfig?.startUrls,
keepProfileChanges: wxtUserConfig?.keepProfileChanges,
chromiumPort: wxtUserConfig?.chromiumPort,
...(wxt.config.browser === 'firefox'
? {
firefox: wxtUserConfig?.binaries?.firefox,
@@ -55,6 +56,7 @@ export function createWebExtRunner(): ExtensionRunner {
),
args: [
'--unsafely-disable-devtools-self-xss-warnings',
'--disable-features=DisableLoadExtensionCommandLineSwitch',
...(wxtUserConfig?.chromiumArgs ?? []),
],
}),
@@ -0,0 +1,46 @@
import { describe, it, expect } from 'vitest';
import { minimatchMultiple } from '../minimatch-multiple';
describe('minimatchMultiple', () => {
it('should return false if the pattern array is undefined', () => {
const patterns = undefined;
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern array is empty', () => {
const patterns: string[] = [];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return true if the pattern array contains a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(true);
});
it('should return false if the pattern array does not contain a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.txt';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern', () => {
const patterns = ['test.*', '!test.json'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern, regardless of order', () => {
const patterns = ['!test.json', 'test.*'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
});
@@ -4,7 +4,7 @@ import {
EntrypointGroup,
ResolvedPublicFile,
} from '../../../types';
import { getPublicFiles } from '../../utils/fs';
import { getPublicFiles } from '../fs';
import fs from 'fs-extra';
import { dirname, resolve } from 'path';
import type { Ora } from 'ora';
@@ -0,0 +1,40 @@
import { minimatch, MinimatchOptions } from 'minimatch';
/**
* Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
* patterns.
*
* Supports negated patterns, the order does not matter. If your `search` string
* matches any of the negative patterns, it will return `false`.
*
* @example
* ```ts
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```
*/
export function minimatchMultiple(
search: string,
patterns: string[] | undefined,
options?: MinimatchOptions,
): boolean {
if (patterns == null) return false;
const negatePatterns: string[] = [];
const positivePatterns: string[] = [];
for (const pattern of patterns) {
if (pattern[0] === '!') negatePatterns.push(pattern.slice(1));
else positivePatterns.push(pattern);
}
if (
negatePatterns.some((negatePattern) =>
minimatch(search, negatePattern, options),
)
)
return false;
return positivePatterns.some((positivePattern) =>
minimatch(search, positivePattern, options),
);
}
+3 -3
View File
@@ -3,7 +3,6 @@ import path from 'node:path';
import fs from 'fs-extra';
import { safeFilename } from './utils/strings';
import { getPackageJson } from './utils/package';
import { minimatch } from 'minimatch';
import { formatDuration } from './utils/time';
import { printFileList } from './utils/log/printFileList';
import { findEntrypoints, internalBuild } from './utils/building';
@@ -11,6 +10,7 @@ import { registerWxt, wxt } from './wxt';
import JSZip from 'jszip';
import glob from 'fast-glob';
import { normalizePath } from './utils/paths';
import { minimatchMultiple } from './utils/minimatch-multiple';
/**
* Build and zip the extension for distribution.
@@ -122,8 +122,8 @@ async function zipDir(
})
).filter((relativePath) => {
return (
options?.include?.some((pattern) => minimatch(relativePath, pattern)) ||
!options?.exclude?.some((pattern) => minimatch(relativePath, pattern))
minimatchMultiple(relativePath, options?.include) ||
!minimatchMultiple(relativePath, options?.exclude)
);
});
const filesToZip = [
+1 -1
View File
@@ -1,7 +1,7 @@
/** @module wxt/utils/app-config */
// @ts-expect-error: Untyped virtual module
import appConfig from 'virtual:app-config';
import type { WxtAppConfig } from '../utils/define-app-config';
import type { WxtAppConfig } from './define-app-config';
export function useAppConfig(): WxtAppConfig {
return appConfig;
@@ -118,6 +118,8 @@ export class ContentScriptContext implements AbortController {
/**
* Wrapper around `window.setInterval` that automatically clears the interval when invalidated.
*
* Intervals can be cleared by calling the normal `clearInterval` function.
*/
setInterval(handler: () => void, timeout?: number): number {
const id = setInterval(() => {
@@ -129,6 +131,8 @@ export class ContentScriptContext implements AbortController {
/**
* Wrapper around `window.setTimeout` that automatically clears the interval when invalidated.
*
* Timeouts can be cleared by calling the normal `setTimeout` function.
*/
setTimeout(handler: () => void, timeout?: number): number {
const id = setTimeout(() => {
@@ -141,6 +145,8 @@ export class ContentScriptContext implements AbortController {
/**
* Wrapper around `window.requestAnimationFrame` that automatically cancels the request when
* invalidated.
*
* Callbacks can be canceled by calling the normal `cancelAnimationFrame` function.
*/
requestAnimationFrame(callback: FrameRequestCallback): number {
const id = requestAnimationFrame((...args) => {
@@ -154,6 +160,8 @@ export class ContentScriptContext implements AbortController {
/**
* Wrapper around `window.requestIdleCallback` that automatically cancels the request when
* invalidated.
*
* Callbacks can be canceled by calling the normal `cancelIdleCallback` function.
*/
requestIdleCallback(
callback: IdleRequestCallback,
@@ -170,6 +178,8 @@ export class ContentScriptContext implements AbortController {
/**
* Call `target.addEventListener` and remove the event listener when the context is invalidated.
*
* Listeners can be canceled by calling the normal `removeEventListener` function.
*
* Includes additional events useful for content scripts:
*
* - `"wxt:locationchange"` - Triggered when HTML5 history mode is used to change URL. Content
@@ -192,7 +202,7 @@ export class ContentScriptContext implements AbortController {
): void;
addEventListener<TType extends keyof DocumentEventMap>(
target: Document,
type: keyof DocumentEventMap,
type: TType,
handler: (event: DocumentEventMap[TType]) => void,
options?: AddEventListenerOptions,
): void;
@@ -1,3 +1,5 @@
/** @module wxt/utils/split-shadow-root-css */
/**
* Given a CSS string that will be loaded into a shadow root, split it into two parts:
* - `documentCss`: CSS that needs to be applied to the document (like `@property`)
+1 -1
View File
@@ -8,4 +8,4 @@ For this reason, the virtual entrypoints get their own TS project to isolate the
When bundling WXT for publishing to NPM, all the `wxt/*` imports are marked as external and resolved when building your application. Other imports are added inline.
See https://github.com/wxt-dev/wxt/issues/286#issuecomment-1858888390 for more details.
See <https://github.com/wxt-dev/wxt/issues/286#issuecomment-1858888390> for more details.
@@ -37,10 +37,22 @@ export async function reloadManifestContentScriptMv3(
if (existing) {
logger.debug('Updating content script', existing);
await browser.scripting.updateContentScripts([{ ...contentScript, id }]);
await browser.scripting.updateContentScripts([
{
...contentScript,
id,
css: contentScript.css ?? [],
},
]);
} else {
logger.debug('Registering new content script...');
await browser.scripting.registerContentScripts([{ ...contentScript, id }]);
await browser.scripting.registerContentScripts([
{
...contentScript,
id,
css: contentScript.css ?? [],
},
]);
}
await reloadTabsForContentScript(contentScript);
+1833 -965
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -1,9 +1,8 @@
#!/bin/bash
#
# Generate code in README.md and sync it with wxt/packages/README.md for NPM
# Sync wxt/packages/README.md with README.md for NPM
#
pnpm dlx automd
echo "<!-- DO NOT EDIT, THIS FILE WAS GENERATED BY '../../scripts/generate-readmes.sh' -->" > packages/wxt/README.md
cat README.md >> packages/wxt/README.md
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
set -e
PACKAGES_DIR="./packages"
export GIT_PAGER=cat
IGNORED_DIRS=("browser" "wxt-demo")
if [ ! -d "$PACKAGES_DIR" ]; then
echo "Error: Directory '$PACKAGES_DIR' not found."
exit 1
fi
echo "Checking for changes in packages since their last tag..."
echo ""
for dir in "$PACKAGES_DIR"/*; do
if [ -d "$dir" ]; then
pkg_name=$(basename "$dir")
# Check if the package name is in the ignored directories list
if [[ " ${IGNORED_DIRS[*]} " =~ " $pkg_name " ]]; then
echo "Skipping ignored package: $pkg_name"
continue # Skip to the next directory
fi
echo "----------------------------------------"
echo "Package: $pkg_name"
# Find the latest tag for the package, e.g., "my-package-v1.2.3"
# Sorts tags by version and picks the most recent one.
last_tag=$(git tag --list "${pkg_name}-v*" --sort=-v:refname | head -n 1)
if [ -n "$last_tag" ]; then
# If a tag is found, show commits since that tag for the specific package directory
echo "Commits since last tag ($last_tag):"
git log "${last_tag}..HEAD" --oneline -- "$dir"
else
# If no tag is found, show all commits for that package directory
echo "No tags found for this package. Listing all commits:"
git log --oneline -- "$dir"
fi
echo ""
fi
done
+360
View File
@@ -0,0 +1,360 @@
import glob from 'fast-glob';
import fs from 'fs-extra';
import * as semver from 'semver';
import { dirname } from 'node:path';
import consola from 'consola';
import spawn from 'nano-spawn';
const HELP_MESSAGE = `
Upgrades dependencies throughout WXT using custom rules.
Usage:
pnpm tsx scripts/upgrade-deps.ts [options]
Options:
--write, -w Write changes to package.json files
--major, -m Allow major version upgrades
--dev-only, -d Only check/upgrade dev dependencies
--prod-only, -p Only check/upgrade production dependencies
--help, -h Display this help message
`.slice(1, -1);
const IGNORED_PACKAGES = [
// Very touchy, don't change:
'typedoc',
'typedoc-plugin-markdown',
'typedoc-vitepress-theme',
// Manually manage version so a single version is used:
'esbuild',
// Maintained manually to match min-node version
'@types/node',
// License changed in newer versions
'ua-parser-js',
];
await main();
async function main(): Promise<never> {
const args = process.argv.slice(2);
const isHelp = ['-h', '--help'].some((arg) => args.includes(arg));
const isWrite = ['-w', '--write'].some((arg) => args.includes(arg));
const isDevOnly = ['-d', '--dev-only'].some((arg) => args.includes(arg));
const isProdOnly = ['-p', '--prod-only'].some((arg) => args.includes(arg));
if (isHelp) {
console.log(HELP_MESSAGE);
process.exit(0);
}
const { packageJsonFiles, dependencyVersionsMap } =
await getPackageJsonDependencies(isDevOnly, isProdOnly);
const dependencyVersionMap = validateNoMultipleVersions(
dependencyVersionsMap,
);
consola.start(
`Fetching ${Object.keys(dependencyVersionsMap).length} dependencies...`,
);
const dependencies = await fetchAllPackageInfos(dependencyVersionMap);
consola.success('Done!');
const isMajor = ['-m', '--major'].some((arg) => args.includes(arg));
const upgrades = await detectUpgrades(dependencies, isMajor);
if (upgrades.length === 0) {
console.log();
consola.info("No upgrades found, you're up to date!");
console.log();
process.exit(0);
}
printUpgrades(upgrades);
if (!isWrite) {
consola.info('Run with `-w` to write changes to package.json files');
console.log();
process.exit(0);
}
consola.start('Writing new versions to package.json files...');
await writeUpgrades(packageJsonFiles, upgrades);
consola.success('Done!');
console.log();
consola.info('Run `pnpm i` to install new dependencies');
console.log();
process.exit(0);
}
type DependencyVersionsMap = Record<string, Set<string>>;
type PackageJsonData = {
content: any;
path: string;
folder: string;
};
async function getPackageJsonDependencies(
isDevOnly: boolean,
isProdOnly: boolean,
): Promise<{
packageJsonFiles: string[];
dependencyVersionsMap: DependencyVersionsMap;
}> {
const packageJsonFiles = await glob(
['package.json', '*/*/package.json', '!**/node_modules'],
{ onlyFiles: true },
);
const packageJsons: PackageJsonData[] = await Promise.all(
packageJsonFiles.map(async (path) => ({
content: await fs.readJson(path),
path,
folder: dirname(path),
})),
);
const dependencyVersionsMap = packageJsons.reduce<DependencyVersionsMap>(
(map, { content }) => {
const addToMap = ([name, version]: [string, any]) => {
if (
name === 'wxt' ||
version.startsWith('workspace:') ||
IGNORED_PACKAGES.includes(name)
)
return;
map[name] ||= new Set();
map[name].add(version as string);
};
if (!isDevOnly)
Object.entries(content.dependencies || {}).forEach(addToMap);
if (!isProdOnly)
Object.entries(content.devDependencies || {}).forEach(addToMap);
return map;
},
{},
);
return {
packageJsonFiles,
dependencyVersionsMap,
};
}
type DependencyVersionMap = Record<string, string>;
function validateNoMultipleVersions(
dependencyVersionsMap: DependencyVersionsMap,
): DependencyVersionMap {
const depsWithMultipleVersions = Object.entries(dependencyVersionsMap).filter(
([_, versions]) => versions.size > 1,
);
if (depsWithMultipleVersions.length === 0) {
return Object.fromEntries(
Object.entries(dependencyVersionsMap).map(([name, versions]) => [
name,
[...versions][0],
]),
);
}
const maxWidth = Math.max(
...depsWithMultipleVersions.map(([name]) => name.length),
);
console.log(maxWidth);
const addCyan = (text: string) => `\x1b[36m${text}\x1b[0m`;
consola.info('Found multiple versions of:');
for (const [name, versions] of depsWithMultipleVersions) {
console.log(
` \x1b[35m${name.padEnd(maxWidth)}\x1b[0m ${Array.from(versions)
.map(addCyan)
.join('\t')}`,
);
}
consola.error(`${depsWithMultipleVersions.length} problem(s) found`);
process.exit(1);
}
async function fetchPackageInfo(name: string): Promise<PackageInfo> {
// Use PNPM instead of API in case dependencies don't come from NPM
const res = await spawn('pnpm', ['view', name, '--json']);
return JSON.parse(res.output);
}
type PackageInfo = {
name: string;
'dist-tags': {
latest: string;
[tag: string]: string;
};
versions: string[];
time: {
created: string;
modified: string;
[version: string]: string;
};
};
type DependencyInfo = {
name: string;
currentVersionRange: string;
info: PackageInfo;
};
async function fetchAllPackageInfos(
deps: DependencyVersionMap,
): Promise<DependencyInfo[]> {
const infos = await Promise.all(
Object.entries(deps).map(async ([name, currentVersionRange]) => ({
name,
currentVersionRange,
info: await fetchPackageInfo(name),
})),
);
return infos.toSorted((a, b) => a.name.localeCompare(b.name));
}
type UpgradeDetails = {
name: string;
rangePrefix: string;
currentVersion: string;
currentVersionReleasedAt: Date;
currentRange: string;
upgradeToVersion: string;
upgradeToVersionReleasedAt: Date;
upgradeToRange: string;
diff: semver.ReleaseType | null;
latestVersion: string;
latestVersionReleasedAt: Date;
};
async function detectUpgrades(
deps: DependencyInfo[],
isMajor: boolean,
): Promise<UpgradeDetails[]> {
const results: UpgradeDetails[] = [];
for (const dep of deps) {
const currentRange = dep.currentVersionRange;
if (currentRange === '*') continue;
const parts = currentRange.split(' || ').map((part) => part.trim());
const lastRange = parts[parts.length - 1];
const isUnion = parts.length > 1;
const rangePrefix = lastRange.match(/^(.?)(\d+\.\d+\.\d+)/)?.[1] ?? '';
const currentVersion = semver.minVersion(lastRange)?.version;
if (currentVersion == null)
throw Error(`Invalid version specifier: ${dep.name}@${currentRange}`);
const currentVersionReleasedAt = new Date(dep.info.time[currentVersion]);
const isPre1 = currentVersion.startsWith('0.');
const latestVersion = dep.info['dist-tags'].latest;
const latestVersionReleasedAt = new Date(dep.info.time[latestVersion]);
semver.RELEASE_TYPES;
const upgradeToVersion = isMajor
? // Always use the latest version for major upgrades
latestVersion
: // Otherwise use the last stable version greater than the current version that is not a major release
(dep.info.versions.findLast(
(v) =>
semver.gt(v, currentVersion) &&
(isPre1 ? ['patch'] : ['patch', 'minor']).includes(
semver.diff(v, currentVersion)!,
) &&
semver.prerelease(v) == null,
) ?? currentVersion);
const upgradeToVersionReleasedAt = new Date(
dep.info.time[upgradeToVersion],
);
let upgradeToRange = `${rangePrefix}${upgradeToVersion}`;
upgradeToRange = isUnion
? semver.satisfies(upgradeToVersion, currentRange)
? currentRange
: `${currentRange} || ${upgradeToRange}`
: upgradeToRange;
let diff = semver.diff(currentVersion, upgradeToVersion);
if (isPre1) {
if (diff === 'minor') diff = 'major';
if (diff === 'patch') diff = 'minor';
}
if (upgradeToRange === currentRange) continue;
// if (currentVersion === latestVersion) continue;
results.push({
name: dep.name,
rangePrefix,
diff,
currentVersion,
currentVersionReleasedAt,
currentRange,
upgradeToVersion,
upgradeToVersionReleasedAt,
upgradeToRange,
latestVersion,
latestVersionReleasedAt,
});
}
return results;
}
function printUpgrades(upgrades: UpgradeDetails[]): void {
const namePadding = Math.max(...upgrades.map((u) => u.name.length));
const currentVersionPadding = Math.max(
...upgrades.map((u) => u.currentRange.length),
);
const upgradeToVersionPadding = Math.max(
...upgrades.map((u) => u.upgradeToRange.length),
);
const numberPadding = String(upgrades.length + 1).length + 1;
consola.info(`Found ${upgrades.length} upgrades:`);
console.log();
for (let i = 0; i < upgrades.length; i++) {
const upgrade = upgrades[i];
const num = `\x1b[2m${(i + 1).toString().padStart(numberPadding)}.\x1b[0m`;
const name = `\x1b[35m${upgrade.name.padEnd(namePadding)}\x1b[0m`;
const color =
upgrade.diff == null
? '\x1b[2m'
: upgrade.diff === 'patch'
? '\x1b[32m'
: upgrade.diff === 'minor'
? '\x1b[33m'
: '\x1b[31m';
const currentVersion = `\x1b[2m${upgrade.currentRange.padEnd(currentVersionPadding)}\x1b[0m`;
const upgradeToVersion = `${color}${upgrade.upgradeToRange.padEnd(upgradeToVersionPadding)}\x1b[0m`;
const latest =
upgrade.latestVersion !== upgrade.upgradeToVersion
? ` \x1b[2m\x1b[31m(${upgrade.latestVersion} available)\x1b[0m`
: '';
console.log(
` ${num} ${name} ${currentVersion} \x1b[2m→\x1b[0m ${upgradeToVersion}${latest}`,
);
}
console.log();
}
async function writeUpgrades(
packageJsonFiles: string[],
upgrades: UpgradeDetails[],
) {
for (const packageJsonFile of packageJsonFiles) {
const oldText = await fs.readFile(packageJsonFile, 'utf8');
let newText = oldText;
for (const upgrade of upgrades) {
const search = `"${upgrade.name}": "${upgrade.currentRange}"`;
const replace = `"${upgrade.name}": "${upgrade.upgradeToRange}"`;
newText = newText.replaceAll(search, replace);
}
if (newText !== oldText) {
await fs.writeFile(packageJsonFile, newText, 'utf8');
}
}
}
-15
View File
@@ -1,15 +0,0 @@
// https://github.com/antfu-collective/taze?tab=readme-ov-file#config-file
export default {
exclude: [
// Very touchy, don't change:
'typedoc',
'typedoc-plugin-markdown',
'typedoc-vitepress-theme',
// Manually manage version so a single version is used:
'esbuild',
// Maintained manually to match min-node version
'@types/node',
// License changed in newer versions
'ua-parser-js',
],
};
+5 -5
View File
@@ -15,14 +15,14 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.9",
"@wxt-dev/module-react": "^1.1.3",
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"wxt": "^0.20.6"
}
}
+2 -2
View File
@@ -15,11 +15,11 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"solid-js": "^1.9.6"
"solid-js": "^1.9.9"
},
"devDependencies": {
"@wxt-dev/module-solid": "^1.1.3",
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"wxt": "^0.20.6"
}
}
+4 -4
View File
@@ -15,12 +15,12 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"@tsconfig/svelte": "^5.0.4",
"@tsconfig/svelte": "^5.0.5",
"@wxt-dev/module-svelte": "^2.0.3",
"svelte": "^5.28.2",
"svelte-check": "^4.1.6",
"svelte": "^5.38.6",
"svelte-check": "^4.3.1",
"tslib": "^2.8.1",
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"wxt": "^0.20.6"
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"wxt": "^0.20.6"
}
}
+2 -2
View File
@@ -19,8 +19,8 @@
},
"devDependencies": {
"@wxt-dev/module-vue": "^1.0.2",
"typescript": "5.6.3",
"vue-tsc": "^2.2.10",
"typescript": "^5.9.2",
"vue-tsc": "^3.0.6",
"wxt": "^0.20.6"
}
}