From 8dfedf2a5aa6fcc82960c87e3446fd1f39426f67 Mon Sep 17 00:00:00 2001 From: T <6601329+cookesan@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:32:46 -0300 Subject: [PATCH] feat: add watch options config (#2439) --- docs/guide/resources/faq.md | 12 ++++++ packages/wxt/e2e/tests/user-config.test.ts | 39 ++++++++++++++++++- packages/wxt/src/core/builders/vite/index.ts | 7 +++- packages/wxt/src/core/create-server.ts | 5 ++- packages/wxt/src/core/resolve-config.ts | 1 + .../src/core/utils/testing/fake-objects.ts | 1 + packages/wxt/src/types.ts | 16 ++++++++ 7 files changed, 78 insertions(+), 3 deletions(-) diff --git a/docs/guide/resources/faq.md b/docs/guide/resources/faq.md index d0bdc452..1de176ee 100644 --- a/docs/guide/resources/faq.md +++ b/docs/guide/resources/faq.md @@ -221,6 +221,18 @@ To run the WXT dev server in a devcontainer, but load the dev build of your exte 3. **Tell WXT to listen on all network interfaces** To enable hot-reloading, your extension has to connect to the WXT dev server running inside your container. WXT will only listen on `localhost` by default, which prevents connections from outside the devcontainer. To fix this you can instruct WXT to listen on all interfaces with `wxt --host 0.0.0.0`. +4. **Enable polling if file changes are not detected** + Some container, WSL, and network file system setups do not emit native file events reliably. Configure Chokidar polling in `wxt.config.ts`: + + ```ts + export default defineConfig({ + watchOptions: { + usePolling: true, + interval: 1000, + }, + }); + ``` + ## How do I use the new Prompt API in Chrome? The service responsible for the [Prompt API](https://developer.chrome.com/docs/ai/prompt-api) is not enabled by default if you let WXT open the browser during dev mode. When checking `LanguageModel.availability`, you will always receive "unavailable". diff --git a/packages/wxt/e2e/tests/user-config.test.ts b/packages/wxt/e2e/tests/user-config.test.ts index 9677a7a9..773bafcd 100644 --- a/packages/wxt/e2e/tests/user-config.test.ts +++ b/packages/wxt/e2e/tests/user-config.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; import { TestProject } from '../utils'; describe('User Config', () => { @@ -150,4 +150,41 @@ describe('User Config', () => { await project.build({ configFile: 'foo.config.ts' }); }); + + it('should pass watch options to the dev server', async () => { + const project = new TestProject(); + const extendViteDevServerConfig = vi.fn(); + project.addFile('entrypoints/popup.html', ''); + + const server = await project.startServer({ + watchOptions: { + usePolling: true, + interval: 1000, + ignored: ['**/generated/**'], + }, + hooks: { + 'vite:devServer:extendConfig': extendViteDevServerConfig, + }, + webExt: { + disabled: true, + }, + }); + + try { + const viteConfig = extendViteDevServerConfig.mock.calls[0]?.[0]; + expect(viteConfig?.server?.watch).toMatchObject({ + usePolling: true, + interval: 1000, + }); + expect(viteConfig?.server?.watch?.ignored).toEqual( + expect.arrayContaining([ + expect.stringContaining('.output/**'), + expect.stringContaining('.wxt/**'), + '**/generated/**', + ]), + ); + } finally { + await server.stop(); + } + }); }); diff --git a/packages/wxt/src/core/builders/vite/index.ts b/packages/wxt/src/core/builders/vite/index.ts index 24247d74..aab18cd0 100644 --- a/packages/wxt/src/core/builders/vite/index.ts +++ b/packages/wxt/src/core/builders/vite/index.ts @@ -72,7 +72,12 @@ export async function createViteBuilder( config.server ??= {}; config.server.watch = { - ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`], + ...wxtConfig.watchOptions, + ignored: [ + `${wxtConfig.outBaseDir}/**`, + `${wxtConfig.wxtDir}/**`, + ...toArray(wxtConfig.watchOptions.ignored ?? []), + ], }; // TODO: Remove once https://github.com/wxt-dev/wxt/pull/1411 is merged diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index da748345..071c2a59 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -152,7 +152,10 @@ async function createServerInternal(): Promise { logBabelSyntaxError(err); wxt.logger.info('Waiting for syntax error to be fixed...'); await new Promise((resolve) => { - const watcher = chokidar.watch(err.id, { ignoreInitial: true }); + const watcher = chokidar.watch(err.id, { + ...wxt.config.watchOptions, + ignoreInitial: true, + }); watcher.on('all', () => { watcher.close(); wxt.logger.info('Syntax error resolved, rebuilding...'); diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index 9e7903a7..82bc342b 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -235,6 +235,7 @@ export async function resolveConfig( alias, experimental: defu(mergedConfig.experimental, {}), suppressWarnings: mergedConfig.suppressWarnings ?? {}, + watchOptions: mergedConfig.watchOptions ?? {}, dev: { server: devServerConfig, reloadCommand, diff --git a/packages/wxt/src/core/utils/testing/fake-objects.ts b/packages/wxt/src/core/utils/testing/fake-objects.ts index 9a97e666..2b3835d3 100644 --- a/packages/wxt/src/core/utils/testing/fake-objects.ts +++ b/packages/wxt/src/core/utils/testing/fake-objects.ts @@ -296,6 +296,7 @@ export const fakeResolvedConfig = fakeObjectCreator(() => { userConfigMetadata: {}, alias: {}, experimental: {}, + watchOptions: {}, dev: { reloadCommand: 'Alt+R', }, diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 5ebee7c7..09f3bf3c 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -131,6 +131,20 @@ export interface InlineConfig { * line `--mv2` or `--mv3` option. */ manifestVersion?: TargetManifestVersion; + /** + * Chokidar options used by dev-mode file watchers. This is useful in + * containers, WSL, and network file systems where native file events can be + * unreliable. + * + * @example + * export default defineConfig({ + * watchOptions: { + * usePolling: true, + * interval: 1000, + * }, + * }); + */ + watchOptions?: vite.WatchOptions; /** * Override the logger used. * @@ -1555,6 +1569,8 @@ export interface ResolvedConfig { firefoxDataCollection?: boolean; firefoxId?: boolean; }; + /** Chokidar options used by dev-mode file watchers. */ + watchOptions: vite.WatchOptions; dev: { /** Only defined during dev command */ server?: {