0

Context

I'm developing a chrome extension using samrum/create-vite-plugin-web-extension which user vite to bundle all the code. In this project, I would like to use executeScript and pass arguments to the script from popup.js.

I'm able to implement this in manifest v2 if i'm using Vanilla JS WITHOUT any frameworks and bundlers with below code

// Vanilla JS - manifest v2 (popup.js) - OPTION 2
chrome.tabs.executeScript(tabId, {
    code: `var eleID = '${eleID}'; var type = '${type}'; var headerHeight = ${offsetHeight};`
}, function () {
    chrome.tabs.executeScript(tabId, { file: './executeScript.js' });
});
// manifest v2 (executeScript.js)

scrollToTarget(eleID, type, headerHeight);

function scrollToTarget(eleID, type, headerHeight = 40) {
   console.log({eleID, type, headerHeight);
}

Problem

But, since I'm using the Vite bundler (samrum/create-vite-plugin-web-extension), I'm not able to achieve the same result in manifest v2, because the name of the bundled file is dynamically generated and the name of the function to execute is also minimized during bundling. Below is what I tried:

OPTION 1:

this give the following error:

Could not load file: './executeScript.js'.
Note that the filename after bundling is different executeScript.1e145e3d.js

browser.tabs.executeScript(tabId, { file: "../executeScript.js" }).then(() => {
    browser.tabs.executeScript(tabId, {
        allFrames: false,
        code: "ht('" + eleID + "', '" + type + "', " + (offsetHeight + 10) + "); ",
    });
});

OPTION 2:

this give the following error:

scrollToTarget is not defined
Note that the function name after bundling is different - eg function ht

browser.tabs.executeScript(tabId, {
    allFrames: false,
    code: "scrollToTarget('" + eleID + "', '" + type + "', " + (offsetHeight + 10) + "); ",
});

Additional info:

I was able to successfully implement this in manifest v3 using the new chrome.scripting API. Below is the code I'm using to do that (reference):

popup.js (Manifest v3)

// Code working in manifest v3 (popup.js)
chrome.scripting.executeScript({target: {tabId}, files: ['./executeScript.js']}, () => {
  chrome.scripting.executeScript({
    target: {tabId},
    args: [eleID, type, offsetHeight + 10],
    func: (...args) => scrollToTarget(...args),
  });
});
// executeScript.js
function scrollToTarget(eleID, type, headerHeight = 40) {
  console.log({eleID, type, headerHeight);
}
Gangula
  • 5,193
  • 4
  • 30
  • 59
  • Remove `../` from the path or use an absolute path that starts with `/`. – wOxxOm Sep 03 '22 at 11:09
  • I've tried all the possible ways for the path `../` & `./` & `/` - I get the same error – Gangula Sep 03 '22 at 12:50
  • Well, you need to use the correct path where this script actually resides after building the extension, relatively to manifest.json, in the built extension directory. – wOxxOm Sep 03 '22 at 14:10
  • Yes, I tried that as well, instead of mentioning the dev file name and path, I added the path of the build file. But that still is causing the same issue. – Gangula Sep 03 '22 at 18:36
  • It means you're using the wrong path. Put the file manually in the same directory in the output as manifest.json and use `'/executeScript.js'` (no dots). – wOxxOm Sep 03 '22 at 19:27
  • Also, you probably configured your bundler to produce a module, which means scrollToTarget is not a global function. You can expose it globally by using `window.scrollToTarget = function` instead of `function scrollToTarget` – wOxxOm Sep 03 '22 at 19:30
  • Thank you so much for your comments @wOxxOm. The simplest way to implement this was to use `window.scrollToTarget = function` along with `export default function` in the `executeScript.js` (to support both manifest v2 & v3) and use `/assets/executeScript.bc3446a0.js` as the file path. But I'm having to manually update the path after every bundle (since the suffix `bc3446a0` in filename is dynamic). Can you suggest any way to automatically update the path or to bundle into commonjs? – Gangula Sep 04 '22 at 07:58
  • There might be a way to redefine the chunk's name e.g. it's possible in webpack. – wOxxOm Sep 04 '22 at 08:54
  • [I have read](https://stackoverflow.com/questions/71504140/#comment126393203_71504140) that you could use [Custom Event](https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events) as an alternative so that you don't have to load the `executeScript.js` file using `browser.tabs.executeScript`. I haven't personally tried it out, but it sounds like a viable option. – Gangula Sep 04 '22 at 14:01

2 Answers2

1

Thanks to the help from @woxxom's comments, I was able to figure the solution:

I originally was mentioning the file path of the dev file in the browser.tabs.executeScript(tabId, { file: "/executeScript.js" }), but my vite.congig.js is configured to bundle all files inside a assets folder, so the path I should mention is

browser.tabs.executeScript(tabId, { file: "/assets/executeScript.js" })

But this alone was not enough because:

  1. vite was configured to produce a module, so the function inside executeScript.js was not exposed globally,
  2. the filename after bundling has a hashname at the end of it like executeScript.95057ea9.js, so the browser.tabs.executeScript was not able to locate the file.

Solution:

  1. To address the 1st issue above, I had to expose the function insideexecuteScript.js like below:

    // executeScript.js
    window.scrollToTarget = function (eleId, type, headerHeight = 40) {
        scrollToTraget_Main(eleId, type, headerHeight)
    }
    
    export default function scrollToTraget_Main(eleId, type, headerHeight = 40) {
       console.log(eleId, type, headerHeight );
    }
    
  2. To address the 2nd issue, I had to change the vite.config.js to NOT add a hashname to "executeScript.js` like below:

     build: {
       rollupOptions: {
         output: {
           chunkFileNames: (chunkInfo) => {
             let hash = '-[hash]'
             if (chunkInfo.name == "executeScript") {
               hash = "";
             }
             return `assets/[name]${hash}.js`;
           },
         },
       },
     }
    

Additional info:

Note that i'm using export default for the function because this is also being used for Manifest v3, which supports ES6 importing.

But this will raise following error for manifest v2, which I chose to ignore - since its not impacting any other functionality of my plugin

Unexpected token 'export'

References:

Gangula
  • 5,193
  • 4
  • 30
  • 59
0

Even after implementing the code from my other answer, I was getting an error in Firefox with Manifest v2 (even though manifest v2 is working fine in Chrome).

So after some search, I decided the expose the scrollToTraget function from executeScript.js to the global window object (as suggested in this answer). This way I also won't have to use the file in executeScript, I can directly use the window.scrollToTraget.

Below is the code:

// executeScript.js
export default function scrollToTraget_Main(eleId, type, headerHeight = 40) {
// ...
}
// contentScript_main.js
// enable import in content script using dynamic import: https://stackoverflow.com/a/53033388/6908282

import scrollToTarget from "../executeScript/executeScript"
window.scrollToTarget = scrollToTarget;
// popup.js
browser.tabs.executeScript(tabId, {
    allFrames: false,
    code: "window.scrollToTarget('" + eleID + "', '" + type + "', " + offsetHeight + "); ",
});

Note:

Note that you cannot use import in contentScript by default, you will have to enable it by using dynamic import

Gangula
  • 5,193
  • 4
  • 30
  • 59