Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0114ee6
chore!: Rename `zipdir` to `zipDir` (#2126)
PatrykKuniczak Feb 21, 2026
54c6fcc
chore: Simplify imports in `wxt/e2e` (#2122)
PatrykKuniczak Feb 21, 2026
d34eac9
chore: Remove `@ts-expect-error` from manifest.test.ts and fix typo (…
PatrykKuniczak Feb 21, 2026
3b15450
chore: Created new types, instead of `any` for `analytics` (#2119)
PatrykKuniczak Feb 21, 2026
636cf1f
fix: Allow `userId` option to return `undefined`
aklinker1 Feb 21, 2026
42d65cb
fix(types): Add type safety to `browser.runtime.executeScript` `files…
nickbar01234 Feb 21, 2026
a6904b6
ci: Create workflow for labeling and adding authors to PRs (#2148)
aklinker1 Feb 21, 2026
09eadf6
docs: Add deep outline to modules page to show recipes
aklinker1 Feb 21, 2026
182d575
feat(modules): Add support for augumenting entrypoint options (#2149)
aklinker1 Feb 21, 2026
68fa36e
ci: Fix auto-labeling workflow for forks
aklinker1 Feb 22, 2026
05e23e7
fix(types): include CSS entrypoints in PublicPath generation (#2150)
dahomita Feb 22, 2026
c9205bb
feat: New `@wxt-dev/is-background` package (#2152)
aklinker1 Feb 22, 2026
4204724
fix: Improve background script detection logic for analytics package …
sm17p Feb 22, 2026
01d1606
fix: auto icons override default icons (#1616)
omerfardemir Feb 22, 2026
2736516
docs: Update section in SECURITY.md around reporting vulnerabilities …
aklinker1 Feb 22, 2026
7da6f44
feat: Add `globalName` entrypoint option. (#2017)
jviney Feb 22, 2026
2ed2314
ci: Fix `scripts/list_unreleased_commits.sh` (#2153)
aklinker1 Feb 22, 2026
323ae25
chore(release): @wxt-dev/i18n v0.2.5
github-actions[bot] Feb 22, 2026
de9979c
chore(release): @wxt-dev/auto-icons v1.1.1
github-actions[bot] Feb 22, 2026
ef4fe90
chore(release): @wxt-dev/storage v1.2.8
github-actions[bot] Feb 22, 2026
8feb449
chore(release): @wxt-dev/analytics v0.5.3
github-actions[bot] Feb 22, 2026
7945452
ci: Fix VHS action (#2155)
aklinker1 Feb 22, 2026
bfd9455
fix: Continue using `useAppConfig` to support older versions of WXT
aklinker1 Feb 22, 2026
d48b122
refactor: make vite a default peer (#1945)
lishaduck Oct 27, 2025
c2dec31
refactor!: move to module-runner (#1946)
lishaduck Dec 16, 2025
2f921d0
feat!: add `{{modeSuffix}}` to zip templates and update defaults (#1623)
Tensai75 Dec 16, 2025
4c78167
fix!: Make `web-ext` a peer dependency (#2079)
aklinker1 Feb 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/workflows/auto-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: ✨ Auto-label PR

on:
pull_request_target:
types: [opened, synchronized, reopened]

jobs:
update-pr:
name: Update PR
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Gather Info
id: check
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
});

// Check if PR has assignees
const hasAssignees = pr.assignees && pr.assignees.length > 0;
core.setOutput('has_assignees', hasAssignees);
core.setOutput('author', pr.user.login);

// Get list of changed files
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
});

// Find all packages that were modified
const packagesRegex = /^packages\/([^\/]+)\//;
const affectedPackages = new Set();

for (const file of files) {
const match = file.filename.match(packagesRegex);
if (match) {
affectedPackages.add(match[1]);
}
}

const labels = Array.from(affectedPackages).map(pkg => `pkg/${pkg}`);
core.setOutput('labels', JSON.stringify(labels));
console.log('Detected package labels:', labels);

// Get current labels on the PR that match pkg/* pattern
const currentPkgLabels = pr.labels
.map(label => label.name)
.filter(name => name.startsWith('pkg/'));

core.setOutput('current_pkg_labels', JSON.stringify(currentPkgLabels));
console.log('Current pkg labels:', currentPkgLabels);

- name: Sync Author
if: steps.check.outputs.has_assignees == 'false'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
assignees: ['${{ steps.check.outputs.author }}']
});

console.log('Assigned PR author: ${{ steps.check.outputs.author }}');

- name: Sync Labels
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const newLabels = ${{ steps.check.outputs.labels }};
const currentLabels = ${{ steps.check.outputs.current_pkg_labels }};

// Find labels to add (in newLabels but not in currentLabels)
const labelsToAdd = newLabels.filter(label => !currentLabels.includes(label));

// Find labels to remove (in currentLabels but not in newLabels)
const labelsToRemove = currentLabels.filter(label => !newLabels.includes(label));

// Add new labels
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: labelsToAdd
});
console.log('Added labels:', labelsToAdd);
}

// Remove obsolete labels
for (const label of labelsToRemove) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
name: label
});
console.log('Removed label:', label);
}

if (labelsToAdd.length === 0 && labelsToRemove.length === 0) {
console.log('No label changes needed');
}
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- analytics
- auto-icons
- i18n
- is-background
- module-react
- module-solid
- module-svelte
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sync-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- analytics
- auto-icons
- i18n
- is-background
- module-react
- module-solid
- module-svelte
Expand Down
14 changes: 10 additions & 4 deletions .github/workflows/vhs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
jobs:
vhs:
name: Create VHS
runs-on: ubuntu-22.04
runs-on: macos-latest
if: ${{ github.repository == 'wxt-dev/wxt' }}
permissions:
contents: write
Expand All @@ -24,16 +24,22 @@ jobs:
with:
install: false

- name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00
with:
go-version: '1.25.1'

# This prevents pnpm dlx from downloading WXT in the video
- name: Pre-install WXT
run: |
pnpm store add wxt@latest
pnpm dlx wxt@latest --version

- name: Record VHS
uses: charmbracelet/vhs-action@59641cdc7fadf3978db65eb8c6937ea2752f4ec3 # v2.1.0
with:
path: 'docs/tapes/init-demo.tape'
run: |
brew install ttyd ffmpeg
go install github.com/charmbracelet/vhs@517bcda0faf416728bcf6b7fe489eb0e2469d9b5 # v0.10.0
vhs docs/tapes/init-demo.tape

- name: Save recorded GIF
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
Expand Down
7 changes: 0 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,6 @@ cp -r templates/vanilla templates/<new-template-name>

That's it. Once your template is merged, it will be available inside `wxt init` immediately. You don't need to release a new version of WXT to release a new template.

## Releasing Updates

Releases are done with GitHub actions:

- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.

## Upgrading Dependencies

WXT has custom rules around what dependencies can be upgraded. Use the `scripts/upgrade-deps.ts` script to upgrade dependencies and follow these rules.
Expand Down
41 changes: 41 additions & 0 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,44 @@ Here's an example of how to ask for a reproduction: <https://github.com/wxt-dev/
## Add yourself as a code owner

If you want to be responsible for a specific package or directory, add yourself to the [`.github/CODEOWNERS`](https://github.com/wxt-dev/wxt/blob/main/.github/CODEOWNERS) file to get added as a reviewer to PRs automatically. You can also add yourself to the default list to be added as a reviewer on all PRs.

## Releasing Package Updates

Releases are done with GitHub actions:

- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.

## Creating New Packages

Example PR: <https://github.com/wxt-dev/wxt/pull/2152>

1. Create the package.

2. Update CI workflow inputs.

3. Add docs page and version for "Other Packages" dropdown.

4. Merge the PR.

5. Tag the commit (look at other tags for pattern):

```sh
git tag <dir-name>-v<version>
git push --tags
```

6. Publish the package to NPM:

```sh
cd packages/<dir-name>
pnpm publish --access public
```

7. Create a basic release on GitHub mentioning the new package is available.

A couple of things to note:

- pkg.pr.new will fail on the original PR. It's fine to ignore and merge your PR as long as it fails due to your new package not being published to NPM yet.
- The regular release workflow DOES NOT WORK for new packages. You have to have at least one `<dir-name>-v<version>` tag created before you can run that workflow for your new package.
- You don't need to create a CHANGELOG.md file for the package, it will be created automatically after future changes are released via the normal release workflow.
8 changes: 2 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ While WXT is in prerelease, only the latest version will receive security update

<img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048">

<!--
## Reporting a Vulnerability

Use this section to tell people how to report a vulnerability.
If you discover a security vulnerability, please email me at <aaronklinker1@gmail.com>. I will respond within a few days to acknowledge receipt of your report.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
-->
If the vulnerability is accepted, I will open a public issue to track the fix. If the vulnerability is not accepted, no further action will be taken.
2 changes: 2 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { version as unocssVersion } from '../../packages/unocss/package.json';
import { version as storageVersion } from '../../packages/storage/package.json';
import { version as analyticsVersion } from '../../packages/analytics/package.json';
import { version as runnerVersion } from '../../packages/runner/package.json';
import { version as isBackgroundVersion } from '../../packages/is-background/package.json';
import addKnowledge from 'vitepress-knowledge';
import {
groupIconMdPlugin,
Expand Down Expand Up @@ -44,6 +45,7 @@ const otherPackages = {
storage: storageVersion,
unocss: unocssVersion,
runner: runnerVersion,
'is-background': isBackgroundVersion,
};

const knowledge = addKnowledge<DefaultTheme.Config>({
Expand Down
Binary file modified docs/assets/init-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 14 additions & 8 deletions docs/guide/essentials/config/browser-startup.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ outline: deep

# Browser Startup

> See the [API Reference](/api/reference/wxt/interfaces/WebExtConfig) for a full list of config.
During development, WXT will use any of the below packages to automatically open a browser with your extension installed.

- [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext)

Just install the dependency you want WXT to use to open the browser.

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.
## `web-ext` Usage

> See the [API Reference](/api/reference/wxt/interfaces/WebExtConfig) for a full list of config.

## Config Files
### Config Files

You can configure browser startup in 3 places:

Expand All @@ -25,9 +31,9 @@ You can configure browser startup in 3 places:
2. `<rootDir>/wxt.config.ts`: Via the [`webExt` config](/api/reference/wxt/interfaces/InlineConfig#webext), included in version control
3. `$HOME/web-ext.config.ts`: Provide default values for all WXT projects on your computer

## Recipes
### Recipes

### Set Browser Binaries
#### Set Browser Binaries

To set or customize the browser opened during development:

Expand Down Expand Up @@ -57,7 +63,7 @@ export default defineConfig({

By default, WXT will try to automatically discover where Chrome/Firefox are installed. However, if you have chrome installed in a non-standard location, you need to set it manually as shown above.

### Persist Data
#### Persist Data

By default, to keep from modifying your browser's existing profiles, `web-ext` creates a brand new profile every time you run the `dev` script.

Expand Down Expand Up @@ -94,9 +100,9 @@ Now, next time you run the `dev` script, a persistent profile will be created in
You can use any directory you'd like for `--user-data-dir`, the examples above create a persistent profile for each WXT project. To create a profile for all WXT projects, you can put the `chrome-data` directory inside your user's home directory.
:::

### Disable Opening Browser
#### Disable Opening Browser

If you prefer to load the extension into your browser manually, you can disable the auto-open behavior:
If you don't want to uninstall `web-ext`, like to test in your normal profile, you can do so via `disabled: true`:

```ts [web-ext.config.ts]
import { defineWebExtConfig } from 'wxt';
Expand Down
56 changes: 56 additions & 0 deletions docs/guide/essentials/wxt-modules.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
outline: deep
---

# WXT Modules

WXT provides a "module system" that let's you run code at different steps in the build process to modify it.
Expand Down Expand Up @@ -137,6 +141,58 @@ console.log(config.myModule);

This is very useful when [generating runtime code](#generate-runtime-module).

#### Add custom entrypoint options

Modules can add custom options to entrypoints by augmenting the entrypoint options types. This allows you to add custom configuration that can be accessed during the build process.

```ts
import { defineWxtModule } from 'wxt/modules';
import 'wxt';

declare module 'wxt' {
export interface BackgroundEntrypointOptions {
// Add custom options to the background entrypoint
myCustomOption?: string;
}
}

export default defineWxtModule({
setup(wxt) {
wxt.hook('entrypoints:resolved', (_, entrypoints) => {
const background = entrypoints.find((e) => e.type === 'background');
if (background) {
console.log('Custom option:', background.options.myCustomOption);
}
});
},
});
```

Now users can set the custom option in their entrypoint:

```ts [entrypoints/background.ts]
export default defineBackground({
myCustomOption: 'custom value',
main() {
// ...
},
});
```

This works for all other JS and HTML entrypoints, here's an example of how to pass a custom option from an HTML file.

```html [entrypoints/popup.html]
<html>
<head>
<meta name="wxt.myHtmlOption" content="custom value" />
<title>Popup</title>
</head>
<body>
<!-- ... -->
</body>
</html>
```

#### Generate output file

```ts
Expand Down
Loading