diff --git a/eslint.config.mjs b/eslint.config.mjs index f4bac84ec2..921be57464 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -99,6 +99,19 @@ export default defineConfig([ "sort-keys": "off" } }, + { + files: [ + "js/main.js", + "js/module.js", + "js/loader.js", + "js/socketclient.js", + "js/translator.js" + ], + rules: { + // Browser ESM entry files must always include the file extension in relative imports. + "import-x/extensions": ["error", "always"] + } + }, { files: ["**/*.js"], ignores: [ diff --git a/index.html b/index.html index dba2552069..806fcc34f7 100644 --- a/index.html +++ b/index.html @@ -43,7 +43,6 @@
- @@ -52,7 +51,6 @@ - diff --git a/js/main.js b/js/main.js index a13e11b41c..a9ab5b8382 100644 --- a/js/main.js +++ b/js/main.js @@ -1,8 +1,8 @@ -/* global addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, io */ +/* global addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions */ -// eslint-disable-next-line import-x/extensions +// Ensure Module global bridge is initialized before main bootstrap logic runs. +import "./module.js"; import { loadModules } from "./loader.js"; -// eslint-disable-next-line import-x/extensions import { Translator } from "./translator.js"; let modules = []; @@ -593,8 +593,9 @@ export const MM = { createDomObjects(); // Setup global socket listener for RELOAD event (watch mode) - if (typeof io !== "undefined") { - const socket = io("/", { + const ioClient = globalThis.io; + if (typeof ioClient !== "undefined") { + const socket = ioClient("/", { path: `${config.basePath || "/"}socket.io` }); diff --git a/js/module.js b/js/module.js index 9f4d0d8520..8ce48b2b84 100644 --- a/js/module.js +++ b/js/module.js @@ -1,10 +1,5 @@ -/* global nunjucks */ - -// eslint-disable-next-line import-x/extensions import { loadFileForModule } from "./loader.js"; -// eslint-disable-next-line import-x/extensions import { MMSocket } from "./socketclient.js"; -// eslint-disable-next-line import-x/extensions import { Translator } from "./translator.js"; /* @@ -166,13 +161,15 @@ export class Module { return this._nunjucksEnvironment; } - this._nunjucksEnvironment = new nunjucks.Environment(new nunjucks.WebLoader(this.file(""), { async: true }), { + const nunjucksEngine = globalThis.nunjucks; + + this._nunjucksEnvironment = new nunjucksEngine.Environment(new nunjucksEngine.WebLoader(this.file(""), { async: true }), { trimBlocks: true, lstripBlocks: true }); this._nunjucksEnvironment.addFilter("translate", (str, variables) => { - return nunjucks.runtime.markSafe(this.translate(str, variables)); + return nunjucksEngine.runtime.markSafe(this.translate(str, variables)); }); return this._nunjucksEnvironment; diff --git a/js/socketclient.js b/js/socketclient.js index 9a2dfbfd98..bf793c9698 100644 --- a/js/socketclient.js +++ b/js/socketclient.js @@ -1,18 +1,18 @@ -/* global io */ - export const MMSocket = function (moduleName) { if (typeof moduleName !== "string") { throw new Error("Please set the module name for the MMSocket."); } + const ioClient = globalThis.io; + if (typeof ioClient !== "function") { + throw new Error("socket.io client is not available."); + } + this.moduleName = moduleName; // Private Methods - let base = "/"; - if (typeof config !== "undefined" && typeof config.basePath !== "undefined") { - base = config.basePath; - } - this.socket = io(`/${this.moduleName}`, { + const base = globalThis.config?.basePath ?? "/"; + this.socket = ioClient(`/${this.moduleName}`, { path: `${base}socket.io`, pingInterval: 120000, // send pings every 2 mins pingTimeout: 120000 // wait up to 2 mins for a pong