Managing Common Code in a Multi-Browser Extension

uBlock is an ad-blocking browser extension that's compatible with Chrome, Firefox, and Safari. The uBlock developers organize the entire codebase in a single repository and are able to share common, browser-independent logic between the three extensions in that way.

Right now I'm working on porting mediathread-chrome to Firefox. The single repository structure works for uBlock, but I'm going to try sketching out the simplest way to do this with a separate repository for each platform. My reasoning is that this is the structure we already have in place, and I'm also thinking that this way simplifies deployment. Each browser has its own set of tools for developing extensions, and I'm worried they'd get in eachother's way if everything's in one repository.

Firefox extensions are tested and packaged with Mozilla's jpm tool, which is a node.js program influenced by npm. It's possible to use node.js modules in Firefox extensions, in fact, this is the preferred way of including third-party code since jpm was introduced in early 2015.

Chrome extensions, on the other hand, have no special relationship to node.js. Chrome doesn't provide an extension development tool like Mozilla's jpm. Instead, everything is handled in the browser. Coming up with a testing scheme is left as an exercise for the developer, but debugging is much more seamless: Just add a debugger; breakpoint in your extension code, and Chrome behaves as if it were JavaScript running on the page. We could use npm to install third-party packages on a Chrome extension, but I'm already using npm for testing purposes on mediathread-chrome.

github-linker-core is an example of an npm module that's compatible with Chrome extensions, by using browserify as part of the build process. So that's one way to approach the problem.

Another solution would be to use Bower as the package manager, instead of npm.

Or we could archive our "core" library manually and distribute the tarball, unpacking it when necessary like we do with Mediathread's bookmarklet. An even simpler way would be to copy over common code to update it, making sure to keep it separate from the browser-specific code. This is the approach I'm going to take for now, since it's the simplest and requires the least amount of configuration.