Modules

From Our World of Text Wiki
Revision as of 22:07, 8 November 2023 by Yagton (talk | contribs)

Modules make it possible to reuse code for OWOT scripts, very similar to how require() works in NodeJS, although in a more decentralized manner. Modules are hosted on GitHub, meaning absolutely anybody can create modules for others to use. They can be loaded using the use(identifier: string) function, which takes in an identifier and returns the module’s exported variable(s). Identifiers are in the format user/repo@version/folder/file.js; version can be either a git tag or commit id, is entirely optional (although highly recommended), and semantic versioning is fully supported.

How to use modules?

Using modules is rather simple, see the example below:

const chat = use("tlras/[email protected]/chat.js");
const misc = use("tlras/[email protected]/misc.js");

chat.informUser("Hello world!", "Foo", "#990000", false);
misc.notify("This is a notification sent using module code.");

They may also be used to include files within the same repository using relative path notation (. refers to same directory, .. is previous directory), which is primarily intended to allow for multi-file projects; keep in mind that due to implementation quirks. this only works for top-level imports as of the current date.

const foo = use("./foo.js");
const bar = use("../other/bar.js");

How do I test/load modules locally?

If you’d like to load local code as a module rather than running from a repo, such as for testing purposes, simply pass a function to useLocal(func: function, identifier: string):

const localMod = useLocal(function(isModule, currentModule) {
	// [insert your module here]...
}, "some_identifer");

The identifier for local modules is not subject to the same strict requirements as remote identifiers, but if you want relative imports to work, it must be the same as if it were in the remote repo.

How can I check if a module is loaded?

The isModuleLoaded(identifier: string) function is just for this, and will return a boolean value depending on whether or not that module has been loaded or not. Keep in mind that modules are only loaded once and that same instance is shared by everything which imports it, so it is redundant to do this check if you are simply going to import if true, just make a use() call; this is primarily useful if you want to add optional functionality if another module is loaded without requiring it.

How do you write modules?

Modules are different from the console scripts we are familiar with in an important way, that is, your top-level functions and variables are not automatically accessible to others, they must be explicitly exported using return at the end of your script; this way, multiple modules running at the same time don’t step on each other’s toes and read/write the same names. For example:

function foo() {
    // [imagine some interesting code here]...
}

/* This function is included in the return statement at the end of the module,
 * and thus will be visible to the script that imports it, but not foo(). */
function bar() {
    return foo() + 1;
}

return { bar };

What if I need to make a global?

Well simply put, you probably don’t! Making use of the fact (1) modules are only loaded once, and (2) that objects are passed by reference, you can do the following:

let shared = {
    foo: 12,
    bar: 34
};

function baz() {
    return shared.foo + shared.bar;
}

return {
    shared,
    baz
};

One gotcha here is that module instances are only shared if they are of the same version; in such cases, making an actual global may be the only valid solution, in which case you can explicitly assign the value to the window namespace.

window.foo = 12;
window.bar = 34;

function baz() {
    return foo + bar;
}

return { baz };