From a8538671501c26d1a41e3073fc9eb70e9ac99ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C5=82uski?= Date: Fri, 13 Mar 2026 13:55:34 +0100 Subject: [PATCH] fix: don't add content script entry to web_accessible_resources when no loader is emitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a content script has no imports, exports, or dynamic imports, the plugin wraps it in an IIFE and lists it directly in content_scripts[].js. Chrome injects these scripts itself — they don't need to be fetched via chrome.runtime.getURL(), so listing them in web_accessible_resources is unnecessary and exposes them to web pages. Per Chrome docs: 'Content scripts themselves do not need to be allowed.' Only add the entry to web_accessible_resources when a loader was actually emitted (i.e. loaderName is set), since the loader uses chrome.runtime.getURL() to dynamically import the real entry. Closes #1130 --- .../src/node/plugin-webAccessibleResources.ts | 12 +++++- .../manifest.json | 12 ++++++ .../src/content.js | 6 +++ .../vite-build.test.ts | 38 +++++++++++++++++++ .../vite.config.ts | 19 ++++++++++ .../__snapshots__/build.test.ts.snap | 13 ------- .../basic-js/__snapshots__/build.test.ts.snap | 12 ------ .../basic-ts/__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 11 ------ .../__snapshots__/build.test.ts.snap | 1 - .../__snapshots__/build.test.ts.snap | 13 ------- .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 1 - .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 12 ------ .../__snapshots__/build.test.ts.snap | 13 ------- 19 files changed, 85 insertions(+), 150 deletions(-) create mode 100644 packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/manifest.json create mode 100644 packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/src/content.js create mode 100644 packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite-build.test.ts create mode 100644 packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite.config.ts diff --git a/packages/vite-plugin/src/node/plugin-webAccessibleResources.ts b/packages/vite-plugin/src/node/plugin-webAccessibleResources.ts index 62227cb6d..764b72663 100644 --- a/packages/vite-plugin/src/node/plugin-webAccessibleResources.ts +++ b/packages/vite-plugin/src/node/plugin-webAccessibleResources.ts @@ -156,8 +156,16 @@ export const pluginWebAccessibleResources: CrxPluginFn = () => { // update content script resources for use by css plugin contentScripts.get(key)!.css = [...css] - // loader files import the entry, so entry file must be web accessible - if (type === 'loader' || isDynamicScript) + // loader files import the entry via chrome.runtime.getURL(), + // so the entry file must be web accessible. + // When no loader is emitted (simple scripts with no imports/exports), + // the entry is listed directly in content_scripts and injected by + // Chrome itself — it does not need to be web accessible. + // See: https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources + // "Content scripts themselves do not need to be allowed." + const script = contentScripts.get(key)! + const hasLoader = !!script.loaderName + if ((type === 'loader' && hasLoader) || isDynamicScript) imports.add(fileName) const resource: diff --git a/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/manifest.json b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/manifest.json new file mode 100644 index 000000000..51512a7ce --- /dev/null +++ b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest_version": 3, + "name": "Content Script No WAR Entry Test", + "description": "Verifies simple content scripts are not in web_accessible_resources", + "version": "1.0.0", + "content_scripts": [ + { + "matches": ["https://example.com/*"], + "js": ["src/content.js"] + } + ] +} diff --git a/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/src/content.js b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/src/content.js new file mode 100644 index 000000000..7a604a1cc --- /dev/null +++ b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/src/content.js @@ -0,0 +1,6 @@ +// Simple content script with no imports or exports. +// Chrome injects this directly — it should NOT appear in web_accessible_resources. +var marker = document.createElement('div') +marker.id = 'crx-simple-content-script' +marker.textContent = 'Simple content script loaded' +document.body.appendChild(marker) diff --git a/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite-build.test.ts b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite-build.test.ts new file mode 100644 index 000000000..9cca775f1 --- /dev/null +++ b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite-build.test.ts @@ -0,0 +1,38 @@ +import fs from 'fs-extra' +import path from 'pathe' +import { expect, test } from 'vitest' +import { build } from '../runners' + +test( + 'simple content script entry is not in web_accessible_resources', + async () => { + const { browser, outDir } = await build(__dirname) + + // 1. Verify the content script still works in the browser + const page = await browser.newPage() + await page.goto('https://example.com') + + const marker = page.locator('#crx-simple-content-script') + await marker.waitFor({ state: 'attached', timeout: 10000 }) + const text = await marker.textContent() + expect(text).toBe('Simple content script loaded') + + // 2. Read the built manifest and verify the entry is NOT in web_accessible_resources + const manifest = await fs.readJson(path.join(outDir, 'manifest.json')) + + // The content script entry should be listed in content_scripts + expect(manifest.content_scripts).toBeDefined() + expect(manifest.content_scripts.length).toBe(1) + const contentScriptFile = manifest.content_scripts[0].js[0] + expect(contentScriptFile).toBeTruthy() + + // The content script entry should NOT be in web_accessible_resources + // Per Chrome docs: "Content scripts themselves do not need to be allowed." + // See: https://github.com/crxjs/chrome-extension-tools/issues/1130 + const warResources = (manifest.web_accessible_resources ?? []).flatMap( + (war: { resources: string[] }) => war.resources, + ) + expect(warResources).not.toContain(contentScriptFile) + }, + { retry: 2 }, +) diff --git a/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite.config.ts b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite.config.ts new file mode 100644 index 000000000..1ee98da81 --- /dev/null +++ b/packages/vite-plugin/tests/e2e/mv3-content-script-no-war-entry/vite.config.ts @@ -0,0 +1,19 @@ +import { crx } from '../../plugin-testOptionsProvider' +import { defineConfig } from 'vite' +import manifest from './manifest.json' + +export default defineConfig({ + build: { + minify: false, + rollupOptions: { + output: { + assetFileNames: 'assets/[name].hash[hash].[ext]', + chunkFileNames: 'assets/[name].hash[hash].js', + entryFileNames: 'assets/[name].hash[hash].js', + }, + }, + }, + clearScreen: false, + logLevel: 'error', + plugins: [crx({ manifest })], +}) diff --git a/packages/vite-plugin/tests/out/basic-js-optional-host-permissions/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/basic-js-optional-host-permissions/__snapshots__/build.test.ts.snap index d5e785a50..af030cd05 100644 --- a/packages/vite-plugin/tests/out/basic-js-optional-host-permissions/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/basic-js-optional-host-permissions/__snapshots__/build.test.ts.snap @@ -35,19 +35,6 @@ Object { "tabs", ], "version": "1.0.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://b.com/*", - "https://a.com/*", - "https://example.com/*", - ], - "resources": Array [ - "assets/content.js.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/basic-js/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/basic-js/__snapshots__/build.test.ts.snap index 30c45ed19..27b26143e 100644 --- a/packages/vite-plugin/tests/out/basic-js/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/basic-js/__snapshots__/build.test.ts.snap @@ -27,18 +27,6 @@ Object { "manifest_version": 3, "name": "Test Extension", "version": "1.0.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://b.com/*", - "https://a.com/*", - ], - "resources": Array [ - "assets/content.js.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/basic-ts/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/basic-ts/__snapshots__/build.test.ts.snap index 45b487dad..868ec0576 100644 --- a/packages/vite-plugin/tests/out/basic-ts/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/basic-ts/__snapshots__/build.test.ts.snap @@ -24,18 +24,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/content-script-world-main/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/content-script-world-main/__snapshots__/build.test.ts.snap index e72a9c84d..d5a8be26b 100644 --- a/packages/vite-plugin/tests/out/content-script-world-main/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/content-script-world-main/__snapshots__/build.test.ts.snap @@ -17,17 +17,6 @@ Object { "manifest_version": 3, "name": "Test Content Script World Main", "version": "1.0.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "https://example.com/*", - ], - "resources": Array [ - "assets/content.js.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/dynamic-script/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/dynamic-script/__snapshots__/build.test.ts.snap index 4519c2020..e68feb067 100644 --- a/packages/vite-plugin/tests/out/dynamic-script/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/dynamic-script/__snapshots__/build.test.ts.snap @@ -27,7 +27,6 @@ Object { "https://a.com/*", ], "resources": Array [ - "assets/declared-script.ts.hash0.js", "assets/main-world.ts.hash1.js", ], "use_dynamic_url": false, diff --git a/packages/vite-plugin/tests/out/vite-content-script-css-imports-2/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-content-script-css-imports-2/__snapshots__/build.test.ts.snap index a38cea844..cba1f6426 100644 --- a/packages/vite-plugin/tests/out/vite-content-script-css-imports-2/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-content-script-css-imports-2/__snapshots__/build.test.ts.snap @@ -31,19 +31,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/index.ts.hash2.js", - "assets/index.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/vite-content-script-css-imports/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-content-script-css-imports/__snapshots__/build.test.ts.snap index 559e3bc3d..a712d9736 100644 --- a/packages/vite-plugin/tests/out/vite-content-script-css-imports/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-content-script-css-imports/__snapshots__/build.test.ts.snap @@ -19,18 +19,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/vite-content-script-manifest-css/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-content-script-manifest-css/__snapshots__/build.test.ts.snap index f8b64c7f5..516ef5555 100644 --- a/packages/vite-plugin/tests/out/vite-content-script-manifest-css/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-content-script-manifest-css/__snapshots__/build.test.ts.snap @@ -19,18 +19,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/vite-declared-script-resources/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-declared-script-resources/__snapshots__/build.test.ts.snap index 7c65b9e27..34fbd2905 100644 --- a/packages/vite-plugin/tests/out/vite-declared-script-resources/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-declared-script-resources/__snapshots__/build.test.ts.snap @@ -23,7 +23,6 @@ Object { "https://google.com/*", ], "resources": Array [ - "assets/content.ts.hash0.js", "assets/font.hash1.otf", "assets/image.hash2.png", "assets/script.ts.hash3.js", diff --git a/packages/vite-plugin/tests/out/vite-self-directive-in-csp/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-self-directive-in-csp/__snapshots__/build.test.ts.snap index 393a424b0..3d828b7b9 100644 --- a/packages/vite-plugin/tests/out/vite-self-directive-in-csp/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-self-directive-in-csp/__snapshots__/build.test.ts.snap @@ -27,18 +27,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/vite-underscore-css-filename/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/vite-underscore-css-filename/__snapshots__/build.test.ts.snap index c5749746d..6d7c0199b 100644 --- a/packages/vite-plugin/tests/out/vite-underscore-css-filename/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/vite-underscore-css-filename/__snapshots__/build.test.ts.snap @@ -19,18 +19,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/with-https-server/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/with-https-server/__snapshots__/build.test.ts.snap index 30c45ed19..27b26143e 100644 --- a/packages/vite-plugin/tests/out/with-https-server/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/with-https-server/__snapshots__/build.test.ts.snap @@ -27,18 +27,6 @@ Object { "manifest_version": 3, "name": "Test Extension", "version": "1.0.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://b.com/*", - "https://a.com/*", - ], - "resources": Array [ - "assets/content.js.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/with-sourcemaps-inline/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/with-sourcemaps-inline/__snapshots__/build.test.ts.snap index aa1596acd..d057a3913 100644 --- a/packages/vite-plugin/tests/out/with-sourcemaps-inline/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/with-sourcemaps-inline/__snapshots__/build.test.ts.snap @@ -24,18 +24,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - ], - "use_dynamic_url": false, - }, - ], } `; diff --git a/packages/vite-plugin/tests/out/with-sourcemaps-separate/__snapshots__/build.test.ts.snap b/packages/vite-plugin/tests/out/with-sourcemaps-separate/__snapshots__/build.test.ts.snap index f2f5a2be8..7159eae41 100644 --- a/packages/vite-plugin/tests/out/with-sourcemaps-separate/__snapshots__/build.test.ts.snap +++ b/packages/vite-plugin/tests/out/with-sourcemaps-separate/__snapshots__/build.test.ts.snap @@ -24,19 +24,6 @@ Object { "manifest_version": 3, "name": "test extension", "version": "0.1.0", - "web_accessible_resources": Array [ - Object { - "matches": Array [ - "http://*/*", - "https://*/*", - ], - "resources": Array [ - "assets/content.ts.hash0.js", - "assets/content.ts.hash0.js.map", - ], - "use_dynamic_url": false, - }, - ], } `;