feat!: Remove webextension-polyfill (#1084)
This commit is contained in:
@@ -4,61 +4,41 @@
|
||||
|
||||
Different browsers provide different global variables for accessing the extension APIs (chrome provides `chrome`, firefox provides `browser`, etc).
|
||||
|
||||
WXT simplifies this - always use `browser`:
|
||||
WXT merges these two into a unified API accessed through the `browser` variable.
|
||||
|
||||
```ts
|
||||
import { browser } from 'wxt/browser';
|
||||
|
||||
browser.action.onClicked.addListener(() => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Other than that, refer to Chrome and Mozilla's documentation for how to use specific APIs. Everything a normal extension can do, WXT can do as well, just via `browser` instead of `chrome`.
|
||||
:::tip
|
||||
With auto-imports enabled, you don't even need to import this variable from `wxt/browser`!
|
||||
:::
|
||||
|
||||
## Webextension Polyfill
|
||||
|
||||
> Since `v0.1.0`
|
||||
|
||||
By default, WXT uses the [`webextension-polyfill` by Mozilla](https://www.npmjs.com/package/webextension-polyfill) to make the extension API consistent between browsers.
|
||||
|
||||
To access types, you should import the relevant namespace from `wxt/browser`:
|
||||
|
||||
```ts
|
||||
import { Runtime } from 'wxt/browser';
|
||||
|
||||
function handleMessage(message: any, sender: Runtime.Sender) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Disabling the polyfill
|
||||
|
||||
> Since `v0.19.0`
|
||||
|
||||
After the release of MV3 and Chrome's official deprecation of MV2 in June 2024, the polyfill isn't really doing anything useful anymore.
|
||||
|
||||
You can disable it with a single line:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
```
|
||||
|
||||
This will change `wxt/browser` to simply export the `browser` or `chrome` globals based on browser at runtime:
|
||||
The `browser` variable WXT provides is a simple export of the `browser` or `chrome` globals provided by the browser at runtime:
|
||||
|
||||
<<< @/../packages/browser/src/index.mjs#snippet
|
||||
|
||||
Accessing types is a little different with the polyfill disabled. They do not need to be imported; they're available on the `browser` object itself:
|
||||
This means you can use the promise-style API for both MV2 and MV3, and it will work across all browsers (Chromium, Firefox, Safari, etc).
|
||||
|
||||
## Accessing Types
|
||||
|
||||
All types can be accessed via WXT's `Browser` namespace:
|
||||
|
||||
```ts
|
||||
function handleMessage(message: any, sender: browser.runtime.Sender) {
|
||||
import { Browser } from 'wxt/browser';
|
||||
|
||||
function handleMessage(message: any, sender: Browser.runtime.MessageSender) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Feature Detection
|
||||
|
||||
Depending on the manifest version and browser, some APIs are not available at runtime. If an API is not available, it will be `undefined`.
|
||||
Depending on the manifest version, browser, and permissions, some APIs are not available at runtime. If an API is not available, it will be `undefined`.
|
||||
|
||||
:::warning
|
||||
Types will not help you here. The types WXT provides for `browser` assume all APIs exist. You are responsible for knowing whether an API is available or not.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import background from '../background';
|
||||
|
||||
chrome.i18n.getMessage = () => 'fake-message';
|
||||
browser.i18n.getMessage = () => 'fake-message';
|
||||
|
||||
const logMock = vi.fn();
|
||||
console.log = logMock;
|
||||
@@ -11,7 +11,7 @@ describe('Background Entrypoint', () => {
|
||||
fakeBrowser.reset();
|
||||
});
|
||||
|
||||
it("should log the extenion's runtime ID", () => {
|
||||
it("should log the extension's runtime ID", () => {
|
||||
const id = 'some-id';
|
||||
fakeBrowser.runtime.id = id;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { presetUno } from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
extensionApi: 'chrome',
|
||||
manifest: {
|
||||
permissions: ['storage'],
|
||||
default_locale: 'en',
|
||||
|
||||
@@ -191,10 +191,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -211,10 +208,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -232,10 +226,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -248,10 +239,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
|
||||
@@ -262,9 +262,6 @@ describe('Output Directory Structure', () => {
|
||||
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
|
||||
|
||||
await project.build({
|
||||
// Simplify the build output for comparison
|
||||
extensionApi: 'chrome',
|
||||
|
||||
vite: () => ({
|
||||
build: {
|
||||
// Make output for snapshot readible
|
||||
@@ -289,11 +286,7 @@ describe('Output Directory Structure', () => {
|
||||
logHello("background");
|
||||
}
|
||||
});
|
||||
// @ts-expect-error
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
|
||||
// @ts-expect-error
|
||||
globalThis.browser
|
||||
);
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) ? globalThis.browser : globalThis.chrome;
|
||||
function print(method, ...args) {
|
||||
return;
|
||||
}
|
||||
@@ -347,9 +340,6 @@ describe('Output Directory Structure', () => {
|
||||
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
|
||||
|
||||
await project.build({
|
||||
// Simplify the build output for comparison
|
||||
extensionApi: 'chrome',
|
||||
|
||||
vite: () => ({
|
||||
build: {
|
||||
// Make output for snapshot readible
|
||||
@@ -381,11 +371,7 @@ describe('Output Directory Structure', () => {
|
||||
background;
|
||||
function initPlugins() {
|
||||
}
|
||||
// @ts-expect-error
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
|
||||
// @ts-expect-error
|
||||
globalThis.browser
|
||||
);
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) ? globalThis.browser : globalThis.chrome;
|
||||
function print(method, ...args) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
import { InlineConfig } from '../../src/types';
|
||||
|
||||
describe('User Config', () => {
|
||||
// Root directory is tested with all tests.
|
||||
@@ -88,24 +87,6 @@ describe('User Config', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should exclude the polyfill when extensionApi="chrome"', async () => {
|
||||
const buildBackground = async (config?: InlineConfig) => {
|
||||
const background = `export default defineBackground(() => console.log(browser.runtime.id));`;
|
||||
const projectWithPolyfill = new TestProject();
|
||||
projectWithPolyfill.addFile('entrypoints/background.ts', background);
|
||||
await projectWithPolyfill.build(config);
|
||||
return await projectWithPolyfill.serializeFile(
|
||||
'.output/chrome-mv3/background.js',
|
||||
);
|
||||
};
|
||||
|
||||
const withPolyfill = await buildBackground();
|
||||
const withoutPolyfill = await buildBackground({
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
expect(withoutPolyfill).not.toBe(withPolyfill);
|
||||
});
|
||||
|
||||
it('should respect changing config files', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
|
||||
@@ -47,12 +47,8 @@
|
||||
"default": "./dist/sandbox/index.mjs"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser/index.d.ts",
|
||||
"default": "./dist/browser/index.mjs"
|
||||
},
|
||||
"./browser/chrome": {
|
||||
"types": "./dist/browser/chrome.d.ts",
|
||||
"import": "./dist/browser/chrome.mjs"
|
||||
"types": "./dist/browser.d.ts",
|
||||
"default": "./dist/browser.mjs"
|
||||
},
|
||||
"./testing": {
|
||||
"types": "./dist/testing/index.d.ts",
|
||||
@@ -84,11 +80,10 @@
|
||||
"dependencies": {
|
||||
"@1natsu/wait-element": "catalog:",
|
||||
"@aklinker1/rollup-plugin-visualizer": "catalog:",
|
||||
"@types/chrome": "catalog:",
|
||||
"@types/webextension-polyfill": "catalog:",
|
||||
"@webext-core/fake-browser": "catalog:",
|
||||
"@webext-core/isolated-element": "catalog:",
|
||||
"@webext-core/match-patterns": "catalog:",
|
||||
"@wxt-dev/browser": "workspace:*",
|
||||
"@wxt-dev/storage": "workspace:^1.0.0",
|
||||
"async-mutex": "catalog:",
|
||||
"c12": "catalog:",
|
||||
@@ -128,8 +123,7 @@
|
||||
"unimport": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-node": "catalog:",
|
||||
"web-ext-run": "catalog:",
|
||||
"webextension-polyfill": "catalog:"
|
||||
"web-ext-run": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
@@ -149,9 +143,5 @@
|
||||
"vitest": "catalog:",
|
||||
"vitest-plugin-random-seed": "catalog:"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/chrome": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
"peerDependenciesMeta": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @module wxt/browser
|
||||
*/
|
||||
import { browser as _browser, type Browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/paths.d.ts`
|
||||
*/
|
||||
export interface WxtRuntime {}
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/i18n.d.ts`
|
||||
*/
|
||||
export interface WxtI18n {}
|
||||
|
||||
export type WxtBrowser = Omit<typeof _browser, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<(typeof _browser)['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<(typeof _browser)['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
export const browser: WxtBrowser = _browser;
|
||||
|
||||
export { Browser };
|
||||
@@ -1,23 +0,0 @@
|
||||
/// <reference types="chrome" />
|
||||
/**
|
||||
* EXPERIMENTAL
|
||||
*
|
||||
* Includes the `chrome` API and types when using `extensionApi: 'chrome'`.
|
||||
*
|
||||
* @module wxt/browser/chrome
|
||||
*/
|
||||
import type { WxtRuntime, WxtI18n } from './index';
|
||||
|
||||
export type WxtBrowser = Omit<typeof chrome, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<(typeof chrome)['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<(typeof chrome)['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
// #region snippet
|
||||
export const browser: WxtBrowser =
|
||||
// @ts-expect-error
|
||||
globalThis.browser?.runtime?.id == null
|
||||
? globalThis.chrome
|
||||
: // @ts-expect-error
|
||||
globalThis.browser;
|
||||
// #endregion snippet
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Includes the `browser` API and types when using `extensionApi: 'webextension-polyfill'` (the default).
|
||||
*
|
||||
* @module wxt/browser
|
||||
*/
|
||||
|
||||
import originalBrowser, { Browser } from 'webextension-polyfill';
|
||||
|
||||
export type AugmentedBrowser = Omit<Browser, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<Browser['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<Browser['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/paths.d.ts`
|
||||
*/
|
||||
export interface WxtRuntime {}
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/i18n.d.ts`
|
||||
*/
|
||||
export interface WxtI18n {}
|
||||
|
||||
export const browser: AugmentedBrowser = originalBrowser;
|
||||
|
||||
// re-export all the types from webextension-polyfill
|
||||
// Because webextension-polyfill uses a weird namespace with "import export", there isn't a good way
|
||||
// to get these types without re-listing them.
|
||||
/** @ignore */
|
||||
export type {
|
||||
ActivityLog,
|
||||
Alarms,
|
||||
Bookmarks,
|
||||
Action,
|
||||
BrowserAction,
|
||||
BrowserSettings,
|
||||
BrowsingData,
|
||||
CaptivePortal,
|
||||
Clipboard,
|
||||
Commands,
|
||||
ContentScripts,
|
||||
ContextualIdentities,
|
||||
Cookies,
|
||||
DeclarativeNetRequest,
|
||||
Devtools,
|
||||
Dns,
|
||||
Downloads,
|
||||
Events,
|
||||
Experiments,
|
||||
Extension,
|
||||
ExtensionTypes,
|
||||
Find,
|
||||
GeckoProfiler,
|
||||
History,
|
||||
I18n,
|
||||
Identity,
|
||||
Idle,
|
||||
Management,
|
||||
Manifest, // TODO: Export custom manifest types that are valid for both Chrome and Firefox.
|
||||
ContextMenus,
|
||||
Menus,
|
||||
NetworkStatus,
|
||||
NormandyAddonStudy,
|
||||
Notifications,
|
||||
Omnibox,
|
||||
PageAction,
|
||||
Permissions,
|
||||
Pkcs11,
|
||||
Privacy,
|
||||
Proxy,
|
||||
Runtime,
|
||||
Scripting,
|
||||
Search,
|
||||
Sessions,
|
||||
SidebarAction,
|
||||
Storage,
|
||||
Tabs,
|
||||
Theme,
|
||||
TopSites,
|
||||
Types,
|
||||
UserScripts,
|
||||
WebNavigation,
|
||||
WebRequest,
|
||||
Windows,
|
||||
} from 'webextension-polyfill';
|
||||
@@ -24,7 +24,7 @@ describe('Content Script Context', () => {
|
||||
const onInvalidated = vi.fn();
|
||||
|
||||
ctx.onInvalidated(onInvalidated);
|
||||
// @ts-expect-error
|
||||
// @ts-ignore
|
||||
delete fakeBrowser.runtime.id;
|
||||
const isValid = ctx.isValid;
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ export async function createViteBuilder(
|
||||
wxtPlugins.tsconfigPaths(wxtConfig),
|
||||
wxtPlugins.noopBackground(),
|
||||
wxtPlugins.globals(wxtConfig),
|
||||
wxtPlugins.resolveExtensionApi(wxtConfig),
|
||||
wxtPlugins.defineImportMeta(),
|
||||
wxtPlugins.wxtPluginLoader(wxtConfig),
|
||||
wxtPlugins.resolveAppConfig(wxtConfig),
|
||||
|
||||
@@ -3,12 +3,7 @@ import type * as vite from 'vite';
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
|
||||
/**
|
||||
* Mock `webextension-polyfill`, `wxt/browser`, and `wxt/browser/*` by inlining
|
||||
* all dependencies that import them and adding a custom alias so that Vite
|
||||
* resolves to a mocked version of the module.
|
||||
*
|
||||
* TODO: Detect non-wxt dependencies (like `@webext-core/*`) that import `webextension-polyfill` via
|
||||
* `npm list` and inline them automatically.
|
||||
* Mock `wxt/browser` and stub the global `browser`/`chrome` types with a fake version of the extension APIs
|
||||
*/
|
||||
export function extensionApiMock(config: ResolvedConfig): vite.PluginOption {
|
||||
const virtualSetupModule = 'virtual:wxt-setup';
|
||||
@@ -27,14 +22,12 @@ export function extensionApiMock(config: ResolvedConfig): vite.PluginOption {
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: 'webextension-polyfill', replacement },
|
||||
// wxt/browser, wxt/browser/...
|
||||
{ find: /^wxt\/browser.*/, replacement },
|
||||
{ find: 'wxt/browser', replacement },
|
||||
],
|
||||
},
|
||||
ssr: {
|
||||
// Inline all WXT modules so vite processes them so the aliases can
|
||||
// be resolved
|
||||
// Inline all WXT modules subdependencies can be mocked
|
||||
noExternal: ['wxt'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ export * from './cssEntrypoints';
|
||||
export * from './bundleAnalysis';
|
||||
export * from './globals';
|
||||
export * from './extensionApiMock';
|
||||
export * from './resolveExtensionApi';
|
||||
export * from './entrypointGroupGlobals';
|
||||
export * from './defineImportMeta';
|
||||
export * from './removeEntrypointMainFunction';
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
import type * as vite from 'vite';
|
||||
|
||||
/**
|
||||
* Apply the experimental config for which extension API is used. This only
|
||||
* effects the extension API included at RUNTIME - during development, types
|
||||
* depend on the import.
|
||||
*
|
||||
* NOTE: this only works if we import `wxt/browser` instead of using the relative path.
|
||||
*/
|
||||
export function resolveExtensionApi(config: ResolvedConfig): vite.Plugin {
|
||||
return {
|
||||
name: 'wxt:resolve-extension-api',
|
||||
config() {
|
||||
// Only apply the config if we're disabling the polyfill
|
||||
if (config.extensionApi === 'webextension-polyfill') return;
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: /^wxt\/browser$/, replacement: 'wxt/browser/chrome' },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -37,11 +37,6 @@ export async function generateWxtDir(entrypoints: Entrypoint[]): Promise<void> {
|
||||
// import.meta.env.*
|
||||
entries.push(await getGlobalsDeclarationEntry());
|
||||
|
||||
// @types/chrome
|
||||
if (wxt.config.extensionApi === 'chrome') {
|
||||
entries.push({ module: '@types/chrome' });
|
||||
}
|
||||
|
||||
// tsconfig.json
|
||||
entries.push(await getTsConfigEntry());
|
||||
|
||||
|
||||
@@ -171,8 +171,6 @@ export async function resolveConfig(
|
||||
{},
|
||||
);
|
||||
|
||||
const extensionApi = mergedConfig.extensionApi ?? 'webextension-polyfill';
|
||||
|
||||
return {
|
||||
browser,
|
||||
command,
|
||||
@@ -182,13 +180,7 @@ export async function resolveConfig(
|
||||
filterEntrypoints,
|
||||
env,
|
||||
fsCache: createFsCache(wxtDir),
|
||||
imports: await getUnimportOptions(
|
||||
wxtDir,
|
||||
srcDir,
|
||||
logger,
|
||||
extensionApi,
|
||||
mergedConfig,
|
||||
),
|
||||
imports: await getUnimportOptions(wxtDir, srcDir, logger, mergedConfig),
|
||||
logger,
|
||||
manifest: await resolveManifestConfig(env, mergedConfig.manifest),
|
||||
manifestVersion,
|
||||
@@ -207,7 +199,6 @@ export async function resolveConfig(
|
||||
analysis: resolveAnalysisConfig(root, mergedConfig),
|
||||
userConfigMetadata: userConfigMetadata ?? {},
|
||||
alias,
|
||||
extensionApi,
|
||||
entrypointLoader: mergedConfig.entrypointLoader ?? 'vite-node',
|
||||
experimental: defu(mergedConfig.experimental, {}),
|
||||
dev: {
|
||||
@@ -342,7 +333,6 @@ async function getUnimportOptions(
|
||||
wxtDir: string,
|
||||
srcDir: string,
|
||||
logger: Logger,
|
||||
extensionApi: ResolvedConfig['extensionApi'],
|
||||
config: InlineConfig,
|
||||
): Promise<WxtResolvedUnimportOptions | false> {
|
||||
if (config.imports === false) return false;
|
||||
@@ -361,10 +351,7 @@ async function getUnimportOptions(
|
||||
// ignored.
|
||||
ignore: ['options'],
|
||||
},
|
||||
{
|
||||
package:
|
||||
extensionApi === 'chrome' ? 'wxt/browser/chrome' : 'wxt/browser',
|
||||
},
|
||||
{ package: 'wxt/browser' },
|
||||
{ package: 'wxt/sandbox' },
|
||||
{ package: 'wxt/storage' },
|
||||
],
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
fakeWxtDevServer,
|
||||
setFakeWxt,
|
||||
} from '../testing/fake-objects';
|
||||
import { Manifest } from 'webextension-polyfill';
|
||||
import {
|
||||
BuildOutput,
|
||||
ContentScriptEntrypoint,
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
} from '../../../types';
|
||||
import { wxt } from '../../wxt';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
const outDir = '/output';
|
||||
const contentScriptOutDir = '/output/content-scripts';
|
||||
@@ -58,7 +58,7 @@ describe('Manifest Utils', () => {
|
||||
outDir,
|
||||
},
|
||||
});
|
||||
const expected: Partial<Manifest.WebExtensionManifest> = {
|
||||
const expected: Partial<Browser.runtime.Manifest> = {
|
||||
action: {
|
||||
default_icon: popup.options.defaultIcon,
|
||||
default_title: popup.options.defaultTitle,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Manifest } from 'wxt/browser';
|
||||
import { BuildOutput, Entrypoint, EntrypointGroup } from '../../../types';
|
||||
import { generateWxtDir } from '../../generate-wxt-dir';
|
||||
import { buildEntrypoints } from './build-entrypoints';
|
||||
import { generateManifest, writeManifest } from '../../utils/manifest';
|
||||
import { wxt } from '../../wxt';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* Given a configuration, list of entrypoints, and an existing, partial output, build the
|
||||
@@ -30,7 +30,7 @@ export async function rebuild(
|
||||
},
|
||||
): Promise<{
|
||||
output: BuildOutput;
|
||||
manifest: Manifest.WebExtensionManifest;
|
||||
manifest: Browser.runtime.Manifest;
|
||||
warnings: any[][];
|
||||
}> {
|
||||
const { default: ora } = await import('ora');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Manifest, Scripting } from 'wxt/browser';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
import { ContentScriptEntrypoint, ResolvedConfig } from '../../types';
|
||||
import { getEntrypointBundlePath } from './entrypoints';
|
||||
import { ManifestContentScript } from './types';
|
||||
|
||||
/**
|
||||
* Returns a unique and consistent string hash based on a content scripts options.
|
||||
@@ -22,13 +23,14 @@ export function hashContentScriptOptions(
|
||||
if (simplifiedOptions[key] == null) delete simplifiedOptions[key];
|
||||
});
|
||||
|
||||
const withDefaults: Manifest.ContentScript = {
|
||||
const withDefaults: ManifestContentScript = {
|
||||
exclude_globs: [],
|
||||
exclude_matches: [],
|
||||
include_globs: [],
|
||||
match_about_blank: false,
|
||||
run_at: 'document_idle',
|
||||
all_frames: false,
|
||||
// @ts-expect-error: Untyped
|
||||
match_origin_as_fallback: false,
|
||||
world: 'ISOLATED',
|
||||
...simplifiedOptions,
|
||||
@@ -49,7 +51,7 @@ export function mapWxtOptionsToContentScript(
|
||||
options: ContentScriptEntrypoint['options'],
|
||||
js: string[] | undefined,
|
||||
css: string[] | undefined,
|
||||
): Manifest.ContentScript {
|
||||
): ManifestContentScript {
|
||||
return {
|
||||
matches: options.matches ?? [],
|
||||
all_frames: options.allFrames,
|
||||
@@ -61,6 +63,7 @@ export function mapWxtOptionsToContentScript(
|
||||
css,
|
||||
js,
|
||||
|
||||
// @ts-expect-error: Untyped
|
||||
match_origin_as_fallback: options.matchOriginAsFallback,
|
||||
world: options.world,
|
||||
};
|
||||
@@ -70,7 +73,7 @@ export function mapWxtOptionsToRegisteredContentScript(
|
||||
options: ContentScriptEntrypoint['options'],
|
||||
js: string[] | undefined,
|
||||
css: string[] | undefined,
|
||||
): Omit<Scripting.RegisteredContentScript, 'id'> {
|
||||
): Omit<Browser.scripting.RegisteredContentScript, 'id'> {
|
||||
return {
|
||||
allFrames: options.allFrames,
|
||||
excludeMatches: options.excludeMatches,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Manifest } from 'wxt/browser';
|
||||
import {
|
||||
Entrypoint,
|
||||
BackgroundEntrypoint,
|
||||
@@ -21,12 +20,14 @@ import { normalizePath } from './paths';
|
||||
import { writeFileIfDifferent } from './fs';
|
||||
import defu from 'defu';
|
||||
import { wxt } from '../wxt';
|
||||
import { ManifestV3WebAccessibleResource } from './types';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* Writes the manifest to the output directory and the build output.
|
||||
*/
|
||||
export async function writeManifest(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
output: BuildOutput,
|
||||
): Promise<void> {
|
||||
const str =
|
||||
@@ -49,7 +50,7 @@ export async function writeManifest(
|
||||
export async function generateManifest(
|
||||
allEntrypoints: Entrypoint[],
|
||||
buildOutput: Omit<BuildOutput, 'manifest'>,
|
||||
): Promise<{ manifest: Manifest.WebExtensionManifest; warnings: any[][] }> {
|
||||
): Promise<{ manifest: Browser.runtime.Manifest; warnings: any[][] }> {
|
||||
const entrypoints = allEntrypoints.filter((entry) => !entry.skipped);
|
||||
|
||||
const warnings: any[][] = [];
|
||||
@@ -67,7 +68,7 @@ export async function generateManifest(
|
||||
}
|
||||
const version = wxt.config.manifest.version ?? simplifyVersion(versionName);
|
||||
|
||||
const baseManifest: Manifest.WebExtensionManifest = {
|
||||
const baseManifest: Browser.runtime.Manifest = {
|
||||
manifest_version: wxt.config.manifestVersion,
|
||||
name: pkg?.name,
|
||||
description: pkg?.description,
|
||||
@@ -83,10 +84,7 @@ export async function generateManifest(
|
||||
);
|
||||
}
|
||||
|
||||
let manifest = defu(
|
||||
userManifest,
|
||||
baseManifest,
|
||||
) as Manifest.WebExtensionManifest;
|
||||
let manifest = defu(userManifest, baseManifest) as Browser.runtime.Manifest;
|
||||
|
||||
// Add reload command in dev mode
|
||||
if (wxt.config.command === 'serve' && wxt.config.dev.reloadCommand) {
|
||||
@@ -177,7 +175,7 @@ function simplifyVersion(versionName: string): string {
|
||||
}
|
||||
|
||||
function addEntrypoints(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
entrypoints: Entrypoint[],
|
||||
buildOutput: Omit<BuildOutput, 'manifest'>,
|
||||
): void {
|
||||
@@ -239,7 +237,6 @@ function addEntrypoints(
|
||||
);
|
||||
} else {
|
||||
manifest.chrome_url_overrides ??= {};
|
||||
// @ts-expect-error: bookmarks is untyped in webextension-polyfill, but supported by chrome
|
||||
manifest.chrome_url_overrides.bookmarks = getEntrypointBundlePath(
|
||||
bookmarks,
|
||||
wxt.config.outDir,
|
||||
@@ -255,7 +252,6 @@ function addEntrypoints(
|
||||
);
|
||||
} else {
|
||||
manifest.chrome_url_overrides ??= {};
|
||||
// @ts-expect-error: history is untyped in webextension-polyfill, but supported by chrome
|
||||
manifest.chrome_url_overrides.history = getEntrypointBundlePath(
|
||||
history,
|
||||
wxt.config.outDir,
|
||||
@@ -279,12 +275,13 @@ function addEntrypoints(
|
||||
wxt.config.outDir,
|
||||
'.html',
|
||||
);
|
||||
const options: Manifest.ActionManifest = {};
|
||||
const options: Browser.runtime.ManifestAction = {};
|
||||
if (popup.options.defaultIcon)
|
||||
options.default_icon = popup.options.defaultIcon;
|
||||
if (popup.options.defaultTitle)
|
||||
options.default_title = popup.options.defaultTitle;
|
||||
if (popup.options.browserStyle)
|
||||
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
|
||||
options.browser_style = popup.options.browserStyle;
|
||||
if (manifest.manifest_version === 3) {
|
||||
manifest.action = {
|
||||
@@ -314,6 +311,7 @@ function addEntrypoints(
|
||||
const page = getEntrypointBundlePath(options, wxt.config.outDir, '.html');
|
||||
manifest.options_ui = {
|
||||
open_in_tab: options.options.openInTab,
|
||||
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
|
||||
browser_style:
|
||||
wxt.config.browser === 'firefox'
|
||||
? options.options.browserStyle
|
||||
@@ -332,7 +330,6 @@ function addEntrypoints(
|
||||
'Sandboxed pages not supported by Firefox. sandbox.pages was not added to the manifest',
|
||||
);
|
||||
} else {
|
||||
// @ts-expect-error: sandbox not typed
|
||||
manifest.sandbox = {
|
||||
pages: sandboxes.map((entry) =>
|
||||
getEntrypointBundlePath(entry, wxt.config.outDir, '.html'),
|
||||
@@ -359,7 +356,6 @@ function addEntrypoints(
|
||||
open_at_install: defaultSidepanel.options.openAtInstall,
|
||||
};
|
||||
} else if (wxt.config.manifestVersion === 3) {
|
||||
// @ts-expect-error: Untyped
|
||||
manifest.side_panel = {
|
||||
default_path: page,
|
||||
};
|
||||
@@ -432,7 +428,7 @@ function addEntrypoints(
|
||||
|
||||
function discoverIcons(
|
||||
buildOutput: Omit<BuildOutput, 'manifest'>,
|
||||
): Manifest.WebExtensionManifest['icons'] {
|
||||
): Browser.runtime.Manifest['icons'] {
|
||||
const icons: [string, string][] = [];
|
||||
// prettier-ignore
|
||||
// #region snippet
|
||||
@@ -464,7 +460,7 @@ function discoverIcons(
|
||||
return icons.length > 0 ? Object.fromEntries(icons) : undefined;
|
||||
}
|
||||
|
||||
function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
|
||||
function addDevModeCsp(manifest: Browser.runtime.Manifest): void {
|
||||
const permission = `http://${wxt.server?.hostname ?? ''}/*`;
|
||||
const allowedCsp = wxt.server?.origin ?? 'http://localhost:*';
|
||||
|
||||
@@ -499,7 +495,7 @@ function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
|
||||
manifest.content_security_policy.sandbox = sandboxCsp.toString();
|
||||
}
|
||||
|
||||
function addDevModePermissions(manifest: Manifest.WebExtensionManifest) {
|
||||
function addDevModePermissions(manifest: Browser.runtime.Manifest) {
|
||||
// For reloading the page
|
||||
addPermission(manifest, 'tabs');
|
||||
|
||||
@@ -543,8 +539,7 @@ export function getContentScriptCssWebAccessibleResources(
|
||||
contentScripts: ContentScriptEntrypoint[],
|
||||
contentScriptCssMap: Record<string, string | undefined>,
|
||||
): any[] {
|
||||
const resources: Manifest.WebExtensionManifestWebAccessibleResourcesC2ItemType[] =
|
||||
[];
|
||||
const resources: ManifestV3WebAccessibleResource[] = [];
|
||||
|
||||
contentScripts.forEach((script) => {
|
||||
if (script.options.cssInjectionMode !== 'ui') return;
|
||||
@@ -554,9 +549,10 @@ export function getContentScriptCssWebAccessibleResources(
|
||||
|
||||
resources.push({
|
||||
resources: [cssFile],
|
||||
matches: script.options.matches?.map((matchPattern) =>
|
||||
stripPathFromMatchPattern(matchPattern),
|
||||
),
|
||||
matches:
|
||||
script.options.matches?.map((matchPattern) =>
|
||||
stripPathFromMatchPattern(matchPattern),
|
||||
) ?? [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -583,16 +579,18 @@ export function getContentScriptsCssMap(
|
||||
}
|
||||
|
||||
function addPermission(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
permission: string,
|
||||
): void {
|
||||
manifest.permissions ??= [];
|
||||
// @ts-expect-error: Allow using strings for permissions for MV2 support
|
||||
if (manifest.permissions.includes(permission)) return;
|
||||
// @ts-expect-error: Allow using strings for permissions for MV2 support
|
||||
manifest.permissions.push(permission);
|
||||
}
|
||||
|
||||
function addHostPermission(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
hostPermission: string,
|
||||
): void {
|
||||
manifest.host_permissions ??= [];
|
||||
@@ -618,7 +616,7 @@ export function stripPathFromMatchPattern(pattern: string) {
|
||||
* targeting MV2, automatically convert their definitions down to the basic MV2 array.
|
||||
*/
|
||||
export function convertWebAccessibleResourcesToMv2(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
): void {
|
||||
if (manifest.web_accessible_resources == null) return;
|
||||
|
||||
@@ -633,17 +631,17 @@ export function convertWebAccessibleResourcesToMv2(
|
||||
}
|
||||
|
||||
function moveHostPermissionsToPermissions(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
): void {
|
||||
if (!manifest.host_permissions?.length) return;
|
||||
|
||||
manifest.host_permissions.forEach((permission) =>
|
||||
manifest.host_permissions.forEach((permission: string) =>
|
||||
addPermission(manifest, permission),
|
||||
);
|
||||
delete manifest.host_permissions;
|
||||
}
|
||||
|
||||
function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void {
|
||||
function convertActionToMv2(manifest: Browser.runtime.Manifest): void {
|
||||
if (
|
||||
manifest.action == null ||
|
||||
manifest.browser_action != null ||
|
||||
@@ -654,7 +652,7 @@ function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void {
|
||||
manifest.browser_action = manifest.action;
|
||||
}
|
||||
|
||||
function convertCspToMv2(manifest: Manifest.WebExtensionManifest): void {
|
||||
function convertCspToMv2(manifest: Browser.runtime.Manifest): void {
|
||||
if (
|
||||
typeof manifest.content_security_policy === 'string' ||
|
||||
manifest.content_security_policy?.extension_pages == null
|
||||
@@ -668,8 +666,8 @@ function convertCspToMv2(manifest: Manifest.WebExtensionManifest): void {
|
||||
/**
|
||||
* Make sure all resources are in MV3 format. If not, add a wanring
|
||||
*/
|
||||
export function validateMv3WebAccessibleResources(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
function validateMv3WebAccessibleResources(
|
||||
manifest: Browser.runtime.Manifest,
|
||||
): void {
|
||||
if (manifest.web_accessible_resources == null) return;
|
||||
|
||||
@@ -688,7 +686,7 @@ export function validateMv3WebAccessibleResources(
|
||||
/**
|
||||
* Remove keys from the manifest based on the build target.
|
||||
*/
|
||||
function stripKeys(manifest: Manifest.WebExtensionManifest): void {
|
||||
function stripKeys(manifest: Browser.runtime.Manifest): void {
|
||||
let keysToRemove: string[] = [];
|
||||
if (wxt.config.manifestVersion === 2) {
|
||||
keysToRemove.push(...mv3OnlyKeys);
|
||||
@@ -699,7 +697,7 @@ function stripKeys(manifest: Manifest.WebExtensionManifest): void {
|
||||
}
|
||||
|
||||
keysToRemove.forEach((key) => {
|
||||
delete manifest[key as keyof Manifest.WebExtensionManifest];
|
||||
delete manifest[key as keyof Browser.runtime.Manifest];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import { resolve } from 'path';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import merge from 'lodash.merge';
|
||||
import type { Manifest } from 'wxt/browser';
|
||||
import {
|
||||
FsCache,
|
||||
ResolvedConfig,
|
||||
@@ -27,6 +26,7 @@ import {
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { vi } from 'vitest';
|
||||
import { setWxtForTesting } from '../../../core/wxt';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
faker.seed(import.meta.test.SEED);
|
||||
|
||||
@@ -208,13 +208,11 @@ export function fakeOutputFile(): OutputFile {
|
||||
return faker.helpers.arrayElement([fakeOutputAsset(), fakeOutputChunk()]);
|
||||
}
|
||||
|
||||
export const fakeManifest = fakeObjectCreator<Manifest.WebExtensionManifest>(
|
||||
() => ({
|
||||
manifest_version: faker.helpers.arrayElement([2, 3]),
|
||||
name: faker.string.alphanumeric(),
|
||||
version: `${faker.number.int()}.${faker.number.int()}.${faker.number.int()}`,
|
||||
}),
|
||||
);
|
||||
export const fakeManifest = fakeObjectCreator<Browser.runtime.Manifest>(() => ({
|
||||
manifest_version: faker.helpers.arrayElement([2, 3]),
|
||||
name: faker.string.alphanumeric(),
|
||||
version: `${faker.number.int()}.${faker.number.int()}.${faker.number.int()}`,
|
||||
}));
|
||||
|
||||
export const fakeUserManifest = fakeObjectCreator<UserManifest>(() => ({
|
||||
name: faker.string.alphanumeric(),
|
||||
@@ -299,7 +297,6 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
|
||||
transformManifest: () => {},
|
||||
userConfigMetadata: {},
|
||||
alias: {},
|
||||
extensionApi: 'webextension-polyfill',
|
||||
entrypointLoader: 'vite-node',
|
||||
experimental: {},
|
||||
dev: {
|
||||
@@ -356,8 +353,8 @@ export const fakeBuildStepOutput = fakeObjectCreator<BuildStepOutput>(() => ({
|
||||
entrypoints: fakeArray(fakeEntrypoint),
|
||||
}));
|
||||
|
||||
export const fakeManifestCommand =
|
||||
fakeObjectCreator<Manifest.WebExtensionManifestCommandsType>(() => ({
|
||||
export const fakeManifestCommand = fakeObjectCreator<Browser.commands.Command>(
|
||||
() => ({
|
||||
description: faker.string.sample(),
|
||||
suggested_key: {
|
||||
default: `${faker.helpers.arrayElement(['ctrl', 'alt'])}+${faker.number.int(
|
||||
@@ -367,7 +364,8 @@ export const fakeManifestCommand =
|
||||
},
|
||||
)}`,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
export const fakeDevServer = fakeObjectCreator<WxtDevServer>(() => ({
|
||||
hostname: 'localhost',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ProxifiedModule, parseModule } from 'magicast';
|
||||
* 1. Removes or clears out `main` function from returned object
|
||||
* 2. Removes any unused functions/variables outside the definition that aren't being called/used
|
||||
* 3. Removes unused imports
|
||||
* 3. Removes value-less, side-effect only imports (like `import "./styles.css"` or `import "webextension-polyfill"`)
|
||||
* 3. Removes value-less, side-effect only imports (like `import "./styles.css"` or `import "polyfill"`)
|
||||
*/
|
||||
export function removeMainFunctionCode(code: string): {
|
||||
code: string;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* Remove optional from key, but keep undefined if present
|
||||
*
|
||||
@@ -6,3 +8,11 @@
|
||||
* // type Test = {a: string | undefined, b: number}
|
||||
*/
|
||||
export type NullablyRequired<T> = { [K in keyof Required<T>]: T[K] };
|
||||
|
||||
export type ManifestContentScript = NonNullable<
|
||||
Browser.runtime.Manifest['content_scripts']
|
||||
>[number];
|
||||
|
||||
export type ManifestV3WebAccessibleResource = NonNullable<
|
||||
Browser.runtime.ManifestV3['web_accessible_resources']
|
||||
>[number];
|
||||
|
||||
+21
-31
@@ -1,5 +1,4 @@
|
||||
import type * as vite from 'vite';
|
||||
import type { Manifest, Scripting } from 'wxt/browser';
|
||||
import { UnimportOptions, Import } from 'unimport';
|
||||
import { LogLevel } from 'consola';
|
||||
import type { ContentScriptContext } from './client/content-scripts/content-script-context';
|
||||
@@ -7,6 +6,8 @@ import type { PluginVisualizerOptions } from '@aklinker1/rollup-plugin-visualize
|
||||
import { ResolvedConfig as C12ResolvedConfig } from 'c12';
|
||||
import { Hookable, NestedHooks } from 'hookable';
|
||||
import type * as Nypm from 'nypm';
|
||||
import { ManifestContentScript } from './core/utils/types';
|
||||
import type { Browser } from '@wxt-dev/browser';
|
||||
|
||||
export interface InlineConfig {
|
||||
/**
|
||||
@@ -267,7 +268,7 @@ export interface InlineConfig {
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
transformManifest?: (manifest: Manifest.WebExtensionManifest) => void;
|
||||
transformManifest?: (manifest: Browser.runtime.Manifest) => void;
|
||||
analysis?: {
|
||||
/**
|
||||
* Explicitly include bundle analysis when running `wxt build`. This can be overridden by the
|
||||
@@ -325,16 +326,6 @@ export interface InlineConfig {
|
||||
* }
|
||||
*/
|
||||
alias?: Record<string, string>;
|
||||
/**
|
||||
* Which extension API to use.
|
||||
*
|
||||
* - `"webextension-polyfill"`: Use `browser` and types from [`webextension-polyfill`](https://www.npmjs.com/package/webextension-polyfill).
|
||||
* - `"chrome"`: Use the regular `chrome` (or `browser` for Firefox/Safari) globals provided by the browser. Types provided by [`@types/chrome`](https://www.npmjs.com/package/@types/chrome).
|
||||
*
|
||||
* @default "webextension-polyfill"
|
||||
* @since 0.19.0
|
||||
*/
|
||||
extensionApi?: 'webextension-polyfill' | 'chrome';
|
||||
/**
|
||||
* @deprecated Will be removed in v0.20.0, please migrate to using `vite-node`, the new default.
|
||||
*
|
||||
@@ -444,7 +435,7 @@ export interface WxtHooks {
|
||||
}
|
||||
|
||||
export interface BuildOutput {
|
||||
manifest: Manifest.WebExtensionManifest;
|
||||
manifest: Browser.runtime.Manifest;
|
||||
publicAssets: OutputAsset[];
|
||||
steps: BuildStepOutput[];
|
||||
}
|
||||
@@ -537,7 +528,7 @@ export interface WxtDevServer
|
||||
|
||||
export interface ReloadContentScriptPayload {
|
||||
registration?: BaseContentScriptEntrypointOptions['registration'];
|
||||
contentScript: Omit<Scripting.RegisteredContentScript, 'id'>;
|
||||
contentScript: Omit<Browser.scripting.RegisteredContentScript, 'id'>;
|
||||
}
|
||||
|
||||
export type TargetBrowser = string;
|
||||
@@ -588,39 +579,39 @@ export interface BackgroundEntrypointOptions extends BaseEntrypointOptions {
|
||||
|
||||
export interface BaseContentScriptEntrypointOptions
|
||||
extends BaseEntrypointOptions {
|
||||
matches?: PerBrowserOption<Manifest.ContentScript['matches']>;
|
||||
matches?: PerBrowserOption<NonNullable<ManifestContentScript['matches']>>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default "documentIdle"
|
||||
*/
|
||||
runAt?: PerBrowserOption<Manifest.ContentScript['run_at']>;
|
||||
runAt?: PerBrowserOption<Browser.scripting.RegisteredContentScript['runAt']>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default false
|
||||
*/
|
||||
matchAboutBlank?: PerBrowserOption<
|
||||
Manifest.ContentScript['match_about_blank']
|
||||
ManifestContentScript['match_about_blank']
|
||||
>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default []
|
||||
*/
|
||||
excludeMatches?: PerBrowserOption<Manifest.ContentScript['exclude_matches']>;
|
||||
excludeMatches?: PerBrowserOption<ManifestContentScript['exclude_matches']>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default []
|
||||
*/
|
||||
includeGlobs?: PerBrowserOption<Manifest.ContentScript['include_globs']>;
|
||||
includeGlobs?: PerBrowserOption<ManifestContentScript['include_globs']>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default []
|
||||
*/
|
||||
excludeGlobs?: PerBrowserOption<Manifest.ContentScript['exclude_globs']>;
|
||||
excludeGlobs?: PerBrowserOption<ManifestContentScript['exclude_globs']>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default false
|
||||
*/
|
||||
allFrames?: PerBrowserOption<Manifest.ContentScript['all_frames']>;
|
||||
allFrames?: PerBrowserOption<ManifestContentScript['all_frames']>;
|
||||
/**
|
||||
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
* @default false
|
||||
@@ -868,7 +859,7 @@ export type ResolvedPerBrowserOptions<T, TOmitted extends keyof T = never> = {
|
||||
* here, they are configured inline.
|
||||
*/
|
||||
export type UserManifest = {
|
||||
[key in keyof chrome.runtime.ManifestV3 as key extends
|
||||
[key in keyof Browser.runtime.ManifestV3 as key extends
|
||||
| 'action'
|
||||
| 'background'
|
||||
| 'chrome_url_overrides'
|
||||
@@ -880,16 +871,16 @@ export type UserManifest = {
|
||||
| 'sandbox'
|
||||
| 'web_accessible_resources'
|
||||
? never
|
||||
: key]?: chrome.runtime.ManifestV3[key];
|
||||
: key]?: Browser.runtime.ManifestV3[key];
|
||||
} & {
|
||||
// Add any Browser-specific or MV2 properties that WXT supports here
|
||||
action?: chrome.runtime.ManifestV3['action'] & {
|
||||
action?: Browser.runtime.ManifestV3['action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
browser_action?: chrome.runtime.ManifestV2['browser_action'] & {
|
||||
browser_action?: Browser.runtime.ManifestV2['browser_action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
page_action?: chrome.runtime.ManifestV2['page_action'] & {
|
||||
page_action?: Browser.runtime.ManifestV2['page_action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
browser_specific_settings?: {
|
||||
@@ -909,12 +900,12 @@ export type UserManifest = {
|
||||
};
|
||||
};
|
||||
permissions?: (
|
||||
| chrome.runtime.ManifestPermissions
|
||||
| Browser.runtime.ManifestPermissions
|
||||
| (string & Record<never, never>)
|
||||
)[];
|
||||
web_accessible_resources?:
|
||||
| string[]
|
||||
| chrome.runtime.ManifestV3['web_accessible_resources'];
|
||||
| Browser.runtime.ManifestV3['web_accessible_resources'];
|
||||
};
|
||||
|
||||
export type UserManifestFn = (
|
||||
@@ -1187,7 +1178,7 @@ export interface WxtHooks {
|
||||
*/
|
||||
'build:manifestGenerated': (
|
||||
wxt: Wxt,
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
manifest: Browser.runtime.Manifest,
|
||||
) => HookResult;
|
||||
/**
|
||||
* Called once the names and paths of all entrypoints have been resolved.
|
||||
@@ -1359,7 +1350,7 @@ export interface ResolvedConfig {
|
||||
/**
|
||||
* @deprecated Use `build:manifestGenerated` hook instead.
|
||||
*/
|
||||
transformManifest?: (manifest: Manifest.WebExtensionManifest) => void;
|
||||
transformManifest?: (manifest: Browser.runtime.Manifest) => void;
|
||||
analysis: {
|
||||
enabled: boolean;
|
||||
open: boolean;
|
||||
@@ -1377,7 +1368,6 @@ export interface ResolvedConfig {
|
||||
* Import aliases to absolute paths.
|
||||
*/
|
||||
alias: Record<string, string>;
|
||||
extensionApi: 'webextension-polyfill' | 'chrome';
|
||||
entrypointLoader: 'vite-node' | 'jiti';
|
||||
experimental: {};
|
||||
dev: {
|
||||
|
||||
@@ -84,7 +84,7 @@ async function reloadTabsForContentScript(contentScript: ContentScript) {
|
||||
await Promise.all(
|
||||
matchingTabs.map(async (tab) => {
|
||||
try {
|
||||
await browser.tabs.reload(tab.id);
|
||||
await browser.tabs.reload(tab.id!);
|
||||
} catch (err) {
|
||||
logger.warn('Failed to reload tab:', err);
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ declare module 'virtual:user-unlisted-script-entrypoint' {
|
||||
}
|
||||
|
||||
declare module 'wxt/browser' {
|
||||
export const browser: import('webextension-polyfill').Browser;
|
||||
export { browser, Browser } from '@wxt-dev/browser';
|
||||
}
|
||||
|
||||
declare module 'virtual:wxt-plugins' {
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"src/client/index.ts",
|
||||
"src/testing/index.ts",
|
||||
"src/sandbox/index.ts",
|
||||
"src/browser/index.ts",
|
||||
"src/browser/chrome.ts",
|
||||
"src/browser.ts",
|
||||
"src/index.ts",
|
||||
"src/modules.ts",
|
||||
"src/storage.ts"
|
||||
|
||||
@@ -11,6 +11,7 @@ export default defineConfig({
|
||||
include: ['src/**'],
|
||||
exclude: ['**/dist', '**/__tests__', 'src/utils/testing'],
|
||||
},
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
globalSetup: ['./vitest.globalSetup.ts'],
|
||||
},
|
||||
server: {
|
||||
@@ -22,7 +23,6 @@ export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'wxt/testing': path.resolve('src/testing'),
|
||||
'webextension-polyfill': path.resolve('src/virtual/mock-browser'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { fakeBrowser } from '@webext-core/fake-browser';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
vi.stubGlobal('chrome', fakeBrowser);
|
||||
vi.stubGlobal('browser', fakeBrowser);
|
||||
Generated
+3
-25
@@ -57,9 +57,6 @@ catalogs:
|
||||
'@types/ua-parser-js':
|
||||
specifier: ^0.7.39
|
||||
version: 0.7.39
|
||||
'@types/webextension-polyfill':
|
||||
specifier: ^0.12.1
|
||||
version: 0.12.1
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.3.4
|
||||
version: 4.3.4
|
||||
@@ -297,9 +294,6 @@ catalogs:
|
||||
web-ext-run:
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1
|
||||
webextension-polyfill:
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0
|
||||
|
||||
patchedDependencies:
|
||||
markdown-it-footnote:
|
||||
@@ -698,12 +692,6 @@ importers:
|
||||
'@aklinker1/rollup-plugin-visualizer':
|
||||
specifier: 'catalog:'
|
||||
version: 5.12.0(rollup@4.34.9)
|
||||
'@types/chrome':
|
||||
specifier: 'catalog:'
|
||||
version: 0.0.280
|
||||
'@types/webextension-polyfill':
|
||||
specifier: 'catalog:'
|
||||
version: 0.12.1
|
||||
'@webext-core/fake-browser':
|
||||
specifier: 'catalog:'
|
||||
version: 1.3.1
|
||||
@@ -713,6 +701,9 @@ importers:
|
||||
'@webext-core/match-patterns':
|
||||
specifier: 'catalog:'
|
||||
version: 1.0.3
|
||||
'@wxt-dev/browser':
|
||||
specifier: workspace:*
|
||||
version: link:../browser
|
||||
'@wxt-dev/storage':
|
||||
specifier: workspace:^1.0.0
|
||||
version: link:../storage
|
||||
@@ -833,9 +824,6 @@ importers:
|
||||
web-ext-run:
|
||||
specifier: 'catalog:'
|
||||
version: 0.2.1
|
||||
webextension-polyfill:
|
||||
specifier: 'catalog:'
|
||||
version: 0.12.0
|
||||
devDependencies:
|
||||
'@aklinker1/check':
|
||||
specifier: 'catalog:'
|
||||
@@ -2324,9 +2312,6 @@ packages:
|
||||
'@types/web-bluetooth@0.0.20':
|
||||
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
|
||||
|
||||
'@types/webextension-polyfill@0.12.1':
|
||||
resolution: {integrity: sha512-xPTFWwQ8BxPevPF2IKsf4hpZNss4LxaOLZXypQH4E63BDLmcwX/RMGdI4tB4VO4Nb6xDBH3F/p4gz4wvof1o9w==}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
|
||||
@@ -5365,9 +5350,6 @@ packages:
|
||||
resolution: {integrity: sha512-5D11VcjdGkA1/xax5UWL0YeAbDySKHzWFe6EpsoPNUMw5Uk9tKk9p6GUOfcaI5N7sINKfBMZYNsTBiu5dzJB9A==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
|
||||
webextension-polyfill@0.12.0:
|
||||
resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==}
|
||||
|
||||
webidl-conversions@7.0.0:
|
||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -6664,8 +6646,6 @@ snapshots:
|
||||
|
||||
'@types/web-bluetooth@0.0.20': {}
|
||||
|
||||
'@types/webextension-polyfill@0.12.1': {}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 20.17.6
|
||||
@@ -10092,8 +10072,6 @@ snapshots:
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
webextension-polyfill@0.12.0: {}
|
||||
|
||||
webidl-conversions@7.0.0: {}
|
||||
|
||||
webpack-sources@3.2.3:
|
||||
|
||||
Vendored
-1
@@ -2,6 +2,5 @@ import { defineConfig } from 'wxt';
|
||||
|
||||
// See https://wxt.dev/api/config.html
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
modules: ['@wxt-dev/module-react'],
|
||||
});
|
||||
|
||||
Vendored
-1
@@ -2,6 +2,5 @@ import { defineConfig } from 'wxt';
|
||||
|
||||
// See https://wxt.dev/api/config.html
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
modules: ['@wxt-dev/module-solid'],
|
||||
});
|
||||
|
||||
Vendored
-1
@@ -3,6 +3,5 @@ import { defineConfig } from 'wxt';
|
||||
// See https://wxt.dev/api/config.html
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
extensionApi: 'chrome',
|
||||
modules: ['@wxt-dev/module-svelte'],
|
||||
});
|
||||
|
||||
Vendored
+1
-3
@@ -1,6 +1,4 @@
|
||||
import { defineConfig } from 'wxt';
|
||||
|
||||
// See https://wxt.dev/api/config.html
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
export default defineConfig({});
|
||||
|
||||
Vendored
-1
@@ -2,6 +2,5 @@ import { defineConfig } from 'wxt';
|
||||
|
||||
// See https://wxt.dev/api/config.html
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
modules: ['@wxt-dev/module-vue'],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user