Skip to content

Commit 4c78167

Browse files
fix!: Make web-ext a peer dependency (#2079)
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
1 parent 2f921d0 commit 4c78167

File tree

20 files changed

+732
-241
lines changed

20 files changed

+732
-241
lines changed

docs/guide/essentials/config/browser-startup.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@ outline: deep
44

55
# Browser Startup
66

7-
> See the [API Reference](/api/reference/wxt/interfaces/WebExtConfig) for a full list of config.
7+
During development, WXT will use any of the below packages to automatically open a browser with your extension installed.
8+
9+
- [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext)
10+
11+
Just install the dependency you want WXT to use to open the browser.
812

9-
During development, WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
13+
## `web-ext` Usage
14+
15+
> See the [API Reference](/api/reference/wxt/interfaces/WebExtConfig) for a full list of config.
1016
11-
## Config Files
17+
### Config Files
1218

1319
You can configure browser startup in 3 places:
1420

@@ -25,9 +31,9 @@ You can configure browser startup in 3 places:
2531
2. `<rootDir>/wxt.config.ts`: Via the [`webExt` config](/api/reference/wxt/interfaces/InlineConfig#webext), included in version control
2632
3. `$HOME/web-ext.config.ts`: Provide default values for all WXT projects on your computer
2733

28-
## Recipes
34+
### Recipes
2935

30-
### Set Browser Binaries
36+
#### Set Browser Binaries
3137

3238
To set or customize the browser opened during development:
3339

@@ -57,7 +63,7 @@ export default defineConfig({
5763

5864
By default, WXT will try to automatically discover where Chrome/Firefox are installed. However, if you have chrome installed in a non-standard location, you need to set it manually as shown above.
5965

60-
### Persist Data
66+
#### Persist Data
6167

6268
By default, to keep from modifying your browser's existing profiles, `web-ext` creates a brand new profile every time you run the `dev` script.
6369

@@ -94,9 +100,9 @@ Now, next time you run the `dev` script, a persistent profile will be created in
94100
You can use any directory you'd like for `--user-data-dir`, the examples above create a persistent profile for each WXT project. To create a profile for all WXT projects, you can put the `chrome-data` directory inside your user's home directory.
95101
:::
96102

97-
### Disable Opening Browser
103+
#### Disable Opening Browser
98104

99-
If you prefer to load the extension into your browser manually, you can disable the auto-open behavior:
105+
If you don't want to uninstall `web-ext`, like to test in your normal profile, you can do so via `disabled: true`:
100106

101107
```ts [web-ext.config.ts]
102108
import { defineWebExtConfig } from 'wxt';

docs/guide/resources/upgrading.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ Listed below are all the breaking changes you should address when upgrading to a
3535

3636
Currently, WXT is in pre-release. This means changes to the second digit, `v0.X`, are considered major and have breaking changes. Once v1 is released, only major version bumps will have breaking changes.
3737

38+
## v0.20.0 &rarr; vX.Y.Z
39+
40+
### Dev Browser Startup
41+
42+
The package used to open the browser on startup has changed. Previously, WXT used `web-ext-run` as a direct dependency, but now we use `web-ext` as a peer dependency.
43+
44+
:::details
45+
46+
`web-ext-run` was a light-weight fork of `web-ext`, but it was difficult to maintain and quickly got out-of-date compared to `web-ext`.
47+
48+
:::
49+
50+
In v0.20, how automatic startup is enabled/disabled has changed:
51+
52+
- To continue opening the browser automatically, add `web-ext` as a dependency. No changes are required in your `web-ext.config.ts` files.
53+
54+
```sh
55+
pnpm add -D web-ext
56+
```
57+
58+
- To disable the browser automatically, **_DON'T_** add `web-ext` as a dependency. Additionally, you can remove any `web-ext.config.ts` files since they're not used if `web-ext` isn't installed:
59+
60+
```sh
61+
rm web-ext.config.ts
62+
63+
# Keep the config in your home dir until all your projects have been upgraded
64+
rm ~/web-ext.config.ts
65+
```
66+
67+
## New Deprecations in v0.20
68+
69+
Deprecated APIs will be removed in the next major release.
70+
71+
- `wxt.config.runnerConfig` renamed to `wxt.config.webExt`.
72+
3873
## v0.19.0 &rarr; v0.20.0
3974

4075
v0.20 is a big release! There are lots of breaking changes because this version is intended to be a release candidate for v1.0. If all goes well, v1.0 will be released with no additional breaking changes.

packages/wxt/e2e/tests/dev.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ describe('Dev Mode', () => {
1010
);
1111

1212
const server = await project.startServer({
13-
runner: {
13+
webExt: {
1414
disabled: true,
1515
},
1616
});
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { expect, describe, vi, it, beforeEach } from 'vitest';
2+
import { ExtensionRunner } from '../../src';
3+
import { createSafariRunner } from '../../src/core/runners/safari';
4+
import { createManualRunner } from '../../src/core/runners/manual';
5+
import { createWebExtRunner } from '../../src/core/runners/web-ext';
6+
import { createWslRunner } from '../../src/core/runners/wsl';
7+
import { TestProject } from '../utils';
8+
import { wxt } from '../../src/core/wxt';
9+
10+
// Globals for modifying mock behaviors
11+
12+
let isWsl = false;
13+
let importWebExtRunnerError: Error | undefined = undefined;
14+
15+
// Mock runners to create constants for checking equality
16+
17+
type TestExtensionRunner = { name: string } & ExtensionRunner;
18+
19+
function createMockExtensionRunner(name: string): TestExtensionRunner {
20+
return {
21+
name,
22+
closeBrowser: () => Promise.resolve(),
23+
openBrowser: () => Promise.resolve(),
24+
};
25+
}
26+
27+
vi.mock('../../src/core/runners/safari', () => {
28+
const runner = createMockExtensionRunner('safari');
29+
return { createSafariRunner: () => runner };
30+
});
31+
const safariRunner = createSafariRunner();
32+
33+
vi.mock('../../src/core/runners/manual', () => {
34+
const runner = createMockExtensionRunner('manual');
35+
return { createManualRunner: () => runner };
36+
});
37+
const manualRunner = createManualRunner();
38+
39+
vi.mock('../../src/core/runners/web-ext', () => {
40+
const runner = createMockExtensionRunner('web-ext');
41+
return {
42+
createWebExtRunner: () => {
43+
if (!importWebExtRunnerError) return runner;
44+
else throw importWebExtRunnerError;
45+
},
46+
};
47+
});
48+
const webExtRunner = createWebExtRunner();
49+
50+
vi.mock('../../src/core/runners/wsl', () => {
51+
const runner = createMockExtensionRunner('wsl');
52+
return { createWslRunner: () => runner };
53+
});
54+
const wslRunner = createWslRunner();
55+
56+
// Other mocks
57+
58+
vi.mock('is-wsl', () => ({
59+
get default() {
60+
return isWsl;
61+
},
62+
}));
63+
64+
/**
65+
* Imitate a real module not found error - needs the correct `code` property.
66+
*/
67+
class ModuleNotFoundError extends Error {
68+
code = 'ERR_MODULE_NOT_FOUND';
69+
70+
constructor(mod: string) {
71+
super(`Cannot find package '${mod}' imported from ...`);
72+
this.name = 'ModuleNotFoundError';
73+
}
74+
}
75+
76+
describe('Runners', () => {
77+
beforeEach(() => {
78+
isWsl = false;
79+
importWebExtRunnerError = undefined;
80+
});
81+
82+
describe('build', () => {
83+
const command = 'build';
84+
85+
it('should use the manual runner as a placeholder since the runner is not used during builds', async () => {
86+
await TestProject.simple().registerWxt(command);
87+
88+
expect(wxt.config.runner).toBe(manualRunner);
89+
});
90+
});
91+
92+
describe('dev', () => {
93+
const command = 'serve';
94+
95+
describe('inside WSL', () => {
96+
beforeEach(() => {
97+
isWsl = true;
98+
});
99+
100+
it('should use the WSL runner', async () => {
101+
await TestProject.simple().registerWxt(command);
102+
103+
expect(wxt.config.runner).toBe(wslRunner);
104+
});
105+
});
106+
107+
describe('web-ext is installed', () => {
108+
it('should use the web-ext runner', async () => {
109+
await TestProject.simple().registerWxt(command);
110+
111+
expect(wxt.config.runner).toBe(webExtRunner);
112+
});
113+
114+
describe('disabled', () => {
115+
it('should use the manual runner', async () => {
116+
await TestProject.simple().registerWxt(command, {
117+
webExt: { disabled: true },
118+
});
119+
120+
expect(wxt.config.runner).toBe(manualRunner);
121+
});
122+
});
123+
});
124+
125+
describe('web-ext is not installed', () => {
126+
beforeEach(() => {
127+
importWebExtRunnerError = new ModuleNotFoundError('web-ext');
128+
});
129+
130+
it('should use the manual runner', async () => {
131+
await TestProject.simple().registerWxt(command);
132+
133+
expect(wxt.config.runner).toBe(manualRunner);
134+
});
135+
});
136+
137+
describe('some other error when importing the web-ext runner', () => {
138+
beforeEach(() => {
139+
importWebExtRunnerError = Error('test');
140+
});
141+
142+
it('should throw the error', async () => {
143+
await expect(TestProject.simple().registerWxt(command)).rejects.toThrow(
144+
importWebExtRunnerError,
145+
);
146+
});
147+
});
148+
149+
describe('targeting safari', () => {
150+
it('should use the safari runner', async () => {
151+
await TestProject.simple().registerWxt(command, {
152+
browser: 'safari',
153+
});
154+
155+
expect(wxt.config.runner).toBe(safariRunner);
156+
});
157+
});
158+
});
159+
});

packages/wxt/e2e/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import { dirname, relative, resolve } from 'path';
66
import {
77
InlineConfig,
88
UserConfig,
9+
WxtCommand,
910
build,
1011
createServer,
1112
prepare,
1213
zip,
1314
} from '../src';
1415
import { normalizePath } from '../src/core/utils';
16+
import merge from 'lodash.merge';
17+
import { registerWxt } from '../src/core/wxt';
1518

1619
// Run "pnpm wxt" to use the "wxt" dev script, not the "wxt" binary from the
1720
// wxt package. This uses the TS files instead of the compiled JS package
@@ -21,6 +24,18 @@ export const WXT_PACKAGE_DIR = resolve(__dirname, '..');
2124
export const E2E_DIR = resolve(WXT_PACKAGE_DIR, 'e2e');
2225

2326
export class TestProject {
27+
/**
28+
* Create the simplest WXT project possible: one blank popup entrypoint, no
29+
* custom config.
30+
*/
31+
static simple(): TestProject {
32+
const project = new TestProject();
33+
34+
project.addFile('entrypoints/popup.html', '<html></html>');
35+
36+
return project;
37+
}
38+
2439
files: Array<[string, string]> = [];
2540
config: UserConfig | undefined;
2641
readonly root: string;
@@ -87,6 +102,15 @@ export class TestProject {
87102
return this.resolvePath(filename);
88103
}
89104

105+
/**
106+
* Register the global `wxt` object for this project. After calling, you can
107+
* import `wxt` like normal and inspect it.
108+
*/
109+
async registerWxt(command: WxtCommand, config: InlineConfig = {}) {
110+
await this.writeProjectToDisk();
111+
await registerWxt(command, { ...config, root: this.root });
112+
}
113+
90114
async prepare(config: InlineConfig = {}) {
91115
await this.writeProjectToDisk();
92116
await prepare({ ...config, root: this.root });

packages/wxt/package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,19 @@
5757
"prompts": "^2.4.2",
5858
"publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.0",
5959
"scule": "^1.3.0",
60-
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0",
61-
"web-ext-run": "^0.2.4"
60+
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0"
6261
},
6362
"peerDependencies": {
6463
"eslint": "^8.57.0 || ^9.0.0",
65-
"vite": "^6.3.4 || ^7.0.0"
64+
"vite": "^6.3.4 || ^7.0.0",
65+
"web-ext": ">=9.2.0"
6666
},
6767
"peerDependenciesMeta": {
6868
"eslint": {
6969
"optional": true
70+
},
71+
"web-ext": {
72+
"optional": true
7073
}
7174
},
7275
"devDependencies": {
@@ -85,7 +88,8 @@
8588
"typescript": "^5.9.3",
8689
"vite": "^7.3.1",
8790
"vitest": "^4.0.18",
88-
"vitest-plugin-random-seed": "^1.1.2"
91+
"vitest-plugin-random-seed": "^1.1.2",
92+
"web-ext": "^9.2.0"
8993
},
9094
"repository": {
9195
"type": "git",

packages/wxt/src/@types/modules.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Custom TS definitions for non-TS packages
22

3-
declare module 'web-ext-run' {
3+
declare module 'web-ext' {
44
export interface WebExtRunInstance {
55
reloadAllExtensions(): Promise<void>;
66
exit(): Promise<void>;
@@ -14,7 +14,7 @@ declare module 'web-ext-run' {
1414
export default webExt;
1515
}
1616

17-
declare module 'web-ext-run/util/logger' {
17+
declare module 'web-ext/util/logger' {
1818
// https://github.com/mozilla/web-ext/blob/e37e60a2738478f512f1255c537133321f301771/src/util/logger.js#L43
1919
export interface IConsoleStream {
2020
stopCapturing(): void;

0 commit comments

Comments
 (0)