diff --git a/apps/extension/scripts/build.js b/apps/extension/scripts/build.js index 3341420..c5c0c99 100644 --- a/apps/extension/scripts/build.js +++ b/apps/extension/scripts/build.js @@ -66,9 +66,12 @@ function buildFilters() { function buildVite() { console.log('📦 Building with Vite...'); + // Vite writes to dist/; pass it explicitly so an ambient + // BROWSER env var can never redirect the output (see vite.config.js). execSync('pnpm vite build', { cwd: ROOT_DIR, stdio: 'inherit', + env: { ...process.env, EXT_BROWSER: 'chrome' }, }); } @@ -115,6 +118,33 @@ function copyDirectorySync(src, dest) { } } +/** + * Read a source manifest and prepare it for distribution. + * + * `http://localhost:*` host permissions are needed when running against a local + * web app, but shipping them is a standing "unnecessary permissions" rejection + * risk on the Chrome Web Store and AMO. Keep them in the source manifests and + * strip them from release builds; set EXT_DEV=1 to keep them. + */ +function readManifestForRelease(browser) { + const manifest = JSON.parse( + readFileSync(join(ROOT_DIR, `src/manifest.${browser}.json`), 'utf-8') + ); + + if (!process.env.EXT_DEV && Array.isArray(manifest.host_permissions)) { + const before = manifest.host_permissions.length; + manifest.host_permissions = manifest.host_permissions.filter( + (h) => !/^https?:\/\/(localhost|127\.0\.0\.1)([:/]|$)/.test(h) + ); + const dropped = before - manifest.host_permissions.length; + if (dropped > 0) { + console.log(` Stripped ${dropped} localhost host permission(s) (EXT_DEV=1 to keep)`); + } + } + + return `${JSON.stringify(manifest, null, 2)}\n`; +} + /** * Copy manifest and assets for Chrome */ @@ -126,8 +156,7 @@ function buildChrome() { // We need to ensure the manifest and icons are correct // Copy Chrome manifest as manifest.json - const chromeManifest = readFileSync(join(ROOT_DIR, 'src/manifest.chrome.json'), 'utf-8'); - writeFileSync(join(CHROME_DIR, 'manifest.json'), chromeManifest); + writeFileSync(join(CHROME_DIR, 'manifest.json'), readManifestForRelease('chrome')); // Copy icons copyIcons(CHROME_DIR); @@ -135,12 +164,6 @@ function buildChrome() { // Copy adblock rulesets copyRules(CHROME_DIR); - // Copy background script - const bgSrc = join(ROOT_DIR, 'src/background/index.js'); - if (existsSync(bgSrc)) { - copyFileSync(bgSrc, join(CHROME_DIR, 'background.js')); - } - console.log('✅ Chrome build complete'); } @@ -157,8 +180,7 @@ function buildFirefox() { } // Copy Firefox manifest as manifest.json (overwrite Chrome manifest) - const firefoxManifest = readFileSync(join(ROOT_DIR, 'src/manifest.firefox.json'), 'utf-8'); - writeFileSync(join(FIREFOX_DIR, 'manifest.json'), firefoxManifest); + writeFileSync(join(FIREFOX_DIR, 'manifest.json'), readManifestForRelease('firefox')); // Copy icons copyIcons(FIREFOX_DIR); @@ -166,12 +188,6 @@ function buildFirefox() { // Copy adblock rulesets copyRules(FIREFOX_DIR); - // Copy background script - const bgSrc = join(ROOT_DIR, 'src/background/index.js'); - if (existsSync(bgSrc)) { - copyFileSync(bgSrc, join(FIREFOX_DIR, 'background.js')); - } - console.log('✅ Firefox build complete'); } @@ -188,8 +204,7 @@ function buildSafari() { } // Copy Safari manifest as manifest.json (overwrite Chrome manifest) - const safariManifest = readFileSync(join(ROOT_DIR, 'src/manifest.safari.json'), 'utf-8'); - writeFileSync(join(SAFARI_DIR, 'manifest.json'), safariManifest); + writeFileSync(join(SAFARI_DIR, 'manifest.json'), readManifestForRelease('safari')); // Copy icons copyIcons(SAFARI_DIR); @@ -197,12 +212,6 @@ function buildSafari() { // Copy adblock rulesets copyRules(SAFARI_DIR); - // Copy background script - const bgSrc = join(ROOT_DIR, 'src/background/index.js'); - if (existsSync(bgSrc)) { - copyFileSync(bgSrc, join(SAFARI_DIR, 'background.js')); - } - console.log('✅ Safari build complete'); console.log(''); console.log('📝 Note: Safari requires additional steps:'); diff --git a/apps/extension/src/manifest.chrome.json b/apps/extension/src/manifest.chrome.json index e1f10e1..ae2f710 100644 --- a/apps/extension/src/manifest.chrome.json +++ b/apps/extension/src/manifest.chrome.json @@ -28,13 +28,11 @@ "bookmarks", "storage", "alarms", + "notifications", "identity", "declarativeNetRequest", "activeTab" ], - "optional_permissions": [ - "tabs" - ], "declarative_net_request": { "rule_resources": [ { @@ -60,24 +58,10 @@ "https://hole.cert.pl/*", "https://raw.githubusercontent.com/*" ], - "oauth2": { - "client_id": "YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com", - "scopes": [ - "https://www.googleapis.com/auth/drive.file" - ] - }, "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" }, "web_accessible_resources": [ - { - "resources": [ - "icons/*" - ], - "matches": [ - "" - ] - }, { "resources": [ "blocked.html" diff --git a/apps/extension/src/manifest.firefox.json b/apps/extension/src/manifest.firefox.json index f94c54d..91aa16d 100644 --- a/apps/extension/src/manifest.firefox.json +++ b/apps/extension/src/manifest.firefox.json @@ -32,13 +32,11 @@ "bookmarks", "storage", "alarms", + "notifications", "identity", "declarativeNetRequest", "activeTab" ], - "optional_permissions": [ - "tabs" - ], "declarative_net_request": { "rule_resources": [ { @@ -68,14 +66,6 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "web_accessible_resources": [ - { - "resources": [ - "icons/*" - ], - "matches": [ - "" - ] - }, { "resources": [ "blocked.html" diff --git a/apps/extension/src/manifest.safari.json b/apps/extension/src/manifest.safari.json index 52df46b..73ce5b9 100644 --- a/apps/extension/src/manifest.safari.json +++ b/apps/extension/src/manifest.safari.json @@ -27,13 +27,11 @@ "bookmarks", "storage", "alarms", + "notifications", "identity", "declarativeNetRequest", "activeTab" ], - "optional_permissions": [ - "tabs" - ], "declarative_net_request": { "rule_resources": [ { @@ -61,14 +59,6 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "web_accessible_resources": [ - { - "resources": [ - "icons/*" - ], - "matches": [ - "" - ] - }, { "resources": [ "blocked.html" diff --git a/apps/extension/vite.config.js b/apps/extension/vite.config.js index c8d0ae9..933fa74 100644 --- a/apps/extension/vite.config.js +++ b/apps/extension/vite.config.js @@ -5,7 +5,18 @@ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync } from 'fs'; const pkg = JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8')); -const browser = process.env.BROWSER || 'chrome'; +// Deliberately NOT `process.env.BROWSER`: that is a standard Linux env var +// (xdg-open et al) and is commonly set to something like `true` or `firefox`, +// which silently redirected the build into dist/ and shipped a zip +// missing the popup, options page, service worker and icons. +const TARGETS = ['chrome', 'firefox', 'safari']; +const requested = process.env.EXT_BROWSER; +if (requested && !TARGETS.includes(requested)) { + throw new Error( + `EXT_BROWSER must be one of ${TARGETS.join(', ')} (got "${requested}")` + ); +} +const browser = requested || 'chrome'; // Plugin to move HTML files from src/* to root after build function moveHtmlPlugin() {