Compare commits

..

12 Commits

Author SHA1 Message Date
GitHub Actions 1b1af245bd chore(release): v0.8.3 2023-10-18 21:38:14 +00:00
Aaron 874a531a62 feat: Add testing utils under wxt/testing (#178) 2023-10-18 16:28:26 -05:00
GitHub Actions a3d409f250 chore(release): v0.8.2 2023-10-17 20:39:45 +00:00
Aaron b6758ca9fa fix(firefox): Stop extending AbortController to fix crash in content scripts (#176) 2023-10-17 15:33:55 -05:00
Aaron 2672308946 chore: Improve output consistency (#175) 2023-10-16 11:00:33 -05:00
GitHub Actions 7ac171ed3b chore(release): v0.8.1 2023-10-16 14:33:06 +00:00
Aaron Klinker 6a93f20bb3 fix: Output action.browser_style correctly 2023-10-16 09:20:40 -05:00
Aaron Klinker 921af6a5a1 Rename jobs 2023-10-15 15:08:34 -05:00
Aaron Klinker 21ebeacd20 chore(deps): Revert node 20 upgrade due to OOM errors in CI 2023-10-15 14:51:36 -05:00
Aaron 527600031f docs: Generate full API docs with typedoc (#174) 2023-10-14 22:03:23 -05:00
Aaron Klinker 91a804c492 Update changelog 2023-10-14 10:40:36 -05:00
Aaron Klinker 54f3785063 Upgrade templates to use v0.8 2023-10-14 10:40:10 -05:00
59 changed files with 688 additions and 561 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 18
cache: 'pnpm'
- name: Install dependencies
+30 -9
View File
@@ -7,13 +7,9 @@ on:
- main
jobs:
wxt:
name: WXT
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
checks:
name: Checks
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -26,7 +22,7 @@ jobs:
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 18
cache: 'pnpm'
- name: Install dependencies
@@ -46,6 +42,7 @@ jobs:
pnpm build:all:firefox-mv3
pnpm tsc --noEmit
pnpm wxt zip
pnpm vitest run
working-directory: demo
- name: Tests
@@ -56,6 +53,30 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
windows-tests:
name: Windows Tests
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Tests
run: pnpm test:coverage --reporter=default --reporter=hanging-process
project-templates:
name: Project Templates
runs-on: ubuntu-22.04
@@ -71,7 +92,7 @@ jobs:
- name: Setup NodeJS
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 18
cache: 'pnpm'
- name: Install dependencies
+1 -2
View File
@@ -17,7 +17,6 @@ web-ext.config.ts
templates/*/pnpm-lock.yaml
templates/*/yarn.lock
templates/*/package-lock.json
docs/api/config.md
docs/api/cli.md
docs/api
stats.html
.tool-versions
+33 -1
View File
@@ -1,5 +1,37 @@
# Changelog
## v0.8.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.2...v0.8.3)
### 🚀 Enhancements
- Add testing utils under `wxt/testing` ([#178](https://github.com/wxt-dev/wxt/pull/178))
## v0.8.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.1...v0.8.2)
### 🩹 Fixes
- **firefox:** Stop extending `AbortController` to fix crash in content scripts ([#176](https://github.com/wxt-dev/wxt/pull/176))
### 🏡 Chore
- Improve output consistency ([#175](https://github.com/wxt-dev/wxt/pull/175))
## v0.8.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.8.0...v0.8.1)
### 🩹 Fixes
- Output `action.browser_style` correctly ([6a93f20](https://github.com/wxt-dev/wxt/commit/6a93f20))
### 📖 Documentation
- Generate full API docs with typedoc ([#174](https://github.com/wxt-dev/wxt/pull/174))
## v0.8.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.7.5...v0.8.0)
@@ -18,7 +50,7 @@
#### ⚠️ Breaking Changes
- ⚠️ Use `defineUnlistedScript` to define unlisted scripts ([#167](https://github.com/wxt-dev/wxt/pull/167))
- ⚠️ Unlisted scripts must now `export default defineUnlistedScript(...)` ([#167](https://github.com/wxt-dev/wxt/pull/167))
- ⚠️ Rename `BackgroundScriptDefintition` to `BackgroundDefinition` ([446f265](https://github.com/wxt-dev/wxt/commit/446f265))
### ❤️ Contributors
+4 -2
View File
@@ -11,14 +11,16 @@
"build:all:chrome-mv2": "wxt build --mv2",
"build:all:firefox-mv3": "wxt build -b firefox --mv3",
"build:all:firefox-mv2": "wxt build -b firefox",
"test": "pnpm -w build && vitest",
"zip": "pnpm -w build && wxt zip",
"compile": "pnpm -w build && tsc --noEmit",
"postinstall": "pnpm -w build && wxt prepare"
},
"dependencies": {
"webextension-polyfill": "^0.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"vitest": "^0.34.6",
"webextension-polyfill": "^0.10.0"
},
"devDependencies": {
"@types/react": "^18.2.14",
@@ -0,0 +1,18 @@
import { describe, expect, it, vi } from 'vitest';
import background from '../background';
browser.i18n.getMessage = () => 'fake-message';
const logMock = vi.fn();
console.log = logMock;
describe('Background Entrypoint', () => {
it("should log the extenion's runtime ID", () => {
const id = 'some-id';
fakeBrowser.runtime.id = id;
background.main();
expect(logMock).toBeCalledWith(id);
});
});
+10
View File
@@ -0,0 +1,10 @@
import { defineConfig } from 'vitest/config';
import { WxtVitest } from 'wxt/testing';
export default defineConfig({
test: {
mockReset: true,
restoreMocks: true,
},
plugins: [WxtVitest()],
});
+20 -8
View File
@@ -1,6 +1,19 @@
import { defineConfig } from 'vitepress';
import { generateConfigDocs } from './plugins/generate-config-docs';
import { DefaultTheme, defineConfig } from 'vitepress';
import { generateCliDocs } from './plugins/generate-cli-docs';
import typedocSidebar from '../api/typedoc-sidebar.json';
const filteredTypedocSidebar = typedocSidebar.filter(
(item) => item.text !== 'API',
);
// Typedoc's markdown theme adds collapse: true to all our items, event ones without any children,
// so they need to be removed.
function removeCollapsedWithNoItems(items: DefaultTheme.SidebarItem[]) {
for (const item of items) {
if (item.items) removeCollapsedWithNoItems(item.items);
else delete item.collapsed;
}
}
removeCollapsedWithNoItems(filteredTypedocSidebar);
const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' WXT';
@@ -18,7 +31,7 @@ export default defineConfig({
description,
vite: {
clearScreen: false,
plugins: [generateConfigDocs(), generateCliDocs()],
plugins: [generateCliDocs()],
},
lastUpdated: true,
sitemap: {
@@ -120,11 +133,10 @@ export default defineConfig({
{
items: [
{ text: 'CLI', link: '/api/cli.md' },
{ text: 'Config', link: '/api/config.md' },
{ text: 'wxt', link: '/api/wxt.md' },
{ text: 'wxt/browser', link: '/api/wxt-browser.md' },
{ text: 'wxt/client', link: '/api/wxt-client.md' },
{ text: 'wxt/sandbox', link: '/api/wxt-sandbox.md' },
{
text: 'Modules',
items: filteredTypedocSidebar,
},
],
},
],
+1 -1
View File
@@ -8,7 +8,7 @@ let hasGenerated = false;
const cliDir = resolve('src/cli/commands');
const cliDirGlob = resolve(cliDir, '**');
const cliTemplatePath = resolve('docs/api/cli.tpl.md');
const cliTemplatePath = resolve('docs/.vitepress/plugins/cli.tpl.md');
const cliPath = resolve('docs/api/cli.md');
const PREFACE = `<!--
@@ -1,145 +0,0 @@
import { relative, resolve } from 'node:path';
import { Project, ts, Type, Node, JSDocableNode } from 'ts-morph';
import { readFileSync, writeFileSync } from 'node:fs';
import { format } from 'prettier';
import consola from 'consola';
let hasGenerated = false;
const externalTypesPath = resolve('src/core/types/external.ts');
const configTemplatePath = resolve('docs/api/config.tpl.md');
const configPath = resolve('docs/api/config.md');
const PREFACE = `<!--
DO NOT EDIT
Generated by \`wxt/docs/.vitepress/plugins/generate-config-docs.ts\`
To make changes to the config reference, update the JSDoc in \`src/core/types/external.ts\`.
-->`;
/**
* Custom property paths that should not be recursively inspected. Usually 3rd party types.
*/
const LEAF_PATHS = ['imports', 'vite', 'server'];
/**
* Override any types that resolve to `import(...)` instead of their type names when calling
* `type.getText()`.
*
* This also stops any further type inspection for objects, meaning the object will be documented,
* not it's properties.
*/
const CUSTOM_TYPES = {
manifest:
'Manifest | Promise<Manifest> | () => Manifest | () => Promise<Manifest>',
imports: "false | Partial<import('unimport').UnimportOptions>",
vite: "Omit<import('vite').UserConfig, 'root' | 'configFile' | 'mode'>",
};
export function generateConfigDocs() {
writeFileSync(configPath, '');
const generateDocs = async () => {
consola.info(`Generating ${relative(process.cwd(), configPath)}`);
try {
const project = new Project({
tsConfigFilePath: resolve('tsconfig.json'),
});
// Load file containing "UserConfig"
const externalTypesFile = project.addSourceFileAtPath(externalTypesPath);
project.resolveSourceFileDependencies();
const inlineConfigInterface =
externalTypesFile.getInterfaceOrThrow('InlineConfig');
const getDocsFor = (path: string[], node: Node<ts.Node>): string[] => {
const pathStr = path.join('.');
let type: Type<ts.Type>;
if (node.isKind(ts.SyntaxKind.InterfaceDeclaration)) {
type = node.getType();
} else if (node.isKind(ts.SyntaxKind.PropertySignature)) {
type = node.getTypeNodeOrThrow()?.getType();
} else if (node.isKind(ts.SyntaxKind.MethodSignature)) {
type = node.getType();
} else {
throw Error('Unsupported type node: ' + node.getKindName());
}
if (type.isObject() && !type.isArray() && !CUSTOM_TYPES[pathStr]) {
return type.getProperties().flatMap((property) => {
const childPath = [...path, property.getName()];
return getDocsFor(childPath, property.getDeclarations()[0]);
});
}
if ('getJsDocs' in node) {
const lines: string[] = [];
const docs = (node as unknown as JSDocableNode).getJsDocs();
let typeText: string;
if (CUSTOM_TYPES[pathStr]) {
typeText = CUSTOM_TYPES[pathStr];
} else if (type.isUnion() && !type.isBoolean()) {
typeText = type
.getUnionTypes()
.map((type) => type.getText())
.join(' | ');
} else {
typeText = type.getText();
}
const defaultValue = docs
.flatMap((doc) => doc.getTags())
.find((tag) => tag.getTagName() === 'default')
?.getCommentText();
lines.push(
'',
`## ${pathStr}`,
'',
`- **Type**: \`${typeText}\``,
`- **Default**: \`${defaultValue}\``,
...docs.flatMap((doc) => doc.getDescription()),
);
return lines;
}
return [];
};
const lines = getDocsFor([], inlineConfigInterface);
const text = await format(
PREFACE +
'\n\n' +
readFileSync(configTemplatePath, 'utf-8').replace(
'{{ DOCS }}',
lines.join('\n'),
),
{ parser: 'markdown' },
);
writeFileSync(configPath, text);
consola.success(`Generated ${relative(process.cwd(), configPath)}`);
} catch (err) {
consola.fail(`Failed to generate ${relative(process.cwd(), configPath)}`);
consola.error(err.message);
}
};
return {
name: 'docs:generate-config-docs',
async config() {
if (!hasGenerated) {
hasGenerated = true;
await generateDocs();
}
},
configureServer(server: any) {
server.watcher.add(externalTypesPath);
},
async handleHotUpdate(ctx: { file: string }) {
if ([externalTypesPath, configTemplatePath].includes(ctx.file)) {
await generateDocs();
}
},
};
}
-5
View File
@@ -1,5 +0,0 @@
# Config Reference
All the options you can use in your `wxt.config.ts` file.
{{ DOCS }}
-10
View File
@@ -1,10 +0,0 @@
# `wxt/browser` Reference
:::warning 🚧&ensp;Under construction
This documentation does not exist yet. All APIs are documented with JSDoc, so for now, you can view the documentation in your editor.
```ts
import { browser } from 'wxt/browser';
```
:::
-14
View File
@@ -1,14 +0,0 @@
# `wxt/client` Reference
:::warning 🚧&ensp;Under construction
This documentation does not exist yet. All APIs are documented with JSDoc, so for now, you can view the documentation in your editor.
```ts
import {
createContentScriptUi,
defineBackground,
defineContentScript,
} from 'wxt/client';
```
:::
-12
View File
@@ -1,12 +0,0 @@
# `wxt/sandbox` Reference
The `wxt/sandbox` module contains exports that do not use the `browser` global (ie: a "sandboxed" environment).
:::warning 🚧&ensp;Under construction
This documentation does not exist yet. All APIs are documented with JSDoc, so for now, you can view the documentation in your editor.
```ts
import { defineUnlistedScript } from 'wxt/sandbox';
```
:::
-17
View File
@@ -1,17 +0,0 @@
# `wxt` Reference
:::warning 🚧&ensp;Under construction
This documentation does not exist yet. All APIs are documented with JSDoc, so for now, you can view the documentation in your editor.
```ts
import {
build,
clean,
createServer,
defineConfig,
defineRunnerConfig,
version,
} from 'wxt';
```
:::
+1 -1
View File
@@ -5,7 +5,7 @@ WXT can build CSS entrypoints individually. CSS entrypoints are always unlisted.
See [Content Script CSS](/entrypoints/content-scripts#css) documentation for the recomended approach to include CSS with a content script.
:::info
If the recommended approach doesn't work for your use case, you can use any of the filename patterns below to build the styles separate from the JS and use the [`transformManifest` hook](/api/config#transformmanifest) to manually add your CSS file to the manifest.
If the recommended approach doesn't work for your use case, you can use any of the filename patterns below to build the styles separate from the JS and use the [`transformManifest` hook](/api/wxt/interfaces/InlineConfig#transformmanifest) to manually add your CSS file to the manifest.
:::
## Filenames
+7 -6
View File
@@ -10,13 +10,14 @@ To setup your test environment for auto-imports, see [Testing](/guide/testing).
Some WXT APIs can be used without importing them:
- [`browser`](/api/wxt-browser#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/api/wxt-client#defiencontentscript) from `wxt/client`
- [`defineBackground`](/api/wxt-client#definebackground) from `wxt/client`
- [`createContentScriptUi`](/api/wxt-client#createcontentscriptui) from `wxt/client`
- [`defineUnlistedScript`](/api/wxt-sandbox#defineunlistedscript) from `wxt/sandbox`
- [`browser`](/api/wxt/browser/variables/browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/api/wxt/client/functions/defineContentScript) from `wxt/client`
- [`defineBackground`](/api/wxt/client/functions/defineBackground) from `wxt/client`
- [`createContentScriptUi`](/api/wxt/client/functions/createContentScriptUi) from `wxt/client`
- [`defineUnlistedScript`](/api/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
- [`fakeBrowser`](/api/wxt/testing/variables/fakeBrowser) from `wxt/testing`
And more. All [`wxt/client`](/api/wxt-client) APIs can be used without imports.
And more. All `wxt/*` APIs can be used without imports.
## Project Auto-imports
+1 -1
View File
@@ -15,7 +15,7 @@ export default defineConfig({
```
:::info
See the [Config reference](/api/config) for a full list of options.
See the [Config reference](/api/wxt/interfaces/InlineConfig) for a full list of options.
:::
## Directory Config
+1 -1
View File
@@ -15,7 +15,7 @@ WXT's main goal is providing the best DX it possibly can. When running your exte
## Configure Browser Startup
WXT uses [`web-ext` by Mozilla](https://github.com/mozilla/web-ext) to automatically open a browser with the extension installed. You can configure the runner's behavior via the [`runner`](/api/config#runner.disabled) option, or in a separate gitignored file, `web-ext.config.ts`.
WXT uses [`web-ext` by Mozilla](https://github.com/mozilla/web-ext) to automatically open a browser with the extension installed. You can configure the runner's behavior via the [`runner`](/api/wxt/interfaces/InlineConfig#runner) option, or in a separate gitignored file, `web-ext.config.ts`.
:::code-group
+132 -71
View File
@@ -1,92 +1,153 @@
# Testing
WXT provides a couple of utils for unit testing your extension.
WXT provides several utils for writing tests.
[[toc]]
## Unit tests
## Fake Browser
If you're using auto-imports (enabled by default), [Vitest](https://vitest.dev/) is the only testing framework that supports them.
The `wxt/fake-browser` package includes an in-memory implementation of the `browser` variable you can use for testing. WXT simply re-exports the `fakeBrowser` variable from [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/guide/fake-browser/).
If you want to use a different testing library/framework (like Jest, mocha, node:test, etc), you can keep using it, but you have two options:
Here's an example test using Vitest:
1. Switch to Vitest (recommended)
2. Configure the testing library manually
- Disable auto-imports by setting `imports: false` in your `wxt.config.ts` file
- Manually add globals normally provided by WXT (like `__BROWSER__`) that you consume to the global scope before accessing them (`globalThis.__BROWSER__ = "chrome"`)
```ts
import { describe, it, expect, vi } from 'vitest';
import { browser } from 'wxt/browser';
import { fakeBrowser } from 'wxt';
### Vitest Setup
// Function we're testing
function onHelloMessage(cb: () => void) {
browser.runtime.onMessage.addEventListener((message) => {
if (message.type === 'hello') return 'world';
});
}
Install vitest and add the `WxtVitest` plugin to your `vitest.config.ts` file.
// Mock the real `browser` object with a fake one
vi.mock('wxt/browser', () => import('wxt/fake-browser'));
describe('onHelloMessage', () => {
it("should call the callback when the message type is 'hello'", () => {
const cb = vi.fn();
const expected = 'world';
onHelloMessage(cb);
const actual = await fakeBrowser.runtime.sendMessage({ type: 'hello' });
expect(cb).toBeCalledTimes(1);
expect(actual).toBe(expected);
});
it("should ignore the message when the message type is not 'hello'", () => {
const cb = vi.fn();
onHelloMessage(cb);
await fakeBrowser.runtime.sendMessage({ type: 'not-hello' }).catch();
expect(cb).not.toBeCalled();
});
});
```sh
pnpm i -D vitest
```
See [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/guide/fake-browser/) for setup, implemented APIs, and example tests.
## Handling Auto-imports
By default, WXT uses auto-imports. For tests, this can cause issues if your test environment is not setup to handle them correctly.
:::warning 🚧&ensp;Testing utils are not implemented yet!
Eventually, WXT will provide utilities for setting up these auto-imports. For now, you'll need to set them up manually.
:::
Not all testing frameworks can handle auto-imports. If your framework or setup is not listed below, it may be easiest to disable auto-imports.
To setup auto-imports manually, use [`unplugin-auto-import`](https://www.npmjs.com/package/unplugin-auto-import). It uses the same tool, `unimport`, as WXT and will result in compatiple auto-imports. `unplugin-auto-import` supports lots of different tools (vite, webpack, esbuild, rollup, etc). You can try and integrate it into your build process.
### Vitest (Recommended)
Vitest is easy, simply add `uplugin-auto-import` to your project.
```ts
// vitest.config.ts
import autoImports from 'unplugin-auto-import/vite';
// <root>/vitest.config.ts
import { defineConfig } from 'vitest/config';
import { WxtVitest } from 'wxt/testing';
export default defineConfig({
plugins: [
autoImports({
imports: [{ name: 'defineConfig', from: 'wxt' }],
presets: [{ package: 'wxt/client' }, { package: 'wxt/browser' }],
dirs: ['components', 'composables', 'hooks', 'utils'],
}),
],
plugins: [WxtVitest()],
});
```
### Jest
And that's it. You're ready to start writing tests.
Don't use jest and auto-imports. You could try and configure jest to be transpiled by one of `unplugin-auto-import`'s supported built tools, but I don't know of a way to configure this. See [unplugin/unplugin-auto-import#33](https://github.com/unplugin/unplugin-auto-import/issues/33) if you want to try and set it up.
### Writing Tests
I would recommend disabling auto-imports or migrating to Vitest if you want to use auto-imports.
Here's a very basic test, written with a few different testing libraries, with a few different approaches for mocking the `browser` global.
### Mocha
:::code-group
TODO: Is this possible? Maybe with `esbuild-mocha`? I would recommend moving to Vitest.
```ts [Vitest]
import { describe, it, expect, vi } from 'vitest';
function logRuntimeId() {
// Vitest automatically mocks "browser" with "fakeBrowser"
console.log(browser.runtime.id);
}
describe('logRuntimeId', () => {
it("should log the extension's runtime ID", () => {
// Set a known ID on fakeBrowser for the test
const id = 'some-runtime-id';
fakeBrowser.runtime.id = id;
const logSpy = vi.spyOn(console, 'log');
logRuntimeId();
expect(logSpy).toBeCalledWith(id);
});
});
```
```ts [Jest - Manual Mock]
import { fakeBrowser } from 'wxt/testing';
import { browser } from 'wxt/browser';
function logRuntimeId() {
console.log(browser.runtime.id);
}
// Manually mock
jest.mock('wxt/browser', () => {
const { fakeBrowser } = require('wxt/testing');
return { browser: fakeBrowser };
});
describe('logRuntimeId', () => {
it("should log the extension's runtime ID", () => {
// Set a known ID on fakeBrowser for the test
const id = 'some-runtime-id';
fakeBrowser.runtime.id = id;
const logSpy = jest.spyOn(console, 'log');
logRuntimeId();
expect(logSpy).toBeCalledWith(id);
});
});
```
```ts [node:test - Parameterized]
import { describe, it, mock } from 'node:test';
import { assert } from 'node:assert';
import { fakeBrowser } from 'wxt/testing';
import { browser } from 'wxt/browser';
// Add browser as a parameter so fakeBrowser can be passed instead of browser
function logRuntimeId(browser = browser) {
console.log(browser.runtime.id);
}
describe('logRuntimeId', () => {
it("should log the extension's runtime ID", () => {
// Set a known ID on fakeBrowser for the test
const id = 'some-runtime-id';
fakeBrowser.runtime.id = id;
console.log = mock.fn();
// pass in fakeBrowser during tests
logRuntimeId(fakeBrowser);
assert.deepStrictEqual(console.log.mock.calls[0].arguments, [id]);
});
});
```
:::
:::warning
Without mocking the `browser` variable, you'll see errors like this:
```
This script should only be loaded in a browser extension.
```
:::
WXT provides an in-memory, partial implementation of `browser`, [`fakeBrowser`](/api/wxt/testing/variables/fakeBrowser), from the [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/guide/fake-browser/) package. `fakeBrowser` works with all testing frameworks/libraries. See their docs for a list of [implemented APIs](https://webext-core.aklinker1.io/guide/fake-browser/implemented-apis.html) and more example tests.
## E2E Tests
WXT does not provide any utils for running E2E tests. There are two libraries you can use to run E2E tests for any chrome extension.
- [`playwright`](https://playwright.dev/docs/chrome-extensions) (recommended) - "A high-level API to automate web browsers"
- [`puppeteer`](https://pptr.dev/guides/chrome-extensions) - "A high-level API to control headless Chrome over the DevTools Protocol"
:::info
Note that both only support running tests on Chrome.
:::
Before running tests with either of these tools, you must build the extension with `wxt build` and then load the extension from the output directory in a new tab.
To test an extension's UI, like the popup or options page, you'll need to know the extension's ID to open the URL directly.
> _chrome-extension://`browser.runtime.id`/popup.html_
- Playwright provides an API to get your extension ID after it has been installed. [See their docs](https://playwright.dev/docs/chrome-extensions#testing).
- Puppeteer requires you know the ID before installing the extension, so you can hard code it into the URLs you open. Follow [Chrome's guide](https://developer.chrome.com/docs/extensions/mv3/manifest/key/) to setup a consistent runtime id.
:::info
You cannot test popups in their normal popup window, you have to open them in a tab.
:::
+2 -1
View File
@@ -2,7 +2,8 @@
# https://docs.netlify.com/routing/redirects/
# Old URLs -> New URLs
/config.html /api/config.html
/config.html /api/wxt/interfaces/InlineConfig.html
/api/config.html /api/wxt/interfaces/InlineConfig.html
/entrypoints /entrypoints/background.html
/get-started/assets.html /guide/assets.html
/get-started/build-targets.html /guide/multiple-browsers.html
+16
View File
@@ -0,0 +1,16 @@
{
"entryPoints": [
"../src",
"../src/client",
"../src/client/browser.ts",
"../src/client/sandbox",
"../src/testing"
],
"plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"],
"out": "./api",
"githubPages": false,
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"readme": "none"
}
+1
View File
@@ -23,6 +23,7 @@ describe('Auto Imports', () => {
const defineConfig: typeof import('wxt')['defineConfig']
const defineContentScript: typeof import('wxt/client')['defineContentScript']
const defineUnlistedScript: typeof import('wxt/sandbox')['defineUnlistedScript']
const fakeBrowser: typeof import('wxt/testing')['fakeBrowser']
}
"
`);
+13 -5
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.8.0",
"version": "0.8.3",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18",
@@ -51,6 +51,11 @@
"./browser": {
"import": "./dist/browser.js",
"types": "./dist/browser.d.ts"
},
"./testing": {
"require": "./dist/testing.cjs",
"import": "./dist/testing.js",
"types": "./dist/testing.d.ts"
}
},
"scripts": {
@@ -63,9 +68,10 @@
"test:coverage": "vitest run --coverage.enabled --coverage.exclude=e2e --coverage.exclude=src/testing",
"prepare": "simple-git-hooks",
"prepublish": "pnpm -s build",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs",
"docs:gen": "typedoc --options docs/typedoc.json",
"docs:dev": "pnpm -s docs:gen && vitepress dev docs",
"docs:build": "pnpm -s docs:gen && vitepress build docs",
"docs:preview": "pnpm -s docs:gen && vitepress preview docs",
"sync-releases": "pnpx changelogen@latest gh release"
},
"dependencies": {
@@ -113,9 +119,11 @@
"npm-run-all": "^4.1.5",
"prettier": "^3.0.1",
"simple-git-hooks": "^2.9.0",
"ts-morph": "^20.0.0",
"tsup": "^7.1.0",
"tsx": "^3.12.7",
"typedoc": "^0.25.2",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.2.2",
"vitepress": "1.0.0-rc.10",
"vitest": "^0.34.6",
+127 -121
View File
@@ -85,7 +85,7 @@ importers:
version: 3.3.0
vite:
specifier: ^4.4.11
version: 4.4.11(@types/node@20.8.6)
version: 4.4.11(@types/node@20.8.6)(sass@1.64.0)
web-ext-run:
specifier: ^0.1.0
version: 0.1.0
@@ -135,15 +135,21 @@ importers:
simple-git-hooks:
specifier: ^2.9.0
version: 2.9.0
ts-morph:
specifier: ^20.0.0
version: 20.0.0
tsup:
specifier: ^7.1.0
version: 7.2.0(typescript@5.2.2)
tsx:
specifier: ^3.12.7
version: 3.12.8
typedoc:
specifier: ^0.25.2
version: 0.25.2(typescript@5.2.2)
typedoc-plugin-markdown:
specifier: 4.0.0-next.23
version: 4.0.0-next.23(typedoc@0.25.2)
typedoc-vitepress-theme:
specifier: 1.0.0-next.3
version: 1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23)
typescript:
specifier: ^5.2.2
version: 5.2.2
@@ -168,6 +174,9 @@ importers:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
vitest:
specifier: ^0.34.6
version: 0.34.6(jsdom@22.1.0)(sass@1.64.0)
webextension-polyfill:
specifier: ^0.10.0
version: 0.10.0
@@ -869,7 +878,6 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@sinclair/typebox': 0.27.8
dev: true
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
@@ -959,7 +967,6 @@ packages:
/@sinclair/typebox@0.27.8:
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
dev: true
/@sindresorhus/is@5.4.1:
resolution: {integrity: sha512-axlrvsHlHlFmKKMEg4VyvMzFr93JWJj4eIfXY1STVuO2fsImCa7ncaiG5gC8HKOX590AW5RtRsC41/B+OfrSqw==}
@@ -976,26 +983,14 @@ packages:
/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'}
dev: true
/@ts-morph/common@0.21.0:
resolution: {integrity: sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==}
dependencies:
fast-glob: 3.3.1
minimatch: 7.4.6
mkdirp: 2.1.6
path-browserify: 1.0.1
dev: true
/@types/chai-subset@1.3.3:
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
dependencies:
'@types/chai': 4.3.5
dev: true
/@types/chai@4.3.5:
resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==}
dev: true
/@types/estree@1.0.1:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
@@ -1109,7 +1104,6 @@ packages:
'@vitest/spy': 0.34.6
'@vitest/utils': 0.34.6
chai: 4.3.10
dev: true
/@vitest/runner@0.34.6:
resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
@@ -1117,21 +1111,18 @@ packages:
'@vitest/utils': 0.34.6
p-limit: 4.0.0
pathe: 1.1.1
dev: true
/@vitest/snapshot@0.34.6:
resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
dependencies:
magic-string: 0.30.2
magic-string: 0.30.3
pathe: 1.1.1
pretty-format: 29.7.0
dev: true
/@vitest/spy@0.34.6:
resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
dependencies:
tinyspy: 2.2.0
dev: true
/@vitest/utils@0.34.6:
resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
@@ -1139,7 +1130,6 @@ packages:
diff-sequences: 29.6.3
loupe: 2.3.7
pretty-format: 29.7.0
dev: true
/@vue/compiler-core@3.3.4:
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
@@ -1325,12 +1315,10 @@ packages:
/abab@2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
dev: true
/acorn-walk@8.2.0:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'}
dev: true
/acorn@8.10.0:
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
@@ -1411,7 +1399,6 @@ packages:
/ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
dev: true
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
@@ -1451,7 +1438,6 @@ packages:
/assertion-error@1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: true
/async-mutex@0.4.0:
resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==}
@@ -1465,7 +1451,6 @@ packages:
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/at-least-node@1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
@@ -1629,7 +1614,6 @@ packages:
loupe: 2.3.7
pathval: 1.1.1
type-detect: 4.0.8
dev: true
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1647,7 +1631,6 @@ packages:
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
dependencies:
get-func-name: 2.0.2
dev: true
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
@@ -1719,10 +1702,6 @@ packages:
wrap-ansi: 7.0.0
dev: false
/code-block-writer@12.0.0:
resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
dev: true
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@@ -1750,7 +1729,6 @@ packages:
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
/commander@11.0.0:
resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==}
@@ -1869,7 +1847,6 @@ packages:
engines: {node: '>=14'}
dependencies:
rrweb-cssom: 0.6.0
dev: true
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
@@ -1882,7 +1859,6 @@ packages:
abab: 2.0.6
whatwg-mimetype: 3.0.0
whatwg-url: 12.0.1
dev: true
/debounce@1.2.1:
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
@@ -1912,7 +1888,6 @@ packages:
/decimal.js@10.4.3:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
dev: true
/decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
@@ -1926,7 +1901,6 @@ packages:
engines: {node: '>=6'}
dependencies:
type-detect: 4.0.8
dev: true
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
@@ -1958,7 +1932,6 @@ packages:
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: true
/destr@2.0.0:
resolution: {integrity: sha512-FJ9RDpf3GicEBvzI3jxc2XhHzbqD8p4ANw/1kPsFBfTvP1b7Gn/Lg1vO7R9J4IVgoMbyUmFrFGZafJ1hPZpvlg==}
@@ -1967,7 +1940,6 @@ packages:
/diff-sequences@29.6.3:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
@@ -1993,7 +1965,6 @@ packages:
engines: {node: '>=12'}
dependencies:
webidl-conversions: 7.0.0
dev: true
/domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
@@ -2302,7 +2273,6 @@ packages:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: true
/fs-extra@11.1.1:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
@@ -2377,7 +2347,6 @@ packages:
/get-func-name@2.0.2:
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
dev: true
/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
@@ -2578,7 +2547,6 @@ packages:
engines: {node: '>=12'}
dependencies:
whatwg-encoding: 2.0.0
dev: true
/html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -2610,7 +2578,6 @@ packages:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/http2-wrapper@2.2.0:
resolution: {integrity: sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==}
@@ -2644,7 +2611,6 @@ packages:
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: true
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -2665,7 +2631,6 @@ packages:
/immutable@4.3.1:
resolution: {integrity: sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==}
dev: true
/import-lazy@4.0.0:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
@@ -2845,7 +2810,6 @@ packages:
/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: true
/is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
@@ -3029,7 +2993,6 @@ packages:
- bufferutil
- supports-color
- utf-8-validate
dev: true
/json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -3219,7 +3182,6 @@ packages:
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
dependencies:
get-func-name: 2.0.2
dev: true
/lowercase-keys@3.0.0:
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
@@ -3232,6 +3194,10 @@ packages:
dependencies:
yallist: 4.0.0
/lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
dev: true
/magic-string@0.30.2:
resolution: {integrity: sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==}
engines: {node: '>=12'}
@@ -3260,6 +3226,12 @@ packages:
resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
dev: true
/marked@4.3.0:
resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
engines: {node: '>= 12'}
hasBin: true
dev: true
/marky@1.2.5:
resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==}
dev: false
@@ -3287,14 +3259,12 @@ packages:
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: true
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: true
/mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
@@ -3320,19 +3290,11 @@ packages:
dependencies:
brace-expansion: 1.1.11
/minimatch@7.4.6:
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimatch@9.0.3:
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: false
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
@@ -3376,12 +3338,6 @@ packages:
hasBin: true
dev: false
/mkdirp@2.1.6:
resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==}
engines: {node: '>=10'}
hasBin: true
dev: true
/mlly@1.4.0:
resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==}
dependencies:
@@ -3541,7 +3497,6 @@ packages:
/nwsapi@2.2.7:
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
dev: true
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
@@ -3627,7 +3582,6 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
yocto-queue: 1.0.0
dev: true
/package-json@8.1.1:
resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==}
@@ -3666,11 +3620,6 @@ packages:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
dependencies:
entities: 4.5.0
dev: true
/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
dev: true
/path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
@@ -3712,7 +3661,6 @@ packages:
/pathval@1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true
/perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
@@ -3795,7 +3743,6 @@ packages:
'@jest/schemas': 29.6.3
ansi-styles: 5.2.0
react-is: 18.2.0
dev: true
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@@ -3822,12 +3769,10 @@ packages:
/psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
dev: true
/punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
dev: true
/pupa@3.1.0:
resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==}
@@ -3838,7 +3783,6 @@ packages:
/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
dev: true
/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -3878,7 +3822,6 @@ packages:
/react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
dev: true
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
@@ -3957,7 +3900,6 @@ packages:
/requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: true
/resolve-alpn@1.2.1:
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
@@ -4051,7 +3993,6 @@ packages:
/rrweb-cssom@0.6.0:
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
dev: true
/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -4078,7 +4019,6 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: true
/sass@1.64.0:
resolution: {integrity: sha512-m7YtAGmQta9uANIUJwXesAJMSncqH+3INc8kdVXs6eV6GUC8Qu2IYKQSN8PRLgiQfpca697G94klm2leYMxSHw==}
@@ -4088,7 +4028,6 @@ packages:
chokidar: 3.5.3
immutable: 4.3.1
source-map-js: 1.0.2
dev: true
/sax@1.2.4:
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
@@ -4099,7 +4038,6 @@ packages:
engines: {node: '>=v12.22.7'}
dependencies:
xmlchars: 2.2.0
dev: true
/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
@@ -4189,7 +4127,6 @@ packages:
/siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
dev: true
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -4281,11 +4218,9 @@ packages:
/stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
/std-env@3.3.3:
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
dev: true
/stdin-discarder@0.1.0:
resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==}
@@ -4447,7 +4382,6 @@ packages:
/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: true
/tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
@@ -4491,17 +4425,14 @@ packages:
/tinybench@2.5.0:
resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==}
dev: true
/tinypool@0.7.0:
resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
engines: {node: '>=14.0.0'}
dev: true
/tinyspy@2.2.0:
resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
engines: {node: '>=14.0.0'}
dev: true
/tmp@0.2.1:
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
@@ -4529,7 +4460,6 @@ packages:
punycode: 2.3.0
universalify: 0.2.0
url-parse: 1.5.10
dev: true
/tr46@1.0.1:
resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
@@ -4542,7 +4472,6 @@ packages:
engines: {node: '>=14'}
dependencies:
punycode: 2.3.0
dev: true
/tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
@@ -4561,13 +4490,6 @@ packages:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
dev: true
/ts-morph@20.0.0:
resolution: {integrity: sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==}
dependencies:
'@ts-morph/common': 0.21.0
code-block-writer: 12.0.0
dev: true
/tslib@2.6.0:
resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==}
dev: false
@@ -4622,7 +4544,6 @@ packages:
/type-detect@4.0.8:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
dev: true
/type-fest@1.4.0:
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
@@ -4656,6 +4577,36 @@ packages:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
dev: false
/typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.2):
resolution: {integrity: sha512-bKD0LnrQxUTbuDRiJfCWYLlzyERuskWldJ5eWKKuNY38xBOyBhSN8P+vD5KxlJoAifTOm76MK58sNrl4wgOoWg==}
peerDependencies:
typedoc: '>=0.25.0'
dependencies:
typedoc: 0.25.2(typescript@5.2.2)
dev: true
/typedoc-vitepress-theme@1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23):
resolution: {integrity: sha512-MPVXYNu+pU3KgB7he3gdKfhEj1CD44uEwIxSp+ojaVG6jN1Iwo7/D2tNwmzszM3Yocz6IBvNb9EVfZBXzyWG/w==}
peerDependencies:
typedoc-plugin-markdown: '>=4.0.0-next.19'
dependencies:
typedoc-plugin-markdown: 4.0.0-next.23(typedoc@0.25.2)
dev: true
/typedoc@0.25.2(typescript@5.2.2):
resolution: {integrity: sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==}
engines: {node: '>= 16'}
hasBin: true
peerDependencies:
typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x
dependencies:
lunr: 2.3.9
marked: 4.3.0
minimatch: 9.0.3
shiki: 0.14.3
typescript: 5.2.2
dev: true
/typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
@@ -4713,7 +4664,6 @@ packages:
/universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
engines: {node: '>= 4.0.0'}
dev: true
/universalify@1.0.0:
resolution: {integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==}
@@ -4759,7 +4709,6 @@ packages:
dependencies:
querystringify: 2.2.0
requires-port: 1.0.0
dev: true
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -4786,7 +4735,7 @@ packages:
spdx-expression-parse: 3.0.1
dev: true
/vite-node@0.34.6(@types/node@20.8.6):
/vite-node@0.34.6(@types/node@20.8.6)(sass@1.64.0):
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
engines: {node: '>=v14.18.0'}
hasBin: true
@@ -4796,7 +4745,7 @@ packages:
mlly: 1.4.2
pathe: 1.1.1
picocolors: 1.0.0
vite: 4.4.11(@types/node@20.8.6)
vite: 4.4.11(@types/node@20.8.6)(sass@1.64.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -4806,9 +4755,8 @@ packages:
- sugarss
- supports-color
- terser
dev: true
/vite@4.4.11(@types/node@20.8.6):
/vite@4.4.11(@types/node@20.8.6)(sass@1.64.0):
resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@@ -4840,6 +4788,7 @@ packages:
esbuild: 0.18.20
postcss: 8.4.27
rollup: 3.28.0
sass: 1.64.0
optionalDependencies:
fsevents: 2.3.3
@@ -4856,7 +4805,7 @@ packages:
mark.js: 8.11.1
minisearch: 6.1.0
shiki: 0.14.3
vite: 4.4.11(@types/node@20.8.6)
vite: 4.4.11(@types/node@20.8.6)(sass@1.64.0)
vue: 3.3.4
transitivePeerDependencies:
- '@algolia/client-search'
@@ -4949,8 +4898,8 @@ packages:
strip-literal: 1.3.0
tinybench: 2.5.0
tinypool: 0.7.0
vite: 4.4.11(@types/node@20.8.6)
vite-node: 0.34.6(@types/node@20.8.6)
vite: 4.4.11(@types/node@20.8.6)(sass@1.64.0)
vite-node: 0.34.6(@types/node@20.8.6)(sass@1.64.0)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
@@ -4962,6 +4911,72 @@ packages:
- terser
dev: true
/vitest@0.34.6(jsdom@22.1.0)(sass@1.64.0):
resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
engines: {node: '>=v14.18.0'}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@vitest/browser': '*'
'@vitest/ui': '*'
happy-dom: '*'
jsdom: '*'
playwright: '*'
safaridriver: '*'
webdriverio: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@vitest/browser':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
playwright:
optional: true
safaridriver:
optional: true
webdriverio:
optional: true
dependencies:
'@types/chai': 4.3.5
'@types/chai-subset': 1.3.3
'@types/node': 20.8.6
'@vitest/expect': 0.34.6
'@vitest/runner': 0.34.6
'@vitest/snapshot': 0.34.6
'@vitest/spy': 0.34.6
'@vitest/utils': 0.34.6
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.10
debug: 4.3.4
jsdom: 22.1.0
local-pkg: 0.4.3
magic-string: 0.30.3
pathe: 1.1.1
picocolors: 1.0.0
std-env: 3.3.3
strip-literal: 1.3.0
tinybench: 2.5.0
tinypool: 0.7.0
vite: 4.4.11(@types/node@20.8.6)(sass@1.64.0)
vite-node: 0.34.6(@types/node@20.8.6)(sass@1.64.0)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
- lightningcss
- sass
- stylus
- sugarss
- supports-color
- terser
dev: false
/vscode-oniguruma@1.7.0:
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
dev: true
@@ -5000,7 +5015,6 @@ packages:
engines: {node: '>=14'}
dependencies:
xml-name-validator: 4.0.0
dev: true
/watchpack@2.4.0:
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
@@ -5054,7 +5068,6 @@ packages:
/webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
dev: true
/webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
@@ -5070,12 +5083,10 @@ packages:
engines: {node: '>=12'}
dependencies:
iconv-lite: 0.6.3
dev: true
/whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
engines: {node: '>=12'}
dev: true
/whatwg-url@12.0.1:
resolution: {integrity: sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==}
@@ -5083,7 +5094,6 @@ packages:
dependencies:
tr46: 4.1.1
webidl-conversions: 7.0.0
dev: true
/whatwg-url@7.1.0:
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
@@ -5148,7 +5158,6 @@ packages:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
dev: true
/widest-line@4.0.1:
resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
@@ -5210,7 +5219,6 @@ packages:
/xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
dev: true
/xml2js@0.5.0:
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
@@ -5227,7 +5235,6 @@ packages:
/xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
@@ -5263,7 +5270,6 @@ packages:
/yocto-queue@1.0.0:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
dev: true
/zip-dir@2.0.0:
resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==}
+7
View File
@@ -55,6 +55,13 @@ await Promise.all([
dts: true,
silent: true,
}),
tsup.build({
entry: { testing: 'src/testing/index.ts' },
format: ['esm', 'cjs'],
sourcemap: 'inline',
dts: true,
silent: true,
}),
...virtualEntrypoints.map((entryName) =>
tsup.build({
entry: {
+3
View File
@@ -1,3 +1,6 @@
/**
* @module wxt/browser
*/
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
+44 -41
View File
@@ -7,7 +7,7 @@ import { ContentScriptContext } from '.';
* Utility for mounting content script UI's with isolated styles. Automatically removed from the DOM
* when the content script's context is invalidated.
*
* See <https://wxt.dev/entrypoints/content-scripts.html#ui> for full documentation.
* See https://wxt.dev/entrypoints/content-scripts.html#ui for full documentation.
*
* @example
* // entrypoints/example-ui.content/index.ts
@@ -31,10 +31,10 @@ import { ContentScriptContext } from '.';
* }
* })
*/
export async function createContentScriptUi<T>(
export async function createContentScriptUi<TApp>(
ctx: ContentScriptContext,
options: ContentScriptUiOptions<T>,
): Promise<ContentScriptUi<T>> {
options: ContentScriptUiOptions<TApp>,
): Promise<ContentScriptUi<TApp>> {
const css = [options.css ?? ''];
if (ctx.options?.cssInjectionMode === 'ui') {
css.push(await loadCss());
@@ -62,7 +62,7 @@ export async function createContentScriptUi<T>(
return resolved ?? undefined;
};
let mounted: T;
let mounted: TApp;
const mount = () => {
const anchor = getAnchor();
@@ -173,7 +173,7 @@ async function loadCss(): Promise<string> {
}
}
export interface ContentScriptUi<T> {
export interface ContentScriptUi<TApp> {
/**
* The `HTMLElement` hosting the shadow root used to isolate the UI's styles. This is the element
* that get's added to the DOM. This element's style is not isolated from the webpage.
@@ -191,7 +191,7 @@ export interface ContentScriptUi<T> {
/**
* Custom data returned from the `options.mount` function.
*/
mounted: T;
mounted: TApp;
/**
* Function that mounts or remounts the UI on the page.
*/
@@ -202,7 +202,7 @@ export interface ContentScriptUi<T> {
remove: () => void;
}
interface BaseContentScriptUiOptions<T> {
interface BaseContentScriptUiOptions<TApp> {
/**
* The name of the custom component used to host the ShadowRoot. Must be kebab-case.
*/
@@ -234,50 +234,53 @@ interface BaseContentScriptUiOptions<T> {
*
* Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback.
*/
mount: (container: Element) => T;
mount: (container: Element) => TApp;
/**
* Callback called when the UI is removed from the webpage. Use to cleanup your UI, like
* unmounting your vue or react apps.
*/
onRemove?: (mounted: T) => void;
onRemove?: (mounted: TApp) => void;
/**
* Custom CSS text to apply to the UI. If your content script imports/generates CSS and you've
* set `cssInjectionMode: "ui"`, the imported CSS will be included automatically. You do not need
* to pass those styles in here. This is for any additional styles not in the imported CSS.
*
* See <https://wxt.dev/entrypoints/content-scripts.html#ui> for more info.
* See https://wxt.dev/entrypoints/content-scripts.html#ui for more info.
*/
css?: string;
}
export type OverlayContentScriptUiOptions<T> = BaseContentScriptUiOptions<T> & {
type: 'overlay';
/**
* When using `type: "overlay"`, the mounted element is 0px by 0px in size. Alignment specifies
* which corner is aligned with that 0x0 pixel space.
*
* @default "top-left"
*/
alignment?: ContentScriptUiOverlayAlignment;
/**
* The `z-index` used on the `shadowHost`. Set to a positive number to show your UI over website
* content.
*/
zIndex?: number;
};
export type OverlayContentScriptUiOptions<TApp> =
BaseContentScriptUiOptions<TApp> & {
type: 'overlay';
/**
* When using `type: "overlay"`, the mounted element is 0px by 0px in size. Alignment specifies
* which corner is aligned with that 0x0 pixel space.
*
* @default "top-left"
*/
alignment?: ContentScriptUiOverlayAlignment;
/**
* The `z-index` used on the `shadowHost`. Set to a positive number to show your UI over website
* content.
*/
zIndex?: number;
};
export type ModalContentScriptUiOptions<T> = BaseContentScriptUiOptions<T> & {
type: 'modal';
/**
* The `z-index` used on the `shadowHost`. Set to a positive number to show your UI over website
* content.
*/
zIndex?: number;
};
export type ModalContentScriptUiOptions<TApp> =
BaseContentScriptUiOptions<TApp> & {
type: 'modal';
/**
* The `z-index` used on the `shadowHost`. Set to a positive number to show your UI over website
* content.
*/
zIndex?: number;
};
export type InlineContentScriptUiOptions<T> = BaseContentScriptUiOptions<T> & {
type: 'inline';
};
export type InlineContentScriptUiOptions<TApp> =
BaseContentScriptUiOptions<TApp> & {
type: 'inline';
};
export type ContentScriptUiOverlayAlignment =
| 'top-left'
@@ -292,7 +295,7 @@ export type ContentScriptAppendMode =
| 'before'
| 'after';
export type ContentScriptUiOptions<T> =
| OverlayContentScriptUiOptions<T>
| ModalContentScriptUiOptions<T>
| InlineContentScriptUiOptions<T>;
export type ContentScriptUiOptions<TApp> =
| OverlayContentScriptUiOptions<TApp>
| ModalContentScriptUiOptions<TApp>
| InlineContentScriptUiOptions<TApp>;
+3
View File
@@ -1,3 +1,6 @@
/**
* @module wxt/client
*/
export * from './defineContentScript';
export * from './defineBackground';
export * from './createContentScriptUi';
+3
View File
@@ -1 +1,4 @@
/**
* @module wxt/sandbox
*/
export * from './defineUnlistedScript';
+14 -5
View File
@@ -3,23 +3,23 @@ import { browser } from '../browser';
import { logger } from './logger';
/**
* Extends [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
* Implements [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
* Used to detect and stop content script code when the script is invalidated.
*
* It also provides several utilities like `ctx.setTimeout` and `ctx.setInterval` that should be used in
* content scripts instead of `window.setTimeout` or `window.setInterval`.
*/
export class ContentScriptContext extends AbortController {
static SCRIPT_STARTED_MESSAGE_TYPE = 'wxt:content-script-started';
export class ContentScriptContext implements AbortController {
private static SCRIPT_STARTED_MESSAGE_TYPE = 'wxt:content-script-started';
#isTopFrame = window.self === window.top;
#abortController: AbortController;
constructor(
private readonly contentScriptName: string,
public readonly options?: Omit<ContentScriptDefinition, 'main'>,
) {
super();
this.#abortController = new AbortController();
if (this.#isTopFrame) {
this.#stopOldScripts();
}
@@ -29,6 +29,14 @@ export class ContentScriptContext extends AbortController {
});
}
get signal() {
return this.#abortController.signal;
}
abort(reason?: any): void {
return this.#abortController.abort(reason);
}
get isInvalid(): boolean {
if (browser.runtime.id == null) {
this.notifyInvalidated(); // Sets `signal.aborted` to true
@@ -150,6 +158,7 @@ export class ContentScriptContext extends AbortController {
}
/**
* @internal
* Abort the abort controller and execute all `onInvalidated` listeners.
*/
notifyInvalidated() {
+1 -1
View File
@@ -1,3 +1,3 @@
import { fakeBrowser as browser } from '@webext-core/fake-browser';
import { fakeBrowser as browser } from '../../testing/fake-browser';
export default browser;
@@ -11,7 +11,7 @@ import { findEntrypoints } from '../findEntrypoints';
import fs from 'fs-extra';
import { importEntrypointFile } from '../../utils/importEntrypointFile';
import glob from 'fast-glob';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { fakeInternalConfig } from '../../../testing-utils/fake-objects';
import { unnormalizePath } from '../../utils/paths';
vi.mock('../../utils/importEntrypointFile');
+34 -34
View File
@@ -38,11 +38,10 @@ export async function findEntrypoints(
relativePaths.sort();
const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
const existingNames: Record<string, Entrypoint | undefined> = {};
const entrypoints: Entrypoint[] = [];
let hasBackground = false;
await Promise.all(
// TODO: This parallelization is bad
const possibleEntrypoints: Array<Entrypoint | undefined> = await Promise.all(
relativePaths.map(async (relativePath) => {
const path = resolve(config.entrypointsDir, relativePath);
const matchingGlob = pathGlobs.find((glob) =>
@@ -50,41 +49,35 @@ export async function findEntrypoints(
);
if (matchingGlob == null) {
return config.logger.warn(
config.logger.warn(
`${relativePath} does not match any known entrypoint. Known entrypoints:\n${JSON.stringify(
PATH_GLOB_TO_TYPE_MAP,
null,
2,
)}`,
);
return;
}
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
if (type === 'ignored') return;
let entrypoint: Entrypoint;
switch (type) {
case 'popup':
entrypoint = await getPopupEntrypoint(config, path);
break;
return await getPopupEntrypoint(config, path);
case 'options':
entrypoint = await getOptionsEntrypoint(config, path);
break;
return await getOptionsEntrypoint(config, path);
case 'background':
entrypoint = await getBackgroundEntrypoint(config, path);
hasBackground = true;
break;
return await getBackgroundEntrypoint(config, path);
case 'content-script':
entrypoint = await getContentScriptEntrypoint(config, path);
break;
return await getContentScriptEntrypoint(config, path);
case 'unlisted-page':
entrypoint = await getUnlistedPageEntrypoint(config, path);
break;
return await getUnlistedPageEntrypoint(config, path);
case 'unlisted-script':
entrypoint = await getUnlistedScriptEntrypoint(config, path);
break;
return await getUnlistedScriptEntrypoint(config, path);
case 'content-script-style':
entrypoint = {
return {
type,
name: getEntrypointName(config.entrypointsDir, path),
inputPath: path,
@@ -94,9 +87,8 @@ export async function findEntrypoints(
exclude: undefined,
},
};
break;
default:
entrypoint = {
return {
type,
name: getEntrypointName(config.entrypointsDir, path),
inputPath: path,
@@ -107,22 +99,30 @@ export async function findEntrypoints(
},
};
}
const withSameName = existingNames[entrypoint.name];
if (withSameName) {
throw Error(
`Multiple entrypoints with the name "${
entrypoint.name
}" detected, but only one is allowed: ${[
relative(config.root, withSameName.inputPath),
relative(config.root, entrypoint.inputPath),
].join(', ')}`,
);
}
entrypoints.push(entrypoint);
existingNames[entrypoint.name] = entrypoint;
}),
);
const entrypoints = possibleEntrypoints.filter(
(entry) => !!entry,
) as Entrypoint[];
// Report duplicate entrypoint names
const existingNames: Record<string, Entrypoint | undefined> = {};
entrypoints.forEach((entrypoint) => {
const withSameName = existingNames[entrypoint.name];
if (withSameName) {
throw Error(
`Multiple entrypoints with the name "${
entrypoint.name
}" detected, but only one is allowed: ${[
relative(config.root, withSameName.inputPath),
relative(config.root, entrypoint.inputPath),
].join(', ')}`,
);
}
existingNames[entrypoint.name] = entrypoint;
});
if (config.command === 'serve' && !hasBackground) {
entrypoints.push(
await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID),
+3
View File
@@ -8,6 +8,9 @@ import pc from 'picocolors';
* Remove generated/temp files from the directory.
*
* @param root The directory to look for generated/temp files in. Defaults to `process.cwd()`. Can be relative to `process.cwd()` or absolute.
*
* @example
* await clean();
*/
export async function clean(root = process.cwd()) {
consola.info('Cleaning Project');
+1 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest';
import { createExtensionRunner } from '..';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { fakeInternalConfig } from '../../../testing-utils/fake-objects';
import { mock } from 'vitest-mock-extended';
import { createSafariRunner } from '../safari';
import { ExtensionRunner } from '../extension-runner';
+10 -10
View File
@@ -110,12 +110,12 @@ export interface InlineConfig {
*
* Available template variables:
*
* - `{name}` - The project's name converted to kebab-case
* - `{version}` - The version_name or version from the manifest
* - `{browser}` - The target browser from the `--browser` CLI flag
* - `{manifestVersion}` - Either "2" or "3"
* - `{{name}}` - The project's name converted to kebab-case
* - `{{version}}` - The version_name or version from the manifest
* - `{{browser}}` - The target browser from the `--browser` CLI flag
* - `{{manifestVersion}}` - Either "2" or "3"
*
* @default "{name}-{version}-{browser}.zip"
* @default "{{name}}-{{version}}-{{browser}}.zip"
*/
artifactTemplate?: string;
/**
@@ -123,12 +123,12 @@ export interface InlineConfig {
*
* Available template variables:
*
* - `{name}` - The project's name converted to kebab-case
* - `{version}` - The version_name or version from the manifest
* - `{browser}` - The target browser from the `--browser` CLI flag
* - `{manifestVersion}` - Either "2" or "3"
* - `{{name}}` - The project's name converted to kebab-case
* - `{{version}}` - The version_name or version from the manifest
* - `{{browser}}` - The target browser from the `--browser` CLI flag
* - `{{manifestVersion}}` - Either "2" or "3"
*
* @default "{name}-{version}-sources.zip"
* @default "{{name}}-{{version}}-sources.zip"
*/
sourcesTemplate?: string;
/**
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { hashContentScriptOptions } from '../content-scripts';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { fakeInternalConfig } from '../../../testing-utils/fake-objects';
describe('Content Script Utils', () => {
describe('hashContentScriptOptions', () => {
@@ -10,7 +10,7 @@ import {
fakePopupEntrypoint,
fakeRollupOutputAsset,
fakeRollupOutputChunk,
} from '../../../testing/fake-objects';
} from '../../../testing-utils/fake-objects';
import { BuildOutput, BuildStepOutput } from '../../types';
describe('Detect Dev Changes', () => {
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { importEntrypointFile } from '../importEntrypointFile';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { fakeInternalConfig } from '../../../testing-utils/fake-objects';
import { resolve } from 'node:path';
const entrypointPath = (filename: string) =>
+4 -1
View File
@@ -9,7 +9,10 @@ export function getUnimportOptions(
const defaultOptions: Partial<UnimportOptions> = {
debugLog: config.logger.debug,
imports: [{ name: 'defineConfig', from: 'wxt' }],
imports: [
{ name: 'defineConfig', from: 'wxt' },
{ name: 'fakeBrowser', from: 'wxt/testing' },
],
presets: [
{ package: 'wxt/client' },
{ package: 'wxt/browser' },
+1 -5
View File
@@ -14,7 +14,6 @@ import * as vite from 'vite';
import { createFsCache } from './createFsCache';
import consola, { LogLevels } from 'consola';
import * as plugins from '../vite-plugins';
import { getGlobals } from './globals';
/**
* Given an inline config, discover the config file if necessary, merge the results, resolve any
@@ -251,10 +250,7 @@ async function resolveInternalViteConfig(
if (finalConfig.analysis.enabled) {
internalVite.plugins.push(plugins.bundleAnalysis());
}
internalVite.plugins.push(plugins.globals(finalConfig));
internalVite.define ??= {};
for (const global of getGlobals(finalConfig)) {
internalVite.define[global.name] = JSON.stringify(global.value);
}
return internalVite;
}
+17 -9
View File
@@ -5,42 +5,42 @@ export function getGlobals(
): Array<{ name: string; value: any; type: string }> {
return [
{
name: '__MANIFEST_VERSION__',
name: surroundInUnderscore('MANIFEST_VERSION'),
value: config.manifestVersion,
type: `2 | 3`,
},
{
name: '__BROWSER__',
name: surroundInUnderscore('BROWSER'),
value: config.browser,
type: `string`,
},
{
name: '__IS_CHROME__',
name: surroundInUnderscore('IS_CHROME'),
value: config.browser === 'chrome',
type: `boolean`,
},
{
name: '__IS_FIREFOX__',
name: surroundInUnderscore('IS_FIREFOX'),
value: config.browser === 'firefox',
type: `boolean`,
},
{
name: '__IS_SAFARI__',
name: surroundInUnderscore('IS_SAFARI'),
value: config.browser === 'safari',
type: `boolean`,
},
{
name: '__IS_EDGE__',
name: surroundInUnderscore('IS_EDGE'),
value: config.browser === 'edge',
type: `boolean`,
},
{
name: '__IS_OPERA__',
name: surroundInUnderscore('IS_OPERA'),
value: config.browser === 'opera',
type: `boolean`,
},
{
name: '__COMMAND__',
name: surroundInUnderscore('COMMAND'),
value: config.command,
type: `"build" | "serve"`,
},
@@ -53,9 +53,17 @@ export function getEntrypointGlobals(
) {
return [
{
name: '__ENTRYPOINT__',
name: surroundInUnderscore('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}__`;
}
+5 -8
View File
@@ -217,6 +217,7 @@ function addEntrypoints(
const options: Manifest.ActionManifest = {
default_icon: popup.options.defaultIcon,
default_title: popup.options.defaultTitle,
browser_style: popup.options.browserStyle,
};
if (manifest.manifest_version === 3) {
manifest.action = {
@@ -323,14 +324,10 @@ function addEntrypoints(
const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
([, scripts]) => ({
...mapWxtOptionsToContentScript(scripts[0].options, config),
// TOOD: Sorting css and js arrays here so we get consistent test results... but we
// shouldn't have to. Where is the inconsistency coming from?
css: getContentScriptCssFiles(scripts, cssMap)?.sort(),
js: scripts
.map((entry) =>
getEntrypointBundlePath(entry, config.outDir, '.js'),
)
.sort(),
css: getContentScriptCssFiles(scripts, cssMap),
js: scripts.map((entry) =>
getEntrypointBundlePath(entry, config.outDir, '.js'),
),
}),
);
if (newContentScripts.length >= 0) {
+18
View File
@@ -0,0 +1,18 @@
import * as vite from 'vite';
import { InternalConfig } from '../types';
import { getGlobals } from '../utils/globals';
export function globals(config: InternalConfig): vite.PluginOption {
return {
name: 'wxt:globals',
config() {
const define: vite.InlineConfig['define'] = {};
for (const global of getGlobals(config)) {
define[global.name] = JSON.stringify(global.value);
}
return {
define,
};
},
};
}
+2
View File
@@ -8,3 +8,5 @@ export * from './tsconfigPaths';
export * from './noopBackground';
export * from './cssEntrypoints';
export * from './bundleAnalysis';
export * from './globals';
export * from './webextensionPolyfillAlias';
@@ -0,0 +1,28 @@
import path from 'node:path';
import * as vite from 'vite';
import { InternalConfig } from '../types';
/**
* Creates an alias to redirect "webextension-polyfill" imports to WXT's `fakeBrowser`.
*
* This should only be used during tests.
*/
export function webextensionPolyfillAlias(
config: InternalConfig,
): vite.PluginOption {
return {
name: 'wxt:webextension-polyfill-test-alias',
config() {
return {
resolve: {
alias: {
'webextension-polyfill': path.resolve(
config.root,
'node_modules/wxt/dist/virtual-modules/fake-browser',
),
},
},
};
},
};
}
+6 -1
View File
@@ -12,7 +12,12 @@ import { printFileList } from './log/printFileList';
* Zip the extension for distribution. Does not build, just zips the output that should exist for
* the given config.
*
* Returns a list of ZIP files that were created.
* @returns A list of ZIP files that were created.
*
* @example
* const config: InlineConfig = {};
* const result = await build(config);
* await zipExtension(config, result);
*/
export async function zipExtension(
config: InternalConfig,
+15 -2
View File
@@ -1,3 +1,6 @@
/**
* @module wxt
*/
import { BuildOutput, WxtDevServer, InlineConfig } from './core/types';
import { getInternalConfig } from './core/utils/getInternalConfig';
import pc from 'picocolors';
@@ -22,6 +25,11 @@ export * from './core/utils/defineRunnerConfig';
/**
* Bundles the extension for production. Returns a promise of the build result.
*
* @example
* const res = await build({
* // Enter config...
* })
*/
export async function build(config: InlineConfig): Promise<BuildOutput> {
const internalConfig = await getInternalConfig(config, 'build');
@@ -29,8 +37,13 @@ export async function build(config: InlineConfig): Promise<BuildOutput> {
}
/**
* Creates a dev server, pre-builds all the files that need to exist to load the extension, and open
* the browser with the extension installed.
* Creates a dev server and pre-builds all the files that need to exist before loading the extension.
*
* @example
* const server = await wxt.createServer({
* // Enter config...
* });
* await server.start();
*/
export async function createServer(
config?: InlineConfig,
+1
View File
@@ -0,0 +1 @@
export { fakeBrowser, type FakeBrowser } from '@webext-core/fake-browser';
+5
View File
@@ -0,0 +1,5 @@
/**
* @module wxt/testing
*/
export * from './fake-browser';
export * from './wxt-vitest-plugin';
+35
View File
@@ -0,0 +1,35 @@
import * as vite from 'vite';
import {
unimport,
download,
tsconfigPaths,
globals,
webextensionPolyfillAlias,
} from '../core/vite-plugins';
import { getInternalConfig } from '../core/utils/getInternalConfig';
import { InlineConfig } from '../core/types';
/**
* Vite plugin that configures Vitest with everything required to test a WXT extension, based on the `<root>/wxt.config.ts`
*
* ```ts
* // vitest.config.ts
* import { defineConfig } from 'vitest/config';
* import { AutoImport } from 'wxt/testing';
*
* export default defineConfig({
* plugins: [AutoImport()],
* });
* ```
*
* @param inlineConfig Customize WXT's config for testing. Any config specified here overrides the config from your `wxt.config.ts` file.
*/
export function WxtVitest(inlineConfig?: InlineConfig): vite.PluginOption {
return getInternalConfig(inlineConfig ?? {}, 'serve').then((config) => [
webextensionPolyfillAlias(config),
unimport(config),
globals(config),
download(config),
tsconfigPaths(config),
]);
}
+1 -1
View File
@@ -23,6 +23,6 @@
"@types/react-dom": "^18.2.6",
"@vitejs/plugin-react": "^4.0.3",
"typescript": "^5.1.6",
"wxt": "^0.7.0"
"wxt": "^0.8.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"devDependencies": {
"typescript": "^5.1.6",
"vite-plugin-solid": "^2.7.0",
"wxt": "^0.7.0"
"wxt": "^0.8.0"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"svelte-check": "^3.4.6",
"tslib": "^2.6.0",
"typescript": "^5.1.6",
"wxt": "^0.7.0"
"wxt": "^0.8.0"
}
}
+1 -1
View File
@@ -16,6 +16,6 @@
},
"devDependencies": {
"typescript": "^5.1.6",
"wxt": "^0.7.0"
"wxt": "^0.8.0"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"@vitejs/plugin-vue": "^4.2.3",
"typescript": "^5.1.6",
"vue-tsc": "^1.8.4",
"wxt": "^0.7.0"
"wxt": "^0.8.0"
}
}