diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index 5775be31c..7c7f4c03f 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -107,6 +107,30 @@ describe('Manifest Utils', () => { }, ); + it('should allow converting action to page_action for Firefox MV3', async () => { + const popup = popupEntrypoint('page_action'); + const buildOutput = fakeBuildOutput(); + setFakeWxt({ + config: { + manifestVersion: 3, + outDir, + browser: 'firefox', + }, + }); + const expected = { + default_icon: popup.options.defaultIcon, + default_title: popup.options.defaultTitle, + default_popup: 'popup.html', + }; + + const { manifest: actual } = await generateManifest( + [popup], + buildOutput, + ); + + expect(actual.page_action).toEqual(expected); + }); + it('should include default_area for Firefox in mv3', async () => { const popup = fakePopupEntrypoint({ options: { @@ -1464,7 +1488,11 @@ describe('Manifest Utils', () => { ['chrome', 2, { ...mv2Manifest, ...hostPermissionsManifest }], ['safari', 2, { ...mv2Manifest, ...hostPermissionsManifest }], ['edge', 2, { ...mv2Manifest, ...hostPermissionsManifest }], - ['firefox', 3, { ...mv3Manifest, ...hostPermissionsManifest }], + [ + 'firefox', + 3, + { ...mv3Manifest, ...hostPermissionsManifest, page_action: {} }, + ], ['chrome', 3, { ...mv3Manifest, ...hostPermissionsManifest }], ['safari', 3, { ...mv3Manifest, ...hostPermissionsManifest }], ['edge', 3, { ...mv3Manifest, ...hostPermissionsManifest }], diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index a0e3b0592..e93498607 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -284,20 +284,19 @@ function addEntrypoints( if (popup.options.themeIcons) // @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox options.theme_icons = popup.options.themeIcons; - if (manifest.manifest_version === 3) { - manifest.action = { - ...manifest.action, - ...options, - default_popup, - }; - } else { - const key = popup.options.mv2Key ?? 'browser_action'; - manifest[key] = { - ...manifest[key], - ...options, - default_popup, - }; - } + + const actionKey = + manifest.manifest_version === 2 + ? (popup.options.mv2Key ?? 'browser_action') + : wxt.config.browser === 'firefox' + ? (popup.options.mv2Key ?? 'action') + : 'action'; + + manifest[actionKey] = { + ...manifest[actionKey], + ...options, + default_popup, + }; } if (devtools) { @@ -699,6 +698,8 @@ function stripKeys(manifest: Browser.runtime.Manifest): void { keysToRemove.push(...firefoxMv3OnlyKeys); } else { keysToRemove.push(...mv2OnlyKeys); + if (wxt.config.browser !== 'firefox') + keysToRemove.push(...chromeMv2OnlyKeys); } keysToRemove.forEach((key) => { @@ -707,7 +708,6 @@ function stripKeys(manifest: Browser.runtime.Manifest): void { } const mv2OnlyKeys = [ - 'page_action', 'browser_action', 'automation', 'content_capabilities', @@ -732,6 +732,7 @@ const mv3OnlyKeys = [ 'optional_host_permissions', 'side_panel', ]; +const chromeMv2OnlyKeys = ['page_action']; const firefoxMv3OnlyKeys = ['host_permissions']; const DEFAULT_MV3_EXTENSION_PAGES_CSP = diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 9a9723a3f..e903e90be 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -721,7 +721,13 @@ export interface ThemeIcon { } export interface PopupEntrypointOptions extends BaseEntrypointOptions { - /** Defaults to "browser_action" to be equivalent to MV3's "action" key */ + /** + * Defaults to "browser_action" to be equivalent to MV3's "action" key. + * + * This option name is bad - it is also respected for Firefox MV3 (though + * `browser_action` is converted to `action`) to allow using a `page_action` + * in MV3. + */ mv2Key?: PerBrowserOption<'browser_action' | 'page_action'>; defaultIcon?: Record; defaultTitle?: PerBrowserOption;