Firefox web extension background script - to bundle or not to bundle?

We are running into a bad problem with the publishing of the firefox web extension. The problem is that there is a limitation on the size of each JS file to not exceed 4MB, and our background script is now 4.4MB. The other problem is that Firefox used to not support dynamic imports in the background script, thus forcing us to need to bundle. In a nutshell, this is what the problem looks like:

  1. Firefox does not support dynamic imports —> must bundle the JS in order to run the background script.

  2. Firefox imposes a JS file size limit of 4MB —> must not bundle and must use import to run the background script.

Therefore, we are stuck now. Should we bundle, or should we use import? If we bundle, we can’t even sign our extension anymore. If we don’t bundle, we can’t run the extension in all older versions of firefox. Therefore, both approaches are now wrong.

Can someone please advise on what we should do here? Thanks in advance for your kind help.

You can bundle into multiple chunks, so have the bundle split over multiple files that are loaded after each other.

Not sure what you’re using for bundling, but for example webpack supports a max size per chunk.

Would that use the “import” or “require” keyword? How would that work in older firefox that do not support dynamic imports? Thanks for your response!

That would work by the bundler putting the different parts from different chunks on a global that the final loader then knows how to assemble.

Do you mind providing a simple but concrete example here? Since older versions of firefox do not support import nor require, I do not see how using a global could work. Thanks.

If you are using a bundler then all you have to do is configure the bundler to output into multiple chunks and load the chunks in the correct order (in the background scripts array or if you have a HTML background page the <script> tags). For webpack the config to split chunks is documented at https://webpack.js.org/configuration/optimization/#optimizationsplitchunks

If you’re doing all of it manually, you can share things between script files, see for example https://github.com/freaktechnik/advanced-github-notifier/blob/main/scripts/client-manager.js#L9 which uses a class from https://github.com/freaktechnik/advanced-github-notifier/blob/main/scripts/storage-manager.js#L45 which are both loaded as background scripts: https://github.com/freaktechnik/advanced-github-notifier/blob/main/manifest.json#L10-L23

Ok great! We use snowpack so the webpack config does not apply, and we consider ourselves doing things manually.

According to your example with having multiple background scripts in manifest.json, I didn’t realize that all the separate scripts are actually treated as one script! If that is the case, I think we can just chunk our code randomly and add them all as background scripts. Does that sound to you like it would work? Thanks so much for your help!

They aren’t treated as once script, but they are loaded into the same page context. It’s like having multiple script tags. So you can’t just chunk randomly, it has to match up with scope boundaries and all that. So only things that are declared in a shared scope will be visible to other scripts, things hidden inside a local scope (like a function) are not visible. The individual files also have to be valid JS.

What you could likely do without trying to find some way to convince snowpack to do splitting (which from a quick search seems to be done most easily by just using webpack for production builds, which is kind of silly imo), would be to have separate entry points that you then somehow manually introduce to each other (for example by one entry point exposing a function that takes the other entry point’s exported object as an argument or similar) - if they even need to know about each other.

Worth mentioning is that likely an easy win is to not bundle libraries that are natively compatible with browser environments and instead use them via their browser global. That also makes it easier for the extension to be reviewed, since you can use the original file that is distributed with the library, thus being recognized by the validator.

Edit: here an example from a retired extension of mine that used webpack with react and friends: https://github.com/freaktechnik/justintv-stream-notifications/blob/master/webpack.config.js#L179-L186

Super helpful info! I like this “like having multiple script tags” and the using libraries as browser globals. I will have to think about how we can restructure our code or build to make sure that it remains scalable. Thanks for your help! :+1: