ESModules
ECMAScript modules can be used as panel scripts by selecting an .mjs file in Script → File.
A top-level .mjs file is compiled and evaluated as an ES module. Top-level .js files remain regular scripts, so existing scripts are not affected.
Imports
Module scripts use standard static import / export syntax. In simple terms, export makes a value available to other modules and import brings that value into the current module.
There are two common forms. A default export is imported without braces:
// message.js
export default function createMessage(text) {
return 'Hello, ' + text;
}
// Main module
import createMessage from './es_modules/message.js';
A module may have one default export. The importing module chooses the local name, so this would also be valid:
import makeMessage from './es_modules/message.js';
A named export is imported with braces, and its name normally has to match the exported name:
// math.mjs
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// Main module
import { add, subtract } from './es_modules/math.mjs';
Named imports can be renamed locally with as:
import { add as sum } from './es_modules/math.mjs';
A module can contain both a default export and named exports. They can then be imported together:
import createMessage, { helperUrl } from './es_modules/message.js';
For relative paths, ./ means "from the directory containing the current module", while ../ means "one directory above". For example, if main.mjs is in scripts/, then:
import { add } from './es_modules/math.mjs';
resolves to scripts/es_modules/math.mjs. Relative imports are always resolved relative to the file containing that particular import, including imports inside imported modules.
Relative module dependencies may use either .js or .mjs. An imported file is compiled as a module regardless of which of these two extensions it uses.
Nested and cyclic dependencies are supported. A resolved module file has one module instance within the panel realm, so importing the same resolved file through different relative paths does not evaluate it as separate modules.
Module scope and JSplitter callbacks
ES modules have module scope. Top-level declarations do not become properties of the global object.
JSplitter panel callbacks therefore need to be explicitly published on globalThis:
// This is module-local and is not a JSplitter callback:
function on_paint(gr) {
}
// Publish the callback explicitly:
globalThis.on_paint = function (gr) {
gr.WriteText('Hello from an ES module', font, 0xffeeeeee, 10, 10, 400, 30);
};
Ordinary module-local variables and functions should stay module-local. Only callback entry points that JSplitter needs to discover have to be assigned to globalThis.
import.meta
import.meta.url is available and contains the module's file:// URL:
console.log(import.meta.url);
include()
include() is not available while executing an ES module. Use static import statements instead.
Error handling
Module loading, parsing and evaluation errors are reported through the normal JSplitter script error path. This includes a missing imported file, a syntax error in a dependency, or an exception thrown while a dependency is being evaluated.
Current documented scope
The documented ES-module feature covers .mjs panel File scripts and local static imports. Dynamic import(), import maps, network modules and bare/package module specifiers are not part of the documented public contract.