From f273416949d575656e051e7320e49db292822135 Mon Sep 17 00:00:00 2001 From: Cezar Augusto Date: Mon, 19 Aug 2024 16:08:19 -0300 Subject: [PATCH] Fix main world extension not working --- examples/content-main-world/background.js | 2 - examples/content-main-world/manifest.json | 1 + programs/develop/install_scripts.sh | 1 - programs/develop/webpack/dev-server.ts | 2 +- programs/develop/webpack/lib/messages.ts | 9 +++ programs/develop/webpack/plugin-css/index.ts | 4 +- .../feature-resolve/steps/resolver-loader.ts | 33 ++++++++++ .../plugin-extension/feature-scripts/index.ts | 31 +++++---- .../steps/add-dynamic-public-path.ts | 63 ------------------- .../steps/add-public-path-for-main-world.ts | 44 +++++++++++++ .../develop/webpack/plugin-extension/index.ts | 1 + .../webpack/plugin-static-assets/index.ts | 1 - programs/develop/webpack/webpack-config.ts | 2 +- programs/develop/webpack/webpack-types.ts | 1 + 14 files changed, 107 insertions(+), 88 deletions(-) delete mode 100644 programs/develop/webpack/plugin-extension/feature-scripts/steps/add-dynamic-public-path.ts create mode 100644 programs/develop/webpack/plugin-extension/feature-scripts/steps/add-public-path-for-main-world.ts diff --git a/examples/content-main-world/background.js b/examples/content-main-world/background.js index bfd9ec37..8c694999 100644 --- a/examples/content-main-world/background.js +++ b/examples/content-main-world/background.js @@ -1,5 +1,3 @@ -console.log('hello from background script') - chrome.runtime.onMessage.addListener((request, sender) => { if (request.action === 'changeBackgroundColor') { changeBackgroundColor(request.color, sender.tab.id) diff --git a/examples/content-main-world/manifest.json b/examples/content-main-world/manifest.json index 52d9390e..c05ff2a4 100644 --- a/examples/content-main-world/manifest.json +++ b/examples/content-main-world/manifest.json @@ -2,6 +2,7 @@ "manifest_version": 3, "version": "0.0.1", "name": "Content Scripts Main World", + "id": "phfmcihmgbbaemimookenmjjkoicadnb", "description": "An Extension.js example.", "icons": { "16": "images/extension_16.png", diff --git a/programs/develop/install_scripts.sh b/programs/develop/install_scripts.sh index e6e593c8..2cee940a 100644 --- a/programs/develop/install_scripts.sh +++ b/programs/develop/install_scripts.sh @@ -15,7 +15,6 @@ resolve_plugin_files=( scripts_plugin_files=( "$(dirname "$0")/webpack/plugin-extension/feature-scripts/steps/inject-content-css-during-dev.ts" "$(dirname "$0")/webpack/plugin-extension/feature-scripts/steps/add-hmr-accept-code.ts" - "$(dirname "$0")/webpack/plugin-extension/feature-scripts/steps/add-dynamic-public-path.ts" "$(dirname "$0")/webpack/plugin-extension/feature-scripts/steps/add-query-param-to-imported-css.ts" ) diff --git a/programs/develop/webpack/dev-server.ts b/programs/develop/webpack/dev-server.ts index 97cf5f92..32956cf9 100644 --- a/programs/develop/webpack/dev-server.ts +++ b/programs/develop/webpack/dev-server.ts @@ -61,7 +61,7 @@ export async function devServer( // Shows a full-screen overlay in the browser // when there are compiler errors or warnings. overlay: false - }, + }, headers: { 'Access-Control-Allow-Origin': '*' }, diff --git a/programs/develop/webpack/lib/messages.ts b/programs/develop/webpack/lib/messages.ts index 0253e059..4f7ce072 100644 --- a/programs/develop/webpack/lib/messages.ts +++ b/programs/develop/webpack/lib/messages.ts @@ -643,3 +643,12 @@ export function defaultPortInUse(manifestName: string, port: number) { `Default port ${port} in use, choose a new port. ` ) } + +export function noExtensionIdError(manifestName: string) { + return ( + `${getLoggingPrefix(manifestName, 'error')} No Extension Id Specified\n\n` + + `For MAIN world content scripts, you must specify an extension ID.\n` + + `Otherwise, the content script won't reload on changes.\n` + + `Add an ${brightYellow('id')} field to your manifest.json file and try again.` + ) +} diff --git a/programs/develop/webpack/plugin-css/index.ts b/programs/develop/webpack/plugin-css/index.ts index 59e10ff4..b032f1b9 100644 --- a/programs/develop/webpack/plugin-css/index.ts +++ b/programs/develop/webpack/plugin-css/index.ts @@ -27,9 +27,7 @@ export class CssPlugin { private async configureOptions(compiler: Compiler) { const projectPath = path.dirname(this.manifestPath) - const plugins: WebpackPluginInstance[] = [ - new MiniCssExtractPlugin() - ] + const plugins: WebpackPluginInstance[] = [new MiniCssExtractPlugin()] plugins.forEach((plugin) => plugin.apply(compiler)) diff --git a/programs/develop/webpack/plugin-extension/feature-resolve/steps/resolver-loader.ts b/programs/develop/webpack/plugin-extension/feature-resolve/steps/resolver-loader.ts index 96def825..7f5a755a 100644 --- a/programs/develop/webpack/plugin-extension/feature-resolve/steps/resolver-loader.ts +++ b/programs/develop/webpack/plugin-extension/feature-resolve/steps/resolver-loader.ts @@ -4,6 +4,7 @@ import {Schema} from 'schema-utils/declarations/validate' import {transformSource} from './transform-source' import {emitResolverModule} from './emit-resolver-module' import {ResolvePluginContext} from './loader-types' +import {type Manifest} from '../../../../types' const schema: Schema = { type: 'object', @@ -16,6 +17,25 @@ const schema: Schema = { } } +function getContentScripts(manifest: Manifest, manifestDir: string): string[] { + if (!manifest.content_scripts) { + return [] + } + + const scripts: string[] = [] + + manifest.content_scripts.forEach((contentScript) => { + if (contentScript.js) { + contentScript.js.forEach((script) => { + // Resolve each script path relative to the manifest directory + scripts.push(path.resolve(manifestDir, script)) + }) + } + }) + + return scripts +} + export default function resolveLoader( this: ResolvePluginContext, source: string @@ -36,6 +56,19 @@ export default function resolveLoader( ) { return source } + + // Get the directory of the manifest file + const manifestDir = path.dirname(options.manifestPath) + // Get the content scripts, resolved relative to the manifest directory + const contentScripts = getContentScripts( + require(options.manifestPath) as Manifest, + manifestDir + ) + + if (contentScripts.some((script) => script === this.resourcePath)) { + return source + } + const transformedSource = transformSource(source, options) const resolverAbsolutePath = path.join(__dirname, resolverName) diff --git a/programs/develop/webpack/plugin-extension/feature-scripts/index.ts b/programs/develop/webpack/plugin-extension/feature-scripts/index.ts index 58e39edb..eee9cb69 100644 --- a/programs/develop/webpack/plugin-extension/feature-scripts/index.ts +++ b/programs/develop/webpack/plugin-extension/feature-scripts/index.ts @@ -1,8 +1,14 @@ import path from 'path' import type webpack from 'webpack' -import {type FilepathList, type PluginInterface} from '../../webpack-types' +import { + type Manifest, + type FilepathList, + type PluginInterface +} from '../../webpack-types' import {AddScripts} from './steps/add-scripts' import {AddPublicPathRuntimeModule} from './steps/add-public-path-runtime-module' +import {AddPublicPathForMainWorld} from './steps/add-public-path-for-main-world' +import {DevOptions} from '../../../module' /** * ScriptsPlugin is responsible for handiling all possible JavaScript @@ -22,11 +28,13 @@ import {AddPublicPathRuntimeModule} from './steps/add-public-path-runtime-module */ export class ScriptsPlugin { public readonly manifestPath: string + public readonly browser?: DevOptions['browser'] public readonly includeList?: FilepathList public readonly excludeList?: FilepathList constructor(options: PluginInterface) { this.manifestPath = options.manifestPath + this.browser = options.browser || 'chrome' this.includeList = options.includeList this.excludeList = options.excludeList } @@ -97,21 +105,12 @@ export class ScriptsPlugin { // 4 - Fix the issue where assets imported via content_scripts // running in the MAIN world could not find the correct public path. - // compiler.options.module.rules.push({ - // test: /\.(js|mjs|jsx|mjsx|ts|mts|tsx|mtsx)$/, - // include: [path.dirname(this.manifestPath)], - // exclude: [path.join(path.dirname(this.manifestPath), 'node_modules')], - // use: [ - // { - // loader: path.resolve(__dirname, './add-dynamic-public-path.js'), - // options: { - // manifestPath: this.manifestPath, - // includeList: this.includeList || {}, - // excludeList: this.excludeList || {} - // } - // } - // ] - // }) + new AddPublicPathForMainWorld({ + manifestPath: this.manifestPath, + browser: this.browser || 'chrome', + includeList: this.includeList || {}, + excludeList: this.excludeList || {} + }).apply(compiler) // 5 - Fix the issue of content_scripts not being able to import // CSS files via import statements. This loader adds the diff --git a/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-dynamic-public-path.ts b/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-dynamic-public-path.ts deleted file mode 100644 index 0087f622..00000000 --- a/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-dynamic-public-path.ts +++ /dev/null @@ -1,63 +0,0 @@ -import path from 'path' -import {urlToRequest} from 'loader-utils' -import {validate} from 'schema-utils' -import {type Schema} from 'schema-utils/declarations/validate' -import {type LoaderContext} from '../../../webpack-types' - -const schema: Schema = { - type: 'object', - properties: { - test: { - type: 'string' - }, - manifestPath: { - type: 'string' - } - } -} - -export default function addDynamicPublicPathLoader( - this: LoaderContext, - source: string -) { - const options = this.getOptions() - const manifestPath = options.manifestPath - const projectPath = path.dirname(manifestPath) - const manifest = require(manifestPath) - - validate(schema, options, { - name: 'scripts:add-dynamic-public-path', - baseDataPath: 'options' - }) - - // @ts-expect-error this is not typed - if (this._compilation?.options.mode === 'production') return source - - const url = urlToRequest(this.resourcePath) - const reloadCode = ` -;__webpack_public_path__ = chrome.extension.getURL('/'); - ` - - if (manifest.background) { - if (manifest.background.scripts) { - for (const bgScript of manifest.background.scripts) { - const absoluteUrl = path.resolve(projectPath, bgScript as string) - if (url.includes(absoluteUrl)) { - return `${reloadCode}${source}` - } - } - } - - if (manifest.background.service_worker) { - const absoluteUrl = path.resolve( - projectPath, - manifest.background.service_worker as string - ) - if (url.includes(absoluteUrl)) { - return `${reloadCode}${source}` - } - } - } - - return source -} diff --git a/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-public-path-for-main-world.ts b/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-public-path-for-main-world.ts new file mode 100644 index 00000000..cc588d85 --- /dev/null +++ b/programs/develop/webpack/plugin-extension/feature-scripts/steps/add-public-path-for-main-world.ts @@ -0,0 +1,44 @@ +import type webpack from 'webpack' +import { + type FilepathList, + type PluginInterface, + type Manifest +} from '../../../webpack-types' +import * as messages from '../../../lib/messages' +import {DevOptions} from '../../../../module' + +export class AddPublicPathForMainWorld { + public readonly manifestPath: string + public readonly browser: DevOptions['browser'] + public readonly includeList: FilepathList + public readonly excludeList: FilepathList + + constructor(options: PluginInterface) { + this.manifestPath = options.manifestPath + this.browser = options.browser || 'chrome' + this.includeList = options.includeList || {} + this.excludeList = options.excludeList || {} + } + + public apply(compiler: webpack.Compiler): void { + const manifest: Manifest = require(this.manifestPath) + if ( + manifest.content_scripts?.some( + // @ts-expect-error - TS doesn't know about content_scripts + (cs) => cs.world && cs.world.toLowerCase() === 'main' + ) + ) { + if (!manifest.id) { + console.error(messages.noExtensionIdError(manifest.name || '')) + process.exit(1) + } + + if (this.browser === 'firefox') { + compiler.options.output.publicPath = `moz-extension://${manifest.id}/` + return + } + + compiler.options.output.publicPath = `${this.browser}-extension://${manifest.id}/` + } + } +} diff --git a/programs/develop/webpack/plugin-extension/index.ts b/programs/develop/webpack/plugin-extension/index.ts index b2f0363a..da69aaf3 100644 --- a/programs/develop/webpack/plugin-extension/index.ts +++ b/programs/develop/webpack/plugin-extension/index.ts @@ -93,6 +93,7 @@ export class ExtensionPlugin { // Get all scripts (bg, content, sw) declared in manifest new ScriptsPlugin({ manifestPath, + browser: this.browser, includeList: { ...manifestFieldsData.scripts, ...specialFoldersData.scripts diff --git a/programs/develop/webpack/plugin-static-assets/index.ts b/programs/develop/webpack/plugin-static-assets/index.ts index 4000a0f3..4d7c6853 100644 --- a/programs/develop/webpack/plugin-static-assets/index.ts +++ b/programs/develop/webpack/plugin-static-assets/index.ts @@ -14,7 +14,6 @@ export class StaticAssetsPlugin { } public async apply(compiler: Compiler) { - // Define the default SVG rule const defaultSvgRule: RuleSetRule = { test: /\.svg$/i, diff --git a/programs/develop/webpack/webpack-config.ts b/programs/develop/webpack/webpack-config.ts index aba3283f..ee3f91e1 100644 --- a/programs/develop/webpack/webpack-config.ts +++ b/programs/develop/webpack/webpack-config.ts @@ -57,7 +57,7 @@ export default function webpackConfig( environment: { bigIntLiteral: true, dynamicImport: true - }, + } }, resolve: { modules: ['node_modules', path.join(projectPath, 'node_modules')], diff --git a/programs/develop/webpack/webpack-types.ts b/programs/develop/webpack/webpack-types.ts index 26391056..16e8f780 100644 --- a/programs/develop/webpack/webpack-types.ts +++ b/programs/develop/webpack/webpack-types.ts @@ -58,6 +58,7 @@ export interface LoaderContext { getOptions: () => { test: string manifestPath: string + browser?: DevOptions['browser'] includeList?: FilepathList excludeList?: FilepathList }