diff --git a/docs/guide/essentials/wxt-modules.md b/docs/guide/essentials/wxt-modules.md index 9033df6a3..bc46561c3 100644 --- a/docs/guide/essentials/wxt-modules.md +++ b/docs/guide/essentials/wxt-modules.md @@ -167,6 +167,31 @@ This file could then be loaded at runtime: const res = await fetch(browser.runtime.getURL('/some-text.txt')); ``` +#### Add custom entrypoints + +Once the existing files under the `entrypoints/` directory have been discovered, the `entrypoints:found` hook can be used to add custom entrypoints. + +:::info +The `entrypoints:found` hook is triggered before validation is carried out on the list of entrypoints. Thus, any custom entrypoints will still be checked for duplicate names and logged during debugging. +::: + +```ts +import { defineWxtModule } from 'wxt/modules'; + +export default defineWxtModule({ + setup(wxt) { + wxt.hook('entrypoints:found', (_, entrypointInfos) => { + // Add your new entrypoint + entrypointInfos.push({ + name: 'my-custom-script', + inputPath: 'path/to/custom-script.js', + type: 'content-script', + }); + }); + }, +}); +``` + #### Generate runtime module Create a file in `.wxt`, add an alias to import it, and add auto-imports for exported variables. diff --git a/packages/wxt/e2e/tests/hooks.test.ts b/packages/wxt/e2e/tests/hooks.test.ts index c7f4a86a8..80071e395 100644 --- a/packages/wxt/e2e/tests/hooks.test.ts +++ b/packages/wxt/e2e/tests/hooks.test.ts @@ -11,6 +11,7 @@ const hooks: WxtHooks = { 'build:done': vi.fn(), 'build:manifestGenerated': vi.fn(), 'build:publicAssets': vi.fn(), + 'entrypoints:found': vi.fn(), 'entrypoints:resolved': vi.fn(), 'entrypoints:grouped': vi.fn(), 'vite:build:extendConfig': vi.fn(), @@ -60,6 +61,7 @@ describe('Hooks', () => { 'build:done': false, 'build:publicAssets': false, 'build:manifestGenerated': false, + 'entrypoints:found': true, 'entrypoints:grouped': false, 'entrypoints:resolved': true, 'vite:build:extendConfig': false, @@ -91,6 +93,7 @@ describe('Hooks', () => { 'build:done': true, 'build:publicAssets': true, 'build:manifestGenerated': true, + 'entrypoints:found': true, 'entrypoints:grouped': true, 'entrypoints:resolved': true, 'vite:build:extendConfig': 1, @@ -122,6 +125,7 @@ describe('Hooks', () => { 'build:done': true, 'build:publicAssets': true, 'build:manifestGenerated': true, + 'entrypoints:found': true, 'entrypoints:grouped': true, 'entrypoints:resolved': true, 'vite:build:extendConfig': 1, @@ -153,6 +157,7 @@ describe('Hooks', () => { 'build:done': true, 'build:publicAssets': true, 'build:manifestGenerated': true, + 'entrypoints:found': 2, 'entrypoints:grouped': true, 'entrypoints:resolved': 2, 'vite:build:extendConfig': 1, @@ -191,6 +196,7 @@ describe('Hooks', () => { 'build:done': true, 'build:publicAssets': true, 'build:manifestGenerated': true, + 'entrypoints:found': true, 'entrypoints:grouped': true, 'entrypoints:resolved': true, 'vite:build:extendConfig': 2, diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index 92c6fd404..10dbbb107 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -3,6 +3,7 @@ import { BackgroundEntrypoint, ContentScriptEntrypoint, Entrypoint, + EntrypointInfo, GenericEntrypoint, OptionsEntrypoint, PopupEntrypoint, @@ -67,6 +68,8 @@ export async function findEntrypoints(): Promise { return results; }, []); + await wxt.hooks.callHook('entrypoints:found', wxt, entrypointInfos); + // Validation preventNoEntrypoints(entrypointInfos); preventDuplicateEntrypointNames(entrypointInfos); @@ -137,6 +140,8 @@ export async function findEntrypoints(): Promise { skipped: isEntrypointSkipped(entry), })); + await wxt.hooks.callHook('entrypoints:resolved', wxt, entrypoints); + wxt.logger.debug('All entrypoints:', entrypoints); const skippedEntrypointNames = entrypoints .filter((item) => item.skipped) @@ -151,17 +156,10 @@ export async function findEntrypoints(): Promise { ].join('\n'), ); } - await wxt.hooks.callHook('entrypoints:resolved', wxt, entrypoints); return entrypoints; } -interface EntrypointInfo { - name: string; - inputPath: string; - type: Entrypoint['type']; -} - /** Returns a map of input paths to the file's options. */ async function importEntrypoints(infos: EntrypointInfo[]) { const resMap: Record | undefined> = {}; diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index beacfc267..3fbf39ba4 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -789,6 +789,13 @@ export type Entrypoint = | OptionsEntrypoint | SidepanelEntrypoint; +export interface EntrypointInfo { + name: string; + /** Absolute path to the entrypoint file. */ + inputPath: string; + type: Entrypoint['type']; +} + export type EntrypointGroup = Entrypoint | Entrypoint[]; export type OnContentScriptStopped = (cb: () => void) => void; @@ -1182,6 +1189,12 @@ export interface WxtHooks { wxt: Wxt, manifest: Manifest.WebExtensionManifest, ) => HookResult; + /** + * Called once the names and paths of all entrypoints have been resolved. + * @param wxt The configured WXT object + * @param infos List of entrypoints found in the project's `entrypoints` directory + */ + 'entrypoints:found': (wxt: Wxt, infos: EntrypointInfo[]) => HookResult; /** * Called once all entrypoints have been loaded from the `entrypointsDir`. * Use `wxt.builder.importEntrypoint` to load entrypoint options from the