diff --git a/package.json b/package.json index b90d5ff..8cbb4e5 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,11 @@ "private": true, "scripts": { "start": "vite build -m development --watch", + "start:chromium": "VITE_BROWSER=chromium pnpm start", "clean": "trash build web-ext-artifacts", - "build": "pnpm clean && vite build && web-ext build --source-dir build && pnpm zip-source", + "build": "pnpm clean && pnpm build:chromium && pnpm build:firefox && pnpm zip-source", + "build:chromium": "VITE_BROWSER=chromium vite build && web-ext build -n web-ext-template-chromium-{version}.zip -s build/chromium", + "build:firefox": "VITE_BROWSER=firefox vite build && web-ext build -n web-ext-template-firefox-{version}.zip -s build/firefox", "zip-source": "git archive --format zip --output web-ext-artifacts/webextension_template-source.zip HEAD", "test": "xo && stylelint 'source/**/*.scss' && tsc" }, diff --git a/source/background-scripts/initialize.ts b/source/background-scripts/initialize.ts index bd9acd5..65c7abf 100644 --- a/source/background-scripts/initialize.ts +++ b/source/background-scripts/initialize.ts @@ -1,8 +1,14 @@ import browser from 'webextension-polyfill'; -browser.browserAction.onClicked.addListener(async () => { +async function browserActionClicked() { await browser.runtime.openOptionsPage(); -}); +} + +if (import.meta.env.VITE_BROWSER === 'chromium') { + browser.action.onClicked.addListener(browserActionClicked); +} else { + browser.browserAction.onClicked.addListener(browserActionClicked); +} browser.runtime.onInstalled.addListener(async () => { console.debug('WebExtension Template has been installed!'); diff --git a/source/manifest.json b/source/manifest.json deleted file mode 100644 index 6da6449..0000000 --- a/source/manifest.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/webextension", - "manifest_version": 2, - "name": "WebExtension Template", - "description": "An opinionated WebExtension template.", - "version": "0.1.0", - "permissions": [ - "downloads", - "storage", - "*://example.org/*" - ], - "content_security_policy": "script-src 'self'; object-src 'self'; style-src 'unsafe-inline'", - "web_accessible_resources": [ - "assets/**" - ], - "icons": { - "128": "assets/web-ext-template-logo.png" - }, - "browser_action": { - "default_icon": { - "128": "assets/web-ext-template-logo.png" - } - }, - "options_ui": { - "page": "options/index.html", - "open_in_tab": true - }, - "background": { - "scripts": [ - "background-scripts/initialize.ts" - ] - }, - "content_scripts": [ - { - "matches": [ - "*://example.org/*" - ], - "run_at": "document_end", - "css": [ - "content-scripts/content-styles.scss" - ], - "js": [ - "content-scripts/initialize.ts" - ] - } - ] -} diff --git a/source/manifest.ts b/source/manifest.ts new file mode 100644 index 0000000..ddb722c --- /dev/null +++ b/source/manifest.ts @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + +export default function createManifest( + target: string, +): Record { + const manifest: Record = { + name: 'WebExtension Template', + description: 'An opinionated WebExtension template.', + version: '0.1.0', + permissions: ['downloads', 'storage'], + options_ui: { + page: 'options/index.html', + open_in_tab: true, + }, + content_scripts: [ + { + matches: ['*://example.org/*'], + run_at: 'document_end', + css: ['content-scripts/content-styles.scss'], + js: ['content-scripts/initialize.ts'], + }, + ], + }; + + const icons = { + 128: 'assets/web-ext-template-logo.png', + }; + + manifest.icons = icons; + + const browserAction = { + default_icon: icons, + }; + + const backgroundScript = 'background-scripts/initialize.ts'; + + if (target === 'chromium') { + manifest.manifest_version = 3; + manifest.action = browserAction; + manifest.background = { + service_worker: backgroundScript, + type: 'module', + }; + manifest.host_permissions = ['*://example.org/*']; + } else { + manifest.manifest_version = 2; + manifest.browser_action = browserAction; + manifest.background = { + scripts: [backgroundScript], + }; + (manifest.permissions as string[]).push('*://example.org/*'); + } + + return manifest; +} diff --git a/source/types.d.ts b/source/types.d.ts index 67e9051..17d41e9 100644 --- a/source/types.d.ts +++ b/source/types.d.ts @@ -13,6 +13,7 @@ declare global { readonly DEV: boolean; readonly MODE: string; readonly PROD: boolean; + readonly VITE_BROWSER: 'chromium' | 'firefox'; } type HtmComponent = ReturnType; diff --git a/vite.config.ts b/vite.config.ts index 75cd156..f6cec9d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; +import process from 'node:process'; import url from 'node:url'; import {defineConfig} from 'vite'; @@ -8,19 +9,38 @@ import {defineConfig} from 'vite'; import preactPreset from '@preact/preset-vite'; import webExtension from 'vite-plugin-web-extension'; -const currentDir = path.dirname(url.fileURLToPath(import.meta.url)); +import createManifest from './source/manifest.js'; -const buildDir = path.join(currentDir, 'build'); +const targetBrowser = process.env.VITE_BROWSER ?? 'firefox'; +process.env.VITE_BROWSER = targetBrowser; + +const currentDir = path.dirname(url.fileURLToPath(import.meta.url)); +const buildDir = path.join(currentDir, 'build', targetBrowser); const sourceDir = path.join(currentDir, 'source'); -// Create the Firefox profile if it doesn't already exist. -fs.mkdirSync(path.join(currentDir, 'firefox'), {recursive: true}); +// Create the browser profile if it doesn't already exist. +fs.mkdirSync(path.join(currentDir, targetBrowser), {recursive: true}); + +const webExtConfig: Record = { + browserConsole: true, + chromiumProfile: 'chromium/', + firefoxProfile: 'firefox/', + keepProfileChanges: true, +}; + +if (targetBrowser === 'chromium') { + webExtConfig.startUrl = 'chrome://extensions/'; + webExtConfig.target = 'chromium'; +} else { + webExtConfig.startUrl = 'about:debugging#/runtime/this-firefox'; + webExtConfig.target = 'firefox-desktop'; +} export default defineConfig({ build: { - minify: false, outDir: buildDir, - sourcemap: true, + minify: false, + sourcemap: 'inline', }, plugins: [ preactPreset(), @@ -28,15 +48,9 @@ export default defineConfig({ // https://github.com/aklinker1/vite-plugin-web-extension webExtension({ assets: 'assets', - browser: 'firefox', - manifest: path.join(sourceDir, 'manifest.json'), - webExtConfig: { - browserConsole: true, - firefoxProfile: 'firefox/', - keepProfileChanges: true, - startUrl: 'about:debugging#/runtime/this-firefox', - target: 'firefox-desktop', - }, + browser: targetBrowser, + manifest: () => createManifest(targetBrowser), + webExtConfig, }), ], root: sourceDir,