EDIT below is still true for plain vanilla require.js, but found a workaround by forking RequireJS
Github: https://github.com/jeroendelau/requirejs
Bower: bower install requirejs-for-browser-extensions
ORIGNAL POST
Injecting it into the content script is by far the hardest. And the above answers are incomplete or incorrect.
Background and Popup:
Go with earlier answer by @nafis, they will work
Content Script
This is a very tricky one, and this part from the api description is key:
Execution environment
Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.
Intuitively this should be correct
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["requirejs.js", "myscript.js"]
}
],
...
"web_accessible_resources": [
"js/*"
],
...
}
myscript.js
require(["myFancyModule"], function (FM) {
...
});
THIS WILL NOT WORK
The problem is that requirejs will proceed to load all your dependencies by injecting <script>
tags in the header. These script tags are executed in the PAGE environment, not in the special EXTENSION environment. And this matters.
- Dependencies fail to load since requirejs is not loaded in the page
environment
- If the website owner has alreay added requirejs, it migh clash
- You can decide to inject require.js into the page, as @Adam suggests, but in that case none of your extension features
will work. storage, messaging, cross-site request are all not
available
So in order for those to work, the modules loaded by requirejs need to be injected into the extensions environment. It is possible to use a requirejs plugin to change load behavior.
Because of the way this works the solution is very inelegant, AND it prevents your from seeing the scripts in the debugger under scripts. But if you're desperate, it will works.
myscript.js
/**
* Inject the plugin straight into requirejs
*/
define("Injector", {
load: function (name, req, onload, config) {
//Load the script using XHR, from background
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", function () {
//Find depenencies in the script, and prepend the
//Injector! plugin, forcing the load to go through this
//plugin.
var modified = getDeps(oReq.response)
//have requirejs load the module from text
//it will evaluate the define, and process dependencies
onload.fromText(modified);
});
oReq.open("GET", req.toUrl(name) + ".js");
oReq.send();
//Find dependencies and prepend Injector!
function getDeps(script)
{
//extract the define call, reduced to a single line
var defineCall = script.match(/define([\s\S])*?{/m)[0].split("\n").join("");
//extract dependenceis from the call
var depsMatch = defineCall.match(/\[([\s\S]*?)\]/);
//if there are dependencies, inject the injectors
if (depsMatch)
{
var deps = depsMatch[0];
var replaced = deps.replace(/(\'|\")([\s\S]*?)\1/g, '$1Injector!$2$1');
return script.replace(/define([\s\S]*?)\[[\s\S]*?\]/m, 'define$1' + replaced);
}
//no dependencies, return script
return script;
}
}
});
/**
* Call all your dependencies using the plugin
*/
require(["Injector!myFancyModule"], function (FM) {
chrome.storage.local.get({something});
});