Short Answer:
You can mimic some of the functionality and get some of the benefits of import
/export
in browser extensions by creating the following file and listing it early in your manifest.json
:
let exportVars, importVarsFrom;
{
const modules = {};
exportVars = varsObj => ({
from(nameSpace) {
modules[nameSpace] || (modules[nameSpace] = {});
for (let [k,v] of Object.entries(varsObj)) {
modules[nameSpace][k] = v;
}
}
});
importVarsFrom = nameSpace => modules[nameSpace];
}
Then, export from one file/module like this:
exportVars({ var1, var2, var3 }).from('my-utility');
Import into another file/module like this:
const { var1, var3: newNameForVar3 } = importVarsFrom('my-utility');
Discussion:
This strategy:
- allows modular code in a browser extension such that you can split code into multiple files but don't have variable clashes due to shared global scope between different files,
- still allows you to export and import variables out of and into different JavaScript files/modules,
- introduces only two global variables, namely the exporting function and the importing function,
- maintains full browser extension functionality in each file (e.g.
chrome.runtime
, etc.) that is eliminated by, e.g., the approach in another answer (currently the accepted answer) using module script tag embedding,
- uses a concise syntax similar to the true
import
and export
functions in JavaScript,
- allows name-spacing which could be the file names of the exporting modules in a manner similar to how the true
import
and export
commands work in JavaScript, but doesn't have to be (i.e. the name-space names could be anything you want), and
- allows variable renaming upon import similar to how
import { fn as myFn }...
works.
To do this, your manifest.json
needs to load your JavaScript as follows:
- the file establishing the exporting/importing functions first (named
modules-start.js
in the example below),
- the exporting files next, and
- the importing files last.
Of course, you might have a file that both imports and exports. In that case, just ensure it is listed after the files it imports from but before the files it exports to.
Working Example
The following code demonstrates this strategy.
It is important to note that all of the code in each module/file is contained within curly braces. The only exception is the first line in modules-start.js
which establishes the exporting and importing functions as global variables.
The code in the snippet below is necessarily contained in a single "place". In a real project, however, the code could be split into separate files. Note, though, that even in this artificial context here (i.e. within the single code snippet below), this strategy allows the different sections of code it contains to be modular and yet still interconnected.
// modules-start.js:
let exportVars, importVarsFrom; // the only line NOT within curly braces
{
const modules = {};
exportVars = varsObj => ({
from(nameSpace) {
modules[nameSpace] || (modules[nameSpace] = {});
for (let [k,v] of Object.entries(varsObj)) {
modules[nameSpace][k] = v;
}
}
});
importVarsFrom = nameSpace => modules[nameSpace];
}
// *** All of the following is just demo code
// *** showing how to use this export/import functionality:
// my-general-utilities.js (an example file that exports):
{
const wontPolluteTheGlobalScope = 'f';
const myString = wontPolluteTheGlobalScope + 'oo';
const myFunction = (a, b) => a + b;
// the export statement:
exportVars({ myString, myFunction }).from('my-general-utilities');
}
// content.js (an example file that imports):
{
// the import statement:
const { myString, myFunction: sum } = importVarsFrom('my-general-utilities');
console.log(`The imported string is "${myString}".`);
console.log(`The renamed imported function shows that 2 + 3 = ${sum(2,3)}.`);
}
With this example, your manifest.json
should list the files in the following order:
{ ...
"content_scripts": [
{
"js": [
"modules-start.js",
"my-general-utilities.js",
"content.js"
]
}
], ...
}