How to reuse a javascript function between content script and service worker in MV2/MV3-compatible way?
E

2

6

In Chrome extension, I have a javascript function that I need to reuse both from content script and from background worker JS files.

How can I organize scripts for that?


So far I only managed to define reusable function in common.js as

export function commonFunction()
{
  ...
}

...and import it from background.js:

import { commonFunction } from './common.js';

However, I can not find a way to reuse that function from content script. Ideally I need the same code to be reusable between Manifest V2- and V3-browsers.


Also, stable Firefox (which is still MV2) fails with that design even for background.js: Uncaught SyntaxError: import declarations may only appear at top level of a module

The error message is not helpful as the import line do appear at the top level of a background.js module.



Update 2: This is the shortest solution I could make for Chrome MV3. Still need help to make it more simple / elegant:

async function commonImporter()
{
    return await import((chrome.runtime.getURL ||
      chrome.extension.getURL)("common.js"));
}

async function contentScriptFunctionUsingCommon()
{
    ...
    result = await (await commonImporter()).commonFunction();
    return result;
}

Update 3: For Firefox with MV2, I am strugging to create background.html properly: with pre-existing background.js, it reads now:

<script src="common.js" type="module"></script>
<script src="background.js" type="module"></script>

...while both common.js and background.js are also listed in background.scripts of manifest.json. However when I remove scripts array from background object of manifest.json...

  "background": {
    "page": "background.html",
    "persistent": false
    },

...Firefox gives error that I fail to google:

Protocol error (NS_BASE_STREAM_CLOSED): Component returned failure code: 0x80470002 (NS_BASE_STREAM_CLOSED) [nsIInputStream.available] from: server0.conn0.webExtensionDescriptor304 (resource://gre/modules/Extension.jsm:777:0)

Expansive answered 19/8, 2022 at 20:0 Comment(0)
C
1

For content scripts, the content script is not a module, but you can dynamically import modules. You can import a module like this:

import((chrome.runtime.getURL || chrome.extension.getURL)('...'));

Note that you will need to add any module imported this way to your web_accessible_resources.

For Firefox, the key part of the message is "of a module". Specifying background scripts directly (without a background page) does not make them run as a module. To get around this, add a background page and run your background script via a script tag with a type of module: <script src="..." type="module"></script> For browsers supporting MV3, you would add "type":"module" to your background object instead.

Chappell answered 19/8, 2022 at 22:2 Comment(5)
Sorry, I grabbed the line from an MV2 extension originally. chrome.extension.getURL has changed to chrome.runtime.getURL for MV3. I updated it in the answer above.Chappell
Refer to the documentation for the dynamic import() function: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Chappell
In particular, note that it returns a promise that will need to be awaited.Chappell
Posted my result as Update2. Still doesn't look very elegant to me, please help to simplify it.Expansive
I also failed to properly implement the background page for MV2 Firefox, see Update #3.Expansive
D
1

For MV3 (according to https://developer.chrome.com/docs/extensions/mv3/mv3-migration/#remotely-hosted-code, "Bundle third-party libraries"):

    // manifest.json:

    content_scripts": [{
    "matches": ["https://example.com/*"],
    "js": ["js/common.js", "js/process.js"]
    }

    // service worker (in this example is located in './js' folder):
    importScripts('./common.js')

It works fine for me without using modules.

Digression answered 21/10, 2022 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.