2 Prelude
sirius edited this page 2026-10-06 17:05:25 +02:00

Prelude are the "invisible lines of code" that runs before the first line of a script file to create variables. Here's an example

// index.js
/*
* You might imagine some invisible lines of code before the first line like
* function require(...) { ... }
* function addEventListener(...) { ... }
*/

let circle = require(...); // but this is the first line of the script file
addEventListener(...);     // where did these functions come from?

JSCore supports per-file preludes.

Using a prelude

Each Prelude object represents a block of code creating one or more variables. Suppose we already have a few of them pre1, pre2 and pre3, they can be applied to a script file during import with

let circle = module.import("circle.js", [pre1, pre2, pre3]);

Then circle.js can use the variables defined by the preludes. The preludes are evaluated in order - if multiple preludes define the same variables, the last prelude defining the variable will overwrite the previous ones.

Warning

All imports to the same file must use the same preludes in the that order, otherwise there will be an error.

Creating a prelude

Use module.createPrelude((targetScope, targetModule) => void) to create a Prelude object, where

  • targetScope is the global scope of the script file the prelude is applied to, setting targetScope.myVar = 3 declares myVar in the global scope of that file
  • targetModule is the module object of the script file the prelude is applied to

Here's an example

const requirePrelude = module.createPrelude((targetScope, targetModule) {
  targetScope.require = function(path) {
    // puts itself as prelude for any file imported with require(...)
    return targetModule.import(path, [requirePrelude]);
  }
});

Avoiding prelude import cycles

Suppose preludeA is created in module A with module.createPrelude, if module B is imported with preludeA, then JSCore will consider B to have "imported" A.

flowchart LR
    A[module A]
    B[module B]
    B -->A

So consider the code below

// A.js
let preludeA = module.createPrelude(...);
let B = module.import('./B.js', [preludeA]);

Then A imports B with module.import, but B also "imports" A as it is imported with preludeA! So there is an import cycle.

flowchart LR
    A[module A]
    B[module B]
    B -->A
    A --> B

The solution is to define the prelude in another module.

// C.js
let preludeC = module.createPrelude(...);
module.exports = preludeC;
// A.js
let preludeC = module.import("./C.js", []);
let B = module.import("./B.js", [preludeC]);

which does not create any cycles.

flowchart TD
    A[module A]
    B[module B]
    C[module C]
    B -->C
    A --> C
    A --> B

This should remind you of how you need a third variable to swap two variables.

// a = 1, b = 2
let temp = b;
b = a;
a = temp;