diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..e4f65931
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: [wxt-dev] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index 597ac8a3..a60583c9 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -4,6 +4,9 @@ inputs:
install:
default: 'true'
description: Whether or not to run 'pnpm install'
+ installArgs:
+ default: ''
+ description: Additional args to append to "pnpm install"
runs:
using: composite
steps:
@@ -17,4 +20,4 @@ runs:
- name: Install Dependencies
if: ${{ inputs.install == 'true' }}
shell: bash
- run: pnpm install
+ run: pnpm install ${{ inputs.installArgs }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4ab2d2c5..76e21651 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,6 +13,7 @@ on:
- module-solid
- module-svelte
- module-vue
+ - storage
- unocss
- wxt
diff --git a/.github/workflows/sync-releases.yml b/.github/workflows/sync-releases.yml
index 4287f089..35f18d26 100644
--- a/.github/workflows/sync-releases.yml
+++ b/.github/workflows/sync-releases.yml
@@ -7,13 +7,14 @@ on:
default: wxt
type: choice
options:
- - wxt
- - module-react
- - module-vue
- - module-svelte
- - module-solid
- auto-icons
- i18n
+ - module-react
+ - module-solid
+ - module-svelte
+ - module-vue
+ - storage
+ - wxt
jobs:
sync:
@@ -21,6 +22,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
+ with:
+ installArgs: --ignore-scripts
- run: pnpm tsx scripts/sync-releases.ts ${{ inputs.package }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 5ca40ed3..0a9e9db5 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -36,7 +36,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
- name: pnpm test:coverage
run: pnpm test:coverage -- --reporter=default --reporter=hanging-process
- - uses: codecov/codecov-action@v4
+ - uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
windows-tests:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..c2ae4b34
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+aaronklinker1@gmail.com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fbe6fdc3..d1711c4c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -32,6 +32,7 @@ Here are some helpful commands:
```sh
# Build WXT package
+cd packages/wxt
pnpm build
```
@@ -144,3 +145,24 @@ pnpm dlx taze -r
```
Configuration is in [`taze.config.ts`](./taze.config.ts).
+
+## Install Unreleased Versions
+
+This repo uses https://pkg.pr.new to publish versions of all it's packages for almost every commit. You can install them via:
+
+```sh
+npm i https://pkg.pr.new/[package-name]@[ref]
+```
+
+Or use one of the shorthands:
+
+```sh
+# Install the latest build of `wxt` from a PR:
+npm i https://pkg.pr.new/wxt@1283
+
+# Install the latest build of `@wxt-dev/module-react` on the `main` branch
+npm i https://pkg.pr.new/@wxt-dev/module-react@main
+
+# Install `@wxt-dev/storage` from a specific commit:
+npm i https://pkg.pr.new/@wxt-dev/module-react@426f907
+```
diff --git a/README.md b/README.md
index 295b4d77..106d3a66 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,12 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
- 📏 Bundle analysis
- ⬇️ Download and bundle remote URL imports
+## Sponsors
+
+WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
+
+
+
## Contributors
diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue
index 610f4e02..40d2e95b 100644
--- a/docs/.vitepress/components/UsingWxtSection.vue
+++ b/docs/.vitepress/components/UsingWxtSection.vue
@@ -4,11 +4,8 @@ import useListExtensionDetails, {
ChromeExtension,
} from '../composables/useListExtensionDetails';
-// Add extension IDs here. Order doesn't matter, will be sorted by weekly active users
-// During the transition from chrome.google.com/webstore to
-// chromewebstore.google.com, queue.wxt.dev might return null for your
-// extension. If it does, use "/" instead of just the ID. The slug
-// can be retrieved from the URL of the item on chromewebstore.google.com
+// Add extension IDs here. Order doesn't matter, will be sorted by a combination of weekly active users and rating.
+// Change the commit message or PR title to: "docs: Added "[extension name]" to the homepage"
const chromeExtensionIds = [
'ocfdgncpifmegplaglcnglhioflaimkd', // GitHub: Better Line Counts
'mgmdkjcljneegjfajchedjpdhbadklcf', // Anime Skip Player
@@ -16,7 +13,7 @@ const chromeExtensionIds = [
'elfaihghhjjoknimpccccmkioofjjfkf', // StayFree - Website Blocker & Web Analytics
'okifoaikfmpfcamplcfjkpdnhfodpkil', // Doozy: Ai Made Easy
'lknmjhcajhfbbglglccadlfdjbaiifig', // tl;dv - Record, Transcribe & ChatGPT for Google Meet
- 'youtube中文配音/oglffgiaiekgeicdgkdlnlkhliajdlja', // Youtube中文配音
+ 'oglffgiaiekgeicdgkdlnlkhliajdlja', // Youtube中文配音
'agjnjboanicjcpenljmaaigopkgdnihi', // PreMiD
'aiakblgmlabokilgljkglggnpflljdgp', // Markdown Sticky Notes
'nomnkbngkijpffepcgbbofhcnafpkiep', // DocVersionRedirector
@@ -50,6 +47,21 @@ const chromeExtensionIds = [
'fcphghnknhkimeagdglkljinmpbagone', // YouTube Auto HD + FPS
'lpomjgbicdemjkgmbnkjncgdebogkhlb', // MultiViewer Companion
'ggiafipgeeaaahnjamgpjcgkdpanhddg', // Sync Watch - Watch videos together on any site
+ 'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker
+ 'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more
+ 'pccbghdfdnnkkbcdcibchpbffdgednkf', // Propbar - Property Data Enhancer
+ 'lfknakglefggmdkjdfhhofkjnnolffkh', // Text Search Pro - Search by case and whole-word match!
+ 'mbenhbocjckkbaojacmaepiameldglij', // Invoice Generator
+ 'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker
+ 'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI
+ 'lhmgechokhmdekdpgkkemoeecelcaonm', // YouTube Hider - Remove Comments By Keywords, Usernames & Tools
+ 'imgheieooppmahcgniieddodaliodeeg', // QA Compass - Record standardized bug reports easily
+ 'npgghjedpchajflknnbngajkjkdhncdo', // aesthetic Notion, styled
+ 'hmdcmlfkchdmnmnmheododdhjedfccka', // Eye Dropper
+ 'eihpmapodnppeemkhkbhikmggfojdkjd', // Cursorful - Screen Recorder with Auto Zoom
+ 'hjjkgbibknbahijglkffklflidncplkn', // Show IP – Live View of Website IPs for Developers
+ 'ilbikcehnpkmldojkcmlldkoelofnbde', // Strong Password Generator
+ 'ocllfkhcdopiafndigclebelbecaiocp', // ZenGram: Mindful Instagram, Your Way
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 1a575731..53ee29cf 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -13,11 +13,12 @@ import { version as wxtVersion } from '../../packages/wxt/package.json';
import { version as i18nVersion } from '../../packages/i18n/package.json';
import { version as autoIconsVersion } from '../../packages/auto-icons/package.json';
import { version as unocssVersion } from '../../packages/unocss/package.json';
+import { version as storageVersion } from '../../packages/storage/package.json';
const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' – WXT';
const description =
- "WXT provides the best developer experience, making it quick, easy, and fun to develop chrome extensions for all browsers. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
+ "WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
const ogTitle = `${title}${titleSuffix}`;
const ogUrl = 'https://wxt.dev';
const ogImage = 'https://wxt.dev/social-preview.png';
@@ -61,6 +62,16 @@ export default defineConfig({
alt: 'WXT logo',
},
+ footer: {
+ message: [
+ '
',
+ '
',
+ 'Released under the MIT License.',
+ ].join('
'),
+ copyright:
+ 'Copyright © 2023-present Aaron Klinker',
+ },
+
editLink: {
pattern: 'https://github.com/wxt-dev/wxt/edit/main/docs/:path',
},
@@ -87,7 +98,7 @@ export default defineConfig({
),
]),
navItem('Other Packages', [
- navItem(`wxt/storage — ${wxtVersion}`, '/storage'),
+ navItem(`@wxt-dev/storage — ${storageVersion}`, '/storage'),
navItem(`@wxt-dev/auto-icons — ${autoIconsVersion}`, '/auto-icons'),
navItem(`@wxt-dev/i18n — ${i18nVersion}`, '/i18n'),
navItem(`@wxt-dev/unocss — ${unocssVersion}`, '/unocss'),
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
index a7aa5980..275cd0c3 100644
--- a/docs/.vitepress/theme/custom.css
+++ b/docs/.vitepress/theme/custom.css
@@ -1,72 +1,28 @@
/* Colors */
:root {
- --wxt-c-green: #53bc4a;
+ --wxt-c-green-1: #0b8a00;
+ --wxt-c-green-2: #096600;
+ --wxt-c-green-3: #096600;
+ --wxt-c-green-soft: rgba(11, 138, 0, 0.14);
+}
+
+.dark {
--wxt-c-green-1: #67d45e;
- --wxt-c-green-2: #4fa048;
- --wxt-c-green-3: #447e3f;
+ --wxt-c-green-2: #329929;
+ --wxt-c-green-3: #21651b;
+ --wxt-c-green-soft: rgba(103, 212, 94, 0.14);
}
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
:root {
- --vp-c-brand: var(--wxt-c-green);
--vp-c-brand-1: var(--wxt-c-green-1);
--vp-c-brand-2: var(--wxt-c-green-2);
--vp-c-brand-3: var(--wxt-c-green-3);
-
- --vp-button-brand-bg: var(--wxt-c-green);
- --vp-button-brand-hover-bg: var(--wxt-c-green-2);
- --vp-button-brand-active-bg: var(--wxt-c-green-3);
-
- --vp-code-link-color: var(--wxt-c-green);
-
- /* --vp-c-text-1: var(--wxt-c-green-1); */
-
- --vp-button-brand-text: var(--vp-c-black);
- --vp-button-brand-hover-text: var(--vp-c-black);
- --vp-button-brand-active-text: var(--vp-c-black);
-
- --vp-custom-block-tip-border: var(--wxt-c-green);
- --vp-custom-block-tip-text: var(--wxt-c-green);
-
- /* --vp-code-block-bg: #222422;
- --vp-code-copy-code-bg: #313431;
- --vp-code-copy-code-hover-bg: #3c403c; */
-
- --vp-custom-block-tip-bg: var(--vp-code-block-bg);
- --vp-custom-block-info-bg: var(--vp-code-block-bg);
-
- --vp-code-color: #476582;
+ --vp-c-brand-soft: var(--wxt-c-green-soft);
}
-.vp-doc a {
- color: var(--wxt-c-green);
-}
-
-.dark {
- --vp-c-bg: #131413;
-
- --vp-c-bg-soft: #1a1b1a;
- --vp-c-bg-soft-up: #1f201f;
- --vp-c-bg-soft-down: #262926;
- --vp-c-bg-soft-mute: #242424;
-
- --vp-c-bg-alt: #171817;
-
- --vp-c-default: #313136;
- --vp-c-default-1: #3a3a3c;
- --vp-c-default-2: #505053;
- --vp-c-default-3: #2c2c30;
- --vp-c-default-soft: #252529;
-
- --vp-code-block-bg: #191a19;
- --vp-code-copy-code-bg: #212321;
- --vp-code-copy-code-hover-bg: #292d29;
-
- --vp-custom-block-info-bg: #191a19;
-
- --vp-code-color: #c9def1;
-}
+/* Customize Individual Components */
.vp-doc .no-vertical-dividers th,
.vp-doc .no-vertical-dividers td {
@@ -97,3 +53,16 @@ body {
vertical-align: middle;
background-color: var(--vp-c-default-2);
}
+
+.light-netlify {
+ display: inline;
+}
+.dark .light-netlify {
+ display: none;
+}
+.dark-netlify {
+ display: none;
+}
+.dark .dark-netlify {
+ display: inline;
+}
diff --git a/docs/assets/init-demo.gif b/docs/assets/init-demo.gif
index 353c8b73..ee6e9ed8 100644
Binary files a/docs/assets/init-demo.gif and b/docs/assets/init-demo.gif differ
diff --git a/docs/guide/essentials/assets.md b/docs/guide/essentials/assets.md
index 29638fd1..4ca40fca 100644
--- a/docs/guide/essentials/assets.md
+++ b/docs/guide/essentials/assets.md
@@ -77,6 +77,84 @@ export default defineContentScript({
## WASM
-:::warning 🚧 Under construction
-These docs will be coming soon!
-:::
+How a `.wasm` file is loaded varies greatly between packages, but most follow a basic setup: Use a JS API to load and execute the `.wasm` file.
+
+For an extension, that means two things:
+
+1. The `.wasm` file needs to be present in output folder so it can be loaded.
+2. You must import the JS API to load and initialize the `.wasm` file, usually provided by the NPM package.
+
+For an example, let's say you have a content script needs to parse TS code into AST. We'll use [`@oxc-parser/wasm`](https://www.npmjs.com/package/@oxc-parser/wasm) to do it!
+
+First, we need to copy the `.wasm` file to the output directory. We'll do it with a [WXT module](/guide/essentials/wxt-modules):
+
+```ts
+// modules/oxc-parser-wasm.ts
+import { resolve } from 'node:path';
+
+export default defineWxtModule((wxt) => {
+ wxt.hook('build:publicAssets', (_, assets) => {
+ assets.push({
+ absoluteSrc: resolve(
+ 'node_modules/@oxc-parser/wasm/web/oxc_parser_wasm_bg.wasm',
+ ),
+ relativeDest: 'oxc_parser_wasm_bg.wasm',
+ });
+ });
+});
+```
+
+Run `wxt build`, and you should see the WASM file copied into your `.output/chrome-mv3` folder!
+
+Next, since this is in a content script and we'll be fetching the WASM file over the network to load it, we need to add the file to the `web_accessible_resources`:
+
+```ts
+// wxt.config.ts
+export default defineConfig({
+ manifest: {
+ web_accessible_resources: [
+ {
+ // We'll use this matches in the cotent script as well
+ matches: ['*://*.github.com/*'],
+ // Use the same path as `relativeDest` from the WXT module
+ resources: ['/oxc_parser_wasm_bg.wasm'],
+ },
+ ],
+ },
+});
+```
+
+And finally, we need to load and initialize the `.wasm` file inside the content script to use it:
+
+```ts
+// entrypoints/content.ts
+import initWasm, { parseSync } from '@oxc-parser/wasm';
+
+export default defineContentScript({
+ matches: '*://*.github.com/*',
+ async main(ctx) {
+ if (!location.pathname.endsWith('.ts')) return;
+
+ // Get text from GitHub
+ const code = document.getElementById(
+ 'read-only-cursor-text-area',
+ )?.textContent;
+ if (!code) return;
+ const sourceFilename = document.getElementById('file-name-id')?.textContent;
+ if (!sourceFilename) return;
+
+ // Load the WASM file:
+ await initWasm({
+ module_or_path: browser.runtime.getURL('/oxc_parser_wasm_bg.wasm'),
+ });
+
+ // Once loaded, we can use `parseSync`!
+ const ast = parseSync(code, { sourceFilename });
+ console.log(ast);
+ },
+});
+```
+
+This code is taken directly from `@oxc-parser/wasm` docs with one exception: We manually pass in a file path. In a standard NodeJS or web project, the default path works just fine so you don't have to pass anything in. However, extensions are different. You should always explicitly pass in the full URL to the WASM file in your output directory, which is what `browser.runtime.getURL` returns.
+
+Run your extension, and you should see OXC parse the TS file!
diff --git a/docs/guide/essentials/config/browser-startup.md b/docs/guide/essentials/config/browser-startup.md
index 676d5e22..24200805 100644
--- a/docs/guide/essentials/config/browser-startup.md
+++ b/docs/guide/essentials/config/browser-startup.md
@@ -62,7 +62,8 @@ import { resolve } from 'node:path';
export default defineRunnerConfig({
// On Windows, the path must be absolute
- chromiumArgs: [`--user-data-dir="${resolve(".wxt/chrome-data")}"`
+ chromiumProfile: resolve('.wxt/chrome-data'),
+ keepProfileChanges: true,
});
```
diff --git a/docs/guide/essentials/config/environment-variables.md b/docs/guide/essentials/config/environment-variables.md
index 8e299665..e287b34a 100644
--- a/docs/guide/essentials/config/environment-variables.md
+++ b/docs/guide/essentials/config/environment-variables.md
@@ -56,3 +56,26 @@ Vite provides two other environment variables, but they aren't useful in WXT pro
- `import.meta.env.BASE_URL`: Use `browser.runtime.getURL` instead.
- `import.meta.env.SSR`: Always `false`.
:::
+
+## Manifest
+
+To use environment variables in the manifest, you need to use the function syntax:
+
+```ts
+export default defineConfig({
+ extensionApi: 'chrome',
+ modules: ['@wxt-dev/module-vue'],
+ manifest: { // [!code --]
+ oauth2: { // [!code --]
+ client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code --]
+ } // [!code --]
+ } // [!code --]
+ manifest: () => ({ // [!code ++]
+ oauth2: { // [!code ++]
+ client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code ++]
+ } // [!code ++]
+ }), // [!code ++]
+});
+```
+
+WXT can't load your `.env` files until after the config file has been loaded. So by using the function syntax for `manifest`, it defers creating the object until after the `.env` files are loaded into the process.
diff --git a/docs/guide/essentials/config/manifest.md b/docs/guide/essentials/config/manifest.md
index 6aeab49d..8063aae4 100644
--- a/docs/guide/essentials/config/manifest.md
+++ b/docs/guide/essentials/config/manifest.md
@@ -156,6 +156,11 @@ Alternatively, you can use [`@wxt-dev/auto-icons`](https://www.npmjs.com/package
> [Chrome docs](https://developer.chrome.com/docs/extensions/reference/permissions/)
+Most of the time, you need to manually add permissions to your manifest. Only in a few specific situations are permissions added automatically:
+
+- During development: the `tabs` and `scripting` permissions will be added to enable hot reloading.
+- When a `sidepanel` entrypoint is present: The `sidepanel` permission is added.
+
```ts
export default defineConfig({
manifest: {
diff --git a/docs/guide/essentials/content-scripts.md b/docs/guide/essentials/content-scripts.md
index caf74255..eda5288c 100644
--- a/docs/guide/essentials/content-scripts.md
+++ b/docs/guide/essentials/content-scripts.md
@@ -239,6 +239,7 @@ export default defineContentScript({
onMount: (container) => {
// Render your app to the UI container
const unmount = render(() => ...
, container);
+ return unmount;
},
onRemove: (unmount) => {
// Unmount the app when the UI is removed
@@ -547,16 +548,21 @@ To use `injectScript`, we need two entrypoints, one content script and one unlis
```ts
// entrypoints/example-main-world.ts
export default defineUnlistedScript(() => {
- console.log('Hello from the main world!');
+ console.log('Hello from the main world');
});
```
```ts
// entrypoints/example.content.ts
-export default defineContentScript(async () => {
- await injectScript('/example-main-world.js', {
- keepInDom: true,
- });
+export default defineContentScript({
+ matches: ['*://*/*'],
+ async main() {
+ console.log('Injecting script...');
+ await injectScript('/example-main-world.js', {
+ keepInDom: true,
+ });
+ console.log('Done!');
+ },
});
```
@@ -570,6 +576,39 @@ For MV3, `injectScript` is synchronous and the injected script will be evaluated
However for MV2, `injectScript` has to `fetch` the script's text content and create an inline `