Compare commits

...

59 Commits

Author SHA1 Message Date
GitHub Actions ca5a52c6f1 chore(release): v0.17.12 2024-04-03 19:04:03 +00:00
Aaron 982c69f8ae feat: Add hooks for extending vite config (#599) 2024-04-03 14:00:48 -05:00
Aaron 6fc7d9254a fix(content-script-ui): Properly assign and unassign mounted value (#598) 2024-04-03 12:38:46 -05:00
dependabot[bot] 2e0e104d48 chore(deps): bump c12 from 1.5.1 to 1.10.0 (#592)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 23:23:11 -05:00
dependabot[bot] 02430fd7b8 chore(deps-dev): bump sass from 1.69.5 to 1.72.0 (#591)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 23:23:01 -05:00
dependabot[bot] 2e6d35d193 chore(deps-dev): bump vue from 3.3.10 to 3.4.21 (#589)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 23:08:37 -05:00
dependabot[bot] 7cf412ac1f chore(deps): bump vitest from 1.2.2 to 1.4.0 (#590)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 22:15:44 -05:00
Aaron 9398cbb596 docs: Add discord server link (#593) 2024-04-01 19:13:12 -05:00
dependabot[bot] ca9b96142e chore(deps-dev): bump @aklinker1/check from 1.1.1 to 1.2.0 (#588)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 18:10:28 -05:00
Aaron db57c8e079 chore: Remove unnecssary 'Omit' types 2024-03-31 08:40:21 -05:00
GitHub Actions 7e37e11caf chore(release): v0.17.11 2024-03-31 13:22:25 +00:00
Aaron bb90029c54 fix: Resolve absolute paths from the public directory properly (#583) 2024-03-31 08:19:06 -05:00
Aaron d3b14afbce chore: Extract helper function 2024-03-30 14:49:10 -05:00
Aaron ba2197a609 chore: Add some array utils (#582) 2024-03-30 13:30:45 -05:00
Aaron 4c558d3eb6 chore: Simplify virtual module setup (#581) 2024-03-30 12:44:27 -05:00
Qiwei Yang dcd175444a docs: Add "GitHub Custom Notifier" to homepage (#580) 2024-03-30 08:37:26 -05:00
Aaron 16fc58404a docs: Fix zip.includeSources example 2024-03-30 08:37:00 -05:00
GitHub Actions 28c7e7d9ed chore(release): v0.17.10 2024-03-29 05:52:30 +00:00
Zizheng Tai 5a3b42a4b0 feat: Add dev.server.port config (#577)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-29 00:49:36 -05:00
Aaron c9c5d721ec chore: Refactor order of config resolution (#578) 2024-03-29 00:21:09 -05:00
Aaron 1556b9ad73 chore: Use @aklinker1/check to simplify checks setup (#550) 2024-03-25 15:15:40 -05:00
GitHub Actions ca15b1f321 chore(release): v0.17.9 2024-03-25 06:13:23 +00:00
Aaron 448436cb08 chore: Merge user config using defu (#568) 2024-03-25 01:09:52 -05:00
Aaron c9ddc3d0f2 fix: Don't override wxt.config.ts options when CLI flags are not passed (#567) 2024-03-25 00:24:56 -05:00
Guillaume 6b72f883b6 feat: Add {{mode}} Template Variable (#566)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-25 00:01:59 -05:00
GitHub Actions cd3446bd7f chore(release): v0.17.8 2024-03-24 16:26:00 +00:00
Aaron 829b371bba fix(dev): Don't crash dev mode when rebuild fails (#565) 2024-03-24 11:23:16 -05:00
Aaron 36b399dd49 feat(analysis): Open stats.html file automatically (#564) 2024-03-24 11:05:34 -05:00
btea 08f32aa685 chore: Add a test covering wxt init in a non-empty directory (#563)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-24 10:15:59 -05:00
Vlad Fedosov 05db264e02 docs: Fixed "Reload the Extension" section (#559)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-20 16:52:52 -05:00
btea de5a01834c fix(init): Don't overwrite existing files when initializing a new project (#556)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-20 09:22:53 -05:00
Lpmvb 1d260dbacb chore(vue-template): Upgrade to vue-tsc v2 (#549) 2024-03-15 08:08:40 -05:00
Aaron 53c63b4556 chore: Switch to seed plugin for testing (#547) 2024-03-15 00:43:20 -05:00
btea 816878bc15 fix: Append option description error (#546) 2024-03-13 23:07:32 -05:00
Guillaume e087a0d6ef docs: Add examples for GitHub Actions (#540)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-13 23:07:17 -05:00
Aaron 56b71491a6 chore: Increase hook timeout for Windows/NPM tests 2024-03-13 12:34:59 -05:00
Aaron 121b521157 docs: Mention adding unlisted scripts and pages to web_accessible_resources
This closes #536
2024-03-12 07:57:38 -05:00
Sunshio 16c2e3218b docs: Add documentation for Bun.sh (#543)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-03-12 07:46:46 -05:00
Aaron d9cba55d34 chore: Increase unit test timeout 2024-03-12 07:45:42 -05:00
Guillaume 832cd5d640 docs: Add Plex skipper to "Using WXT" section (#541) 2024-03-10 09:03:18 -05:00
btea bb95e08d71 docs: Update README demo video (#539) 2024-03-10 09:03:03 -05:00
Aaron 35269dad9e docs: Update homepage demo video
This closes #535
2024-03-09 10:04:23 -06:00
btea 64d1498460 fix: Remove deprecated extension from Vue template (#534) 2024-03-08 21:03:56 -06:00
Luca Dalli 0d540a6df9 docs: Replace pnpx with pnpm dlx (#527) 2024-03-06 16:04:52 -06:00
Aaron b47c150daa fix(init): Better error logging when templates fail to load
This fixes #528
2024-03-06 07:46:26 -06:00
GitHub Actions 38345481df chore(release): v0.17.7 2024-03-04 16:29:56 +00:00
Aaron da92449e0f Undo change to log 2024-03-04 10:26:30 -06:00
Aaron 23b3980645 fix(zip): List .wxt/local_modules overrides relative to the package.json they're added to (#526) 2024-03-04 10:23:18 -06:00
Aaron 81b4b629c9 chore: Increase test timeout for windows NPM tests (#525) 2024-03-04 07:48:39 -06:00
Aaron 4f7e9cb050 chore: Bump templates to v0.17 (#524) 2024-03-04 07:32:18 -06:00
GitHub Actions b226d9b947 chore(release): v0.17.6 2024-03-03 16:05:15 +00:00
Aaron aabba41e86 fix(zip): List all private packages correctly in a PNPM workspace (#520) 2024-03-03 09:56:42 -06:00
Aaron f19e6908b0 feat: Automatically generate browser_action based on action for MV2 (#519) 2024-03-03 09:52:43 -06:00
Aaron 5f651cc3fe feat: Automatically remove top-level MV2-only or MV3-only keys (#518)
When targeting MV2, all MV3 keys will be removed. When targeting MV3, all MV2 keys will be removed.
2024-03-03 09:22:24 -06:00
Aaron 8a6acbaaf2 feat: Add warnings when important directories are missing (#516) 2024-03-02 08:56:20 -06:00
dependabot[bot] 13ab8e4d01 chore(deps-dev): bump @types/react from 18.2.34 to 18.2.61 (#510)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 08:55:51 -06:00
dependabot[bot] bd039dcbab chore(deps): bump chokidar from 3.5.3 to 3.6.0 (#511)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 08:55:00 -06:00
Aaron Klinker 9cd4e8342b docs: Mentions moving folders into srcDir
Apart of #515
2024-03-02 08:42:31 -06:00
dependabot[bot] c9633cefec chore(deps): bump linkedom from 0.16.1 to 0.16.8 (#513)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-01 17:04:05 -06:00
79 changed files with 1537 additions and 655 deletions
+2 -14
View File
@@ -7,24 +7,12 @@ on:
- main
jobs:
formatting:
checks:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm format:check
lint:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm lint
type-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm compile
- run: pnpm check
validate-demo:
runs-on: ubuntu-22.04
steps:
+162
View File
@@ -1,5 +1,167 @@
# Changelog
## v0.17.12
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.11...v0.17.12)
### 🚀 Enhancements
- Add hooks for extending vite config ([#599](https://github.com/wxt-dev/wxt/pull/599))
### 🩹 Fixes
- **content-script-ui:** Properly assign and unassign mounted value ([#598](https://github.com/wxt-dev/wxt/pull/598))
### 📖 Documentation
- Add discord server link ([#593](https://github.com/wxt-dev/wxt/pull/593))
### 🏡 Chore
- Remove unnecssary 'Omit' types ([db57c8e](https://github.com/wxt-dev/wxt/commit/db57c8e))
- **deps-dev:** Bump @aklinker1/check from 1.1.1 to 1.2.0 ([#588](https://github.com/wxt-dev/wxt/pull/588))
- **deps-dev:** Bump vue from 3.3.10 to 3.4.21 ([#589](https://github.com/wxt-dev/wxt/pull/589))
- **deps-dev:** Bump sass from 1.69.5 to 1.72.0 ([#591](https://github.com/wxt-dev/wxt/pull/591))
## v0.17.11
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.10...v0.17.11)
### 🩹 Fixes
- Resolve absolute paths from the public directory properly ([#583](https://github.com/wxt-dev/wxt/pull/583))
### 📖 Documentation
- Fix `zip.includeSources` example ([16fc584](https://github.com/wxt-dev/wxt/commit/16fc584))
- Add "GitHub Custom Notifier" to homepage ([#580](https://github.com/wxt-dev/wxt/pull/580))
### 🏡 Chore
- Simplify virtual module setup ([#581](https://github.com/wxt-dev/wxt/pull/581))
- Add some array utils ([#582](https://github.com/wxt-dev/wxt/pull/582))
- Extract helper function ([d3b14af](https://github.com/wxt-dev/wxt/commit/d3b14af))
### ❤️ Contributors
- Qiwei Yang ([@qiweiii](http://github.com/qiweiii))
## v0.17.10
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.9...v0.17.10)
### 🚀 Enhancements
- Add `dev.server.port` config ([#577](https://github.com/wxt-dev/wxt/pull/577))
### 🏡 Chore
- Use `@aklinker1/check` to simplify checks setup ([#550](https://github.com/wxt-dev/wxt/pull/550))
- Refactor order of config resolution ([#578](https://github.com/wxt-dev/wxt/pull/578))
### ❤️ Contributors
- Zizheng Tai ([@zizhengtai](http://github.com/zizhengtai))
## v0.17.9
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.8...v0.17.9)
### 🚀 Enhancements
- Add `{{mode}}` Template Variable ([#566](https://github.com/wxt-dev/wxt/pull/566))
### 🩹 Fixes
- Don't override `wxt.config.ts` options when CLI flags are not passed ([#567](https://github.com/wxt-dev/wxt/pull/567))
### 🏡 Chore
- Merge user config using `defu` ([#568](https://github.com/wxt-dev/wxt/pull/568))
### ❤️ Contributors
- Guillaume ([@GuiEpi](http://github.com/GuiEpi))
## v0.17.8
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.7...v0.17.8)
### 🚀 Enhancements
- **analysis:** Open `stats.html` file automatically ([#564](https://github.com/wxt-dev/wxt/pull/564))
### 🩹 Fixes
- **init:** Better error logging when templates fail to load ([b47c150](https://github.com/wxt-dev/wxt/commit/b47c150))
- Remove deprecated extension from Vue template ([#534](https://github.com/wxt-dev/wxt/pull/534))
- Append option description error ([#546](https://github.com/wxt-dev/wxt/pull/546))
- **init:** Don't overwrite existing files when initializing a new project ([#556](https://github.com/wxt-dev/wxt/pull/556))
- **dev:** Don't crash dev mode when rebuild fails ([#565](https://github.com/wxt-dev/wxt/pull/565))
### 📖 Documentation
- Replace `pnpx` with `pnpm dlx` ([#527](https://github.com/wxt-dev/wxt/pull/527))
- Update homepage demo video ([35269da](https://github.com/wxt-dev/wxt/commit/35269da))
- Update README demo video ([#539](https://github.com/wxt-dev/wxt/pull/539))
- Add Plex skipper to "Using WXT" section ([#541](https://github.com/wxt-dev/wxt/pull/541))
- Add documentation for Bun.sh ([#543](https://github.com/wxt-dev/wxt/pull/543))
- Mention adding unlisted scripts and pages to `web_accessible_resources` ([121b521](https://github.com/wxt-dev/wxt/commit/121b521))
- Add examples for GitHub Actions ([#540](https://github.com/wxt-dev/wxt/pull/540))
- Fixed "Reload the Extension" section ([#559](https://github.com/wxt-dev/wxt/pull/559))
### 🏡 Chore
- Increase unit test timeout ([d9cba55](https://github.com/wxt-dev/wxt/commit/d9cba55))
- Increase hook timeout for Windows/NPM tests ([56b7149](https://github.com/wxt-dev/wxt/commit/56b7149))
- Switch to seed plugin for testing ([#547](https://github.com/wxt-dev/wxt/pull/547))
- **vue-template:** Upgrade to `vue-tsc` v2 ([#549](https://github.com/wxt-dev/wxt/pull/549))
- Add a test covering `wxt init` in a non-empty directory ([#563](https://github.com/wxt-dev/wxt/pull/563))
### ❤️ Contributors
- Btea ([@btea](http://github.com/btea))
- Vlad Fedosov ([@StyleT](http://github.com/StyleT))
- Lpmvb ([@Lpmvb](http://github.com/Lpmvb))
- Guillaume ([@GuiEpi](http://github.com/GuiEpi))
- Sunshio ([@MPB-Tech](http://github.com/MPB-Tech))
- Luca Dalli ([@lucadalli](http://github.com/lucadalli))
## v0.17.7
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.6...v0.17.7)
### 🩹 Fixes
- **zip:** List `.wxt/local_modules` overrides relative to the `package.json` they're added to ([#526](https://github.com/wxt-dev/wxt/pull/526))
### 🏡 Chore
- Bump templates to v0.17 ([#524](https://github.com/wxt-dev/wxt/pull/524))
- Increase test timeout for windows NPM tests ([#525](https://github.com/wxt-dev/wxt/pull/525))
## v0.17.6
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.5...v0.17.6)
### 🚀 Enhancements
- Add warnings when important directories are missing ([#516](https://github.com/wxt-dev/wxt/pull/516))
- Automatically remove top-level MV2-only or MV3-only keys ([#518](https://github.com/wxt-dev/wxt/pull/518))
- Automatically generate `browser_action` based on `action` for MV2 ([#519](https://github.com/wxt-dev/wxt/pull/519))
### 🩹 Fixes
- **zip:** List all private packages correctly in a PNPM workspace ([#520](https://github.com/wxt-dev/wxt/pull/520))
### 📖 Documentation
- Mentions moving folders into `srcDir` ([9cd4e83](https://github.com/wxt-dev/wxt/commit/9cd4e83))
### 🏡 Chore
- **deps-dev:** Bump @types/react from 18.2.34 to 18.2.61 ([#510](https://github.com/wxt-dev/wxt/pull/510))
## v0.17.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.4...v0.17.5)
+4 -2
View File
@@ -29,20 +29,22 @@
<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://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
## Demo
https://github.com/wxt-dev/wxt/assets/10101283/07359e53-f491-43b6-8e8f-fae94aec8063
https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94
## Quick Start
Bootstrap a new project:
```sh
pnpx wxt@latest init <project-name>
pnpm dlx wxt@latest init <project-name>
```
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
+1 -1
View File
@@ -1,4 +1,4 @@
import messages from 'public/_locales/en/messages.json';
import messages from '~/public/_locales/en/messages.json';
export default defineBackground({
// type: 'module',
+3 -3
View File
@@ -12,10 +12,10 @@ export default defineConfig({
},
],
},
alias: {
public: 'src/public',
},
zip: {
downloadPackages: ['sass'],
},
analysis: {
open: true,
},
});
@@ -20,6 +20,8 @@ const chromeExtensionIds = [
'agjnjboanicjcpenljmaaigopkgdnihi', // PreMiD
'aiakblgmlabokilgljkglggnpflljdgp', // Markdown Sticky Notes
'nomnkbngkijpffepcgbbofhcnafpkiep', // DocVersionRedirector
'ceicccfeikoipigeghddpocceifjelph', // Plex Skipper
'aelkipgppclpfimeamgmlonimflbhlgf', // GitHub Custom Notifier
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
+4 -1
View File
@@ -146,6 +146,9 @@ export default defineConfig({
],
},
socialLinks: [{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' }],
socialLinks: [
{ icon: 'discord', link: 'https://discord.gg/ZFsZqGery9' },
{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' },
],
},
});
+2
View File
@@ -2,6 +2,8 @@
HTML pages that are bundled and shipped with the extension, but are not included in the manifest.
If you plan on using the page in an iframe, don't forget to add the page to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
### Examples
- Onboarding
+1 -1
View File
@@ -2,7 +2,7 @@
TypeScript files that are bundled and shipped with the extension, but are not included in the manifest.
You are responsible for loading/running these scripts where needed.
You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
## Filenames
+11 -1
View File
@@ -99,7 +99,17 @@ For a full list of options, see the [API Reference](/api/wxt/interfaces/Extensio
Normally, to manually reload an extension, you have to visit `chrome://extensions` and click the reload button for your extension.
When running `wxt` command to start the dev server, WXT adds a keyboard shortcut, `ctrl+E` for Windows/Linux and `cmd+E` for Mac, that reloads the extension when pressed, without visiting `chrome://extensions`.
When running `wxt` command to start the dev server, WXT adds a keyboard shortcut `Alt+R`, that reloads the extension when pressed, without visiting `chrome://extensions`. This can also be customized or disabled:
```ts [wxt.config.ts]
import { defineConfig } from 'wxt';
export default defineConfig({
dev: {
reloadCommand: 'Alt+T', // false, to disable
},
});
```
:::info
This shortcut is only available during development, and is not be added to your extension when running `wxt build` or `wxt-zip`.
+16 -2
View File
@@ -7,13 +7,17 @@ Bootstrap a new project, start from scratch, or [migrate an existing project](/g
:::code-group
```sh [pnpm]
pnpx wxt@latest init <project-name>
pnpm dlx wxt@latest init <project-name>
```
```sh [npm]
npx wxt@latest init <project-name>
```
```sh [bun]
bunx wxt@latest init <project-name>
```
:::
There are several starting templates available.
@@ -55,6 +59,12 @@ cd project-name
yarn init
```
```sh [bun]
mkdir project-name
cd project-name
bun init
```
:::
Then install `wxt`:
@@ -73,6 +83,10 @@ npm i --save-dev wxt
yarn add --dev wxt
```
```sh [bun]
bun add --dev wxt
```
:::
Add your first entrypoint:
@@ -102,7 +116,7 @@ Finally, add scripts to your `package.json`:
## Migrate an Existing Project
Before starting the migration, it is recommended to run `pnpx wxt@latest init` to see what a basic project looks like. Once you have an understanding of how WXT projects are structured, you're ready to convert the project, using the initialized project as a reference.
Before starting the migration, it is recommended to run `pnpm dlx wxt@latest init` to see what a basic project looks like. Once you have an understanding of how WXT projects are structured, you're ready to convert the project, using the initialized project as a reference.
Migrating a project to WXT comes down to a few steps:
+66
View File
@@ -130,6 +130,72 @@ See the official localization examples for more details:
<ExampleList tag="i18n" />
## Per-Manifest Version Config
WXT applies several transformations to your manifest to simplify managing both MV2 and MV3 keys in your `wxt.config.ts` file:
1. Top level MV2-only or MV3-only keys are stripped from the final manifest when targeting the other manifest version
2. Some keys, are automatically converted between versions when possible:
- Define `web_accessible_resources` in it's MV3 style and it will be converted to the MV2 style automatically
- `action` will automatically be converted to `browser_action` for MV3. To use `page_action` instead, add both `action` and `page_action` entries to your manifest
For example, a `wxt.config.ts` file that looks like this:
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
mainfest: {
action: {
default_title: 'Some Title',
},
web_accessible_resources: [
{
matches: ['*://*.google.com/*'],
resources: ['icon/*.png'],
},
],
},
});
```
Will be output differently for each manifest version:
:::code-group
```json [MV2]
{
"manifest_version": 2,
// ...
"browser_action": {
"default_title": "Some Title"
},
"web_accessible_resources": ["icon/*.png"]
}
```
```json [MV3]
{
"manifest_version": 3,
// ...
"action": {
"default_title": "Some Title"
},
"web_accessible_resources": [
{
"matches": ["*://*.google.com/*"],
"resources": ["icon/*.png"]
}
]
}
```
:::
:::tip
If this isn't enough control for your use-case, remember you can use a function for the `manifest` key and generate it however you'd like, or you can use the `build:manifestGenerated` hook to apply additional transformations.
:::
## Per-Browser Configuration
The `manifest` field can be a function. If you are building and extension for multiple browsers, and need to modify the manifest per browser, using a function instead of an object is very useful.
+1 -1
View File
@@ -12,7 +12,7 @@ Always start by generating a new vanilla project and merging it into your projec
```sh
cd path/to/your/project
pnpx wxt@latest init example-wxt --template vanilla
pnpm dlx wxt@latest init example-wxt --template vanilla
```
In general, you'll need to:
+54 -2
View File
@@ -64,12 +64,59 @@ See the [Firefox Addon Store](#firefox-addon-store) section for more details abo
## GitHub Action
Here's an example of a GitHub Action to automate submitting new versions of your extension for review. Ensure that you've added all required secrets used in the workflow to the repo's settings.
Here's an example of a GitHub Action that submits new versions of an extension for review. Ensure that you've added all required secrets used in the workflow to the repo's settings.
```yml
# TODO
name: Release
on:
workflow_dispatch:
jobs:
submit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 'latest'
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Zip extensions
run: |
pnpm zip
pnpm zip:firefox
- name: Submit to stores
run: |
pnpm wxt submit \
--chrome-zip .output/*-chrome.zip \
--firefox-zip .output/*-firefox.zip --firefox-sources-zip .output/*-sources.zip
env:
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
```
The action above lays the foundation for a basic workflow, including `zip` and `submit` steps. To further enhance your GitHub Action and delve into more complex scenarios, consider exploring the following examples from real projects. They introduce advanced features such as version management, changelog generation, and GitHub releases, tailored for different needs:
- [`aklinker1/github-better-line-counts`](https://github.com/aklinker1/github-better-line-counts/blob/main/.github/workflows/submit.yml) - Conventional commits, automated version bump and changelog generation, triggered manually, optional dry run for testing
- [`GuiEpi/plex-skipper`](https://github.com/GuiEpi/plex-skipper/blob/main/.github/workflows/deploy.yml) - Triggered automatically when `package.json` version is changed, creates and uploads artifacts to GitHub release.
> These examples are designed to provide clear insights and are a good starting point for customizing your own workflows. Feel free to explore and adapt them to your project needs.
## Stores
### Chrome Web Store
@@ -124,6 +171,11 @@ yarn
yarn zip:firefox
```
```sh [bun]
bun i
bun zip:firefox
```
:::
Make sure the build output is the exact same when running `wxt build -b firefox` in your main project and inside the zipped sources.
+2 -2
View File
@@ -70,14 +70,14 @@ features:
linkText: Read docs
---
<section class="vp-doc">
<section class="vp-doc" style="padding: 0 48px">
<div class="container">
<h2>Put <span style="color: var(--vp-c-brand-1)">Developer Experience</span> First</h2>
<p>
WXT's simplifies the chrome extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
</p>
<div style="margin: auto; width: 100%; max-width: 900px; text-align: center">
<video src="https://github.com/wxt-dev/wxt/assets/10101283/b32e6766-ec11-45a4-9677-226ee4718e1c" controls></video>
<video src="https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94" controls></video>
<br />
<small>
And who doesn't appreciate a beautiful CLI?
+29 -1
View File
@@ -1,6 +1,14 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TestProject } from '../utils';
import { resetBundleIncrement } from '~/core/builders/vite/plugins';
import open from 'open';
vi.mock('open');
const openMock = vi.mocked(open);
vi.mock('ci-info', () => ({
isCI: false,
}));
describe('Analysis', () => {
beforeEach(() => {
@@ -89,4 +97,24 @@ describe('Analysis', () => {
expect(await project.fileExists('stats/bundle-0.json')).toBe(true);
expect(await project.fileExists('stats/bundle-1.json')).toBe(true);
});
it('should open the stats in the browser when requested', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html');
project.addFile('entrypoints/options.html');
project.addFile(
'entrypoints/background.ts',
'export default defineBackground(() => {});',
);
await project.build({
analysis: {
enabled: true,
open: true,
},
});
expect(openMock).toBeCalledTimes(1);
expect(openMock).toBeCalledWith(project.resolvePath('stats.html'));
});
});
+16 -3
View File
@@ -9,16 +9,21 @@ const hooks: WxtHooks = {
'build:manifestGenerated': vi.fn(),
'entrypoints:resolved': vi.fn(),
'entrypoints:grouped': vi.fn(),
'vite:build:extendConfig': vi.fn(),
'vite:devServer:extendConfig': vi.fn(),
};
function expectHooksToBeCalled(called: Record<keyof WxtHooks, boolean>) {
function expectHooksToBeCalled(
called: Record<keyof WxtHooks, boolean | number>,
) {
Object.keys(hooks).forEach((key) => {
const hookName = key as keyof WxtHooks;
const times = called[hookName] ? 1 : 0;
const value = called[hookName];
const times = typeof value === 'number' ? value : value ? 1 : 0;
expect(
hooks[hookName],
`Expected "${hookName}" to be called ${times} time(s)`,
).toBeCalledTimes(called[hookName] ? 1 : 0);
).toBeCalledTimes(times);
});
}
@@ -40,6 +45,8 @@ describe('Hooks', () => {
'build:manifestGenerated': false,
'entrypoints:grouped': false,
'entrypoints:resolved': true,
'vite:build:extendConfig': false,
'vite:devServer:extendConfig': false,
});
});
@@ -56,6 +63,8 @@ describe('Hooks', () => {
'build:manifestGenerated': true,
'entrypoints:grouped': true,
'entrypoints:resolved': true,
'vite:build:extendConfig': 1,
'vite:devServer:extendConfig': false,
});
});
@@ -72,6 +81,8 @@ describe('Hooks', () => {
'build:manifestGenerated': true,
'entrypoints:grouped': true,
'entrypoints:resolved': true,
'vite:build:extendConfig': 1,
'vite:devServer:extendConfig': false,
});
});
@@ -94,6 +105,8 @@ describe('Hooks', () => {
'build:manifestGenerated': true,
'entrypoints:grouped': true,
'entrypoints:resolved': true,
'vite:build:extendConfig': 2,
'vite:devServer:extendConfig': 1,
});
});
});
+14
View File
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { execaCommand } from 'execa';
import glob from 'fast-glob';
import { mkdir, writeJson } from 'fs-extra';
describe('Init command', () => {
it('should download and create a template', async () => {
@@ -42,4 +43,17 @@ describe('Init command', () => {
]
`);
});
it('should throw an error if the directory is not empty', async () => {
const project = new TestProject();
await mkdir(project.root);
await writeJson(project.resolvePath('package.json'), {});
await expect(() =>
execaCommand(`pnpm -s wxt init ${project.root} -t vue --pm npm`, {
env: { ...process.env, CI: 'true' },
stdio: 'ignore',
}),
).rejects.toThrowError('Command failed with exit code 1:');
});
});
+25
View File
@@ -71,4 +71,29 @@ describe('Zipping', () => {
}"
`);
});
it('should correctly apply template variables for zip file names based on provided config', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
});
project.addFile(
'entrypoints/background.ts',
'export default defineBackground(() => {});',
);
const artifactZip = '.output/test-1.0.0-firefox-development.zip';
const sourcesZip = '.output/test-1.0.0-development-sources.zip';
await project.zip({
browser: 'firefox',
mode: 'development',
zip: {
artifactTemplate: '{{name}}-{{version}}-{{browser}}-{{mode}}.zip',
sourcesTemplate: '{{name}}-{{version}}-{{mode}}-sources.zip',
},
});
expect(await project.fileExists(artifactZip)).toBe(true);
expect(await project.fileExists(sourcesZip)).toBe(true);
});
});
+8 -9
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.17.5",
"version": "0.17.12",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18",
@@ -87,14 +87,9 @@
"scripts": {
"wxt": "tsx src/cli/index.ts",
"build": "tsx scripts/build.ts",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "run-p -c -s lint:*",
"lint:eslint": "echo 'ESLint: TODO'",
"lint:package": "publint",
"compile": "run-s -c compile:*",
"compile:wxt": "tsc --noEmit",
"compile:virtual": "tsc --noEmit -p src/virtual",
"check": "run-s -c check:*",
"check:default": "check",
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
"test": "vitest",
"test:coverage": "vitest run --coverage.enabled \"--coverage.include=src/**\" \"--coverage.exclude=src/core/utils/testing/**\" \"--coverage.exclude=**/*.d.ts\"",
"prepare": "simple-git-hooks",
@@ -115,6 +110,7 @@
"c12": "^1.5.1",
"cac": "^6.7.14",
"chokidar": "^3.5.3",
"ci-info": "^4.0.0",
"consola": "^3.2.3",
"defu": "^6.1.3",
"dequal": "^2.0.3",
@@ -134,6 +130,7 @@
"natural-compare": "^1.4.0",
"normalize-path": "^3.0.0",
"nypm": "^0.3.6",
"open": "^10.1.0",
"ora": "^7.0.1",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
@@ -144,6 +141,7 @@
"webextension-polyfill": "^0.10.0"
},
"devDependencies": {
"@aklinker1/check": "^1.1.1",
"@faker-js/faker": "^8.3.1",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
@@ -171,6 +169,7 @@
"vitepress": "1.0.0-rc.34",
"vitest": "^1.2.2",
"vitest-mock-extended": "^1.3.1",
"vitest-plugin-random-seed": "^1.0.2",
"vue": "^3.3.10"
},
"packageManager": "pnpm@8.6.3",
+235 -209
View File
@@ -28,13 +28,16 @@ importers:
version: 0.4.0
c12:
specifier: ^1.5.1
version: 1.5.1
version: 1.10.0
cac:
specifier: ^6.7.14
version: 6.7.14
chokidar:
specifier: ^3.5.3
version: 3.5.3
version: 3.6.0
ci-info:
specifier: ^4.0.0
version: 4.0.0
consola:
specifier: ^3.2.3
version: 3.2.3
@@ -79,7 +82,7 @@ importers:
version: 3.10.1
linkedom:
specifier: ^0.16.1
version: 0.16.1
version: 0.16.8
minimatch:
specifier: ^9.0.3
version: 9.0.3
@@ -92,6 +95,9 @@ importers:
nypm:
specifier: ^0.3.6
version: 0.3.6
open:
specifier: ^10.1.0
version: 10.1.0
ora:
specifier: ^7.0.1
version: 7.0.1
@@ -117,6 +123,9 @@ importers:
specifier: ^0.10.0
version: 0.10.0
devDependencies:
'@aklinker1/check':
specifier: ^1.1.1
version: 1.2.0(typescript@5.3.3)
'@faker-js/faker':
specifier: ^8.3.1
version: 8.3.1
@@ -198,9 +207,12 @@ importers:
vitest-mock-extended:
specifier: ^1.3.1
version: 1.3.1(typescript@5.3.3)(vitest@1.2.2)
vitest-plugin-random-seed:
specifier: ^1.0.2
version: 1.0.2(vite@5.1.3)
vue:
specifier: ^3.3.10
version: 3.3.10(typescript@5.3.3)
version: 3.4.21(typescript@5.3.3)
demo:
dependencies:
@@ -229,6 +241,18 @@ importers:
packages:
/@aklinker1/check@1.2.0(typescript@5.3.3):
resolution: {integrity: sha512-8GEoPcifx5f7Yx2t1KCGEQyrLOOb2ErP3cG+H1+TwWBYjkT2HV9ZHCLYy0Ws8CET8VeUcKDvLKmn2DW3HBt+FA==}
hasBin: true
peerDependencies:
typescript: ^5.0.0
dependencies:
'@antfu/utils': 0.7.7
ci-info: 4.0.0
citty: 0.1.6
typescript: 5.3.3
dev: true
/@aklinker1/rollup-plugin-visualizer@5.12.0:
resolution: {integrity: sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ==}
engines: {node: '>=14'}
@@ -393,6 +417,10 @@ packages:
'@jridgewell/trace-mapping': 0.3.18
dev: true
/@antfu/utils@0.7.7:
resolution: {integrity: sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==}
dev: true
/@babel/code-frame@7.22.5:
resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
engines: {node: '>=6.9.0'}
@@ -418,8 +446,8 @@ packages:
js-tokens: 4.0.0
dev: false
/@babel/parser@7.23.6:
resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
/@babel/parser@7.24.1:
resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
@@ -1155,7 +1183,7 @@ packages:
/@types/react-dom@18.2.14:
resolution: {integrity: sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==}
dependencies:
'@types/react': 18.2.34
'@types/react': 18.2.61
dev: true
/@types/react@18.2.34:
@@ -1166,6 +1194,14 @@ packages:
csstype: 3.1.2
dev: true
/@types/react@18.2.61:
resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==}
dependencies:
'@types/prop-types': 15.7.9
'@types/scheduler': 0.16.5
csstype: 3.1.3
dev: true
/@types/scheduler@0.16.5:
resolution: {integrity: sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==}
dev: true
@@ -1185,7 +1221,7 @@ packages:
'@types/node': 20.10.3
optional: true
/@vitejs/plugin-vue@5.0.2(vite@5.1.3)(vue@3.4.3):
/@vitejs/plugin-vue@5.0.2(vite@5.1.3)(vue@3.4.21):
resolution: {integrity: sha512-kEjJHrLb5ePBvjD0SPZwJlw1QTRcjjCA9sB5VyfonoXVBxTS7TMnqL6EkLt1Eu61RDeiuZ/WN9Hf6PxXhPI2uA==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
@@ -1193,7 +1229,7 @@ packages:
vue: ^3.2.25
dependencies:
vite: 5.1.3(@types/node@20.10.3)(sass@1.69.5)
vue: 3.4.3(typescript@5.3.3)
vue: 3.4.21(typescript@5.3.3)
dev: true
/@vitest/coverage-v8@1.2.2(vitest@1.2.2):
@@ -1231,13 +1267,13 @@ packages:
dependencies:
'@vitest/utils': 1.2.2
p-limit: 5.0.0
pathe: 1.1.1
pathe: 1.1.2
/@vitest/snapshot@1.2.2:
resolution: {integrity: sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==}
dependencies:
magic-string: 0.30.5
pathe: 1.1.1
magic-string: 0.30.8
pathe: 1.1.2
pretty-format: 29.7.0
/@vitest/spy@1.2.2:
@@ -1253,179 +1289,96 @@ packages:
loupe: 2.3.7
pretty-format: 29.7.0
/@vue/compiler-core@3.3.10:
resolution: {integrity: sha512-doe0hODR1+i1menPkRzJ5MNR6G+9uiZHIknK3Zn5OcIztu6GGw7u0XUzf3AgB8h/dfsZC9eouzoLo3c3+N/cVA==}
/@vue/compiler-core@3.4.21:
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
dependencies:
'@babel/parser': 7.23.6
'@vue/shared': 3.3.10
estree-walker: 2.0.2
source-map-js: 1.0.2
dev: true
/@vue/compiler-core@3.4.3:
resolution: {integrity: sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==}
dependencies:
'@babel/parser': 7.23.6
'@vue/shared': 3.4.3
'@babel/parser': 7.24.1
'@vue/shared': 3.4.21
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.0.2
dev: true
/@vue/compiler-dom@3.3.10:
resolution: {integrity: sha512-NCrqF5fm10GXZIK0GrEAauBqdy+F2LZRt3yNHzrYjpYBuRssQbuPLtSnSNjyR9luHKkWSH8we5LMB3g+4z2HvA==}
/@vue/compiler-dom@3.4.21:
resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
dependencies:
'@vue/compiler-core': 3.3.10
'@vue/shared': 3.3.10
'@vue/compiler-core': 3.4.21
'@vue/shared': 3.4.21
dev: true
/@vue/compiler-dom@3.4.3:
resolution: {integrity: sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==}
/@vue/compiler-sfc@3.4.21:
resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
dependencies:
'@vue/compiler-core': 3.4.3
'@vue/shared': 3.4.3
dev: true
/@vue/compiler-sfc@3.3.10:
resolution: {integrity: sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==}
dependencies:
'@babel/parser': 7.23.6
'@vue/compiler-core': 3.3.10
'@vue/compiler-dom': 3.3.10
'@vue/compiler-ssr': 3.3.10
'@vue/reactivity-transform': 3.3.10
'@vue/shared': 3.3.10
'@babel/parser': 7.24.1
'@vue/compiler-core': 3.4.21
'@vue/compiler-dom': 3.4.21
'@vue/compiler-ssr': 3.4.21
'@vue/shared': 3.4.21
estree-walker: 2.0.2
magic-string: 0.30.5
postcss: 8.4.32
source-map-js: 1.0.2
dev: true
/@vue/compiler-sfc@3.4.3:
resolution: {integrity: sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==}
dependencies:
'@babel/parser': 7.23.6
'@vue/compiler-core': 3.4.3
'@vue/compiler-dom': 3.4.3
'@vue/compiler-ssr': 3.4.3
'@vue/shared': 3.4.3
estree-walker: 2.0.2
magic-string: 0.30.5
magic-string: 0.30.8
postcss: 8.4.35
source-map-js: 1.0.2
dev: true
/@vue/compiler-ssr@3.3.10:
resolution: {integrity: sha512-12iM4jA4GEbskwXMmPcskK5wImc2ohKm408+o9iox3tfN9qua8xL0THIZtoe9OJHnXP4eOWZpgCAAThEveNlqQ==}
/@vue/compiler-ssr@3.4.21:
resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
dependencies:
'@vue/compiler-dom': 3.3.10
'@vue/shared': 3.3.10
dev: true
/@vue/compiler-ssr@3.4.3:
resolution: {integrity: sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==}
dependencies:
'@vue/compiler-dom': 3.4.3
'@vue/shared': 3.4.3
'@vue/compiler-dom': 3.4.21
'@vue/shared': 3.4.21
dev: true
/@vue/devtools-api@6.5.1:
resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
dev: true
/@vue/reactivity-transform@3.3.10:
resolution: {integrity: sha512-0xBdk+CKHWT+Gev8oZ63Tc0qFfj935YZx+UAynlutnrDZ4diFCVFMWixn65HzjE3S1iJppWOo6Tt1OzASH7VEg==}
/@vue/reactivity@3.4.21:
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
dependencies:
'@babel/parser': 7.23.6
'@vue/compiler-core': 3.3.10
'@vue/shared': 3.3.10
estree-walker: 2.0.2
magic-string: 0.30.5
'@vue/shared': 3.4.21
dev: true
/@vue/reactivity@3.3.10:
resolution: {integrity: sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==}
/@vue/runtime-core@3.4.21:
resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
dependencies:
'@vue/shared': 3.3.10
'@vue/reactivity': 3.4.21
'@vue/shared': 3.4.21
dev: true
/@vue/reactivity@3.4.3:
resolution: {integrity: sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==}
/@vue/runtime-dom@3.4.21:
resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
dependencies:
'@vue/shared': 3.4.3
dev: true
/@vue/runtime-core@3.3.10:
resolution: {integrity: sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==}
dependencies:
'@vue/reactivity': 3.3.10
'@vue/shared': 3.3.10
dev: true
/@vue/runtime-core@3.4.3:
resolution: {integrity: sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==}
dependencies:
'@vue/reactivity': 3.4.3
'@vue/shared': 3.4.3
dev: true
/@vue/runtime-dom@3.3.10:
resolution: {integrity: sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==}
dependencies:
'@vue/runtime-core': 3.3.10
'@vue/shared': 3.3.10
csstype: 3.1.2
dev: true
/@vue/runtime-dom@3.4.3:
resolution: {integrity: sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==}
dependencies:
'@vue/runtime-core': 3.4.3
'@vue/shared': 3.4.3
'@vue/runtime-core': 3.4.21
'@vue/shared': 3.4.21
csstype: 3.1.3
dev: true
/@vue/server-renderer@3.3.10(vue@3.3.10):
resolution: {integrity: sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==}
/@vue/server-renderer@3.4.21(vue@3.4.21):
resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
peerDependencies:
vue: 3.3.10
vue: 3.4.21
dependencies:
'@vue/compiler-ssr': 3.3.10
'@vue/shared': 3.3.10
vue: 3.3.10(typescript@5.3.3)
'@vue/compiler-ssr': 3.4.21
'@vue/shared': 3.4.21
vue: 3.4.21(typescript@5.3.3)
dev: true
/@vue/server-renderer@3.4.3(vue@3.4.3):
resolution: {integrity: sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==}
peerDependencies:
vue: 3.4.3
dependencies:
'@vue/compiler-ssr': 3.4.3
'@vue/shared': 3.4.3
vue: 3.4.3(typescript@5.3.3)
/@vue/shared@3.4.21:
resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
dev: true
/@vue/shared@3.3.10:
resolution: {integrity: sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==}
dev: true
/@vue/shared@3.4.3:
resolution: {integrity: sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==}
dev: true
/@vueuse/core@10.7.1(vue@3.4.3):
/@vueuse/core@10.7.1(vue@3.4.21):
resolution: {integrity: sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==}
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.7.1
'@vueuse/shared': 10.7.1(vue@3.4.3)
vue-demi: 0.14.6(vue@3.4.3)
'@vueuse/shared': 10.7.1(vue@3.4.21)
vue-demi: 0.14.6(vue@3.4.21)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: true
/@vueuse/integrations@10.7.1(focus-trap@7.5.4)(vue@3.4.3):
/@vueuse/integrations@10.7.1(focus-trap@7.5.4)(vue@3.4.21):
resolution: {integrity: sha512-cKo5LEeKVHdBRBtMTOrDPdR0YNtrmN9IBfdcnY2P3m5LHVrsD0xiHUtAH1WKjHQRIErZG6rJUa6GA4tWZt89Og==}
peerDependencies:
async-validator: '*'
@@ -1466,10 +1419,10 @@ packages:
universal-cookie:
optional: true
dependencies:
'@vueuse/core': 10.7.1(vue@3.4.3)
'@vueuse/shared': 10.7.1(vue@3.4.3)
'@vueuse/core': 10.7.1(vue@3.4.21)
'@vueuse/shared': 10.7.1(vue@3.4.21)
focus-trap: 7.5.4
vue-demi: 0.14.6(vue@3.4.3)
vue-demi: 0.14.6(vue@3.4.21)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -1479,10 +1432,10 @@ packages:
resolution: {integrity: sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==}
dev: true
/@vueuse/shared@10.7.1(vue@3.4.3):
/@vueuse/shared@10.7.1(vue@3.4.21):
resolution: {integrity: sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==}
dependencies:
vue-demi: 0.14.6(vue@3.4.3)
vue-demi: 0.14.6(vue@3.4.21)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -1506,8 +1459,8 @@ packages:
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
engines: {node: '>=0.4.0'}
/acorn@8.11.2:
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
/acorn@8.11.3:
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -1736,6 +1689,13 @@ packages:
run-applescript: 5.0.0
dev: false
/bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
dependencies:
run-applescript: 7.0.0
dev: false
/bundle-require@4.0.1(esbuild@0.19.8):
resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -1757,22 +1717,21 @@ packages:
safe-json-stringify: 1.2.0
dev: false
/c12@1.5.1:
resolution: {integrity: sha512-BWZRJgDEveT8uI+cliCwvYSSSSvb4xKoiiu5S0jaDbKBopQLQF7E+bq9xKk1pTcG+mUa3yXuFO7bD9d8Lr9Xxg==}
/c12@1.10.0:
resolution: {integrity: sha512-0SsG7UDhoRWcuSvKWHaXmu5uNjDCDN3nkQLRL4Q42IlFy+ze58FcCoI3uPwINXinkz7ZinbhEgyzYFw9u9ZV8g==}
dependencies:
chokidar: 3.5.3
chokidar: 3.6.0
confbox: 0.1.3
defu: 6.1.4
dotenv: 16.3.1
giget: 1.1.3
dotenv: 16.4.5
giget: 1.2.3
jiti: 1.21.0
mlly: 1.4.2
mlly: 1.6.1
ohash: 1.1.3
pathe: 1.1.1
pathe: 1.1.2
perfect-debounce: 1.0.0
pkg-types: 1.0.3
rc9: 2.1.1
transitivePeerDependencies:
- supports-color
dev: false
/cac@6.7.14:
@@ -1846,8 +1805,8 @@ packages:
dependencies:
get-func-name: 2.0.2
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
/chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
@@ -1883,11 +1842,14 @@ packages:
engines: {node: '>=8'}
dev: false
/ci-info@4.0.0:
resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==}
engines: {node: '>=8'}
/citty@0.1.6:
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
dependencies:
consola: 3.2.3
dev: false
/cli-boxes@3.0.0:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
@@ -2004,6 +1966,10 @@ packages:
typedarray: 0.0.6
dev: false
/confbox@0.1.3:
resolution: {integrity: sha512-eH3ZxAihl1PhKfpr4VfEN6/vUd87fmgb6JkldHgg/YR6aEBhW63qUDgzP2Y6WM0UumdsYp5H3kibalXAdHfbgg==}
dev: false
/config-chain@1.1.13:
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
dependencies:
@@ -2025,7 +1991,6 @@ packages:
/consola@3.2.3:
resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
engines: {node: ^14.18.0 || >=16.10.0}
dev: false
/convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -2140,6 +2105,11 @@ packages:
untildify: 4.0.0
dev: false
/default-browser-id@5.0.0:
resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
engines: {node: '>=18'}
dev: false
/default-browser@4.0.0:
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
engines: {node: '>=14.16'}
@@ -2150,6 +2120,14 @@ packages:
titleize: 3.0.0
dev: false
/default-browser@5.2.1:
resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
engines: {node: '>=18'}
dependencies:
bundle-name: 4.1.0
default-browser-id: 5.0.0
dev: false
/defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
dependencies:
@@ -2242,6 +2220,11 @@ packages:
engines: {node: '>=12'}
dev: false
/dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
dev: false
/dtrace-provider@0.8.8:
resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==}
engines: {node: '>=0.10'}
@@ -2705,6 +2688,20 @@ packages:
- supports-color
dev: false
/giget@1.2.3:
resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==}
hasBin: true
dependencies:
citty: 0.1.6
consola: 3.2.3
defu: 6.1.4
node-fetch-native: 1.6.4
nypm: 0.3.8
ohash: 1.1.3
pathe: 1.1.2
tar: 6.2.0
dev: false
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -3394,8 +3391,8 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/linkedom@0.16.1:
resolution: {integrity: sha512-kbK4txFSjGstS8aHkYo3sRSD7viQbE1TAlYRqiqU13oihzzQFp23D1OwW3VdAQJuzqzBB+1qo9Qvp0xOeoVKig==}
/linkedom@0.16.8:
resolution: {integrity: sha512-+HtHVHBb3yZKlP9pgcJdi1AIG9tsAuo+Qtlz+79cCTsxgQwDzajsZjYvpp+DEckCK/zoGVhzkADniYZQ57KcFQ==}
dependencies:
css-select: 5.1.0
cssom: 0.5.0
@@ -3471,7 +3468,7 @@ packages:
resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
engines: {node: '>=14'}
dependencies:
mlly: 1.4.2
mlly: 1.6.1
pkg-types: 1.0.3
/lodash.camelcase@4.3.0:
@@ -3544,10 +3541,16 @@ packages:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
/magic-string@0.30.8:
resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
/magicast@0.3.3:
resolution: {integrity: sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==}
dependencies:
'@babel/parser': 7.23.6
'@babel/parser': 7.24.1
'@babel/types': 7.23.9
source-map-js: 1.0.2
dev: true
@@ -3678,7 +3681,16 @@ packages:
/mlly@1.4.2:
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
dependencies:
acorn: 8.11.2
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.4.0
dev: false
/mlly@1.6.1:
resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==}
dependencies:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.4.0
@@ -3763,6 +3775,10 @@ packages:
resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==}
dev: false
/node-fetch-native@1.6.4:
resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
dev: false
/node-forge@1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'}
@@ -3865,6 +3881,18 @@ packages:
ufo: 1.4.0
dev: false
/nypm@0.3.8:
resolution: {integrity: sha512-IGWlC6So2xv6V4cIDmoV0SwwWx7zLG086gyqkyumteH2fIgCAM4nDVFB2iDRszDvmdSVW9xb1N+2KjQ6C7d4og==}
engines: {node: ^14.16.0 || >=16.10.0}
hasBin: true
dependencies:
citty: 0.1.6
consola: 3.2.3
execa: 8.0.1
pathe: 1.1.2
ufo: 1.4.0
dev: false
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -3892,7 +3920,7 @@ packages:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
dependencies:
destr: 2.0.2
node-fetch-native: 1.4.1
node-fetch-native: 1.6.4
ufo: 1.4.0
dev: false
@@ -3917,6 +3945,16 @@ packages:
dependencies:
mimic-fn: 4.0.0
/open@10.1.0:
resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==}
engines: {node: '>=18'}
dependencies:
default-browser: 5.2.1
define-lazy-prop: 3.0.0
is-inside-container: 1.0.0
is-wsl: 3.1.0
dev: false
/open@8.4.2:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
@@ -4116,7 +4154,7 @@ packages:
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
dependencies:
jsonc-parser: 3.2.0
mlly: 1.4.2
mlly: 1.6.1
pathe: 1.1.2
/postcss-load-config@4.0.1:
@@ -4135,15 +4173,6 @@ packages:
yaml: 2.3.4
dev: true
/postcss@8.4.32:
resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/postcss@8.4.35:
resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
engines: {node: ^10 || ^12 || >=14}
@@ -4444,6 +4473,11 @@ packages:
execa: 5.1.1
dev: false
/run-applescript@7.0.0:
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
engines: {node: '>=18'}
dev: false
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -4479,7 +4513,7 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
dependencies:
chokidar: 3.5.3
chokidar: 3.6.0
immutable: 4.3.4
source-map-js: 1.0.2
@@ -4834,7 +4868,7 @@ packages:
/strip-literal@1.3.0:
resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
dependencies:
acorn: 8.11.2
acorn: 8.11.3
/sucrase@3.32.0:
resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==}
@@ -4989,7 +5023,7 @@ packages:
dependencies:
bundle-require: 4.0.1(esbuild@0.19.8)
cac: 6.7.14
chokidar: 3.5.3
chokidar: 3.6.0
debug: 4.3.4
esbuild: 0.19.8
execa: 5.1.1
@@ -5147,8 +5181,8 @@ packages:
/unplugin@1.5.0:
resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==}
dependencies:
acorn: 8.11.2
chokidar: 3.5.3
acorn: 8.11.3
chokidar: 3.6.0
webpack-sources: 3.2.3
webpack-virtual-modules: 0.5.0
dev: false
@@ -5210,7 +5244,7 @@ packages:
dependencies:
cac: 6.7.14
debug: 4.3.4
pathe: 1.1.1
pathe: 1.1.2
picocolors: 1.0.0
vite: 5.1.3(@types/node@20.10.3)(sass@1.69.5)
transitivePeerDependencies:
@@ -5274,10 +5308,10 @@ packages:
'@docsearch/css': 3.5.2
'@docsearch/js': 3.5.2
'@types/markdown-it': 13.0.7
'@vitejs/plugin-vue': 5.0.2(vite@5.1.3)(vue@3.4.3)
'@vitejs/plugin-vue': 5.0.2(vite@5.1.3)(vue@3.4.21)
'@vue/devtools-api': 6.5.1
'@vueuse/core': 10.7.1(vue@3.4.3)
'@vueuse/integrations': 10.7.1(focus-trap@7.5.4)(vue@3.4.3)
'@vueuse/core': 10.7.1(vue@3.4.21)
'@vueuse/integrations': 10.7.1(focus-trap@7.5.4)(vue@3.4.21)
focus-trap: 7.5.4
mark.js: 8.11.1
minisearch: 6.3.0
@@ -5286,7 +5320,7 @@ packages:
shikiji-core: 0.9.16
shikiji-transformers: 0.9.16
vite: 5.1.3(@types/node@20.10.3)(sass@1.69.5)
vue: 3.4.3(typescript@5.3.3)
vue: 3.4.21(typescript@5.3.3)
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/node'
@@ -5326,6 +5360,14 @@ packages:
vitest: 1.2.2(@types/node@20.10.3)(happy-dom@13.3.8)(sass@1.69.5)
dev: true
/vitest-plugin-random-seed@1.0.2(vite@5.1.3):
resolution: {integrity: sha512-Kt5QHmx7I1p2WShsmgJSK/L8hNPZpyNu85zSml5yv/fHDe7gJspcKc7kNYrLVVwZdGcqcZxyHAeH503szzQ95g==}
peerDependencies:
vite: ^4.0.0 || ^5.0.0
dependencies:
vite: 5.1.3(@types/node@20.10.3)(sass@1.69.5)
dev: true
/vitest@1.2.2(@types/node@20.10.3)(happy-dom@13.3.8)(sass@1.69.5):
resolution: {integrity: sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -5391,7 +5433,7 @@ packages:
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
dev: true
/vue-demi@0.14.6(vue@3.4.3):
/vue-demi@0.14.6(vue@3.4.21):
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
engines: {node: '>=12'}
hasBin: true
@@ -5403,38 +5445,22 @@ packages:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.4.3(typescript@5.3.3)
vue: 3.4.21(typescript@5.3.3)
dev: true
/vue@3.3.10(typescript@5.3.3):
resolution: {integrity: sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==}
/vue@3.4.21(typescript@5.3.3):
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@vue/compiler-dom': 3.3.10
'@vue/compiler-sfc': 3.3.10
'@vue/runtime-dom': 3.3.10
'@vue/server-renderer': 3.3.10(vue@3.3.10)
'@vue/shared': 3.3.10
typescript: 5.3.3
dev: true
/vue@3.4.3(typescript@5.3.3):
resolution: {integrity: sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@vue/compiler-dom': 3.4.3
'@vue/compiler-sfc': 3.4.3
'@vue/runtime-dom': 3.4.3
'@vue/server-renderer': 3.4.3(vue@3.4.3)
'@vue/shared': 3.4.3
'@vue/compiler-dom': 3.4.21
'@vue/compiler-sfc': 3.4.21
'@vue/runtime-dom': 3.4.21
'@vue/server-renderer': 3.4.21(vue@3.4.21)
'@vue/shared': 3.4.21
typescript: 5.3.3
dev: true
+12 -17
View File
@@ -8,6 +8,10 @@ import { consola } from 'consola';
import pMap from 'p-map';
import os from 'node:os';
import path from 'node:path';
import {
virtualModuleNames,
virtualEntrypointModuleNames,
} from '~/core/utils/virtual-modules';
const spinnerText = 'Building WXT';
const spinner = ora(spinnerText).start();
@@ -20,12 +24,7 @@ const preset = {
dts: true,
silent: true,
sourcemap: false,
external: [
'virtual:user-unlisted-script',
'virtual:user-content-script-isolated-world',
'virtual:user-content-script-main-world',
'virtual:user-background',
],
external: virtualEntrypointModuleNames.map((name) => `virtual:user-${name}`),
} satisfies tsup.Options;
function spinnerPMap(configs: tsup.Options[]) {
@@ -74,17 +73,13 @@ const config: tsup.Options[] = [
},
{
...preset,
entry: {
'virtual/background-entrypoint': 'src/virtual/background-entrypoint.ts',
'virtual/content-script-isolated-world-entrypoint':
'src/virtual/content-script-isolated-world-entrypoint.ts',
'virtual/content-script-main-world-entrypoint':
'src/virtual/content-script-main-world-entrypoint.ts',
'virtual/mock-browser': 'src/virtual/mock-browser.ts',
'virtual/reload-html': 'src/virtual/reload-html.ts',
'virtual/unlisted-script-entrypoint':
'src/virtual/unlisted-script-entrypoint.ts',
},
entry: virtualModuleNames.reduce<Record<string, string>>(
(acc, moduleName) => {
acc[`virtual/${moduleName}`] = `src/virtual/${moduleName}.ts`;
return acc;
},
{},
),
format: ['esm'],
splitting: false,
dts: false,
+1 -1
View File
@@ -51,7 +51,7 @@ export type {
Identity,
Idle,
Management,
Manifest,
Manifest, // TODO: Export custom manifest types that are valid for both Chrome and Firefox.
ContextMenus,
Menus,
NetworkStatus,
+14
View File
@@ -116,6 +116,20 @@ describe('CLI', () => {
});
});
it('should respect passing --port', async () => {
const expectedPort = 3100;
mockArgv('--port', String(expectedPort));
await importCli();
expect(createServerMock).toBeCalledWith({
dev: {
server: {
port: expectedPort,
},
},
});
});
it('should respect passing --debug', async () => {
mockArgv('--debug');
await importCli();
+3 -2
View File
@@ -1,5 +1,6 @@
import { CAC, Command } from 'cac';
import consola, { LogLevels } from 'consola';
import { filterTruthy, toArray } from '~/core/utils/arrays';
import { printHeader } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import { ValidationError } from '~/core/utils/validation';
@@ -56,8 +57,8 @@ export function getArrayFromFlags<T>(
flags: any,
name: string,
): T[] | undefined {
const array = [flags[name]].flat() as Array<T | undefined>;
const result = array.filter((item) => item != null) as T[];
const array = toArray<T | undefined>(flags[name]);
const result = filterTruthy(array);
return result.length ? result : undefined;
}
+16 -1
View File
@@ -16,6 +16,7 @@ cli
.option('-c, --config <file>', 'use specified config file')
.option('-m, --mode <mode>', 'set env mode')
.option('-b, --browser <browser>', 'specify a browser')
.option('-p, --port <port>', 'specify a port for the dev server')
.option(
'-e, --filter-entrypoint <entrypoint>',
'only build specific entrypoints',
@@ -35,6 +36,14 @@ cli
configFile: flags.config,
debug: flags.debug,
filterEntrypoints: getArrayFromFlags(flags, 'filterEntrypoint'),
dev:
flags.port == null
? undefined
: {
server: {
port: parseInt(flags.port),
},
},
});
await server.start();
return { isOngoing: true };
@@ -57,6 +66,7 @@ cli
.option('--mv3', 'target manifest v3')
.option('--mv2', 'target manifest v2')
.option('--analyze', 'visualize extension bundle')
.option('--analyze-open', 'automatically open stats.html in browser')
.action(
wrapAction(async (root, flags) => {
await build({
@@ -66,7 +76,12 @@ cli
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
analysis: flags.analyze ? { enabled: true } : undefined,
analysis: flags.analyze
? {
enabled: true,
open: flags.analyzeOpen,
}
: undefined,
filterEntrypoints: getArrayFromFlags(flags, 'filterEntrypoint'),
});
}),
@@ -404,4 +404,62 @@ describe('Content Script UIs', () => {
});
});
});
describe('mounted value', () => {
describe('integrated', () => {
it('should set the mounted value based on the onMounted return value', () => {
const expected = Symbol();
const ui = createIntegratedUi(new ContentScriptContext('test'), {
position: 'inline',
onMount: () => expected,
});
expect(ui.mounted).toBeUndefined();
ui.mount();
expect(ui.mounted).toBe(expected);
ui.remove();
expect(ui.mounted).toBeUndefined();
});
});
describe('iframe', () => {
it('should set the mounted value based on the onMounted return value', async () => {
const expected = Symbol();
const ui = createIframeUi(new ContentScriptContext('test'), {
page: '',
position: 'inline',
onMount: () => expected,
});
expect(ui.mounted).toBeUndefined();
ui.mount();
expect(ui.mounted).toBe(expected);
ui.remove();
expect(ui.mounted).toBeUndefined();
});
});
describe('shadow-root', () => {
it('should set the mounted value based on the onMounted return value', async () => {
const expected = Symbol();
const ui = await createShadowRootUi(new ContentScriptContext('test'), {
name: 'test',
position: 'inline',
onMount: () => expected,
});
expect(ui.mounted).toBeUndefined();
ui.mount();
expect(ui.mounted).toBe(expected);
ui.remove();
expect(ui.mounted).toBeUndefined();
});
});
});
});
+14 -4
View File
@@ -35,12 +35,15 @@ export function createIntegratedUi<TMounted>(
const remove = () => {
options.onRemove?.(mounted);
wrapper.remove();
mounted = undefined;
};
ctx.onInvalidated(remove);
return {
mounted,
get mounted() {
return mounted;
},
wrapper,
mount,
remove,
@@ -71,12 +74,15 @@ export function createIframeUi<TMounted>(
const remove = () => {
options.onRemove?.(mounted);
wrapper.remove();
mounted = undefined;
};
ctx.onInvalidated(remove);
return {
mounted,
get mounted() {
return mounted;
},
iframe,
wrapper,
mount,
@@ -116,7 +122,7 @@ export async function createShadowRootUi<TMounted>(
});
shadowHost.setAttribute('data-wxt-shadow-root', '');
let mounted: TMounted;
let mounted: TMounted | undefined;
const mount = () => {
// Add shadow root element to DOM
@@ -134,6 +140,8 @@ export async function createShadowRootUi<TMounted>(
// Remove children from uiContainer
while (uiContainer.lastChild)
uiContainer.removeChild(uiContainer.lastChild);
// Clear mounted value
mounted = undefined;
};
ctx.onInvalidated(remove);
@@ -144,7 +152,9 @@ export async function createShadowRootUi<TMounted>(
uiContainer,
mount,
remove,
mounted: mounted!,
get mounted() {
return mounted;
},
};
}
+1 -1
View File
@@ -207,7 +207,7 @@ export interface ContentScriptAnchoredOptions {
* In combination with `anchor`, decide how to add the UI to the DOM.
*
* - `"last"` (default) - Add the UI as the last child of the `anchor` element
* - `"first"` - Add the UI as the last child of the `anchor` element
* - `"first"` - Add the UI as the first child of the `anchor` element
* - `"replace"` - Replace the `anchor` element with the UI.
* - `"before"` - Add the UI as the sibling before the `anchor` element
* - `"after"` - Add the UI as the sibling after the `anchor` element
+33 -26
View File
@@ -2,23 +2,28 @@ import type * as vite from 'vite';
import {
BuildStepOutput,
Entrypoint,
InlineConfig,
ResolvedConfig,
UserConfig,
VirtualEntrypointType,
WxtBuilder,
WxtBuilderServer,
WxtDevServer,
WxtHooks,
} from '~/types';
import * as wxtPlugins from './plugins';
import {
getEntrypointBundlePath,
isHtmlEntrypoint,
} from '~/core/utils/entrypoints';
import {
VirtualEntrypointType,
VirtualModuleId,
} from '~/core/utils/virtual-modules';
import { Hookable } from 'hookable';
import { toArray } from '~/core/utils/arrays';
export async function createViteBuilder(
inlineConfig: InlineConfig,
userConfig: UserConfig,
wxtConfig: Omit<ResolvedConfig, 'builder'>,
wxtConfig: ResolvedConfig,
hooks: Hookable<WxtHooks>,
server?: WxtDevServer,
): Promise<WxtBuilder> {
const vite = await import('vite');
@@ -26,14 +31,9 @@ export async function createViteBuilder(
* Returns the base vite config shared by all builds based on the inline and user config.
*/
const getBaseConfig = async () => {
const resolvedInlineConfig =
(await inlineConfig.vite?.(wxtConfig.env)) ?? {};
const resolvedUserConfig = (await userConfig.vite?.(wxtConfig.env)) ?? {};
const config: vite.InlineConfig = vite.mergeConfig(
resolvedUserConfig,
resolvedInlineConfig,
);
const config: vite.InlineConfig = await wxtConfig.vite({
...wxtConfig.env,
});
config.root = wxtConfig.root;
config.configFile = false;
@@ -41,6 +41,8 @@ export async function createViteBuilder(
config.mode = wxtConfig.mode;
config.build ??= {};
config.publicDir = wxtConfig.publicDir;
config.build.copyPublicDir = false;
config.build.outDir = wxtConfig.outDir;
config.build.emptyOutDir = false;
// Disable minification for the dev command
@@ -55,13 +57,10 @@ export async function createViteBuilder(
config.plugins ??= [];
config.plugins.push(
wxtPlugins.download(wxtConfig),
wxtPlugins.devHtmlPrerender(wxtConfig),
wxtPlugins.devHtmlPrerender(wxtConfig, server),
wxtPlugins.unimport(wxtConfig),
wxtPlugins.virtualEntrypoint('background', wxtConfig),
wxtPlugins.virtualEntrypoint('content-script-isolated-world', wxtConfig),
wxtPlugins.virtualEntrypoint('content-script-main-world', wxtConfig),
wxtPlugins.virtualEntrypoint('unlisted-script', wxtConfig),
wxtPlugins.devServerGlobals(wxtConfig),
wxtPlugins.resolveVirtualModules(wxtConfig),
wxtPlugins.devServerGlobals(wxtConfig, server),
wxtPlugins.tsconfigPaths(wxtConfig),
wxtPlugins.noopBackground(),
wxtPlugins.globals(wxtConfig),
@@ -205,6 +204,11 @@ export async function createViteBuilder(
else entryConfig = getLibModeConfig(group);
const buildConfig = vite.mergeConfig(await getBaseConfig(), entryConfig);
await hooks.callHook(
'vite:build:extendConfig',
toArray(group),
buildConfig,
);
const result = await vite.build(buildConfig);
return {
entrypoints: group,
@@ -221,9 +225,9 @@ export async function createViteBuilder(
},
};
const baseConfig = await getBaseConfig();
const viteServer = await vite.createServer(
vite.mergeConfig(baseConfig, serverConfig),
);
const finalConfig = vite.mergeConfig(baseConfig, serverConfig);
await hooks.callHook('vite:devServer:extendConfig', finalConfig);
const viteServer = await vite.createServer(finalConfig);
const server: WxtBuilderServer = {
async listen() {
@@ -277,7 +281,10 @@ function getRollupEntry(entrypoint: Entrypoint): string {
: 'content-script-isolated-world';
break;
}
return virtualEntrypointType
? `virtual:wxt-${virtualEntrypointType}?${entrypoint.inputPath}`
: entrypoint.inputPath;
if (virtualEntrypointType) {
const moduleId: VirtualModuleId = `virtual:wxt-${virtualEntrypointType}-entrypoint`;
return `${moduleId}?${entrypoint.inputPath}`;
}
return entrypoint.inputPath;
}
@@ -5,9 +5,7 @@ import path from 'node:path';
let increment = 0;
export function bundleAnalysis(
config: Omit<ResolvedConfig, 'builder'>,
): vite.Plugin {
export function bundleAnalysis(config: ResolvedConfig): vite.Plugin {
return visualizer({
template: 'raw-data',
filename: path.resolve(
@@ -14,7 +14,7 @@ import { getEntrypointBundlePath } from '~/core/utils/entrypoints';
*/
export function cssEntrypoints(
entrypoint: Entrypoint,
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
): vite.Plugin {
return {
name: 'wxt:css-entrypoint',
@@ -12,7 +12,8 @@ let reactRefreshPreamble = '';
* Pre-renders the HTML entrypoints when building the extension to connect to the dev server.
*/
export function devHtmlPrerender(
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
server: WxtDevServer | undefined,
): vite.PluginOption {
const htmlReloadId = '@wxt/reload-html';
const resolvedHtmlReloadId = resolve(
@@ -38,7 +39,6 @@ export function devHtmlPrerender(
// Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
// before the paths are replaced with their bundled path
transform(code, id) {
const server = config.server;
if (
config.command !== 'serve' ||
server == null ||
@@ -68,7 +68,6 @@ export function devHtmlPrerender(
// Pass the HTML through the dev server to add dev-mode specific code
async transformIndexHtml(html, ctx) {
const server = config.server;
if (config.command !== 'serve' || server == null) return;
const originalUrl = `${server.origin}${ctx.path}`;
@@ -134,7 +133,7 @@ export function devHtmlPrerender(
}
export function pointToDevServer(
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
server: WxtDevServer,
id: string,
document: Document,
@@ -1,22 +1,23 @@
import { Plugin } from 'vite';
import { ResolvedConfig } from '~/types';
import { ResolvedConfig, WxtDevServer } from '~/types';
/**
* Defines global constants about the dev server. Helps scripts connect to the server's web socket.
*/
export function devServerGlobals(
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
server: WxtDevServer | undefined,
): Plugin {
return {
name: 'wxt:dev-server-globals',
config() {
if (config.server == null || config.command == 'build') return;
if (server == null || config.command == 'build') return;
return {
define: {
__DEV_SERVER_PROTOCOL__: JSON.stringify('ws:'),
__DEV_SERVER_HOSTNAME__: JSON.stringify(config.server.hostname),
__DEV_SERVER_PORT__: JSON.stringify(config.server.port),
__DEV_SERVER_HOSTNAME__: JSON.stringify(server.hostname),
__DEV_SERVER_PORT__: JSON.stringify(server.port),
},
};
},
+1 -1
View File
@@ -9,7 +9,7 @@ import { fetchCached } from '~/core/utils/network';
* @example
* import "url:https://google-tagmanager.com/gtag?id=XYZ";
*/
export function download(config: Omit<ResolvedConfig, 'builder'>): Plugin {
export function download(config: ResolvedConfig): Plugin {
return {
name: 'wxt:download',
resolveId(id) {
@@ -6,9 +6,7 @@ import type * as vite from 'vite';
* `webextension-polyfill` module to a virtual module and exporting the `chrome` global from the
* virtual module.
*/
export function excludeBrowserPolyfill(
config: Omit<ResolvedConfig, 'builder'>,
): vite.Plugin {
export function excludeBrowserPolyfill(config: ResolvedConfig): vite.Plugin {
const virtualId = 'virtual:wxt-webextension-polyfill-disabled';
return {
+1 -3
View File
@@ -2,9 +2,7 @@ import type * as vite from 'vite';
import { ResolvedConfig } from '~/types';
import { getGlobals } from '~/core/utils/globals';
export function globals(
config: Omit<ResolvedConfig, 'builder'>,
): vite.PluginOption {
export function globals(config: ResolvedConfig): vite.PluginOption {
return {
name: 'wxt:globals',
config() {
+1 -1
View File
@@ -3,7 +3,7 @@ export * from './devServerGlobals';
export * from './download';
export * from './multipageMove';
export * from './unimport';
export * from './virtualEntrypoint';
export * from './resolveVirtualModules';
export * from './tsconfigPaths';
export * from './noopBackground';
export * from './cssEntrypoints';
@@ -20,7 +20,7 @@ import { normalizePath } from '~/core/utils/paths';
*/
export function multipageMove(
entrypoints: Entrypoint[],
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
): vite.Plugin {
return {
name: 'wxt:multipage-move',
@@ -0,0 +1,41 @@
import { Plugin } from 'vite';
import { ResolvedConfig } from '~/types';
import { normalizePath } from '~/core/utils/paths';
import {
VirtualModuleId,
virtualModuleNames,
} from '~/core/utils/virtual-modules';
import fs from 'fs-extra';
import { resolve } from 'path';
/**
* Resolve all the virtual modules to the `node_modules/wxt/dist/virtual` directory.
*/
export function resolveVirtualModules(config: ResolvedConfig): Plugin[] {
return virtualModuleNames.map((name) => {
const virtualId: `${VirtualModuleId}?` = `virtual:wxt-${name}?`;
const resolvedVirtualId = '\0' + virtualId;
return {
name: `wxt:resolve-virtual-${name}`,
resolveId(id) {
// Id doesn't start with prefix, it looks like this:
// /path/to/project/virtual:wxt-background?/path/to/project/entrypoints/background.ts
const index = id.indexOf(virtualId);
if (index === -1) return;
const inputPath = normalizePath(id.substring(index + virtualId.length));
return resolvedVirtualId + inputPath;
},
async load(id) {
if (!id.startsWith(resolvedVirtualId)) return;
const inputPath = id.replace(resolvedVirtualId, '');
const template = await fs.readFile(
resolve(config.wxtModuleDir, `dist/virtual/${name}.js`),
'utf-8',
);
return template.replace(`virtual:user-${name}`, inputPath);
},
};
});
}
@@ -1,9 +1,7 @@
import { ResolvedConfig } from '~/types';
import type * as vite from 'vite';
export function tsconfigPaths(
config: Omit<ResolvedConfig, 'builder'>,
): vite.Plugin {
export function tsconfigPaths(config: ResolvedConfig): vite.Plugin {
return {
name: 'wxt:aliases',
async config() {
+1 -3
View File
@@ -15,9 +15,7 @@ const ENABLED_EXTENSIONS = new Set([
/**
* Inject any global imports defined by unimport
*/
export function unimport(
config: Omit<ResolvedConfig, 'builder'>,
): vite.PluginOption {
export function unimport(config: ResolvedConfig): vite.PluginOption {
const options = config.imports;
if (options === false) return [];
@@ -1,39 +0,0 @@
import { Plugin } from 'vite';
import { ResolvedConfig, VirtualEntrypointType } from '~/types';
import fs from 'fs-extra';
import { resolve } from 'path';
import { normalizePath } from '~/core/utils/paths';
/**
* Wraps a user's entrypoint with a vitual version with additional logic.
*/
export function virtualEntrypoint(
type: VirtualEntrypointType,
config: Omit<ResolvedConfig, 'builder'>,
): Plugin {
const virtualId = `virtual:wxt-${type}?`;
const resolvedVirtualId = `\0${virtualId}`;
return {
name: `wxt:virtual-entrypoint`,
resolveId(id) {
// Id doesn't start with prefix, it looks like this:
// /path/to/project/virtual:background?/path/to/project/entrypoints/background.ts
const index = id.indexOf(virtualId);
if (index === -1) return;
const inputPath = normalizePath(id.substring(index + virtualId.length));
return resolvedVirtualId + inputPath;
},
async load(id) {
if (!id.startsWith(resolvedVirtualId)) return;
const inputPath = id.replace(resolvedVirtualId, '');
const template = await fs.readFile(
resolve(config.wxtModuleDir, `dist/virtual/${type}-entrypoint.js`),
'utf-8',
);
return template.replace(`virtual:user-${type}`, inputPath);
},
};
}
@@ -16,7 +16,7 @@ import { ResolvedConfig } from '~/types';
* `npm list` and inline them automatically.
*/
export function webextensionPolyfillMock(
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
): vite.PluginOption {
return {
name: 'wxt:testing-inline-deps',
+93 -91
View File
@@ -26,7 +26,10 @@ import pc from 'picocolors';
import { relative } from 'node:path';
import { registerWxt, wxt } from './wxt';
import { unnormalizePath } from './utils/paths';
import { mapWxtOptionsToRegisteredContentScript } from './utils/content-scripts';
import {
getContentScriptJs,
mapWxtOptionsToRegisteredContentScript,
} from './utils/content-scripts';
/**
* Creates a dev server and pre-builds all the files that need to exist before loading the extension.
@@ -40,14 +43,63 @@ import { mapWxtOptionsToRegisteredContentScript } from './utils/content-scripts'
export async function createServer(
inlineConfig?: InlineConfig,
): Promise<WxtDevServer> {
const port = await getPort();
const hostname = 'localhost';
const origin = `http://${hostname}:${port}`;
const serverInfo: ServerInfo = {
port,
hostname,
origin,
};
await registerWxt('serve', inlineConfig, async (config) => {
const { port, hostname } = config.dev.server!;
const serverInfo: ServerInfo = {
port,
hostname,
origin: `http://${hostname}:${port}`,
};
// Server instance must be created first so its reference can be added to the internal config used
// to pre-render entrypoints
const server: WxtDevServer = {
...serverInfo,
get watcher() {
return builderServer.watcher;
},
get ws() {
return builderServer.ws;
},
currentOutput: undefined,
async start() {
await builderServer.listen();
wxt.logger.success(`Started dev server @ ${serverInfo.origin}`);
await buildAndOpenBrowser();
},
async stop() {
await runner.closeBrowser();
await builderServer.close();
},
async restart() {
await closeAndRecreateRunner();
await buildAndOpenBrowser();
},
transformHtml(url, html, originalUrl) {
return builderServer.transformHtml(url, html, originalUrl);
},
reloadContentScript(payload) {
server.ws.send('wxt:reload-content-script', payload);
},
reloadPage(path) {
server.ws.send('wxt:reload-page', path);
},
reloadExtension() {
server.ws.send('wxt:reload-extension');
},
async restartBrowser() {
await closeAndRecreateRunner();
await runner.openBrowser();
},
};
return server;
});
const server = wxt.server!;
let [runner, builderServer] = await Promise.all([
createExtensionRunner(),
wxt.builder.createServer(server),
]);
const buildAndOpenBrowser = async () => {
// Build after starting the dev server so it can be used to transform HTML files
@@ -74,55 +126,6 @@ export async function createServer(
runner = await createExtensionRunner();
};
// Server instance must be created first so its reference can be added to the internal config used
// to pre-render entrypoints
const server: WxtDevServer = {
...serverInfo,
get watcher() {
return builderServer.watcher;
},
get ws() {
return builderServer.ws;
},
currentOutput: undefined,
async start() {
await builderServer.listen();
wxt.logger.success(`Started dev server @ ${serverInfo.origin}`);
await buildAndOpenBrowser();
},
async stop() {
await runner.closeBrowser();
await builderServer.close();
},
async restart() {
await closeAndRecreateRunner();
await buildAndOpenBrowser();
},
transformHtml(url, html, originalUrl) {
return builderServer.transformHtml(url, html, originalUrl);
},
reloadContentScript(payload) {
server.ws.send('wxt:reload-content-script', payload);
},
reloadPage(path) {
server.ws.send('wxt:reload-page', path);
},
reloadExtension() {
server.ws.send('wxt:reload-extension');
},
async restartBrowser() {
await closeAndRecreateRunner();
await runner.openBrowser();
},
};
await registerWxt('serve', inlineConfig, server);
let [runner, builderServer] = await Promise.all([
createExtensionRunner(),
wxt.config.builder.createServer(server),
]);
// Register content scripts for the first time after the background starts up since they're not
// listed in the manifest
server.ws.on('wxt:background-initialized', () => {
@@ -137,11 +140,6 @@ export async function createServer(
return server;
}
async function getPort(): Promise<number> {
const { default: getPort, portNumbers } = await import('get-port');
return await getPort({ port: portNumbers(3000, 3010) });
}
/**
* Returns a function responsible for reloading different parts of the extension when a file
* changes.
@@ -189,34 +187,38 @@ function createFileReloader(server: WxtDevServer) {
// Rebuild entrypoints on change
const allEntrypoints = await findEntrypoints();
const { output: newOutput } = await rebuild(
allEntrypoints,
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
changes.rebuildGroups,
changes.cachedOutput,
);
server.currentOutput = newOutput;
try {
const { output: newOutput } = await rebuild(
allEntrypoints,
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
changes.rebuildGroups,
changes.cachedOutput,
);
server.currentOutput = newOutput;
// Perform reloads
switch (changes.type) {
case 'extension-reload':
server.reloadExtension();
consola.success(`Reloaded extension`);
break;
case 'html-reload':
const { reloadedNames } = reloadHtmlPages(
changes.rebuildGroups,
server,
);
consola.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
break;
case 'content-script-reload':
reloadContentScripts(changes.changedSteps, server);
const rebuiltNames = changes.rebuildGroups
.flat()
.map((entry) => entry.name);
consola.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
break;
// Perform reloads
switch (changes.type) {
case 'extension-reload':
server.reloadExtension();
consola.success(`Reloaded extension`);
break;
case 'html-reload':
const { reloadedNames } = reloadHtmlPages(
changes.rebuildGroups,
server,
);
consola.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
break;
case 'content-script-reload':
reloadContentScripts(changes.changedSteps, server);
const rebuiltNames = changes.rebuildGroups
.flat()
.map((entry) => entry.name);
consola.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
break;
}
} catch (err) {
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
}
});
};
@@ -233,7 +235,7 @@ function reloadContentScripts(steps: BuildStepOutput[], server: WxtDevServer) {
const entry = step.entrypoints;
if (Array.isArray(entry) || entry.type !== 'content-script') return;
const js = [getEntrypointBundlePath(entry, wxt.config.outDir, '.js')];
const js = getContentScriptJs(wxt.config, entry);
const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
const css = getContentScriptCssFiles([entry], cssMap);
+12 -1
View File
@@ -59,6 +59,16 @@ export async function initialize(options: {
input.template ??= defaultTemplate;
input.packageManager ??= options.packageManager;
const isExists = await fs.pathExists(input.directory);
if (isExists) {
const isEmpty = (await fs.readdir(input.directory)).length === 0;
if (!isEmpty) {
consola.error(
`The directory ${path.resolve(input.directory)} is not empty. Aborted.`,
);
process.exit(1);
}
}
await cloneProject(input);
const cdPath = path.relative(process.cwd(), path.resolve(input.directory));
@@ -117,7 +127,8 @@ async function listTemplates(): Promise<Template[]> {
return l.name.localeCompare(r.name);
});
} catch (err) {
throw Error(`Cannot load templates: ${JSON.stringify(err, null, 2)}`);
consola.error(err);
throw Error(`Failed to load templates`);
}
}
@@ -10,7 +10,7 @@ describe('NPM Package Management Utils', () => {
beforeAll(async () => {
// NPM needs the modules installed for 'npm ls' to work
await execaCommand('npm i', { cwd });
});
}, 60e3);
describe('listDependencies', () => {
it('should list direct dependencies', async () => {
+1 -1
View File
@@ -7,7 +7,7 @@ export const pnpm: WxtPackageManagerImpl = {
return npm.downloadDependency(...args);
},
async listDependencies(options) {
const args = ['ls', '--json'];
const args = ['ls', '-r', '--json'];
if (options?.all) {
args.push('--depth', 'Infinity');
}
+145 -4
View File
@@ -111,6 +111,7 @@ describe('Manifest Utils', () => {
setFakeWxt({
config: {
outDir,
manifestVersion: 3,
manifest: {
action: {
default_icon: 'icon-16.png',
@@ -119,13 +120,83 @@ describe('Manifest Utils', () => {
},
},
});
const expected: Partial<Manifest.WebExtensionManifest> = {
action: wxt.config.manifest.action,
};
const { manifest: actual } = await generateManifest([], buildOutput);
expect(actual).toMatchObject(expected);
expect(actual.action).toEqual(wxt.config.manifest.action);
expect(actual.browser_action).toBeUndefined();
expect(actual.page_action).toBeUndefined();
});
it('should generate `browser_action` for MV2 when only `action` is defined', async () => {
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
outDir,
manifestVersion: 2,
manifest: {
action: {
default_title: 'Action',
},
},
},
});
const { manifest: actual } = await generateManifest([], buildOutput);
expect(actual.action).toBeUndefined();
expect(actual.browser_action).toEqual(wxt.config.manifest.action);
expect(actual.page_action).toBeUndefined();
});
it('should keep the `page_action` for MV2 when both `action` and `page_action` are defined', async () => {
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
outDir,
manifestVersion: 2,
manifest: {
action: {
default_title: 'Action',
},
page_action: {
default_title: 'Page Action',
},
},
},
});
const { manifest: actual } = await generateManifest([], buildOutput);
expect(actual.action).toBeUndefined();
expect(actual.browser_action).toBeUndefined();
expect(actual.page_action).toEqual(wxt.config.manifest.page_action);
});
it('should keep the custom `browser_action` for MV2 when both `action` and `browser_action` are defined', async () => {
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
outDir,
manifestVersion: 2,
manifest: {
action: {
default_title: 'Action',
},
browser_action: {
default_title: 'Browser Action',
},
},
},
});
const { manifest: actual } = await generateManifest([], buildOutput);
expect(actual.action).toBeUndefined();
expect(actual.browser_action).toEqual(
wxt.config.manifest.browser_action,
);
expect(actual.page_action).toBeUndefined();
});
});
@@ -1306,6 +1377,76 @@ describe('Manifest Utils', () => {
expect(actual.commands).toBeUndefined();
});
});
describe('Stripping keys', () => {
const mv2Manifest = {
page_action: {},
browser_action: {},
automation: {},
content_capabilities: {},
converted_from_user_script: {},
current_locale: {},
differential_fingerprint: {},
event_rules: {},
file_browser_handlers: {},
file_system_provider_capabilities: {},
input_components: {},
nacl_modules: {},
natively_connectable: {},
offline_enabled: {},
platforms: {},
replacement_web_app: {},
system_indicator: {},
user_scripts: {},
};
const mv3Manifest = {
action: {},
export: {},
optional_host_permissions: {},
side_panel: {},
};
const hostPermissionsManifest = {
host_permissions: {},
};
const manifest: any = {
...mv2Manifest,
...mv3Manifest,
...hostPermissionsManifest,
};
it.each([
['firefox', 2, mv2Manifest],
['chrome', 2, { ...mv2Manifest, ...hostPermissionsManifest }],
['safari', 2, { ...mv2Manifest, ...hostPermissionsManifest }],
['edge', 2, { ...mv2Manifest, ...hostPermissionsManifest }],
['firefox', 3, { ...mv3Manifest, ...hostPermissionsManifest }],
['chrome', 3, { ...mv3Manifest, ...hostPermissionsManifest }],
['safari', 3, { ...mv3Manifest, ...hostPermissionsManifest }],
['edge', 3, { ...mv3Manifest, ...hostPermissionsManifest }],
] as const)(
"%s MV%s should only include that version's keys",
async (browser, manifestVersion, expected) => {
setFakeWxt({
config: {
browser,
manifest,
manifestVersion,
command: 'build',
},
});
const output = fakeBuildOutput();
const { manifest: actual } = await generateManifest([], output);
expect(actual).toEqual({
name: expect.any(String),
version: expect.any(String),
manifest_version: manifestVersion,
...expected,
});
},
);
});
});
describe('stripPathFromMatchPattern', () => {
@@ -0,0 +1,20 @@
import { describe, it } from 'vitest';
import {
VirtualModuleId,
VirtualModuleName,
VirtualEntrypointType,
VirtualEntrypointModuleName,
} from '../virtual-modules';
describe('Virtual Modules', () => {
it('should resolve types to litteral values, not string', () => {
// @ts-expect-error
const _c: VirtualEntrypointType = '';
// @ts-expect-error
const _d: VirtualEntrypointModuleName = '';
// @ts-expect-error
const _b: VirtualModuleName = '';
// @ts-expect-error
const _a: VirtualModuleId = '';
});
});
+11
View File
@@ -21,3 +21,14 @@ export function some<T>(
if (predicate(array[i], i)) return true;
return false;
}
/**
* Convert an item or array to an array.
*/
export function toArray<T>(a: T | T[]): T[] {
return Array.isArray(a) ? a : [a];
}
export function filterTruthy<T>(array: Array<T | undefined>): T[] {
return array.filter((item) => !!item) as T[];
}
+4 -2
View File
@@ -5,6 +5,7 @@ import { dirname, resolve } from 'path';
import type { Ora } from 'ora';
import pc from 'picocolors';
import { wxt } from '../../wxt';
import { toArray } from '../arrays';
export async function buildEntrypoints(
groups: EntrypointGroup[],
@@ -13,13 +14,14 @@ export async function buildEntrypoints(
const steps: BuildStepOutput[] = [];
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
const groupNames = [group].flat().map((e) => e.name);
const groupNames = toArray(group).map((e) => e.name);
const groupNameColored = groupNames.join(pc.dim(', '));
spinner.text =
pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNameColored}`;
try {
steps.push(await wxt.config.builder.build(group));
steps.push(await wxt.builder.build(group));
} catch (err) {
spinner.stop().clear();
wxt.logger.error(err);
throw Error(`Failed to build ${groupNames.join(', ')}`, { cause: err });
}
+11 -1
View File
@@ -18,6 +18,7 @@ import {
import consola from 'consola';
import { wxt } from '../../wxt';
import { mergeJsonOutputs } from '@aklinker1/rollup-plugin-visualizer';
import { isCI } from 'ci-info';
/**
* Builds the extension based on an internal config. No more config discovery is performed, the
@@ -36,7 +37,7 @@ export async function internalBuild(): Promise<BuildOutput> {
const target = `${wxt.config.browser}-mv${wxt.config.manifestVersion}`;
wxt.logger.info(
`${verb} ${pc.cyan(target)} for ${pc.cyan(wxt.config.mode)} with ${pc.green(
`${wxt.config.builder.name} ${wxt.config.builder.version}`,
`${wxt.builder.name} ${wxt.builder.version}`,
)}`,
);
const startTime = Date.now();
@@ -81,6 +82,15 @@ export async function internalBuild(): Promise<BuildOutput> {
wxt.logger.info(
`Analysis complete:\n ${pc.gray('└─')} ${pc.yellow(statsPath)}`,
);
if (wxt.config.analysis.open) {
if (isCI) {
wxt.logger.debug(`Skipped opening ${pc.yellow(statsPath)} in CI`);
} else {
wxt.logger.info(`Opening ${pc.yellow(statsPath)} in browser...`);
const { default: open } = await import('open');
open(wxt.config.analysis.outputFile);
}
}
}
return output;
+118 -100
View File
@@ -7,17 +7,18 @@ import {
UserManifestFn,
UserManifest,
ExtensionRunnerConfig,
WxtDevServer,
WxtResolvedUnimportOptions,
Logger,
WxtCommand,
} from '~/types';
import path from 'node:path';
import { createFsCache } from '~/core/utils/cache';
import consola, { LogLevels } from 'consola';
import { createViteBuilder } from '~/core/builders/vite';
import defu from 'defu';
import { NullablyRequired } from '../types';
import { isModuleInstalled } from '../package';
import fs from 'fs-extra';
import { normalizePath } from '../paths';
/**
* Given an inline config, discover the config file if necessary, merge the results, resolve any
@@ -28,8 +29,7 @@ import { isModuleInstalled } from '../package';
*/
export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
server?: WxtDevServer,
command: WxtCommand,
): Promise<ResolvedConfig> {
// Load user config
@@ -51,7 +51,7 @@ export async function resolveConfig(
// Merge it into the inline config
const mergedConfig = mergeInlineConfig(inlineConfig, userConfig);
const mergedConfig = await mergeInlineConfig(inlineConfig, userConfig);
// Apply defaults to make internal config.
@@ -63,8 +63,7 @@ export async function resolveConfig(
const manifestVersion =
mergedConfig.manifestVersion ??
(browser === 'firefox' || browser === 'safari' ? 2 : 3);
const mode =
mergedConfig.mode ?? (command === 'build' ? 'production' : 'development');
const mode = mergedConfig.mode ?? COMMAND_MODES[command];
const env: ConfigEnv = { browser, command, manifestVersion, mode };
const root = path.resolve(
@@ -77,10 +76,16 @@ export async function resolveConfig(
srcDir,
mergedConfig.entrypointsDir ?? 'entrypoints',
);
if (await isDirMissing(entrypointsDir)) {
logMissingDir(logger, 'Entrypoints', entrypointsDir);
}
const filterEntrypoints = !!mergedConfig.filterEntrypoints?.length
? new Set(mergedConfig.filterEntrypoints)
: undefined;
const publicDir = path.resolve(srcDir, mergedConfig.publicDir ?? 'public');
if (await isDirMissing(publicDir)) {
logMissingDir(logger, 'Public', publicDir);
}
const typesDir = path.resolve(wxtDir, 'types');
const outBaseDir = path.resolve(root, mergedConfig.outDir ?? '.output');
const outDir = path.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
@@ -105,14 +110,20 @@ export async function resolveConfig(
}).map(([key, value]) => [key, path.resolve(root, value)]),
);
const analysisOutputFile = path.resolve(
root,
mergedConfig.analysis?.outputFile ?? 'stats.html',
);
const analysisOutputDir = path.dirname(analysisOutputFile);
const analysisOutputName = path.parse(analysisOutputFile).name;
let devServerConfig: ResolvedConfig['dev']['server'];
if (command === 'serve') {
let port = mergedConfig.dev?.server?.port;
if (port == null || !isFinite(port)) {
const { default: getPort, portNumbers } = await import('get-port');
port = await getPort({ port: portNumbers(3000, 3010) });
}
devServerConfig = {
port,
hostname: 'localhost',
};
}
const finalConfig: Omit<ResolvedConfig, 'builder'> = {
return {
browser,
command,
debug,
@@ -134,41 +145,20 @@ export async function resolveConfig(
srcDir,
typesDir,
wxtDir,
zip: resolveInternalZipConfig(root, mergedConfig),
transformManifest(manifest) {
userConfig.transformManifest?.(manifest);
inlineConfig.transformManifest?.(manifest);
},
analysis: {
enabled: mergedConfig.analysis?.enabled ?? false,
template: mergedConfig.analysis?.template ?? 'treemap',
outputFile: analysisOutputFile,
outputDir: analysisOutputDir,
outputName: analysisOutputName,
keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false,
},
zip: resolveZipConfig(root, mergedConfig),
transformManifest: mergedConfig.transformManifest,
analysis: resolveAnalysisConfig(root, mergedConfig),
userConfigMetadata: userConfigMetadata ?? {},
alias,
experimental: {
includeBrowserPolyfill:
mergedConfig.experimental?.includeBrowserPolyfill ?? true,
},
server,
experimental: defu(mergedConfig.experimental, {
includeBrowserPolyfill: true,
}),
dev: {
server: devServerConfig,
reloadCommand,
},
hooks: mergedConfig.hooks ?? {},
};
const builder = await createViteBuilder(
inlineConfig,
userConfig,
finalConfig,
);
return {
...finalConfig,
builder,
vite: mergedConfig.vite ?? (() => ({})),
};
}
@@ -184,77 +174,45 @@ async function resolveManifestConfig(
/**
* Merge the inline config and user config. Inline config is given priority. Defaults are not applied here.
*/
function mergeInlineConfig(
async function mergeInlineConfig(
inlineConfig: InlineConfig,
userConfig: UserConfig,
): NullablyRequired<InlineConfig> {
let imports: InlineConfig['imports'];
if (inlineConfig.imports === false || userConfig.imports === false) {
imports = false;
} else if (userConfig.imports == null && inlineConfig.imports == null) {
imports = undefined;
} else {
imports = defu(inlineConfig.imports ?? {}, userConfig.imports ?? {});
}
): Promise<InlineConfig> {
// Merge imports option
const imports: InlineConfig['imports'] =
inlineConfig.imports === false || userConfig.imports === false
? false
: userConfig.imports == null && inlineConfig.imports == null
? undefined
: defu(inlineConfig.imports ?? {}, userConfig.imports ?? {});
// Merge manifest option
const manifest: UserManifestFn = async (env) => {
const user = await resolveManifestConfig(env, userConfig.manifest);
const inline = await resolveManifestConfig(env, inlineConfig.manifest);
return defu(inline, user);
};
const runner: InlineConfig['runner'] = defu(
inlineConfig.runner ?? {},
userConfig.runner ?? {},
);
const zip: InlineConfig['zip'] = defu(
inlineConfig.zip ?? {},
userConfig.zip ?? {},
);
const hooks: InlineConfig['hooks'] = defu(
inlineConfig.hooks ?? {},
userConfig.hooks ?? {},
);
// Merge transformManifest option
const transformManifest: InlineConfig['transformManifest'] = (manifest) => {
userConfig.transformManifest?.(manifest);
inlineConfig.transformManifest?.(manifest);
};
// Builders
const builderConfig = await mergeBuilderConfig(inlineConfig, userConfig);
return {
root: inlineConfig.root ?? userConfig.root,
browser: inlineConfig.browser ?? userConfig.browser,
manifestVersion: inlineConfig.manifestVersion ?? userConfig.manifestVersion,
configFile: inlineConfig.configFile,
debug: inlineConfig.debug ?? userConfig.debug,
entrypointsDir: inlineConfig.entrypointsDir ?? userConfig.entrypointsDir,
filterEntrypoints:
inlineConfig.filterEntrypoints ?? userConfig.filterEntrypoints,
...defu(inlineConfig, userConfig),
// Custom merge values
transformManifest,
imports,
logger: inlineConfig.logger ?? userConfig.logger,
manifest,
mode: inlineConfig.mode ?? userConfig.mode,
publicDir: inlineConfig.publicDir ?? userConfig.publicDir,
runner,
srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
outDir: inlineConfig.outDir ?? userConfig.outDir,
zip,
analysis: {
...userConfig.analysis,
...inlineConfig.analysis,
},
alias: {
...userConfig.alias,
...inlineConfig.alias,
},
experimental: {
...userConfig.experimental,
...inlineConfig.experimental,
},
vite: undefined,
transformManifest: undefined,
dev: {
...userConfig.dev,
...inlineConfig.dev,
},
hooks,
...builderConfig,
};
}
function resolveInternalZipConfig(
function resolveZipConfig(
root: string,
mergedConfig: InlineConfig,
): NullablyRequired<ResolvedConfig['zip']> {
@@ -283,6 +241,28 @@ function resolveInternalZipConfig(
};
}
function resolveAnalysisConfig(
root: string,
mergedConfig: InlineConfig,
): NullablyRequired<ResolvedConfig['analysis']> {
const analysisOutputFile = path.resolve(
root,
mergedConfig.analysis?.outputFile ?? 'stats.html',
);
const analysisOutputDir = path.dirname(analysisOutputFile);
const analysisOutputName = path.parse(analysisOutputFile).name;
return {
enabled: mergedConfig.analysis?.enabled ?? false,
open: mergedConfig.analysis?.open ?? false,
template: mergedConfig.analysis?.template ?? 'treemap',
outputFile: analysisOutputFile,
outputDir: analysisOutputDir,
outputName: analysisOutputName,
keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false,
};
}
async function getUnimportOptions(
wxtDir: string,
logger: Logger,
@@ -339,3 +319,41 @@ async function resolveWxtModuleDir() {
// require.resolve returns the wxt/dist/index file, not the package's root directory, which we want to return
return path.resolve(requireResolve('wxt'), '../..');
}
async function isDirMissing(dir: string) {
return !(await fs.exists(dir));
}
function logMissingDir(logger: Logger, name: string, expected: string) {
logger.warn(
`${name} directory not found: ./${normalizePath(
path.relative(process.cwd(), expected),
)}`,
);
}
/**
* Map of `ConfigEnv` commands to their default modes.
*/
const COMMAND_MODES: Record<WxtCommand, string> = {
build: 'production',
serve: 'development',
};
export async function mergeBuilderConfig(
inlineConfig: InlineConfig,
userConfig: UserConfig,
): Promise<Pick<InlineConfig, 'vite'>> {
const vite = await import('vite').catch(() => void 0);
if (vite) {
return {
vite: async (env) => {
const resolvedInlineConfig = (await inlineConfig.vite?.(env)) ?? {};
const resolvedUserConfig = (await userConfig.vite?.(env)) ?? {};
return vite.mergeConfig(resolvedUserConfig, resolvedInlineConfig);
},
};
}
throw Error('Builder not found. Make sure vite is installed.');
}
+9 -1
View File
@@ -1,5 +1,6 @@
import type { Manifest, Scripting } from '~/browser';
import { ContentScriptEntrypoint } from '~/types';
import { ContentScriptEntrypoint, ResolvedConfig } from '~/types';
import { getEntrypointBundlePath } from './entrypoints';
/**
* Returns a unique and consistent string hash based on a content scripts options.
@@ -83,3 +84,10 @@ export function mapWxtOptionsToRegisteredContentScript(
world: options.world,
};
}
export function getContentScriptJs(
config: ResolvedConfig,
entrypoint: ContentScriptEntrypoint,
): string[] {
return [getEntrypointBundlePath(entrypoint, config.outDir, '.js')];
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { ResolvedConfig } from '~/types';
export function getGlobals(
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
): Array<{ name: string; value: any; type: string }> {
return [
{
+67 -5
View File
@@ -111,16 +111,20 @@ export async function generateManifest(
if (wxt.config.command === 'serve') addDevModePermissions(manifest);
// TODO: Remove in v1
wxt.config.transformManifest(manifest);
wxt.config.transformManifest?.(manifest);
await wxt.hooks.callHook('build:manifestGenerated', wxt, manifest);
if (wxt.config.manifestVersion === 2)
if (wxt.config.manifestVersion === 2) {
convertWebAccessibleResourcesToMv2(manifest);
convertActionToMv2(manifest);
}
if (wxt.config.manifestVersion === 3) {
validateMv3WebAccessbileResources(manifest);
}
stripKeys(manifest);
if (manifest.name == null)
throw Error(
"Manifest 'name' is missing. Either:\n1. Set the name in your <rootDir>/package.json\n2. Set a name via the manifest option in your wxt.config.ts",
@@ -451,8 +455,8 @@ function discoverIcons(
}
function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
const permission = `http://${wxt.config.server?.hostname ?? ''}/*`;
const allowedCsp = wxt.config.server?.origin ?? 'http://localhost:*';
const permission = `http://${wxt.server?.hostname ?? ''}/*`;
const allowedCsp = wxt.server?.origin ?? 'http://localhost:*';
if (manifest.manifest_version === 3) {
addHostPermission(manifest, permission);
@@ -469,7 +473,7 @@ function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
"script-src 'self'; object-src 'self';", // default CSP for MV2
);
if (wxt.config.server) csp.add('script-src', allowedCsp);
if (wxt.server) csp.add('script-src', allowedCsp);
if (manifest.manifest_version === 3) {
manifest.content_security_policy ??= {};
@@ -613,6 +617,17 @@ export function convertWebAccessibleResourcesToMv2(
);
}
function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void {
if (
manifest.action == null ||
manifest.browser_action != null ||
manifest.page_action != null
)
return;
manifest.browser_action = manifest.action;
}
/**
* Make sure all resources are in MV3 format. If not, add a wanring
*/
@@ -632,3 +647,50 @@ export function validateMv3WebAccessbileResources(
);
}
}
/**
* Remove keys from the manifest based on the build target.
*/
function stripKeys(manifest: Manifest.WebExtensionManifest): void {
let keysToRemove: string[] = [];
if (wxt.config.manifestVersion === 2) {
keysToRemove.push(...mv3OnlyKeys);
if (wxt.config.browser === 'firefox')
keysToRemove.push(...firefoxMv3OnlyKeys);
} else {
keysToRemove.push(...mv2OnlyKeys);
}
keysToRemove.forEach((key) => {
delete manifest[key as keyof Manifest.WebExtensionManifest];
});
}
const mv2OnlyKeys = [
'page_action',
'browser_action',
'automation',
'content_capabilities',
'converted_from_user_script',
'current_locale',
'differential_fingerprint',
'event_rules',
'file_browser_handlers',
'file_system_provider_capabilities',
'input_components',
'nacl_modules',
'natively_connectable',
'offline_enabled',
'platforms',
'replacement_web_app',
'system_indicator',
'user_scripts',
];
const mv3OnlyKeys = [
'action',
'export',
'optional_host_permissions',
'side_panel',
];
const firefoxMv3OnlyKeys = ['host_permissions'];
+1 -1
View File
@@ -26,7 +26,7 @@ export async function isOnline(): Promise<boolean> {
*/
export async function fetchCached(
url: string,
config: Omit<ResolvedConfig, 'builder'>,
config: ResolvedConfig,
): Promise<string> {
let content: string = '';
+6 -3
View File
@@ -27,7 +27,7 @@ import { mock } from 'vitest-mock-extended';
import { vi } from 'vitest';
import { setWxtForTesting } from '~/core/wxt';
faker.seed(__TEST_SEED__);
faker.seed(import.meta.test.SEED);
type DeepPartial<T> = T extends object
? {
@@ -270,9 +270,9 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
srcDir: fakeDir(),
typesDir: fakeDir(),
wxtDir: fakeDir(),
server: mock<WxtDevServer>(),
analysis: {
enabled: false,
open: false,
template: 'treemap',
outputFile: fakeFile(),
outputDir: fakeDir(),
@@ -295,11 +295,11 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
experimental: {
includeBrowserPolyfill: true,
},
builder: mock(),
dev: {
reloadCommand: 'Alt+R',
},
hooks: {},
vite: () => ({}),
};
});
@@ -309,11 +309,14 @@ export const fakeWxt = fakeObjectCreator<Wxt>(() => ({
logger: mock(),
reloadConfig: vi.fn(),
pm: mock(),
server: faker.helpers.arrayElement([undefined, mock<WxtDevServer>()]),
builder: mock(),
}));
export function setFakeWxt(overrides?: DeepPartial<Wxt>) {
const wxt = fakeWxt(overrides);
setWxtForTesting(wxt);
return wxt;
}
export const fakeBuildOutput = fakeObjectCreator<BuildOutput>(() => ({
+37
View File
@@ -0,0 +1,37 @@
export const virtualEntrypointTypes = [
'content-script-main-world' as const,
'content-script-isolated-world' as const,
'background' as const,
'unlisted-script' as const,
];
export type VirtualEntrypointType = (typeof virtualEntrypointTypes)[0];
/**
* All the names of entrypoint files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
*/
export const virtualEntrypointModuleNames = virtualEntrypointTypes.map(
(name) => `${name}-entrypoint` as const,
);
/**
* Name of entrypoint files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
*/
export type VirtualEntrypointModuleName =
(typeof virtualEntrypointModuleNames)[0];
/**
* All the names of files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
*/
export const virtualModuleNames = [
...virtualEntrypointModuleNames,
'mock-browser' as const,
'reload-html' as const,
];
/**
* Name of files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
*/
export type VirtualModuleName = (typeof virtualModuleNames)[0];
/**
* Import alias used for importing a virtual module
*/
export type VirtualModuleId = `virtual:wxt-${VirtualModuleName}`;
+17 -5
View File
@@ -1,7 +1,15 @@
import { InlineConfig, Wxt, WxtDevServer, WxtHooks } from '~/types';
import {
InlineConfig,
ResolvedConfig,
Wxt,
WxtCommand,
WxtDevServer,
WxtHooks,
} from '~/types';
import { resolveConfig } from './utils/building';
import { createHooks } from 'hookable';
import { createWxtPackageManager } from './package-managers';
import { createViteBuilder } from './builders/vite';
/**
* Global variable set once `createWxt` is called once. Since this variable is used everywhere, this
@@ -13,12 +21,14 @@ export let wxt: Wxt;
* Create and register a global instance of the Wxt interface for use throughout the project.
*/
export async function registerWxt(
command: 'build' | 'serve',
command: WxtCommand,
inlineConfig: InlineConfig = {},
server?: WxtDevServer,
getServer?: (config: ResolvedConfig) => Promise<WxtDevServer>,
): Promise<void> {
const config = await resolveConfig(inlineConfig, command, server);
const hooks = createHooks<WxtHooks>();
const config = await resolveConfig(inlineConfig, command);
const server = await getServer?.(config);
const builder = await createViteBuilder(config, hooks, server);
const pm = await createWxtPackageManager(config.root);
wxt = {
@@ -28,9 +38,11 @@ export async function registerWxt(
return config.logger;
},
async reloadConfig() {
wxt.config = await resolveConfig(inlineConfig, command, server);
wxt.config = await resolveConfig(inlineConfig, command);
},
pm,
builder,
server,
};
// Initialize hooks
+15 -11
View File
@@ -38,6 +38,7 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
'{{version}}',
output.manifest.version_name ?? output.manifest.version,
)
.replaceAll('{{mode}}', wxt.config.mode)
.replaceAll('{{manifestVersion}}', `mv${wxt.config.manifestVersion}`);
await fs.ensureDir(wxt.config.outBaseDir);
@@ -62,9 +63,9 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
await zipDir(wxt.config.zip.sourcesRoot, sourcesZipPath, {
include: wxt.config.zip.includeSources,
exclude: wxt.config.zip.excludeSources,
transform(file, content) {
if (file.endsWith('package.json')) {
return addOverridesToPackageJson(content, overrides);
transform(absolutePath, zipPath, content) {
if (zipPath.endsWith('package.json')) {
return addOverridesToPackageJson(absolutePath, content, overrides);
}
},
additionalFiles: downloadedPackages,
@@ -88,7 +89,8 @@ async function zipDir(
include?: string[];
exclude?: string[];
transform?: (
file: string,
absolutePath: string,
zipPath: string,
content: string,
) => Promise<string | undefined | void> | string | undefined | void;
additionalWork?: (archive: JSZip) => Promise<void> | void;
@@ -125,7 +127,7 @@ async function zipDir(
const content = await fs.readFile(absolutePath, 'utf-8');
archive.file(
file,
(await options?.transform?.(file, content)) || content,
(await options?.transform?.(absolutePath, file, content)) || content,
);
} else {
const content = await fs.readFile(absolutePath);
@@ -159,8 +161,7 @@ async function downloadPrivatePackages() {
wxt.config.zip.downloadedPackagesDir,
);
files.push(tgzPath);
overrides[id] =
'file://./' + normalizePath(path.relative(wxt.config.root, tgzPath));
overrides[id] = tgzPath;
}
}
@@ -168,18 +169,21 @@ async function downloadPrivatePackages() {
}
function addOverridesToPackageJson(
absolutePackageJsonPath: string,
content: string,
overrides: Record<string, string>,
): string {
if (Object.keys(overrides).length === 0) return content;
const packageJsonDir = path.dirname(absolutePackageJsonPath);
const oldPackage = JSON.parse(content);
const newPackage = {
...oldPackage,
[wxt.pm.overridesKey]: {
...oldPackage[wxt.pm.overridesKey],
...overrides,
},
[wxt.pm.overridesKey]: { ...oldPackage[wxt.pm.overridesKey] },
};
Object.entries(overrides).forEach(([key, absolutePath]) => {
newPackage[wxt.pm.overridesKey][key] =
'file://./' + normalizePath(path.relative(packageJsonDir, absolutePath));
});
return JSON.stringify(newPackage, null, 2);
}
+2 -1
View File
@@ -8,6 +8,7 @@
import { Storage, browser } from '~/browser';
import { dequal } from 'dequal/lite';
import { logger } from './sandbox/utils/logger';
import { toArray } from './core/utils/arrays';
export const storage = createStorage();
@@ -107,7 +108,7 @@ function createStorage(): WxtStorage {
await driver.removeItem(metaKey);
} else {
const newFields = getMetaValue(await driver.getItem(metaKey));
[properties].flat().forEach((field) => delete newFields[field]);
toArray(properties).forEach((field) => delete newFields[field]);
await driver.setItem(metaKey, newFields);
}
};
+1 -1
View File
@@ -4,7 +4,7 @@ declare const __DEV_SERVER_PORT__: string;
// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts
interface ImportMetaEnv {
readonly COMMAND: 'build' | 'serve';
readonly COMMAND: WxtCommand;
readonly MANIFEST_VERSION: 2 | 3;
readonly ENTRYPOINT: string;
}
+76 -21
View File
@@ -21,6 +21,9 @@ export interface InlineConfig {
* Directory containing all source code. Set to `"src"` to move all source code to a `src/`
* directory.
*
* After changing, don't forget to move the `public/` and `entrypoints/` directories into the new
* source dir.
*
* @default config.root
*/
srcDir?: string;
@@ -114,10 +117,11 @@ export interface InlineConfig {
*
* Available template variables:
*
* - `{{name}}` - The project's name converted to kebab-case
* - `{{version}}` - The version_name or version from the manifest
* - `{{browser}}` - The target browser from the `--browser` CLI flag
* - `{{manifestVersion}}` - Either "2" or "3"
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
* - <span v-pre>`{{mode}}`</span> - The current mode
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
*
* @default "{{name}}-{{version}}-{{browser}}.zip"
*/
@@ -127,10 +131,11 @@ export interface InlineConfig {
*
* Available template variables:
*
* - `{{name}}` - The project's name converted to kebab-case
* - `{{version}}` - The version_name or version from the manifest
* - `{{browser}}` - The target browser from the `--browser` CLI flag
* - `{{manifestVersion}}` - Either "2" or "3"
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
* - <span v-pre>`{{mode}}`</span> - The current mode
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
*
* @default "{{name}}-{{version}}-sources.zip"
*/
@@ -168,7 +173,7 @@ export interface InlineConfig {
*
* @example
* [
* "coverage", // Ignore the coverage directory in the `sourcesRoot`
* "coverage", // Include the coverage directory in the `sourcesRoot`
* ]
*/
excludeSources?: string[];
@@ -226,6 +231,12 @@ export interface InlineConfig {
* @default false
*/
enabled?: boolean;
/**
* Set to true to automatically open the `stats.html` file when the build is finished. When building in CI, the browser will never open.
*
* @default false
*/
open?: boolean;
/**
* When running `wxt build --analyze` or setting `analysis.enabled` to true, customize how the
* bundle will be visualized. See
@@ -298,6 +309,12 @@ export interface InlineConfig {
* Config effecting dev mode only.
*/
dev?: {
server?: {
/**
* Port to run the dev server on. Defaults to the first open port from 3000 to 3010.
*/
port?: number;
};
/**
* Controls whether a custom keyboard shortcut command, `Alt+R`, is added during dev mode to
* quickly reload the extension.
@@ -333,12 +350,39 @@ export interface InlineConfig {
vite?: (env: ConfigEnv) => WxtViteConfig | Promise<WxtViteConfig>;
}
// TODO: Move into @wxt/vite-builder
export interface ResolvedConfig {
vite: (env: ConfigEnv) => WxtViteConfig | Promise<WxtViteConfig>;
}
// TODO: Move into @wxt/vite-builder
export type WxtViteConfig = Omit<
vite.UserConfig,
'root' | 'configFile' | 'mode'
>;
// TODO: Move into @wxt/vite-builder
export interface WxtHooks {
/**
* Called when WXT has created Vite's config for a build step. Useful if you
* want to add plugins or update the vite config per entrypoint group.
*
* @param entrypoints The list of entrypoints being built with the provided config.
* @param viteConfig The config that will be used for the dev server.
*/
'vite:build:extendConfig': (
entrypoints: readonly Entrypoint[],
viteConfig: vite.InlineConfig,
) => HookResult;
/**
* Called when WXT has created Vite's config for the dev server. Useful if
* you want to add plugins or update the vite config per entrypoint group.
*
* @param viteConfig The config that will be used to build the entrypoints. Can be updated by reference.
*/
'vite:devServer:extendConfig': (config: vite.InlineConfig) => HookResult;
}
export interface BuildOutput {
manifest: Manifest.WebExtensionManifest;
publicAssets: OutputAsset[];
@@ -763,7 +807,7 @@ export interface ConfigEnv {
/**
* The command used to run WXT. `"serve"` during development and `"build"` for any other command.
*/
command: 'build' | 'serve';
command: WxtCommand;
/**
* Browser passed in from the CLI via the `-b` or `--browser` flag. Defaults to `"chrome"` when not passed.
*/
@@ -776,6 +820,8 @@ export interface ConfigEnv {
manifestVersion: 2 | 3;
}
export type WxtCommand = 'build' | 'serve';
/**
* Configure how the browser starts up.
*/
@@ -989,6 +1035,14 @@ export interface Wxt {
* Package manager utilities.
*/
pm: WxtPackageManager;
/**
* If the dev server was started, it will be availble.
*/
server?: WxtDevServer;
/**
* The module in charge of executing all the build steps.
*/
builder: WxtBuilder;
}
export interface ResolvedConfig {
@@ -1007,7 +1061,7 @@ export interface ResolvedConfig {
*/
wxtModuleDir: string;
mode: string;
command: 'build' | 'serve';
command: WxtCommand;
browser: TargetBrowser;
manifestVersion: TargetManifestVersion;
env: ConfigEnv;
@@ -1015,7 +1069,6 @@ export interface ResolvedConfig {
imports: false | WxtResolvedUnimportOptions;
manifest: UserManifest;
fsCache: FsCache;
server?: WxtDevServer;
runnerConfig: C12ResolvedConfig<ExtensionRunnerConfig>;
zip: {
name?: string;
@@ -1027,9 +1080,13 @@ export interface ResolvedConfig {
downloadedPackagesDir: string;
downloadPackages: string[];
};
transformManifest: (manifest: Manifest.WebExtensionManifest) => void;
/**
* @deprecated Use `build:manifestGenerated` hook instead.
*/
transformManifest?: (manifest: Manifest.WebExtensionManifest) => void;
analysis: {
enabled: boolean;
open: boolean;
template: NonNullable<PluginVisualizerOptions['template']>;
/** Absolute file path to the `stats.html` file */
outputFile: string;
@@ -1047,11 +1104,15 @@ export interface ResolvedConfig {
experimental: {
includeBrowserPolyfill: boolean;
};
builder: WxtBuilder;
dev: {
/** Only defined during dev command */
server?: {
port: number;
hostname: string;
};
reloadCommand: string | false;
};
hooks: Partial<WxtHooks>;
hooks: NestedHooks<WxtHooks>;
}
export interface FsCache {
@@ -1064,12 +1125,6 @@ export interface ExtensionRunner {
closeBrowser(): Promise<void>;
}
export type VirtualEntrypointType =
| 'content-script-main-world'
| 'content-script-isolated-world'
| 'background'
| 'unlisted-script';
export type EslintGlobalsPropValue =
| boolean
| 'readonly'
-1
View File
@@ -1 +0,0 @@
declare const __TEST_SEED__: number;
+1 -1
View File
@@ -1,4 +1,4 @@
import definition from 'virtual:user-background';
import definition from 'virtual:user-background-entrypoint';
import { setupWebSocket } from './utils/setup-web-socket';
import { logger } from '../sandbox/utils/logger';
import { browser } from 'wxt/browser';
@@ -1,4 +1,4 @@
import definition from 'virtual:user-content-script-isolated-world';
import definition from 'virtual:user-content-script-isolated-world-entrypoint';
import { logger } from '../sandbox/utils/logger';
import { ContentScriptContext } from 'wxt/client';
@@ -1,4 +1,4 @@
import definition from 'virtual:user-content-script-main-world';
import definition from 'virtual:user-content-script-main-world-entrypoint';
import { logger } from '../sandbox/utils/logger';
(async () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import definition from 'virtual:user-unlisted-script';
import definition from 'virtual:user-unlisted-script-entrypoint';
import { logger } from '../sandbox/utils/logger';
(async () => {
+4 -4
View File
@@ -1,11 +1,11 @@
// Types required to make the virtual modules happy.
declare module 'virtual:user-background' {
declare module 'virtual:user-background-entrypoint' {
const definition: { main: () => void };
export default definition;
}
declare module 'virtual:user-content-script-isolated-world' {
declare module 'virtual:user-content-script-isolated-world-entrypoint' {
const definition: {
main: (
ctx: import('wxt/client').ContentScriptContext,
@@ -14,12 +14,12 @@ declare module 'virtual:user-content-script-isolated-world' {
export default definition;
}
declare module 'virtual:user-content-script-main-world' {
declare module 'virtual:user-content-script-main-world-entrypoint' {
const definition: { main: () => void | Promise<void> };
export default definition;
}
declare module 'virtual:user-unlisted-script' {
declare module 'virtual:user-unlisted-script-entrypoint' {
const definition: { main: () => void | Promise<void> };
export default definition;
}
+1 -1
View File
@@ -23,6 +23,6 @@
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.3",
"wxt": "^0.16.0"
"wxt": "^0.17.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"devDependencies": {
"typescript": "^5.3.3",
"vite-plugin-solid": "^2.8.0",
"wxt": "^0.16.0"
"wxt": "^0.17.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"svelte-check": "^3.6.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"wxt": "^0.16.0"
"wxt": "^0.17.0"
}
}
+1 -1
View File
@@ -16,6 +16,6 @@
},
"devDependencies": {
"typescript": "^5.3.3",
"wxt": "^0.16.0"
"wxt": "^0.17.0"
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
"recommendations": ["Vue.volar"]
}
+2 -2
View File
@@ -20,7 +20,7 @@
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.1",
"typescript": "^5.3.3",
"vue-tsc": "^1.8.27",
"wxt": "^0.16.0"
"vue-tsc": "^2.0.6",
"wxt": "^0.17.0"
}
}
+1
View File
@@ -1,6 +1,7 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"types": ["vitest-plugin-random-seed/types"],
"paths": {
"~/*": ["./src/*"]
}
+3 -16
View File
@@ -1,20 +1,7 @@
import { defineWorkspace } from 'vitest/config';
import fs from 'fs-extra';
import pc from 'picocolors';
import type { Plugin } from 'vite';
import path from 'node:path';
const seed = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
console.info('Test seed: ' + pc.cyan(seed));
// config.define doesn't work with workspaces, so we have to set it inside a plugin
const testSeed = (): Plugin => ({
name: 'test-seed',
config(config) {
config.define ??= {};
config.define.__TEST_SEED__ = JSON.stringify(seed);
},
});
import RandomSeed from 'vitest-plugin-random-seed';
const resolve = {
alias: {
@@ -36,7 +23,7 @@ export default defineWorkspace([
restoreMocks: true,
setupFiles: 'vitest.setup.ts',
},
plugins: [testSeed()],
plugins: [RandomSeed()],
resolve,
},
{
@@ -45,7 +32,7 @@ export default defineWorkspace([
dir: 'e2e',
testTimeout: 120e3,
},
plugins: [testSeed()],
plugins: [RandomSeed()],
resolve,
},
]);