zotero/resource/require.js

195 lines
5.5 KiB
JavaScript

'use strict';
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Manages the base loader (loader.sys.mjs) instance used to load CJS modules.
*/
const {
Loader,
Require,
resolveURI,
unload,
} = ChromeUtils.importESModule("resource://zotero/loader.sys.mjs");
const DEFAULT_SANDBOX_NAME = "Zotero (Module loader)";
var gNextLoaderID = 0;
/**
* The main loader API. The standard instance of this loader is exported as
* |loader| below, but if a fresh copy of the loader is needed, then a new
* one can also be created.
*
* The two following boolean flags are used to control the sandboxes into
* which the modules are loaded.
* @param freshCompartment boolean
* If true, the modules will be forced to be loaded in a distinct
* compartment. It is typically used to load the modules in a distinct
* system compartment, different from the main one, which is shared by
* all ESMs, XPCOMs and modules loaded with this flag set to true.
* We use this in order to debug modules loaded in this shared system
* compartment. The debugger actor has to be running in a distinct
* compartment than the context it is debugging.
* @param useLoaderGlobal boolean
* If true, the loader will reuse the current global to load other
* modules instead of creating a sandbox with custom options. Cannot be
* used with freshCompartment.
*/
function ZoteroLoader({
freshCompartment = false,
useLoaderGlobal = false,
} = {}) {
if (useLoaderGlobal && freshCompartment) {
throw new Error(
"Loader cannot use freshCompartment if useLoaderGlobal is true"
);
}
const paths = {
'': 'resource://zotero/',
'containers/': 'chrome://zotero/content/containers/',
'components/': 'chrome://zotero/content/components/',
'zotero/': 'chrome://zotero/content/'
};
// In case the Loader ESM is loaded in the existing global,
// also reuse this global for all CommonJS modules.
const sharedGlobal =
useLoaderGlobal ||
// eslint-disable-next-line mozilla/reject-globalThis-modification
Cu.getRealmLocation(globalThis) == "Zotero global"
? Cu.getGlobalForObject({})
: undefined;
this.loader = new Loader({
paths,
sharedGlobal,
freshCompartment,
sandboxName: useLoaderGlobal
? "Zotero (Server Module Loader)"
: DEFAULT_SANDBOX_NAME,
// Make sure `define` function exists. JSON Viewer needs modules in AMD
// format, as it currently uses RequireJS from a content document and
// can't access our usual loaders. So, any modules shared with the JSON
// Viewer should include a define wrapper:
//
// // Make this available to both AMD and CJS environments
// define(function(require, exports, module) {
// ... code ...
// });
//
supportAMDModules: true,
requireHook: (id, require) => {
// if (id.startsWith("raw!") || id.startsWith("theme-loader!")) {
// return requireRawId(id, require);
// }
return require(id);
},
});
this.require = Require(this.loader, { id: "zotero" });
let win = typeof window !== "undefined" ? window : {};
let doc = win.document ?? {};
let navigator = typeof win.navigator !== 'undefined' ? win.navigator : {};
// Various globals are available from ESM, but not from sandboxes,
// inject them into the globals list.
// Changes here should be mirrored to .eslintrc.
const injectedGlobals = {
BrowsingContext,
CanonicalBrowsingContext,
ChromeWorker,
console,
DebuggerNotificationObserver,
DOMPoint,
DOMQuad,
DOMRect,
fetch,
HeapSnapshot,
IOUtils,
L10nRegistry,
Localization,
NamedNodeMap,
NodeFilter,
PathUtils,
Services,
StructuredCloneHolder,
WebExtensionPolicy,
WebSocket,
WindowGlobalChild,
WindowGlobalParent,
window: win,
document: doc,
navigator,
setTimeout: win.setTimeout,
clearTimeout: win.clearTimeout,
setInterval: win.setInterval,
clearInterval: win.clearInterval,
requestAnimationFrame: win.requestAnimationFrame,
cancelAnimationFrame: win.requestAnimationFrame,
ResizeObserver: win.ResizeObserver,
};
for (const name in injectedGlobals) {
this.loader.globals[name] = injectedGlobals[name];
}
Object.defineProperty(this.loader.globals, "Zotero", {
get: () => {
const { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs");
// TODO: Cache
return Zotero;
}
})
// If we're using a new sandbox, patch Date#toLocale*String() there to
// follow the app locale
if (!sharedGlobal) {
Services.scriptloader.loadSubScript(
'chrome://zotero/content/dateOverrides.js',
this.loader.sharedGlobal
);
}
// Fetch custom pseudo modules and globals
const { modules, globals } = {
// TODO: TEMP: Stub this out
modules: {},
globals: {},
}
// Register custom pseudo modules to the current loader instance
for (const id in modules) {
const uri = resolveURI(id, this.loader.mapping);
this.loader.modules[uri] = {
get exports() {
return modules[id];
},
};
}
// Register custom globals to the current loader instance
Object.defineProperties(
this.loader.sharedGlobal,
Object.getOwnPropertyDescriptors(globals)
);
this.id = gNextLoaderID++;
}
ZoteroLoader.prototype = {
destroy(reason = "shutdown") {
unload(this.loader, reason);
delete this.loader;
},
};
// Export the standard instance of ZoteroLoader used by the tools.
// TODO: Zotero: Not making require.js an ESM for now, so this isn't exposed
// Should it be?
let loader = new ZoteroLoader();
var require = loader.require;