From f19e6908b0984aa2d99cb5c270628997648fee98 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 3 Mar 2024 09:52:43 -0600 Subject: [PATCH] feat: Automatically generate `browser_action` based on `action` for MV2 (#519) --- docs/guide/manifest.md | 10 ++- src/core/utils/__tests__/manifest.test.ts | 78 +++++++++++++++++++++-- src/core/utils/manifest.ts | 19 +++++- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/docs/guide/manifest.md b/docs/guide/manifest.md index 64e6e20a..baa57835 100644 --- a/docs/guide/manifest.md +++ b/docs/guide/manifest.md @@ -137,6 +137,7 @@ WXT applies several transformations to your manifest to simplify managing both M 1. Top level MV2-only or MV3-only keys are stripped from the final manifest when targeting the other manifest version 2. Some keys, are automatically converted between versions when possible: - Define `web_accessible_resources` in it's MV3 style and it will be converted to the MV2 style automatically + - `action` will automatically be converted to `browser_action` for MV3. To use `page_action` instead, add both `action` and `page_action` entries to your manifest For example, a `wxt.config.ts` file that looks like this: @@ -146,10 +147,7 @@ import { defineConfig } from 'wxt'; export default defineConfig({ mainfest: { action: { - default_title: 'Some MV3 Title', - }, - browser_action: { - default_title: 'Some MV2 Title', + default_title: 'Some Title', }, web_accessible_resources: [ { @@ -170,7 +168,7 @@ Will be output differently for each manifest version: "manifest_version": 2, // ... "browser_action": { - "default_title": "Some MV2 Title" + "default_title": "Some Title" }, "web_accessible_resources": ["icon/*.png"] } @@ -181,7 +179,7 @@ Will be output differently for each manifest version: "manifest_version": 3, // ... "action": { - "default_title": "Some MV3 Title" + "default_title": "Some Title" }, "web_accessible_resources": [ { diff --git a/src/core/utils/__tests__/manifest.test.ts b/src/core/utils/__tests__/manifest.test.ts index dfa6eed0..7bdaad94 100644 --- a/src/core/utils/__tests__/manifest.test.ts +++ b/src/core/utils/__tests__/manifest.test.ts @@ -120,13 +120,83 @@ describe('Manifest Utils', () => { }, }, }); - const expected: Partial = { - action: wxt.config.manifest.action, - }; const { manifest: actual } = await generateManifest([], buildOutput); - expect(actual).toMatchObject(expected); + expect(actual.action).toEqual(wxt.config.manifest.action); + expect(actual.browser_action).toBeUndefined(); + expect(actual.page_action).toBeUndefined(); + }); + + it('should generate `browser_action` for MV2 when only `action` is defined', async () => { + const buildOutput = fakeBuildOutput(); + setFakeWxt({ + config: { + outDir, + manifestVersion: 2, + manifest: { + action: { + default_title: 'Action', + }, + }, + }, + }); + + const { manifest: actual } = await generateManifest([], buildOutput); + + expect(actual.action).toBeUndefined(); + expect(actual.browser_action).toEqual(wxt.config.manifest.action); + expect(actual.page_action).toBeUndefined(); + }); + + it('should keep the `page_action` for MV2 when both `action` and `page_action` are defined', async () => { + const buildOutput = fakeBuildOutput(); + setFakeWxt({ + config: { + outDir, + manifestVersion: 2, + manifest: { + action: { + default_title: 'Action', + }, + page_action: { + default_title: 'Page Action', + }, + }, + }, + }); + + const { manifest: actual } = await generateManifest([], buildOutput); + + expect(actual.action).toBeUndefined(); + expect(actual.browser_action).toBeUndefined(); + expect(actual.page_action).toEqual(wxt.config.manifest.page_action); + }); + + it('should keep the custom `browser_action` for MV2 when both `action` and `browser_action` are defined', async () => { + const buildOutput = fakeBuildOutput(); + setFakeWxt({ + config: { + outDir, + manifestVersion: 2, + manifest: { + action: { + default_title: 'Action', + }, + browser_action: { + default_title: 'Browser Action', + }, + }, + }, + }); + + const { manifest: actual } = await generateManifest([], buildOutput); + + expect(actual.action).toBeUndefined(); + expect(actual.browser_action).toEqual( + wxt.config.manifest.browser_action, + ); + expect(actual.page_action).toBeUndefined(); }); }); diff --git a/src/core/utils/manifest.ts b/src/core/utils/manifest.ts index f932a8ef..15fe3f28 100644 --- a/src/core/utils/manifest.ts +++ b/src/core/utils/manifest.ts @@ -110,19 +110,21 @@ export async function generateManifest( if (wxt.config.command === 'serve') addDevModeCsp(manifest); if (wxt.config.command === 'serve') addDevModePermissions(manifest); - stripKeys(manifest); - // TODO: Remove in v1 wxt.config.transformManifest(manifest); await wxt.hooks.callHook('build:manifestGenerated', wxt, manifest); - if (wxt.config.manifestVersion === 2) + if (wxt.config.manifestVersion === 2) { convertWebAccessibleResourcesToMv2(manifest); + convertActionToMv2(manifest); + } if (wxt.config.manifestVersion === 3) { validateMv3WebAccessbileResources(manifest); } + stripKeys(manifest); + if (manifest.name == null) throw Error( "Manifest 'name' is missing. Either:\n1. Set the name in your /package.json\n2. Set a name via the manifest option in your wxt.config.ts", @@ -615,6 +617,17 @@ export function convertWebAccessibleResourcesToMv2( ); } +function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void { + if ( + manifest.action == null || + manifest.browser_action != null || + manifest.page_action != null + ) + return; + + manifest.browser_action = manifest.action; +} + /** * Make sure all resources are in MV3 format. If not, add a wanring */