Compare commits

..

31 Commits

Author SHA1 Message Date
GitHub Actions 2db4ea77a9 chore(release): v0.15.4 2024-01-31 17:42:07 +00:00
Aaron ba5349ccaf fix(submit): Load .env.submit automatically when running wxt submit and wxt submit init (#391) 2024-01-31 11:38:11 -06:00
GitHub Actions addb689de9 chore(release): v0.15.3 2024-01-31 14:59:26 +00:00
Aaron 2af8d28b82 fix(dev): Reload <name>/index.html entrypoints properly on save (#390) 2024-01-31 08:50:23 -06:00
Aaron Klinker ddbc66813e Update changelog 2024-01-30 13:04:32 -06:00
GitHub Actions 0c8fc013a8 chore(release): v0.15.2 2024-01-30 19:01:54 +00:00
Aaron 26e30aaae9 feat: Add submit command (#370) 2024-01-30 12:55:17 -06:00
Nenad Novaković 6557dfe430 fix(dev): Resolve script and link aliases (#387)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2024-01-30 12:05:31 -06:00
Aaron Klinker 9cee9eefb4 Update changelog 2024-01-29 14:12:27 -06:00
GitHub Actions f9cd7aaf05 chore(release): v0.15.1 2024-01-29 20:10:18 +00:00
0x7a7a 9049804413 docs: Fixed doc errors on the guide/extension-api page (#383)
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2024-01-29 14:06:27 -06:00
Aaron d027a496f5 Upgrade wxt init templates to v0.15 (#385) 2024-01-29 14:04:58 -06:00
Aaron dd7abddb7c feat: Allow passing custom preferences to chrome, enabling dev mode on chrome://extensions and allowing content script sourcemaps automatically (#384) 2024-01-29 13:58:46 -06:00
Aaron Klinker 98d2792e02 chore: Fix vite version conflicts in demo extension 2024-01-29 13:53:57 -06:00
Aaron Klinker 39b76d3ec0 fix(security): Upgrade to vite@5.0.12 to resolve CVE-2024-23331 2024-01-29 11:25:31 -06:00
Aaron Klinker bc29bfd434 Update changelog 2024-01-29 11:22:41 -06:00
GitHub Actions 5e60f37ec5 chore(release): v0.15.0 2024-01-29 17:18:38 +00:00
Aaron 6f901b32eb fix: Enable inline sourcemaps by default during development (#382) 2024-01-29 11:15:57 -06:00
Aaron 927aca612e fix!: Move browser constants to import.meta.env (#380)
BREAKING CHANGE: Renamed undocumented constants:

- `__BROWSER__` &rarr; `import.meta.env.BROWSER`
- `__COMMAND__` &rarr; `import.meta.env.COMMAND`
- `__MANIFEST_VERSION__` &rarr; `import.meta.env.MANIFEST_VERSION`
- `__IS_CHROME__` &rarr; `import.meta.env.CHROME`
- `__IS_FIREFOX__` &rarr; `import.meta.env.FIREFOX`
- `__IS_SAFARI__` &rarr; `import.meta.env.SAFARI`
- `__IS_EDGE__` &rarr; `import.meta.env.EDGE`
- `__IS_OPERA__` &rarr; `import.meta.env.OPERA`
2024-01-29 11:14:18 -06:00
Nenad Novaković 2dcdae91eb feat(zip)!: add includeSources and rename ignoredSources to excludeSources (#378)
Co-authored-by: Aaron <aaronklinker1@gmail.com>

BREAKING CHANGE: Renamed `ignoredSources` to `excludeSources`
2024-01-29 11:13:39 -06:00
Aaron Klinker eb758bdeb8 chore: Update contributor docs
See https://github.com/wxt-dev/wxt/pull/378\#issuecomment-1915064721
2024-01-29 10:59:54 -06:00
Aaron 5acf51af9d fix: Generate missing sourcemap in wxt:unimport plugin (#381) 2024-01-29 10:30:42 -06:00
Aaron Klinker f9718a1b71 docs: Fix typo 2024-01-28 09:42:45 -06:00
GitHub Actions cf7f23d4c3 chore(release): v0.14.7 2024-01-27 17:31:33 +00:00
Aaron Klinker 62ecb6f41e fix: Upgrade web-ext-run
This closes #324
2024-01-27 11:25:49 -06:00
Aaron Klinker fc45c37f22 docs: Add missing await to createShadowRootUi examples
This closes #374
2024-01-27 11:02:15 -06:00
Aaron Klinker 68874e607e docs: Document defined constants for the build target 2024-01-27 10:56:14 -06:00
Aaron Klinker 35865adcb2 fix(storage): Throw better error message when importing outside a extension environment
This closes #371
2024-01-27 00:26:35 -06:00
Aaron Klinker 3b63a51475 fix: Improve error messages when importing and building entrypoints 2024-01-27 00:20:48 -06:00
東奈比 d32e59e38f docs: fix incorrect sample code (#372) 2024-01-26 10:26:10 -06:00
Aaron Klinker dab8efa140 docs: Add matches to content script examples 2024-01-24 07:58:47 -06:00
51 changed files with 1255 additions and 449 deletions
+118
View File
@@ -1,5 +1,123 @@
# Changelog
## v0.15.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.15.3...v0.15.4)
### 🩹 Fixes
- **submit:** Load `.env.submit` automatically when running `wxt submit` and `wxt submit init` ([#391](https://github.com/wxt-dev/wxt/pull/391))
## v0.15.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.15.2...v0.15.3)
### 🩹 Fixes
- **dev:** Reload `<name>/index.html` entrypoints properly on save ([#390](https://github.com/wxt-dev/wxt/pull/390))
## v0.15.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.15.1...v0.15.2)
### 🚀 Enhancements
- Add `submit` command ([#370](https://github.com/wxt-dev/wxt/pull/370))
### 🩹 Fixes
- **dev:** Resolve `script` and `link` aliases ([#387](https://github.com/wxt-dev/wxt/pull/387))
### ❤️ Contributors
- Nenad Novaković ([@dvlden](https://github.com/dvlden))
## v0.15.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.15.0...v0.15.1)
### 🚀 Enhancements
- Allow passing custom preferences to chrome, enabling dev mode on `chrome://extensions` and allowing content script sourcemaps automatically ([#384](https://github.com/wxt-dev/wxt/pull/384))
### 🩹 Fixes
- **security:** Upgrade to vite@5.0.12 to resolve CVE-2024-23331 ([39b76d3](https://github.com/wxt-dev/wxt/commit/39b76d3))
### 📖 Documentation
- Fixed doc errors on the guide/extension-api page ([#383](https://github.com/wxt-dev/wxt/pull/383))
### 🏡 Chore
- Fix vite version conflicts in demo extension ([98d2792](https://github.com/wxt-dev/wxt/commit/98d2792))
### ❤️ Contributors
- 0x7a7a ([@0x7a7a](https://github.com/0x7a7a))
## v0.15.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.14.7...v0.15.0)
### 🚀 Enhancements
- **zip:** ⚠️ Add `includeSources` and rename `ignoredSources` to `excludeSources` ([#378](https://github.com/wxt-dev/wxt/pull/378))
### 🩹 Fixes
- Generate missing sourcemap in `wxt:unimport` plugin ([#381](https://github.com/wxt-dev/wxt/pull/381))
- ⚠️ Move browser constants to `import.meta.env` ([#380](https://github.com/wxt-dev/wxt/pull/380))
- Enable inline sourcemaps by default during development ([#382](https://github.com/wxt-dev/wxt/pull/382))
### 📖 Documentation
- Fix typo ([f9718a1](https://github.com/wxt-dev/wxt/commit/f9718a1))
### 🏡 Chore
- Update contributor docs ([eb758bd](https://github.com/wxt-dev/wxt/commit/eb758bd))
#### ⚠️ Breaking Changes
Renamed `zip.ignoredSources` to `zip.excludeSources` in [#378](https://github.com/wxt-dev/wxt/pull/378)
Renamed undocumented constants for detecting the build config at runtime in [#380](https://github.com/wxt-dev/wxt/pull/380). Now documented here: https://wxt.dev/guide/multiple-browsers.html#runtime
- `__BROWSER__` &rarr; `import.meta.env.BROWSER`
- `__COMMAND__` &rarr; `import.meta.env.COMMAND`
- `__MANIFEST_VERSION__` &rarr; `import.meta.env.MANIFEST_VERSION`
- `__IS_CHROME__` &rarr; `import.meta.env.CHROME`
- `__IS_FIREFOX__` &rarr; `import.meta.env.FIREFOX`
- `__IS_SAFARI__` &rarr; `import.meta.env.SAFARI`
- `__IS_EDGE__` &rarr; `import.meta.env.EDGE`
- `__IS_OPERA__` &rarr; `import.meta.env.OPERA`
### ❤️ Contributors
- Nenad Novaković ([@dvlden](https://github.com/dvlden))
## v0.14.7
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.14.6...v0.14.7)
### 🩹 Fixes
- Improve error messages when importing and building entrypoints ([3b63a51](https://github.com/wxt-dev/wxt/commit/3b63a51))
- **storage:** Throw better error message when importing outside a extension environment ([35865ad](https://github.com/wxt-dev/wxt/commit/35865ad))
- Upgrade `web-ext-run` ([62ecb6f](https://github.com/wxt-dev/wxt/commit/62ecb6f))
### 📖 Documentation
- Add `matches` to content script examples ([dab8efa](https://github.com/wxt-dev/wxt/commit/dab8efa))
- Fix incorrect sample code ([#372](https://github.com/wxt-dev/wxt/pull/372))
- Document defined constants for the build target ([68874e6](https://github.com/wxt-dev/wxt/commit/68874e6))
- Add missing `await` to `createShadowRootUi` examples ([fc45c37](https://github.com/wxt-dev/wxt/commit/fc45c37))
### ❤️ Contributors
- 東奈比 ([@dongnaebi](http://github.com/dongnaebi))
## v0.14.6
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.14.5...v0.14.6)
+9 -1
View File
@@ -57,9 +57,17 @@ pnpm test
pnpm docs:dev
```
## Updating Docs
Documentation is written with VitePress, and is located in the `docs/` directory.
The API reference is generated from JSDoc comments in the source code. If there's a typo or change you want to make in there, you'll need to update the source code instead of a file in the `docs/` directory.
## Testing
WXT has unit and E2E tests. When making a change or adding a feature, make sure to update the tests or add new ones.
WXT has unit and E2E tests. When making a change or adding a feature, make sure to update the tests or add new ones, if they exist.
> If they don't exist, feel free to create them, but that's a lot for a one-time contributor. A maintainer might add them to your PR though.
To run tests for a specific file, add the filename at the end of the test command:
+4 -4
View File
@@ -4,10 +4,10 @@ export default defineBackground(() => {
console.log(browser.runtime.id);
logId();
console.log({
browser: __BROWSER__,
chrome: __IS_CHROME__,
firefox: __IS_FIREFOX__,
manifestVersion: __MANIFEST_VERSION__,
browser: import.meta.env.BROWSER,
chrome: import.meta.env.CHROME,
firefox: import.meta.env.FIREFOX,
manifestVersion: import.meta.env.MANIFEST_VERSION,
messages,
});
+26 -10
View File
@@ -21,6 +21,8 @@ Integrated content script UIs are injected alongside the content of a page. This
```ts [Vanilla]
// entrypoints/example-ui.content.ts
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
@@ -45,6 +47,8 @@ import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
@@ -73,6 +77,8 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
@@ -100,6 +106,8 @@ export default defineContentScript({
import App from './App.svelte';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
@@ -128,6 +136,8 @@ export default defineContentScript({
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
main(ctx) {
const ui = createIntegratedUi(ctx, {
position: 'inline',
@@ -174,6 +184,7 @@ To use `createShadowRootUi`, follow these steps:
import './style.css';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
@@ -182,7 +193,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
anchor: '#anchor',
type: 'inline',
position: 'inline',
onMount(container) {
// Define how your UI will be mounted inside the container
const app = document.createElement('p');
@@ -204,6 +215,7 @@ import { createApp } from 'vue';
import App from './App.vue';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
@@ -212,7 +224,7 @@ export default defineContentScript({
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
anchor: '#anchor',
type: 'inline',
position: 'inline',
onMount: (container) => {
// Define how your UI will be mounted inside the container
const app = createApp(App);
@@ -238,12 +250,13 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
main(ctx) {
async main(ctx) {
// 3. Define your UI
const ui = createShadowRootUi(ctx, {
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: '#anchor',
@@ -271,12 +284,13 @@ import './style.css';
import App from './App.svelte';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
main(ctx) {
async main(ctx) {
// 3. Define your UI
const ui = createShadowRootUi(ctx, {
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: '#anchor',
@@ -305,12 +319,13 @@ import './style.css';
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['<all_urls>'],
// 2. Set cssInjectionMode
cssInjectionMode: 'ui',
main(ctx) {
async main(ctx) {
// 3. Define your UI
const ui = createShadowRootUi(ctx, {
const ui = await createShadowRootUi(ctx, {
name: 'example-ui',
position: 'inline',
anchor: '#anchor',
@@ -377,13 +392,14 @@ WXT provides a helper function, [`createIframeUi`](/api/wxt/client/functions/cre
```ts
export default defineContentScript({
// ...
matches: ['<all_urls>'],
async main(ctx) {
// Define the UI
const ui = await createIframeUi(ctx, {
page: '/example-iframe.html',
anchor: '#anchor',
type: 'inline',
position: 'inline',
onMount: (wrapper, iframe) => {
// Add styles to the iframe like width
iframe.width = 123;
+7 -9
View File
@@ -57,21 +57,19 @@ Here's a basic request/response example:
// popup/main.ts
const res = await browser.runtime.sendMessage('ping');
console.log('res'); // "pong"
console.log(res); // "pong"
```
```ts
// background.ts
export default defineBackground(() => {
browser.runtime.onMessage.addEventListener(
(message, sender, sendResponse) => {
console.log(message); // "ping"
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log(message); // "ping"
// Wait 1 second and respond with "pong"
setTimeout(() => sendResponse('pong'), 1000);
return true;
},
);
// Wait 1 second and respond with "pong"
setTimeout(() => sendResponse('pong'), 1000);
return true;
});
});
```
+22 -3
View File
@@ -17,11 +17,9 @@ Separate build targets are written to their own output directories:
To build for a specific browser, pass the `-b --browser` flag from the CLI:
```
```sh
wxt --browser firefox
wxt build --browser firefox
```
By default, it will build for `chrome`. When excluding the [manifest version flags](#target-manifest-version), it will default to the commonly accepted manifest version for that browser.
@@ -112,3 +110,24 @@ export default defineContentScript({
:::warning
Only `defineBackground` and `defineContentScript` support per-browser options right now.
:::
## Runtime
To determine the browser or manifest version at runtime, you can use any of the below variables:
- `import.meta.env.BROWSER`: A string, the target browser, usually equal to the `--browser` flag
- `import.meta.env.MANIFEST_VERSION`: A number, either `2` or `3`, depending on the manifest version targetted
- `import.meta.env.CHROME`: A boolean equivalent to `import.meta.env.BROWSER === "chrome"`
- `import.meta.env.FIREFOX`: A boolean equivalent to `import.meta.env.BROWSER === "firefox"`
- `import.meta.env.EDGE`: A boolean equivalent to `import.meta.env.BROWSER === "edge"`
- `import.meta.env.SAFARI`: A boolean equivalent to `import.meta.env.BROWSER === "safari"`
- `import.meta.env.OPERA`: A boolean equivalent to `import.meta.env.BROWSER === "opera"`
- `import.meta.env.COMMAND`: A string, `"serve"` when running `wxt` for development or `"build"` in all other cases.
:::info
These variables are constants defined at build time based on the build target. They do not actually detect which browser the code is running in.
For example, if you build for `--browser chrome` and publish it on Edge, `import.meta.env.BROWSER` will be `"chrome"`, not `"edge"`. You have to build a separate ZIP for `--browser edge` before `import.meta.env.BROWSER` will be `"edge"`.
If you need to know the actual browser your code is being ran on, you should use a [user agent parser](https://www.npmjs.com/package/ua-parser-js).
:::
+76 -90
View File
@@ -1,94 +1,66 @@
---
outline: deep
---
# Publishing
WXT offers several utilities that simplify the publishing process.
WXT will help you ZIP your extensions and submit them to the stores for review.
## First Time Publishing
If you're publishing an extension to a store for the first time, you must manually navigate the process. Each store has unique steps and requirements that you need to familiarize yourself with.
If you're publishing an extension to a store for the first time, you must manually navigate the process. WXT doesn't help you create listings, each store has unique steps and requirements that you need to familiarize yourself with.
Each store requires that a ZIP file be uploaded. You can generate these using the `wxt zip` command:
For specific details about each store, see the stores sections below.
- [Chrome Web Store](#chrome-web-store)
- [Firefox Addon Store](#firefox-addon-store)
- [Edge Addons](#edge-addons)
## Automation
WXT provides two commands to help automate the release process:
- `wxt submit`: Submit new versions of your extension for review (and publish them automatically once approved)
- `wxt submit init`: Help setup all the required secrets and options for the `wxt submit` command
To get started, run `wxt submit init` and follow the prompts. Once finished, you should have a `.env.submit` file! WXT will use this file to submit your updates.
> In CI, make sure you add all the environment variables to the submit step.
To release an update, build all the ZIPs you plan on releasing:
```sh
wxt zip
wxt zip -b firefox
# etc
```
Generated ZIP files are stored in the `.output` directory.
Then run the `wxt submit` command, passing in all the ZIP files you want to release. In this case, we'll do a release for all 3 major stores: Chrome Web Store, Edge Addons, and Firefox Addons Store.
## Automation
If it's your first time running the command, you'll want to test your secrets by passing the `--dry-run` flag:
To automate releasing updates, use the [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension) package.
```sh
wxt submit --dry-run \
--chrome-zip .output/<your-extension>-<version>-chrome.zip \
--firefox-zip .output/<your-extension>-<version>-firefox.zip --firefox-sources-zip .output/<your-extension>-<version>-sources.zip \
--edge-zip .output/<your-extension>-<version>-chrome.zip
```
:::info
🚧 WXT plans to eventually incorporate the `publish-browser-extension` package into its own `wxt submit` command.
If the dry run passes, remove the flag and do the actual release:
```sh
wxt submit \
--chrome-zip .output/<your-extension>-<version>-chrome.zip \
--firefox-zip .output/<your-extension>-<version>-firefox.zip --firefox-sources-zip .output/<your-extension>-<version>-sources.zip \
--edge-zip .output/<your-extension>-<version>-chrome.zip
```
:::tip
If you only need to release to a single store, only pass that store's ZIP flag.
:::
1. Install the necessary dependencies:
```sh
pnpm add -D publish-browser-extension env-cmd
```
2. Add scripts to your `package.json` file:
```json
{
"scripts": {
"submit": "env-cmd -f .env.submit -- publish-extension",
"submit:dry": "env-cmd -f .env.submit -- publish-extension --dry-run"
}
}
```
3. Create a `.env.submit` file and include the code below. If you're not publishing to certain stores, simply ignore their respective variables.
```txt
CHROME_EXTENSION_ID=""
CHROME_CLIENT_ID=""
CHROME_CLIENT_SECRET=""
CHROME_REFRESH_TOKEN=""
FIREFOX_EXTENSION_ID=""
FIREFOX_JWT_ISSUER=""
FIREFOX_JWT_SECRET=""
EDGE_PRODUCT_ID=""
EDGE_CLIENT_ID=""
EDGE_CLIENT_SECRET=""
EDGE_ACCESS_TOKEN_URL=""
```
> Each value will be filled in during the next step.
4. Run `npx publish-extension --help` for assistance with filling out all the values. Insert the obtained values within the double quotes.
5. ZIP all the targets you plan to publish, in this case Chrome and Firefox.
```sh
wxt zip
wxt zip -b firefox
```
6. Test your credentials by running the `submit:dry` command:
```sh
pnpm submit:dry \
--chrome-zip .output/your-extension-X.Y.Z-chrome.zip \
--firefox-zip .output/your-extension-X.Y.Z-firefox.zip \
--firefox-sources-zip .output/your-extension-X.Y.Z-sources.zip \
--edge-zip .output/your-extension-X.Y.Z-chrome.zip
```
7. Upload and submit your extension for review:
```sh
pnpm submit \
--chrome-zip .output/your-extension-X.Y.Z-chrome.zip \
--firefox-zip .output/your-extension-X.Y.Z-firefox.zip \
--firefox-sources-zip .output/your-extension-X.Y.Z-sources.zip \
--edge-zip .output/your-extension-X.Y.Z-chrome.zip
```
:::tip
See the [Firefox Addon Store](#firefox-addon-store) section for more details about the `--firefox-sources-zip` option.
:::
## GitHub Action
@@ -98,9 +70,11 @@ Here's an example of a GitHub Action to automate submiting new versions of your
# TODO
```
## Chrome Web Store
## Stores
✅ Automated &bull; [Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard) &bull; [Publishing Docs](https://developer.chrome.com/docs/webstore/publish/)
### Chrome Web Store
> ✅ Supported &bull; [Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard) &bull; [Publishing Docs](https://developer.chrome.com/docs/webstore/publish/)
To create a ZIP for Chrome:
@@ -108,15 +82,15 @@ To create a ZIP for Chrome:
wxt zip
```
## Firefox Addon Store
### Firefox Addon Store
✅ Automated &bull; [Developer Dashboard](https://addons.mozilla.org/developers/) &bull; [Publishing Docs](https://extensionworkshop.com/documentation/publish/submitting-an-add-on/)
> ✅ Supported &bull; [Developer Dashboard](https://addons.mozilla.org/developers/) &bull; [Publishing Docs](https://extensionworkshop.com/documentation/publish/submitting-an-add-on/)
Firefox requires you to upload a ZIP of your source code. This allows them to rebuild your extension and review the code in a readable way. More details can be found in [Firefox's docs](https://extensionworkshop.com/documentation/publish/source-code-submission/).
WXT and `publish-browser-extension` both fully support generating and automatically submitting a source code ZIP.
WXT fully supports generating and automatically submitting a source code ZIP.
When you run `wxt zip -b firefox`, your sources are zipped into the `.output` directory along with your built extension. WXT is configured to exclude certain files such as config files, hidden files, and tests. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension.
When you run `wxt zip -b firefox`, your sources are zipped into the `.output` directory alongside the extension. WXT will automatically exclude certain files such as config files, hidden files, and tests. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension.
To customize which files are zipped, add the `zip` option to your config file.
@@ -152,22 +126,34 @@ yarn zip:firefox
:::
Ensure that you have a `README.md` or `SOURCE_CODE_REVIEW.md` file with the above commands so that the Firefox team knows how to build your extension.
## Safari
🚧 Not automated at this time
Make sure the build output is the exact same when running `wxt build -b firefox` in your main project and inside the zipped sources.
:::warning
🚧 WXT does not currently support automated publishing for Safari. Safari extensions require a native MacOS or iOS app wrapper, which WXT cannot create at this time. For now, if you want to publish to Safari, follow this guide:
https://developer.apple.com/documentation/safariservices/safari_web_extensions/distributing_your_safari_web_extension
If you use a `.env` files, they can effect the chunk hashes in the output directory. Either delete the .env file before running `wxt zip -b firefox`, or include it in your sources zip with the [`zip.includeSources`](/api/wxt/interfaces/InlineConfig#includesources) option. Be careful to not include any secrets in your `.env` files.
See Issue [#377](https://github.com/wxt-dev/wxt/issues/377) for more details.
:::
## Edge Addons
Ensure that you have a `README.md` or `SOURCE_CODE_REVIEW.md` file with the above commands so that the Firefox team knows how to build your extension.
✅ Automated &bull; [Developer Dashboard](https://aka.ms/PartnerCenterLogin) &bull; [Publishing Docs](https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/publish-extension)
### Safari
> 🚧 Not supported yet
WXT does not currently support automated publishing for Safari. Safari extensions require a native MacOS or iOS app wrapper, which WXT does not create yet. For now, if you want to publish to Safari, follow this guide:
- [Converting a web extension for Safari](https://developer.apple.com/documentation/safariservices/safari_web_extensions/converting_a_web_extension_for_safari) - "Convert your existing extension to a Safari web extension using Xcodes command-line tool."
When running the `safari-web-extension-converter` CLI tool, pass the `.output/safari-mv2` or `.output/safari-mv3` directory, not your source code directory.
```sh
pnpm wxt build -b safari
xcrun safari-web-extension-converter .output/safari-mv2
```
### Edge Addons
> ✅ Supported &bull; [Developer Dashboard](https://aka.ms/PartnerCenterLogin) &bull; [Publishing Docs](https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/publish-extension)
No need to create a specific ZIP for Edge. If you're already publishing to the Chrome Web Store, you can reuse your Chrome ZIP.
+1 -1
View File
@@ -19,7 +19,7 @@ If you want to try to use a different framework for unit tests, you will need to
- **Auto-imports**: Add `unimport` to your test environment or disable them by setting `imports: false` in your `wxt.config.ts` file
- **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.js`
- **[Remote Code Bundling](/guide/remote-code)**: If you use it, configure your environment to handle the `url:` module prefix
- **Global Variables**: If you consume them, manually define globals provided by WXT (like `__BROWSER__`) by adding them to the global scope before accessing them (`globalThis.__BROWSER__ = "chrome"`)
- **Global Variables**: If you consume them, manually define globals provided by WXT (like `import.meta.env.BROWSER`) by adding them to the global scope before accessing them (`import.meta.env.BROWSER = "chrome"`)
- **Import paths**: If you use the `@/` or `~/` path aliases, add them to your test environment
[Here's how Vitest is configured](https://github.com/wxt-dev/wxt/blob/main/src/testing/wxt-vitest-plugin.ts) for reference.
+14 -11
View File
@@ -14,16 +14,19 @@ describe('TypeScript Project', () => {
----------------------------------------
// Generated by wxt
export {}
declare global {
const __MANIFEST_VERSION__: 2 | 3;
const __BROWSER__: string;
const __IS_CHROME__: boolean;
const __IS_FIREFOX__: boolean;
const __IS_SAFARI__: boolean;
const __IS_EDGE__: boolean;
const __IS_OPERA__: boolean;
const __COMMAND__: "build" | "serve";
const __ENTRYPOINT__: string;
interface ImportMetaEnv {
readonly MANIFEST_VERSION: 2 | 3;
readonly BROWSER: string;
readonly CHROME: boolean;
readonly FIREFOX: boolean;
readonly SAFARI: boolean;
readonly EDGE: boolean;
readonly OPERA: boolean;
readonly COMMAND: "build" | "serve";
readonly ENTRYPOINT: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
"
`);
@@ -133,7 +136,7 @@ describe('TypeScript Project', () => {
options?: GetMessageOptions,
): string;
/**
*
* No message description.
*
* "<browser.i18n.getUiLocale()>"
*/
+5 -4
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.14.6",
"version": "0.15.4",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18",
@@ -82,7 +82,7 @@
}
},
"scripts": {
"wxt": "tsx src/cli.ts",
"wxt": "tsx src/cli/index.ts",
"build": "tsx scripts/build.ts",
"format": "prettier --write .",
"format:check": "prettier --check .",
@@ -132,10 +132,11 @@
"ora": "^7.0.1",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.1.0",
"rollup-plugin-visualizer": "^5.9.2",
"unimport": "^3.4.0",
"vite": "^5.0.0",
"web-ext-run": "^0.1.0",
"vite": "^5.0.12",
"web-ext-run": "^0.2.0",
"webextension-polyfill": "^0.10.0",
"zip-dir": "^2.0.0"
},
+395 -99
View File
@@ -95,6 +95,9 @@ importers:
prompts:
specifier: ^2.4.2
version: 2.4.2
publish-browser-extension:
specifier: ^2.1.0
version: 2.1.0
rollup-plugin-visualizer:
specifier: ^5.9.2
version: 5.9.3
@@ -102,11 +105,11 @@ importers:
specifier: ^3.4.0
version: 3.4.0
vite:
specifier: ^5.0.0
version: 5.0.5(@types/node@20.10.3)(sass@1.69.5)
specifier: ^5.0.12
version: 5.0.12(@types/node@20.10.3)(sass@1.69.5)
web-ext-run:
specifier: ^0.1.0
version: 0.1.0
specifier: ^0.2.0
version: 0.2.0
webextension-polyfill:
specifier: ^0.10.0
version: 0.10.0
@@ -412,8 +415,8 @@ packages:
'@babel/types': 7.23.5
dev: true
/@babel/runtime@7.22.11:
resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==}
/@babel/runtime@7.23.9:
resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.0
@@ -442,8 +445,8 @@ packages:
engines: {node: '>= 0.10.4'}
dev: false
/@devicefarmer/adbkit@3.2.5:
resolution: {integrity: sha512-+J479WWZW3GU3t40flicDfiDrFz6vpiy2RcBQPEhFcs/3La9pOtr4Bgz2Q02E4luUG2RAL068rqIkKNUTy3tZw==}
/@devicefarmer/adbkit@3.2.6:
resolution: {integrity: sha512-8lO1hSeTgtxcOHhp4tTWq/JaOysp5KNbbyFoxNEBnwkCDZu/Bji3ZfOaG++Riv9jN6c9bgdLBOZqJTC5VJPRKQ==}
engines: {node: '>= 0.10.4'}
hasBin: true
dependencies:
@@ -1165,14 +1168,22 @@ packages:
resolution: {integrity: sha512-LlmbFLUB7+BDrb9nMuM0wlqtx9LZbBV2x3W98o02cD7Y8i10+sBenTlhG56vr47dzC7WIVXbURii+5jMJsyjLw==}
dev: false
/@vitejs/plugin-vue@5.0.2(vite@5.0.10)(vue@3.4.3):
/@types/yauzl@2.10.3:
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
requiresBuild: true
dependencies:
'@types/node': 20.10.3
dev: false
optional: true
/@vitejs/plugin-vue@5.0.2(vite@5.0.12)(vue@3.4.3):
resolution: {integrity: sha512-kEjJHrLb5ePBvjD0SPZwJlw1QTRcjjCA9sB5VyfonoXVBxTS7TMnqL6EkLt1Eu61RDeiuZ/WN9Hf6PxXhPI2uA==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0
vue: ^3.2.25
dependencies:
vite: 5.0.10(@types/node@20.10.3)
vite: 5.0.12(@types/node@20.10.3)(sass@1.69.5)
vue: 3.4.3(typescript@5.3.2)
dev: true
@@ -1534,7 +1545,6 @@ packages:
engines: {node: '>=14.16'}
dependencies:
type-fest: 3.13.1
dev: true
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
@@ -1632,6 +1642,11 @@ packages:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
/big-integer@1.6.52:
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
engines: {node: '>=0.6'}
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
@@ -1666,6 +1681,13 @@ packages:
wrap-ansi: 8.1.0
dev: false
/bplist-parser@0.2.0:
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
engines: {node: '>= 5.10.0'}
dependencies:
big-integer: 1.6.52
dev: false
/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@@ -1683,6 +1705,10 @@ packages:
dependencies:
fill-range: 7.0.1
/buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: false
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: false
@@ -1694,6 +1720,13 @@ packages:
ieee754: 1.2.1
dev: false
/bundle-name@3.0.0:
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
engines: {node: '>=12'}
dependencies:
run-applescript: 5.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}
@@ -1787,6 +1820,14 @@ packages:
escape-string-regexp: 1.0.5
supports-color: 5.5.0
/chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
dev: false
/chalk@5.3.0:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
@@ -1815,15 +1856,15 @@ packages:
engines: {node: '>=10'}
dev: false
/chrome-launcher@0.15.1:
resolution: {integrity: sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg==}
/chrome-launcher@1.1.0:
resolution: {integrity: sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==}
engines: {node: '>=12.13.0'}
hasBin: true
dependencies:
'@types/node': 20.10.3
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
lighthouse-logger: 2.0.1
transitivePeerDependencies:
- supports-color
dev: false
@@ -1844,6 +1885,19 @@ packages:
dependencies:
restore-cursor: 4.0.0
/cli-highlight@2.1.11:
resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
engines: {node: '>=8.0.0', npm: '>=5.0.0'}
hasBin: true
dependencies:
chalk: 4.1.2
highlight.js: 10.7.3
mz: 2.7.0
parse5: 5.1.1
parse5-htmlparser2-tree-adapter: 6.0.1
yargs: 16.2.0
dev: false
/cli-spinners@2.9.0:
resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==}
engines: {node: '>=6'}
@@ -1855,7 +1909,14 @@ packages:
dependencies:
slice-ansi: 5.0.0
string-width: 7.0.0
dev: true
/cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
dev: false
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
@@ -1866,6 +1927,11 @@ packages:
wrap-ansi: 7.0.0
dev: false
/clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
dev: false
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@@ -2054,6 +2120,30 @@ packages:
engines: {node: '>=4.0.0'}
dev: false
/default-browser-id@3.0.0:
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
engines: {node: '>=12'}
dependencies:
bplist-parser: 0.2.0
untildify: 4.0.0
dev: false
/default-browser@4.0.0:
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
engines: {node: '>=14.16'}
dependencies:
bundle-name: 3.0.0
default-browser-id: 3.0.0
execa: 7.2.0
titleize: 3.0.0
dev: false
/defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
dependencies:
clone: 1.0.4
dev: false
/defer-to-connect@2.0.1:
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
engines: {node: '>=10'}
@@ -2064,6 +2154,11 @@ packages:
engines: {node: '>=8'}
dev: false
/define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
dev: false
/define-properties@1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
@@ -2154,7 +2249,6 @@ packages:
/emoji-regex@10.3.0:
resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==}
dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2164,6 +2258,12 @@ packages:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: false
/end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
dev: false
/entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -2323,7 +2423,6 @@ packages:
/eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
dev: true
/execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
@@ -2338,7 +2437,21 @@ packages:
onetime: 5.1.2
signal-exit: 3.0.7
strip-final-newline: 2.0.0
dev: true
/execa@7.2.0:
resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==}
engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
dependencies:
cross-spawn: 7.0.3
get-stream: 6.0.1
human-signals: 4.3.1
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.1.0
onetime: 6.0.0
signal-exit: 3.0.7
strip-final-newline: 3.0.0
dev: false
/execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
@@ -2354,6 +2467,20 @@ packages:
signal-exit: 4.1.0
strip-final-newline: 3.0.0
/extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
hasBin: true
dependencies:
debug: 4.3.4
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
'@types/yauzl': 2.10.3
transitivePeerDependencies:
- supports-color
dev: false
/fast-glob@3.3.1:
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
engines: {node: '>=8.6.0'}
@@ -2369,6 +2496,12 @@ packages:
dependencies:
reusify: 1.0.4
/fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
dependencies:
pend: 1.2.0
dev: false
/filesize@10.1.0:
resolution: {integrity: sha512-GTLKYyBSDz3nPhlLVPjPWZCnhkd9TrrRArNcy8Z+J2cqScB7h2McAzR6NBX6nYOoWafql0roY8hrocxnZBv9CQ==}
engines: {node: '>= 10.4.0'}
@@ -2380,8 +2513,8 @@ packages:
dependencies:
to-regex-range: 5.0.1
/firefox-profile@4.3.2:
resolution: {integrity: sha512-/C+Eqa0YgIsQT2p66p7Ygzqe7NlE/GNTbhw2SBCm5V3OsWDPITNdTPEcH2Q2fe7eMpYYNPKdUcuVioZBZiR6kA==}
/firefox-profile@4.5.0:
resolution: {integrity: sha512-goE2XxbmYVSafvCjcy64/AK3xOr14HCUCD4+TpYWEIMy4nrJfNAacLGzwqKwZhCW0hHI2TYMGH+G/YBvOE8L6g==}
hasBin: true
dependencies:
adm-zip: 0.5.10
@@ -2413,6 +2546,11 @@ packages:
engines: {node: '>= 14.17'}
dev: false
/formdata-node@6.0.3:
resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==}
engines: {node: '>= 18'}
dev: false
/fs-extra@11.1.1:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
engines: {node: '>=14.14'}
@@ -2422,6 +2560,15 @@ packages:
universalify: 2.0.0
dev: false
/fs-extra@11.2.0:
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'}
dependencies:
graceful-fs: 4.2.11
jsonfile: 6.1.0
universalify: 2.0.0
dev: false
/fs-extra@9.0.1:
resolution: {integrity: sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==}
engines: {node: '>=10'}
@@ -2467,8 +2614,8 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true
/fx-runner@1.3.0:
resolution: {integrity: sha512-5b37H4GCyhF+Nf8xk9mylXoDq4wb7pbGAXxlCXp/631UTeeZomWSYcEGXumY4wk8g2QAqjPMGdWW+RbNt8PUcA==}
/fx-runner@1.4.0:
resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==}
hasBin: true
dependencies:
commander: 2.9.0
@@ -2487,7 +2634,6 @@ packages:
/get-east-asian-width@1.2.0:
resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
engines: {node: '>=18'}
dev: true
/get-func-name@2.0.2:
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
@@ -2506,6 +2652,13 @@ packages:
engines: {node: '>=16'}
dev: false
/get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
dependencies:
pump: 3.0.0
dev: false
/get-stream@6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
@@ -2671,7 +2824,6 @@ packages:
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
/has-property-descriptors@1.0.0:
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
@@ -2708,6 +2860,10 @@ packages:
function-bind: 1.1.1
dev: true
/highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
dev: false
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
@@ -2754,7 +2910,11 @@ packages:
/human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
dev: true
/human-signals@4.3.1:
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
engines: {node: '>=14.18.0'}
dev: false
/human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
@@ -2917,14 +3077,12 @@ packages:
/is-fullwidth-code-point@4.0.0:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
engines: {node: '>=12'}
dev: true
/is-fullwidth-code-point@5.0.0:
resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
engines: {node: '>=18'}
dependencies:
get-east-asian-width: 1.2.0
dev: true
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
@@ -2984,6 +3142,18 @@ packages:
engines: {node: '>=8'}
dev: false
/is-plain-object@2.0.4:
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
engines: {node: '>=0.10.0'}
dependencies:
isobject: 3.0.1
dev: false
/is-primitive@3.0.1:
resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==}
engines: {node: '>=0.10.0'}
dev: false
/is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
@@ -3006,7 +3176,6 @@ packages:
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
dev: true
/is-stream@3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
@@ -3082,6 +3251,11 @@ packages:
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
/isobject@3.0.1:
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
engines: {node: '>=0.10.0'}
dev: false
/istanbul-lib-coverage@3.2.2:
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
engines: {node: '>=8'}
@@ -3191,8 +3365,8 @@ packages:
immediate: 3.0.6
dev: false
/lighthouse-logger@1.4.2:
resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==}
/lighthouse-logger@2.0.1:
resolution: {integrity: sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==}
dependencies:
debug: 2.6.9
marky: 1.2.5
@@ -3256,10 +3430,22 @@ packages:
colorette: 2.0.20
eventemitter3: 5.0.1
log-update: 6.0.0
rfdc: 1.3.0
rfdc: 1.3.1
wrap-ansi: 9.0.0
dev: true
/listr2@8.0.2:
resolution: {integrity: sha512-v5jEMOeEJUpRjSXSB4U3w5A3YPmURYMUO/86f1PA4GGYcdbUQYpkbvKYT7Xaq1iu4Zjn51Rv1UeD1zsBXRijiQ==}
engines: {node: '>=18.0.0'}
dependencies:
cli-truncate: 4.0.0
colorette: 2.0.20
eventemitter3: 5.0.1
log-update: 6.0.0
rfdc: 1.3.1
wrap-ansi: 9.0.0
dev: false
/load-json-file@4.0.0:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
engines: {node: '>=4'}
@@ -3287,9 +3473,21 @@ packages:
mlly: 1.4.2
pkg-types: 1.0.3
/lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
dev: false
/lodash.kebabcase@4.1.1:
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
dev: false
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
/lodash.snakecase@4.1.1:
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
dev: false
/lodash.sortby@4.7.0:
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
dev: true
@@ -3311,7 +3509,6 @@ packages:
slice-ansi: 7.1.0
strip-ansi: 7.1.0
wrap-ansi: 9.0.0
dev: true
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
@@ -3647,7 +3844,6 @@ packages:
engines: {node: '>=8'}
dependencies:
path-key: 3.1.1
dev: true
/npm-run-path@5.1.0:
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
@@ -3684,6 +3880,14 @@ packages:
object-keys: 1.1.1
dev: true
/ofetch@1.3.3:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
dependencies:
destr: 2.0.2
node-fetch-native: 1.4.1
ufo: 1.3.1
dev: false
/ohash@1.1.3:
resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==}
dev: false
@@ -3714,6 +3918,31 @@ packages:
is-wsl: 2.2.0
dev: false
/open@9.1.0:
resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==}
engines: {node: '>=14.16'}
dependencies:
default-browser: 4.0.0
define-lazy-prop: 3.0.0
is-inside-container: 1.0.0
is-wsl: 2.2.0
dev: false
/ora@6.3.1:
resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
chalk: 5.3.0
cli-cursor: 4.0.0
cli-spinners: 2.9.0
is-interactive: 2.0.0
is-unicode-supported: 1.3.0
log-symbols: 5.1.0
stdin-discarder: 0.1.0
strip-ansi: 7.1.0
wcwidth: 1.0.1
dev: false
/ora@7.0.1:
resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==}
engines: {node: '>=16'}
@@ -3772,8 +4001,8 @@ packages:
json-parse-better-errors: 1.0.2
dev: true
/parse-json@7.1.0:
resolution: {integrity: sha512-ihtdrgbqdONYD156Ap6qTcaGcGdkdAxodO1wLqQ/j7HP1u2sFYppINiq4jyC8F+Nm+4fVufylCV00QmkTHkSUg==}
/parse-json@7.1.1:
resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==}
engines: {node: '>=16'}
dependencies:
'@babel/code-frame': 7.22.5
@@ -3783,6 +4012,20 @@ packages:
type-fest: 3.13.1
dev: false
/parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
dependencies:
parse5: 6.0.1
dev: false
/parse5@5.1.1:
resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
dev: false
/parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: false
/path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
@@ -3822,6 +4065,10 @@ packages:
/pathval@1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
/pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: false
/perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
dev: false
@@ -3937,6 +4184,37 @@ packages:
sade: 1.8.1
dev: true
/publish-browser-extension@2.1.0:
resolution: {integrity: sha512-OdnhaGTZ5d3PPNh8gUjTvLhrjH4H0lawe+y5gUvGHAt3hVS33FBTt0r/NyP5qgNtN9TM3iHfTduN4GydXiXt8w==}
engines: {node: '18', pnpm: '8'}
hasBin: true
dependencies:
cac: 6.7.14
cli-highlight: 2.1.11
consola: 3.2.3
dotenv: 16.3.1
extract-zip: 2.0.1
formdata-node: 6.0.3
listr2: 8.0.2
lodash.camelcase: 4.3.0
lodash.kebabcase: 4.1.1
lodash.snakecase: 4.1.1
ofetch: 1.3.3
open: 9.1.0
ora: 6.3.1
prompts: 2.4.2
zod: 3.22.4
transitivePeerDependencies:
- supports-color
dev: false
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: false
/punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
@@ -4103,9 +4381,8 @@ packages:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
/rfdc@1.3.0:
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
dev: true
/rfdc@1.3.1:
resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==}
/rimraf@2.4.5:
resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==}
@@ -4158,6 +4435,13 @@ packages:
'@rollup/rollup-win32-x64-msvc': 4.6.1
fsevents: 2.3.3
/run-applescript@5.0.0:
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
engines: {node: '>=12'}
dependencies:
execa: 5.1.1
dev: false
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -4233,6 +4517,14 @@ packages:
dependencies:
lru-cache: 6.0.0
/set-value@4.1.0:
resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==}
engines: {node: '>=11.0'}
dependencies:
is-plain-object: 2.0.4
is-primitive: 3.0.1
dev: false
/setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
dev: false
@@ -4335,7 +4627,6 @@ packages:
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 4.0.0
dev: true
/slice-ansi@7.1.0:
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
@@ -4343,7 +4634,6 @@ packages:
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 5.0.0
dev: true
/source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
@@ -4460,7 +4750,6 @@ packages:
emoji-regex: 10.3.0
get-east-asian-width: 1.2.0
strip-ansi: 7.1.0
dev: true
/string.prototype.padend@3.1.4:
resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==}
@@ -4528,7 +4817,6 @@ packages:
/strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
dev: true
/strip-final-newline@3.0.0:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
@@ -4574,7 +4862,6 @@ packages:
engines: {node: '>=8'}
dependencies:
has-flag: 4.0.0
dev: true
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
@@ -4632,6 +4919,11 @@ packages:
resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
engines: {node: '>=14.0.0'}
/titleize@3.0.0:
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
engines: {node: '>=12'}
dev: false
/tmp@0.2.1:
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
engines: {node: '>=8.17.0'}
@@ -4862,6 +5154,11 @@ packages:
webpack-virtual-modules: 0.5.0
dev: false
/untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
dev: false
/update-notifier@6.0.2:
resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==}
engines: {node: '>=14.16'}
@@ -4916,7 +5213,7 @@ packages:
debug: 4.3.4
pathe: 1.1.1
picocolors: 1.0.0
vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)
vite: 5.0.12(@types/node@20.10.3)(sass@1.69.5)
transitivePeerDependencies:
- '@types/node'
- less
@@ -4927,44 +5224,8 @@ packages:
- supports-color
- terser
/vite@5.0.10(@types/node@20.10.3):
resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || >=20.0.0
less: '*'
lightningcss: ^1.21.0
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 20.10.3
esbuild: 0.19.8
postcss: 8.4.32
rollup: 4.6.1
optionalDependencies:
fsevents: 2.3.3
dev: true
/vite@5.0.5(@types/node@20.10.3)(sass@1.69.5):
resolution: {integrity: sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==}
/vite@5.0.12(@types/node@20.10.3)(sass@1.69.5):
resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -5014,7 +5275,7 @@ 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.0.10)(vue@3.4.3)
'@vitejs/plugin-vue': 5.0.2(vite@5.0.12)(vue@3.4.3)
'@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)
@@ -5025,7 +5286,7 @@ packages:
shikiji: 0.9.16
shikiji-core: 0.9.16
shikiji-transformers: 0.9.16
vite: 5.0.10(@types/node@20.10.3)
vite: 5.0.12(@types/node@20.10.3)(sass@1.69.5)
vue: 3.4.3(typescript@5.3.2)
transitivePeerDependencies:
- '@algolia/client-search'
@@ -5111,7 +5372,7 @@ packages:
strip-literal: 1.3.0
tinybench: 2.5.1
tinypool: 0.8.1
vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)
vite: 5.0.12(@types/node@20.10.3)(sass@1.69.5)
vite-node: 1.1.0(@types/node@20.10.3)(sass@1.69.5)
why-is-node-running: 2.2.2
transitivePeerDependencies:
@@ -5186,32 +5447,39 @@ packages:
graceful-fs: 4.2.11
dev: false
/web-ext-run@0.1.0:
resolution: {integrity: sha512-42ezvTBirf1v2hw0q4FqYvAzh39bCn3cMQojTLlaE5Y8DWebVkRGdd0jdSJRzPkB5jZa0CHfnqe8gx6oUllqyw==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
/wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies:
'@babel/runtime': 7.22.11
'@devicefarmer/adbkit': 3.2.5
defaults: 1.0.4
dev: false
/web-ext-run@0.2.0:
resolution: {integrity: sha512-lAm05ELMr2WDPniyaHmyuPK0rb9tsftC8f/Ui5AQvlU6F3LqoBDfyzOaaUVQrLxtm4F5oax8AHPWswf/XjZzAg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
dependencies:
'@babel/runtime': 7.23.9
'@devicefarmer/adbkit': 3.2.6
bunyan: 1.8.15
chrome-launcher: 0.15.1
chrome-launcher: 1.1.0
debounce: 1.2.1
es6-error: 4.1.1
firefox-profile: 4.3.2
fs-extra: 11.1.1
fx-runner: 1.3.0
firefox-profile: 4.5.0
fs-extra: 11.2.0
fx-runner: 1.4.0
mkdirp: 1.0.4
multimatch: 6.0.0
mz: 2.7.0
node-notifier: 10.0.1
parse-json: 7.1.0
parse-json: 7.1.1
promise-toolbox: 0.21.0
set-value: 4.1.0
source-map-support: 0.5.21
strip-bom: 5.0.0
strip-json-comments: 5.0.1
tmp: 0.2.1
update-notifier: 6.0.2
watchpack: 2.4.0
ws: 8.13.0
ws: 8.16.0
zip-dir: 2.0.0
transitivePeerDependencies:
- bufferutil
@@ -5350,7 +5618,6 @@ packages:
ansi-styles: 6.2.1
string-width: 7.0.0
strip-ansi: 7.1.0
dev: true
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@@ -5364,8 +5631,8 @@ packages:
typedarray-to-buffer: 3.1.5
dev: false
/ws@8.13.0:
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
/ws@8.16.0:
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -5408,11 +5675,29 @@ packages:
engines: {node: '>= 14'}
dev: true
/yargs-parser@20.2.9:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
engines: {node: '>=10'}
dev: false
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: false
/yargs@16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
engines: {node: '>=10'}
dependencies:
cliui: 7.0.4
escalade: 3.1.1
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 20.2.9
dev: false
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
@@ -5426,6 +5711,13 @@ packages:
yargs-parser: 21.1.1
dev: false
/yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
dependencies:
buffer-crc32: 0.2.13
fd-slicer: 1.1.0
dev: false
/yocto-queue@1.0.0:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
@@ -5436,3 +5728,7 @@ packages:
async: 3.2.4
jszip: 3.10.1
dev: false
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
dev: false
+1 -1
View File
@@ -94,7 +94,7 @@ const config: tsup.Options[] = [
{
...preset,
entry: {
cli: 'src/cli.ts',
cli: 'src/cli/index.ts',
},
format: ['esm'],
banner: {
@@ -1,29 +1,29 @@
import { describe, it, vi, beforeEach, expect } from 'vitest';
import { build } from '../core/build';
import { createServer } from '../core/create-server';
import { zip } from '../core/zip';
import { prepare } from '../core/prepare';
import { clean } from '../core/clean';
import { initialize } from '../core/initialize';
import { build } from '~/core/build';
import { createServer } from '~/core/create-server';
import { zip } from '~/core/zip';
import { prepare } from '~/core/prepare';
import { clean } from '~/core/clean';
import { initialize } from '~/core/initialize';
import { mock } from 'vitest-mock-extended';
import consola from 'consola';
vi.mock('../core/build');
vi.mock('~/core/build');
const buildMock = vi.mocked(build);
vi.mock('../core/create-server');
vi.mock('~/core/create-server');
const createServerMock = vi.mocked(createServer);
vi.mock('../core/zip');
vi.mock('~/core/zip');
const zipMock = vi.mocked(zip);
vi.mock('../core/prepare');
vi.mock('~/core/prepare');
const prepareMock = vi.mocked(prepare);
vi.mock('../core/clean');
vi.mock('~/core/clean');
const cleanMock = vi.mocked(clean);
vi.mock('../core/initialize');
vi.mock('~/core/initialize');
const initializeMock = vi.mocked(initialize);
consola.wrapConsole();
@@ -34,6 +34,10 @@ function mockArgv(...args: string[]) {
process.argv = ['/bin/node', 'bin/wxt.mjs', ...args];
}
async function importCli() {
await import('~/cli');
}
describe('CLI', () => {
beforeEach(() => {
vi.resetModules();
@@ -44,14 +48,14 @@ describe('CLI', () => {
describe('dev', () => {
it('should not pass any config when no flags are passed', async () => {
mockArgv();
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({});
});
it('should respect passing a custom root', async () => {
mockArgv('path/to/root');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
root: 'path/to/root',
@@ -60,7 +64,7 @@ describe('CLI', () => {
it('should respect a custom config file', async () => {
mockArgv('-c', './path/to/config.ts');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
configFile: './path/to/config.ts',
@@ -69,7 +73,7 @@ describe('CLI', () => {
it('should respect passing a custom mode', async () => {
mockArgv('-m', 'development');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
mode: 'development',
@@ -78,7 +82,7 @@ describe('CLI', () => {
it('should respect passing a custom browser', async () => {
mockArgv('-b', 'firefox');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
browser: 'firefox',
@@ -87,7 +91,7 @@ describe('CLI', () => {
it('should pass correct filtered entrypoints', async () => {
mockArgv('-e', 'popup', '-e', 'options');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
filterEntrypoints: ['popup', 'options'],
@@ -96,7 +100,7 @@ describe('CLI', () => {
it('should respect passing --mv2', async () => {
mockArgv('--mv2');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
manifestVersion: 2,
@@ -105,7 +109,7 @@ describe('CLI', () => {
it('should respect passing --mv3', async () => {
mockArgv('--mv3');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
manifestVersion: 3,
@@ -114,7 +118,7 @@ describe('CLI', () => {
it('should respect passing --debug', async () => {
mockArgv('--debug');
await import('../cli');
await importCli();
expect(createServerMock).toBeCalledWith({
debug: true,
@@ -125,14 +129,14 @@ describe('CLI', () => {
describe('build', () => {
it('should not pass any config when no flags are passed', async () => {
mockArgv('build');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({});
});
it('should respect passing a custom root', async () => {
mockArgv('build', 'path/to/root');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
root: 'path/to/root',
@@ -141,7 +145,7 @@ describe('CLI', () => {
it('should respect a custom config file', async () => {
mockArgv('build', '-c', './path/to/config.ts');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
configFile: './path/to/config.ts',
@@ -150,7 +154,7 @@ describe('CLI', () => {
it('should respect passing a custom mode', async () => {
mockArgv('build', '-m', 'development');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
mode: 'development',
@@ -159,7 +163,7 @@ describe('CLI', () => {
it('should respect passing a custom browser', async () => {
mockArgv('build', '-b', 'firefox');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
browser: 'firefox',
@@ -168,7 +172,7 @@ describe('CLI', () => {
it('should pass correct filtered entrypoints', async () => {
mockArgv('build', '-e', 'popup', '-e', 'options');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
filterEntrypoints: ['popup', 'options'],
@@ -177,7 +181,7 @@ describe('CLI', () => {
it('should respect passing --mv2', async () => {
mockArgv('build', '--mv2');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
manifestVersion: 2,
@@ -186,7 +190,7 @@ describe('CLI', () => {
it('should respect passing --mv3', async () => {
mockArgv('build', '--mv3');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
manifestVersion: 3,
@@ -195,7 +199,7 @@ describe('CLI', () => {
it('should include analysis in the build', async () => {
mockArgv('build', '--analyze');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
analysis: {
@@ -206,7 +210,7 @@ describe('CLI', () => {
it('should respect passing --debug', async () => {
mockArgv('build', '--debug');
await import('../cli');
await importCli();
expect(buildMock).toBeCalledWith({
debug: true,
@@ -217,14 +221,14 @@ describe('CLI', () => {
describe('zip', () => {
it('should not pass any config when no flags are passed', async () => {
mockArgv('zip');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({});
});
it('should respect passing a custom root', async () => {
mockArgv('zip', 'path/to/root');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
root: 'path/to/root',
@@ -233,7 +237,7 @@ describe('CLI', () => {
it('should respect a custom config file', async () => {
mockArgv('zip', '-c', './path/to/config.ts');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
configFile: './path/to/config.ts',
@@ -242,7 +246,7 @@ describe('CLI', () => {
it('should respect passing a custom mode', async () => {
mockArgv('zip', '-m', 'development');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
mode: 'development',
@@ -251,7 +255,7 @@ describe('CLI', () => {
it('should respect passing a custom browser', async () => {
mockArgv('zip', '-b', 'firefox');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
browser: 'firefox',
@@ -260,7 +264,7 @@ describe('CLI', () => {
it('should respect passing --mv2', async () => {
mockArgv('zip', '--mv2');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
manifestVersion: 2,
@@ -269,7 +273,7 @@ describe('CLI', () => {
it('should respect passing --mv3', async () => {
mockArgv('zip', '--mv3');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
manifestVersion: 3,
@@ -278,7 +282,7 @@ describe('CLI', () => {
it('should respect passing --debug', async () => {
mockArgv('zip', '--debug');
await import('../cli');
await importCli();
expect(zipMock).toBeCalledWith({
debug: true,
@@ -289,14 +293,14 @@ describe('CLI', () => {
describe('prepare', () => {
it('should not pass any config when no flags are passed', async () => {
mockArgv('prepare');
await import('../cli');
await importCli();
expect(prepareMock).toBeCalledWith({});
});
it('should respect passing a custom root', async () => {
mockArgv('prepare', 'path/to/root');
await import('../cli');
await importCli();
expect(prepareMock).toBeCalledWith({
root: 'path/to/root',
@@ -305,7 +309,7 @@ describe('CLI', () => {
it('should respect a custom config file', async () => {
mockArgv('prepare', '-c', './path/to/config.ts');
await import('../cli');
await importCli();
expect(prepareMock).toBeCalledWith({
configFile: './path/to/config.ts',
@@ -314,7 +318,7 @@ describe('CLI', () => {
it('should respect passing --debug', async () => {
mockArgv('prepare', '--debug');
await import('../cli');
await importCli();
expect(prepareMock).toBeCalledWith({
debug: true,
@@ -325,14 +329,14 @@ describe('CLI', () => {
describe('clean', () => {
it('should not pass any config when no flags are passed', async () => {
mockArgv('clean');
await import('../cli');
await importCli();
expect(cleanMock).toBeCalledWith(undefined);
});
it('should respect passing a custom root', async () => {
mockArgv('clean', 'path/to/root');
await import('../cli');
await importCli();
expect(cleanMock).toBeCalledWith('path/to/root');
});
@@ -341,14 +345,14 @@ describe('CLI', () => {
describe('init', () => {
it('should not pass any options when no flags are passed', async () => {
mockArgv('init');
await import('../cli');
await importCli();
expect(initializeMock).toBeCalledWith({});
});
it('should respect the provided folder', async () => {
mockArgv('init', 'path/to/folder');
await import('../cli');
await importCli();
expect(initializeMock).toBeCalledWith({
directory: 'path/to/folder',
@@ -357,7 +361,7 @@ describe('CLI', () => {
it('should respect passing --template', async () => {
mockArgv('init', '-t', 'vue');
await import('../cli');
await importCli();
expect(initializeMock).toBeCalledWith({
template: 'vue',
@@ -366,7 +370,7 @@ describe('CLI', () => {
it('should respect passing --pm', async () => {
mockArgv('init', '--pm', 'pnpm');
await import('../cli');
await importCli();
expect(initializeMock).toBeCalledWith({
packageManager: 'pnpm',
+93
View File
@@ -0,0 +1,93 @@
import { CAC, Command } from 'cac';
import consola, { LogLevels } from 'consola';
import { getInternalConfig } from '~/core/utils/building';
import { exec } from '~/core/utils/exec';
import { printHeader } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import { ValidationError } from '~/core/utils/validation';
/**
* Wrap an action handler to add a timer, error handling, and maybe enable debug mode.
*/
export function wrapAction(
cb: (
...args: any[]
) => void | { isOngoing?: boolean } | Promise<void | { isOngoing?: boolean }>,
options?: {
disableFinishedLog?: boolean;
},
) {
return async (...args: any[]) => {
// Enable consola's debug mode globally at the start of all commands when the `--debug` flag is
// passed
const isDebug = !!args.find((arg) => arg?.debug);
if (isDebug) {
consola.level = LogLevels.debug;
}
const startTime = Date.now();
try {
printHeader();
const status = await cb(...args);
if (!status?.isOngoing && !options?.disableFinishedLog)
consola.success(
`Finished in ${formatDuration(Date.now() - startTime)}`,
);
} catch (err) {
consola.fail(
`Command failed after ${formatDuration(Date.now() - startTime)}`,
);
if (err instanceof ValidationError) {
// Don't log these errors, they've already been logged
} else {
consola.error(err);
}
process.exit(1);
}
};
}
/**
* Array flags, when not passed, are either `undefined` or `[undefined]`. This function filters out
* the
*/
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[];
return result.length ? result : undefined;
}
const aliasCommandNames = new Set<string>();
export function createAliasedCommand(
base: CAC,
name: string,
alias: string,
docsUrl: string,
) {
const aliasedCommand = base
.command(name, `Alias for ${alias} (${docsUrl})`)
.allowUnknownOptions()
.action(async () => {
try {
const config = await getInternalConfig({}, 'build');
const args = process.argv.slice(
process.argv.indexOf(aliasedCommand.name) + 1,
);
await exec(config, alias, args, {
stdio: 'inherit',
});
} catch {
// Let the other aliased CLI log errors, just exit
process.exit(1);
}
});
aliasCommandNames.add(aliasedCommand.name);
}
export function isAliasedCommand(command: Command | undefined): boolean {
return !!command && aliasCommandNames.has(command.name);
}
+13 -63
View File
@@ -1,17 +1,12 @@
import cac from 'cac';
import { version } from '~/version';
import { build, clean, createServer, initialize, prepare, zip } from '~/core';
import consola, { LogLevels } from 'consola';
import { printHeader } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import { ValidationError } from './core/utils/validation';
// TODO: Remove. See https://github.com/wxt-dev/wxt/issues/277
process.env.VITE_CJS_IGNORE_WARNING = 'true';
import {
createAliasedCommand,
getArrayFromFlags,
wrapAction,
} from './cli-utils';
const cli = cac('wxt');
cli.help();
cli.version(version);
cli.option('--debug', 'enable debug mode');
@@ -140,57 +135,12 @@ cli
),
);
cli.parse(process.argv);
// SUBMIT
createAliasedCommand(
cli,
'submit',
'publish-extension',
'https://www.npmjs.com/publish-browser-extension',
);
/**
* Wrap an action handler to add a timer, error handling, and maybe enable debug mode.
*/
function wrapAction(
cb: (
...args: any[]
) => void | { isOngoing?: boolean } | Promise<void | { isOngoing?: boolean }>,
options?: {
disableFinishedLog?: boolean;
},
) {
return async (...args: any[]) => {
// Enable consola's debug mode globally at the start of all commands when the `--debug` flag is
// passed
const isDebug = !!args.find((arg) => arg?.debug);
if (isDebug) {
consola.level = LogLevels.debug;
}
const startTime = Date.now();
try {
printHeader();
const status = await cb(...args);
if (!status?.isOngoing && !options?.disableFinishedLog)
consola.success(
`Finished in ${formatDuration(Date.now() - startTime)}`,
);
} catch (err) {
consola.fail(
`Command failed after ${formatDuration(Date.now() - startTime)}`,
);
if (err instanceof ValidationError) {
// Don't log these errors, they've already been logged
} else {
consola.error(err);
}
process.exit(1);
}
};
}
/**
* Array flags, when not passed, are either `undefined` or `[undefined]`. This function filters out
* the
*/
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[];
return result.length ? result : undefined;
}
export default cli;
+19
View File
@@ -0,0 +1,19 @@
import cli from './commands';
import { version } from '~/version';
import { isAliasedCommand } from './cli-utils';
// TODO: Remove. See https://github.com/wxt-dev/wxt/issues/277
process.env.VITE_CJS_IGNORE_WARNING = 'true';
// Grab the command that we're trying to run
cli.parse(process.argv, { run: false });
// If it's not an alias, add the help and version options, then parse again
if (!isAliasedCommand(cli.matchedCommand)) {
cli.help();
cli.version(version);
cli.parse(process.argv, { run: false });
}
// Run the alias or command
await cli.runMatchedCommand();
+5 -3
View File
@@ -15,10 +15,12 @@ export class WxtLocationChangeEvent extends Event {
* Returns an event name unique to the extension and content script that's running.
*/
export function getUniqueEventName(eventName: string): string {
// During the build process, __ENTRYPOINT__ is not defined when importing entrypoints to get their
// metadata.
// During the build process, import.meta.env is not defined when importing
// entrypoints to get their metadata.
const entrypointName =
typeof __ENTRYPOINT__ === 'undefined' ? 'build' : __ENTRYPOINT__;
typeof import.meta.env === 'undefined'
? 'build'
: import.meta.env.ENTRYPOINT;
return `${browser.runtime.id}:${entrypointName}:${eventName}`;
}
+3 -1
View File
@@ -236,7 +236,9 @@ function mountUi(
* Load the CSS for the current entrypoint.
*/
async function loadCss(): Promise<string> {
const url = browser.runtime.getURL(`/content-scripts/${__ENTRYPOINT__}.css`);
const url = browser.runtime.getURL(
`/content-scripts/${import.meta.env.ENTRYPOINT}.css`,
);
try {
const res = await fetch(url);
return await res.text();
+4
View File
@@ -44,6 +44,10 @@ export async function createViteBuilder(
if (config.build.minify == null && wxtConfig.command === 'serve') {
config.build.minify = false;
}
// Enable inline sourcemaps for the dev command (so content scripts have sourcemaps)
if (config.build.sourcemap == null && wxtConfig.command === 'serve') {
config.build.sourcemap = 'inline';
}
config.plugins ??= [];
config.plugins.push(
@@ -0,0 +1,64 @@
import { describe, expect, it } from 'vitest';
import { Window } from 'happy-dom';
import { pointToDevServer } from '../devHtmlPrerender';
import {
fakeDevServer,
fakeInternalConfig,
} from '~/core/utils/testing/fake-objects';
import { normalizePath } from '~/core/utils/paths';
import { resolve } from 'node:path';
describe('Dev HTML Prerender Plugin', () => {
describe('pointToDevServer', () => {
it.each([
// File paths should be resolved
['style.css', 'http://localhost:5173/entrypoints/popup/style.css'],
['./style.css', 'http://localhost:5173/entrypoints/popup/style.css'],
['../style.css', 'http://localhost:5173/entrypoints/style.css'],
['~/assets/style.css', 'http://localhost:5173/assets/style.css'],
['~~/assets/style.css', 'http://localhost:5173/assets/style.css'],
['~local/style.css', 'http://localhost:5173/style.css'],
['~absolute/style.css', 'http://localhost:5173/assets/style.css'],
['~file', 'http://localhost:5173/example.css'],
// Absolute paths are loaded with the `/@fs/` base path
[
'~outside/test.css',
`http://localhost:5173/@fs${
process.platform === 'win32'
? '/' + normalizePath(resolve('/some/non-root/test.css')) // "/D:/some/non-root/test.css"
: '/some/non-root/test.css'
}`,
],
// URLs should not be changed
['https://example.com/style.css', 'https://example.com/style.css'],
])('should transform "%s" into "%s"', (input, expected) => {
const { document } = new Window({
url: 'http://localhost',
});
const root = '/some/root';
const config = fakeInternalConfig({
root,
alias: {
'~local': '.',
'~absolute': `${root}/assets`,
'~file': `${root}/example.css`,
'~outside': `${root}/../non-root`,
'~~': root,
'~': root,
},
});
const server = fakeDevServer({
hostname: 'localhost',
port: 5173,
origin: 'http://localhost:5173',
});
const id = root + '/entrypoints/popup/index.html';
document.head.innerHTML = `<link rel="stylesheet" href="${input}" />`;
pointToDevServer(config, server, id, document as any, 'link', 'href');
const actual = document.querySelector('link')!;
expect(actual.getAttribute('href')).toBe(expected);
});
});
});
@@ -1,8 +1,9 @@
import type * as vite from 'vite';
import { InternalConfig } from '~/types';
import { InternalConfig, WxtDevServer } from '~/types';
import { getEntrypointName } from '~/core/utils/entrypoints';
import { parseHTML } from 'linkedom';
import { dirname, isAbsolute, relative, resolve } from 'node:path';
import { dirname, relative, resolve } from 'node:path';
import { normalizePath } from '~/core/utils/paths';
// Cache the preamble script for all devHtmlPrerender plugins, not just one
let reactRefreshPreamble = '';
@@ -47,25 +48,10 @@ export function devHtmlPrerender(
const { document } = parseHTML(code);
const pointToDevServer = (
querySelector: string,
attr: string,
): void => {
document.querySelectorAll(querySelector).forEach((element) => {
const src = element.getAttribute(attr);
if (!src) return;
if (isAbsolute(src)) {
element.setAttribute(attr, server.origin + src);
} else if (src.startsWith('.')) {
const abs = resolve(dirname(id), src);
const pathname = relative(config.root, abs);
element.setAttribute(attr, `${server.origin}/${pathname}`);
}
});
};
pointToDevServer('script[type=module]', 'src');
pointToDevServer('link[rel=stylesheet]', 'href');
const _pointToDevServer = (querySelector: string, attr: string) =>
pointToDevServer(config, server, id, document, querySelector, attr);
_pointToDevServer('script[type=module]', 'src');
_pointToDevServer('link[rel=stylesheet]', 'href');
// Add a script to add page reloading
const reloader = document.createElement('script');
@@ -146,3 +132,63 @@ export function devHtmlPrerender(
},
];
}
export function pointToDevServer(
config: Omit<InternalConfig, 'builder'>,
server: WxtDevServer,
id: string,
document: Document,
querySelector: string,
attr: string,
) {
document.querySelectorAll(querySelector).forEach((element) => {
const src = element.getAttribute(attr);
if (!src || isUrl(src)) return;
let resolvedAbsolutePath: string | undefined;
// Check if src uses a project alias
const matchingAlias = Object.entries(config.alias).find(([key]) =>
src.startsWith(key),
);
if (matchingAlias) {
// Matches a import alias
const [alias, replacement] = matchingAlias;
resolvedAbsolutePath = resolve(
config.root,
src.replace(alias, replacement),
);
} else {
// Some file path relative to the HTML file
resolvedAbsolutePath = resolve(dirname(id), src);
}
// Apply the final file path
if (resolvedAbsolutePath) {
const relativePath = normalizePath(
relative(config.root, resolvedAbsolutePath),
);
if (relativePath.startsWith('.')) {
// Outside the config.root directory, serve the absolute path
let path = normalizePath(resolvedAbsolutePath);
// Add "/" to start of windows paths ("D:/some/path" -> "/D:/some/path")
if (!path.startsWith('/')) path = '/' + path;
element.setAttribute(attr, `${server.origin}/@fs${path}`);
} else {
// Inside the project, use relative path
const url = new URL(relativePath, server.origin);
element.setAttribute(attr, url.href);
}
}
});
}
function isUrl(str: string): boolean {
try {
new URL(str);
return true;
} catch {
return false;
}
}
@@ -14,7 +14,7 @@ export function entrypointGroupGlobals(
const define: vite.InlineConfig['define'] = {};
let name = Array.isArray(entrypointGroup) ? 'html' : entrypointGroup.name;
for (const global of getEntrypointGlobals(name)) {
define[global.name] = JSON.stringify(global.value);
define[`import.meta.env.${global.name}`] = JSON.stringify(global.value);
}
return {
define,
+1 -1
View File
@@ -10,7 +10,7 @@ export function globals(
config() {
const define: vite.InlineConfig['define'] = {};
for (const global of getGlobals(config)) {
define[global.name] = JSON.stringify(global.value);
define[`import.meta.env.${global.name}`] = JSON.stringify(global.value);
}
return {
define,
+5 -1
View File
@@ -36,7 +36,11 @@ export function unimport(
// Don't transform non-js files
if (!ENABLED_EXTENSIONS.has(extname(id))) return;
return unimport.injectImports(code, id);
const injected = await unimport.injectImports(code, id);
return {
code: injected.code,
map: injected.s.generateMap({ hires: 'boundary', source: id }),
};
},
};
}
+16
View File
@@ -1,6 +1,7 @@
import type { WebExtRunInstance } from 'web-ext-run';
import { ExtensionRunner } from '~/types';
import { formatDuration } from '../utils/time';
import defu from 'defu';
/**
* Create an `ExtensionRunner` backed by `web-ext`.
@@ -40,6 +41,10 @@ export function createWebExtRunner(): ExtensionRunner {
: {
chromiumBinary: wxtUserConfig?.binaries?.[config.browser],
chromiumProfile: wxtUserConfig?.chromiumProfile,
chromiumPref: defu(
wxtUserConfig?.chromiumPref,
DEFAULT_CHROMIUM_PREFS,
),
args: wxtUserConfig?.chromiumArgs,
}),
};
@@ -75,3 +80,14 @@ export function createWebExtRunner(): ExtensionRunner {
// https://github.com/mozilla/web-ext/blob/e37e60a2738478f512f1255c537133321f301771/src/util/logger.js#L12
const WARN_LOG_LEVEL = 40;
const ERROR_LOG_LEVEL = 50;
const DEFAULT_CHROMIUM_PREFS = {
devtools: {
synced_preferences_sync_disabled: {
// Remove content scripts from sourcemap debugger ignore list so stack traces
// and log locations show up properly, see:
// https://github.com/wxt-dev/wxt/issues/236#issuecomment-1915364520
skipContentScripts: false,
},
},
};
@@ -167,9 +167,9 @@ describe('Detect Dev Changes', () => {
});
describe('HTML Pages', () => {
it('should rebuild then reload only the effected pages', async () => {
it('should detect changes to entrypoints/<name>.html files', async () => {
const config = fakeInternalConfig();
const changedPath = '/root/page1/index.html';
const changedPath = '/root/page1.html';
const htmlPage1 = fakePopupEntrypoint({
inputPath: changedPath,
});
@@ -184,16 +184,66 @@ describe('Detect Dev Changes', () => {
const step1: BuildStepOutput = {
entrypoints: [htmlPage1, htmlPage2],
chunks: [
fakeOutputChunk({
moduleIds: [fakeFile(), changedPath],
fakeOutputAsset({
fileName: 'page1.html',
}),
],
};
const step2: BuildStepOutput = {
entrypoints: [htmlPage3],
chunks: [
fakeOutputChunk({
moduleIds: [fakeFile(), fakeFile(), fakeFile()],
fakeOutputAsset({
fileName: 'page2.html',
}),
],
};
const currentOutput: BuildOutput = {
manifest: fakeManifest(),
publicAssets: [],
steps: [step1, step2],
};
const expected: DevModeChange = {
type: 'html-reload',
cachedOutput: {
...currentOutput,
steps: [step2],
},
rebuildGroups: [[htmlPage1, htmlPage2]],
};
const actual = detectDevChanges(config, [changedPath], currentOutput);
expect(actual).toEqual(expected);
});
it('should detect changes to entrypoints/<name>/index.html files', async () => {
const config = fakeInternalConfig();
const changedPath = '/root/page1/index.html';
const htmlPage1 = fakePopupEntrypoint({
inputPath: changedPath,
});
const htmlPage2 = fakeOptionsEntrypoint({
inputPath: '/root/page2/index.html',
});
const htmlPage3 = fakeGenericEntrypoint({
type: 'sandbox',
inputPath: '/root/page3/index.html',
});
const step1: BuildStepOutput = {
entrypoints: [htmlPage1, htmlPage2],
chunks: [
fakeOutputAsset({
fileName: 'page1.html',
}),
],
};
const step2: BuildStepOutput = {
entrypoints: [htmlPage3],
chunks: [
fakeOutputAsset({
fileName: 'page2.html',
}),
],
};
+9 -6
View File
@@ -18,12 +18,15 @@ 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)
.join(pc.dim(', '));
spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNames}`;
steps.push(await config.builder.build(group));
const groupNames = [group].flat().map((e) => e.name);
const groupNameColored = groupNames.join(pc.dim(', '));
spinner.text =
pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNameColored}`;
try {
steps.push(await config.builder.build(group));
} catch (err) {
throw Error(`Failed to build ${groupNames.join(', ')}`, { cause: err });
}
}
const publicAssets = await copyPublicDirectory(config);
@@ -123,11 +123,12 @@ function findEffectedSteps(
const changedPath = normalizePath(changedFile);
const isChunkEffected = (chunk: OutputFile): boolean =>
// If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
// fileName is normalized, relative bundle path
(chunk.type === 'asset' && changedPath.endsWith(chunk.fileName)) ||
// If it's an HTML file with the same path, is is effected because HTML files need to be re-rendered
// - fileName is normalized, relative bundle path, "<entrypoint-name>.html"
(chunk.type === 'asset' &&
changedPath.replace('/index.html', '.html').endsWith(chunk.fileName)) ||
// If it's a chunk that depends on the changed file, it is effected
// moduleIds are absolute, normalized paths
// - moduleIds are absolute, normalized paths
(chunk.type === 'chunk' && chunk.moduleIds.includes(changedPath));
for (const step of currentOutput.steps) {
+6 -3
View File
@@ -136,7 +136,7 @@ declare module "wxt/browser" {
const overrides = messages.map((message) => {
return ` /**
* ${message.description ?? 'No message description.'}
* ${message.description || 'No message description.'}
*
* "${message.message}"
*/
@@ -164,8 +164,11 @@ async function writeGlobalsDeclarationFile(
[
'// Generated by wxt',
'export {}',
'declare global {',
...globals.map((global) => ` const ${global.name}: ${global.type};`),
'interface ImportMetaEnv {',
...globals.map((global) => ` readonly ${global.name}: ${global.type};`),
'}',
'interface ImportMeta {',
' readonly env: ImportMetaEnv',
'}',
].join('\n') + '\n',
);
@@ -241,8 +241,9 @@ function resolveInternalZipConfig(
sourcesTemplate: '{{name}}-{{version}}-sources.zip',
artifactTemplate: '{{name}}-{{version}}-{{browser}}.zip',
sourcesRoot: root,
includeSources: [],
...mergedConfig.zip,
ignoredSources: [
excludeSources: [
'**/node_modules',
// WXT files
'**/web-ext.config.ts',
@@ -252,7 +253,7 @@ function resolveInternalZipConfig(
'**/__tests__/**',
'**/*.+(test|spec).?(c|m)+(j|t)s?(x)',
// From user
...(mergedConfig.zip?.ignoredSources ?? []),
...(mergedConfig.zip?.excludeSources ?? []),
],
};
}
+3 -2
View File
@@ -89,16 +89,17 @@ export async function importEntrypointFile<T>(
const res = await jiti(path);
return res.default;
} catch (err) {
const filePath = relative(config.root, path);
if (err instanceof ReferenceError) {
// "XXX is not defined" - usually due to WXT removing imports
const variableName = err.message.replace(' is not defined', '');
const filePath = relative(config.root, path);
throw Error(
`${filePath}: Cannot use imported variable "${variableName}" outside the main function. See https://wxt.dev/guide/entrypoints.html#side-effects`,
{ cause: err },
);
} else {
throw err;
config.logger.error(err);
throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err });
}
}
}
+6 -11
View File
@@ -8,8 +8,7 @@ import { printBuildSummary } from '~/core/utils/log';
import glob from 'fast-glob';
import { unnormalizePath } from '~/core/utils/paths';
import { rebuild } from './rebuild';
import managePath from 'manage-path';
import { resolve, relative } from 'node:path';
import { relative } from 'node:path';
import {
ValidationError,
ValidationResult,
@@ -17,6 +16,7 @@ import {
validateEntrypoints,
} from '../validation';
import consola from 'consola';
import { exec } from '../exec';
/**
* Builds the extension based on an internal config. No more config discovery is performed, the
@@ -88,21 +88,16 @@ export async function internalBuild(
}
async function combineAnalysisStats(config: InternalConfig): Promise<void> {
const { execaCommand } = await import('execa');
const unixFiles = await glob(`stats-*.json`, {
cwd: config.outDir,
absolute: true,
});
const absolutePaths = unixFiles.map(unnormalizePath);
const alterPath = managePath(process.env);
// Add subdependency path for PNPM shamefully-hoist=false
alterPath.push(resolve(config.root, 'node_modules/wxt/node_modules/.bin'));
await execaCommand(
`rollup-plugin-visualizer ${absolutePaths.join(' ')} --template ${
config.analysis.template
}`,
await exec(
config,
'rollup-plugin-visualizer',
[...absolutePaths, '--template', config.analysis.template],
{ cwd: config.root, stdio: 'inherit' },
);
}
+25
View File
@@ -0,0 +1,25 @@
import type { Options } from 'execa';
import managePath from 'manage-path';
import { resolve } from 'node:path';
import { InternalConfig } from '~/types';
const managedPath = managePath(process.env);
/**
* Wrapper around `execa` with a modified `PATH` variable containing CLI tools from WXT's dependencies.
*/
export const exec = async (
config: InternalConfig,
file: string,
args?: readonly string[],
options?: Options,
) => {
// Reset so the same path isn't added multiple times
managedPath.restore();
// Add subdependency path for PNPM shamefully-hoist=false
managedPath.push(resolve(config.root, 'node_modules/wxt/node_modules/.bin'));
const { execa } = await import('execa');
return await execa(file, args, options);
};
+9 -17
View File
@@ -5,42 +5,42 @@ export function getGlobals(
): Array<{ name: string; value: any; type: string }> {
return [
{
name: surroundInUnderscore('MANIFEST_VERSION'),
name: 'MANIFEST_VERSION',
value: config.manifestVersion,
type: `2 | 3`,
},
{
name: surroundInUnderscore('BROWSER'),
name: 'BROWSER',
value: config.browser,
type: `string`,
},
{
name: surroundInUnderscore('IS_CHROME'),
name: 'CHROME',
value: config.browser === 'chrome',
type: `boolean`,
},
{
name: surroundInUnderscore('IS_FIREFOX'),
name: 'FIREFOX',
value: config.browser === 'firefox',
type: `boolean`,
},
{
name: surroundInUnderscore('IS_SAFARI'),
name: 'SAFARI',
value: config.browser === 'safari',
type: `boolean`,
},
{
name: surroundInUnderscore('IS_EDGE'),
name: 'EDGE',
value: config.browser === 'edge',
type: `boolean`,
},
{
name: surroundInUnderscore('IS_OPERA'),
name: 'OPERA',
value: config.browser === 'opera',
type: `boolean`,
},
{
name: surroundInUnderscore('COMMAND'),
name: 'COMMAND',
value: config.command,
type: `"build" | "serve"`,
},
@@ -50,17 +50,9 @@ export function getGlobals(
export function getEntrypointGlobals(entrypointName: string) {
return [
{
name: surroundInUnderscore('ENTRYPOINT'),
name: 'ENTRYPOINT',
value: entrypointName,
type: `string`,
},
];
}
/**
* Don't hardcode the complete name so that the string litterals in this file aren't replaced during
* tests (which causes syntax errors), only during builds.
*/
function surroundInUnderscore(name: string): string {
return `__${name}__`;
}
+20 -1
View File
@@ -22,6 +22,7 @@ import {
UserManifest,
} from '~/types';
import { mock } from 'vitest-mock-extended';
import { vi } from 'vitest';
faker.seed(__TEST_SEED__);
@@ -232,7 +233,8 @@ export const fakeInternalConfig = fakeObjectCreator<InternalConfig>(() => {
},
zip: {
artifactTemplate: '{{name}}-{{version}}.zip',
ignoredSources: [],
includeSources: [],
excludeSources: [],
sourcesRoot: fakeDir(),
sourcesTemplate: '{{name}}-sources.zip',
name: faker.person.firstName().toLowerCase(),
@@ -268,3 +270,20 @@ export const fakeManifestCommand = fakeObjectCreator<Commands.Command>(() => ({
max: 9,
})}`,
}));
export const fakeDevServer = fakeObjectCreator<WxtDevServer>(() => ({
hostname: 'localhost',
origin: 'http://localhost',
port: 5173,
reloadContentScript: vi.fn(),
reloadExtension: vi.fn(),
reloadPage: vi.fn(),
restart: vi.fn(),
restartBrowser: vi.fn(),
stop: vi.fn(),
start: vi.fn(),
watcher: mock(),
transformHtml: vi.fn(),
ws: mock(),
currentOutput: undefined,
}));
+8 -3
View File
@@ -62,10 +62,15 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
saveTo: sourcesZipPath,
filter(path) {
const relativePath = relative(internalConfig.zip.sourcesRoot, path);
const matchedPattern = internalConfig.zip.ignoredSources.find(
(pattern) => minimatch(relativePath, pattern),
return (
internalConfig.zip.includeSources.some((pattern) =>
minimatch(relativePath, pattern),
) ||
!internalConfig.zip.excludeSources.some((pattern) =>
minimatch(relativePath, pattern),
)
);
return matchedPattern == null;
},
});
zipFiles.push(sourcesZipPath);
+11 -1
View File
@@ -308,10 +308,20 @@ function createDriver(
storageArea: 'local' | 'session' | 'sync' | 'managed',
): WxtStorageDriver {
const getStorageArea = () => {
if (browser.storage == null)
if (browser.runtime == null) {
throw Error(
[
"'wxt/storage' must be loaded in a web extension environment",
'\n - If thrown during a build, see https://github.com/wxt-dev/wxt/issues/371',
" - If thrown during tests, mock 'wxt/browser' correctly. See https://wxt.dev/guide/testing.html\n",
].join('\n'),
);
}
if (browser.storage == null) {
throw Error(
"You must add the 'storage' permission to your manifest to use 'wxt/storage'",
);
}
return browser.storage[storageArea];
};
+42 -2
View File
@@ -143,9 +143,22 @@ export interface InlineConfig {
* @default config.root
*/
sourcesRoot?: string;
/**
* [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to include when
* creating a ZIP of all your source code for Firefox. Patterns are relative to your
* `config.zip.sourcesRoot`.
*
* This setting overrides `excludeSources`. So if a file matches both lists, it is included in the ZIP.
*
* @example
* [
* "coverage", // Ignore the coverage directory in the `sourcesRoot`
* ]
*/
includeSources?: string[];
/**
* [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to exclude when
* creating a ZIP of all your source code for Firfox. Patterns are relative to your
* creating a ZIP of all your source code for Firefox. Patterns are relative to your
* `config.zip.sourcesRoot`.
*
* Hidden files, node_modules, and tests are ignored by default.
@@ -155,7 +168,7 @@ export interface InlineConfig {
* "coverage", // Ignore the coverage directory in the `sourcesRoot`
* ]
*/
ignoredSources?: string[];
excludeSources?: string[];
};
/**
@@ -661,6 +674,33 @@ export interface ExtensionRunnerConfig {
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#chromium-profile
*/
chromiumProfile?: string;
/**
* An map of chrome preferences from https://chromium.googlesource.com/chromium/src/+/main/chrome/common/pref_names.h
*
* @example
* // change your downloads directory
* {
* download: {
* default_directory: "/my/custom/dir",
* },
* }
*
* @default
* // Enable dev mode and allow content script sourcemaps
* {
* devtools: {
* synced_preferences_sync_disabled: {
* skipContentScripts: false,
* },
* }
* extensions: {
* ui: {
* developer_mode: true,
* },
* }
* }
*/
chromiumPref?: string;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#pref
*/
+7 -4
View File
@@ -1,7 +1,10 @@
// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts
declare const __COMMAND__: 'build' | 'serve';
declare const __DEV_SERVER_PROTOCOL__: string;
declare const __DEV_SERVER_HOSTNAME__: string;
declare const __DEV_SERVER_PORT__: string;
declare const __MANIFEST_VERSION__: 2 | 3;
declare const __ENTRYPOINT__: string;
// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts
interface ImportMetaEnv {
readonly COMMAND: 'build' | 'serve';
readonly MANIFEST_VERSION: 2 | 3;
readonly ENTRYPOINT: string;
}
+2 -1
View File
@@ -40,7 +40,8 @@ export interface InternalConfig {
name?: string;
artifactTemplate: string;
sourcesTemplate: string;
ignoredSources: string[];
includeSources: string[];
excludeSources: string[];
sourcesRoot: string;
};
transformManifest: (manifest: Manifest.WebExtensionManifest) => void;
+9 -1
View File
@@ -51,7 +51,15 @@ declare module 'web-ext-run/util/logger' {
}
declare module 'manage-path' {
export default function managePath(env: object): string[];
export interface ManagedPath {
push(...paths: string[]);
push(paths: string[]);
shift(...paths: string[]);
shift(paths: string[]);
get(): string;
restore(): void;
}
export default function managePath(env: object): ManagedPath;
}
declare module 'wxt/browser' {
+2 -2
View File
@@ -5,7 +5,7 @@ import { browser } from 'wxt/browser';
import { keepServiceWorkerAlive } from './utils/keep-service-worker-alive';
import { reloadContentScript } from './utils/reload-content-scripts';
if (__COMMAND__ === 'serve') {
if (import.meta.env.COMMAND === 'serve') {
try {
const ws = setupWebSocket((message) => {
if (message.event === 'wxt:reload-extension') browser.runtime.reload();
@@ -13,7 +13,7 @@ if (__COMMAND__ === 'serve') {
reloadContentScript(message.data);
});
if (__MANIFEST_VERSION__ === 3) {
if (import.meta.env.MANIFEST_VERSION === 3) {
// Tell the server the background script is loaded and ready to go
ws.addEventListener('open', () => {
const msg = { type: 'custom', event: 'wxt:background-initialized' };
@@ -5,12 +5,12 @@ import { ContentScriptContext } from 'wxt/client';
(async () => {
try {
const { main, ...options } = definition;
const ctx = new ContentScriptContext(__ENTRYPOINT__, options);
const ctx = new ContentScriptContext(import.meta.env.ENTRYPOINT, options);
await main(ctx);
} catch (err) {
logger.error(
`The content script "${__ENTRYPOINT__}" crashed on startup!`,
`The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
err,
);
}
@@ -7,7 +7,7 @@ import { logger } from '../sandbox/utils/logger';
await main();
} catch (err) {
logger.error(
`The content script "${__ENTRYPOINT__}" crashed on startup!`,
`The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
err,
);
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { logger } from '../sandbox/utils/logger';
import { setupWebSocket } from './utils/setup-web-socket';
if (__COMMAND__ === 'serve') {
if (import.meta.env.COMMAND === 'serve') {
try {
setupWebSocket((message) => {
if (message.event === 'wxt:reload-page') {
+1 -1
View File
@@ -6,7 +6,7 @@ import { logger } from '../sandbox/utils/logger';
await definition.main();
} catch (err) {
logger.error(
`The unlisted script "${__ENTRYPOINT__}" crashed on startup!`,
`The unlisted script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
err,
);
}
+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.14.0"
"wxt": "^0.15.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"devDependencies": {
"typescript": "^5.3.3",
"vite-plugin-solid": "^2.8.0",
"wxt": "^0.14.0"
"wxt": "^0.15.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"svelte-check": "^3.6.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"wxt": "^0.14.0"
"wxt": "^0.15.0"
}
}
+1 -1
View File
@@ -16,6 +16,6 @@
},
"devDependencies": {
"typescript": "^5.3.3",
"wxt": "^0.14.0"
"wxt": "^0.15.0"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"@vitejs/plugin-vue": "^5.0.1",
"typescript": "^5.3.3",
"vue-tsc": "^1.8.27",
"wxt": "^0.14.0"
"wxt": "^0.15.0"
}
}