diff --git a/packages/wxt/e2e/tests/hooks.test.ts b/packages/wxt/e2e/tests/hooks.test.ts index 0f9babc80..42665c238 100644 --- a/packages/wxt/e2e/tests/hooks.test.ts +++ b/packages/wxt/e2e/tests/hooks.test.ts @@ -21,6 +21,9 @@ const hooks: WxtHooks = { 'zip:sources:start': vi.fn(), 'zip:sources:done': vi.fn(), 'zip:done': vi.fn(), + 'server:created': vi.fn(), + 'server:started': vi.fn(), + 'server:closed': vi.fn(), }; function expectHooksToBeCalled( @@ -67,6 +70,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -95,6 +101,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -123,6 +132,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': true, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -151,6 +163,9 @@ describe('Hooks', () => { 'zip:sources:start': true, 'zip:sources:done': true, 'zip:done': true, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -164,6 +179,7 @@ describe('Hooks', () => { disabled: true, }, }); + expect(hooks['server:closed']).not.toBeCalled(); await server.stop(); expectHooksToBeCalled({ @@ -185,6 +201,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': 1, + 'server:started': 1, + 'server:closed': 1, }); }); }); diff --git a/packages/wxt/e2e/tests/modules.test.ts b/packages/wxt/e2e/tests/modules.test.ts index 367099b52..11d866884 100644 --- a/packages/wxt/e2e/tests/modules.test.ts +++ b/packages/wxt/e2e/tests/modules.test.ts @@ -6,7 +6,7 @@ import { normalizePath } from '../../src/core/utils/paths'; describe('Module Helpers', () => { describe('options', () => { - it('should recieve the options defined in wxt.config.ts based on the configKey field', async () => { + it('should receive the options defined in wxt.config.ts based on the configKey field', async () => { const options = { key: '123' }; const reportOptions = vi.fn(); vi.stubGlobal('reportOptions', reportOptions); diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index 0a04eb39c..c0b64339a 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -42,7 +42,9 @@ export async function createServer( ): Promise { await registerWxt('serve', inlineConfig); - return (wxt.server = await createServerInternal()); + wxt.server = await createServerInternal(); + await wxt.hooks.callHook('server:created', wxt, wxt.server); + return wxt.server; } async function createServerInternal(): Promise { @@ -92,6 +94,8 @@ async function createServerInternal(): Promise { await builderServer.listen(); wxt.logger.success(`Started dev server @ ${server.origin}`); + await wxt.hooks.callHook('server:started', wxt, server); + await buildAndOpenBrowser(); // Register content scripts for the first time after the background starts up since they're not @@ -109,6 +113,8 @@ async function createServerInternal(): Promise { wasStopped = true; await runner.closeBrowser(); await builderServer.close(); + await wxt.hooks.callHook('server:closed', wxt, server); + deinitWxtModules(); server.currentOutput = undefined; }, diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index f60d18eec..da818b570 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1100,7 +1100,8 @@ export type HookResult = Promise | void; export interface WxtHooks { /** - * Called only one time after WXT initialization, when the WXT instance is ready to work. + * Called after WXT modules are initialized, when the WXT instance is ready to + * be used. `wxt.server` isn't available yet, use `server:created` to get it. * @param wxt The configured WXT object */ ready: (wxt: Wxt) => HookResult; @@ -1196,39 +1197,52 @@ export interface WxtHooks { * @param wxt The configured WXT object */ 'zip:start': (wxt: Wxt) => HookResult; - /** * Called before zipping the extension files. * @param wxt The configured WXT object */ 'zip:extension:start': (wxt: Wxt) => HookResult; - /** * Called after zipping the extension files. * @param wxt The configured WXT object * @param zipPath The path to the created extension zip file */ 'zip:extension:done': (wxt: Wxt, zipPath: string) => HookResult; - /** * Called before zipping the source files (for Firefox). * @param wxt The configured WXT object */ 'zip:sources:start': (wxt: Wxt) => HookResult; - /** * Called after zipping the source files (for Firefox). * @param wxt The configured WXT object * @param zipPath The path to the created sources zip file */ 'zip:sources:done': (wxt: Wxt, zipPath: string) => HookResult; - /** * Called after the entire zip process is complete. * @param wxt The configured WXT object * @param zipFiles An array of paths to all created zip files */ 'zip:done': (wxt: Wxt, zipFiles: string[]) => HookResult; + /** + * Called when the dev server is created (and `wxt.server` is assigned). Server has not been started yet. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:created': (wxt: Wxt, server: WxtDevServer) => HookResult; + /** + * Called when the dev server is started. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:started': (wxt: Wxt, server: WxtDevServer) => HookResult; + /** + * Called when the dev server is stopped. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:closed': (wxt: Wxt, server: WxtDevServer) => HookResult; } export interface Wxt {