From 729f7f0fd1328a3f54b6d2e476adbc95ed5d93e2 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:55:56 +0100 Subject: [PATCH] [core] refactor: enable ESLint rule require-await and handle detected issues (#4038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the `require-await` ESLint rule. Async functions without `await` are just regular functions with extra overhead — marking them `async` adds implicit Promise wrapping, can hide missing `return` statements, and misleads readers into expecting asynchronous behavior where there is none. While fixing the violations, I removed unnecessary `async` keywords from source files and from various test callbacks that never used `await`. --- defaultmodules/weather/providers/envcanada.js | 2 +- defaultmodules/weather/providers/openweathermap.js | 2 +- defaultmodules/weather/providers/pirateweather.js | 2 +- defaultmodules/weather/providers/smhi.js | 2 +- .../weather/providers/ukmetofficedatahub.js | 2 +- defaultmodules/weather/providers/weatherapi.js | 2 +- defaultmodules/weather/providers/weatherbit.js | 2 +- defaultmodules/weather/providers/weatherflow.js | 2 +- eslint.config.mjs | 1 + js/loader.js | 6 +++--- js/module.js | 2 +- serveronly/watcher.js | 4 ++-- tests/e2e/clientonly_spec.js | 4 ++-- tests/e2e/config_variables_spec.js | 8 ++++---- tests/e2e/modules/newsfeed_spec.js | 2 +- tests/unit/classes/translator_spec.js | 12 ++++++------ tests/unit/functions/server_functions_spec.js | 2 +- tests/unit/functions/updatenotification_spec.js | 2 +- tests/unit/helpers/global-setup.js | 2 +- tests/unit/modules/default/utils_spec.js | 2 +- .../default/weather/providers/envcanada_spec.js | 4 ++-- .../default/weather/providers/openweathermap_spec.js | 4 ++-- 22 files changed, 36 insertions(+), 35 deletions(-) diff --git a/defaultmodules/weather/providers/envcanada.js b/defaultmodules/weather/providers/envcanada.js index a50a8121..d09715fc 100644 --- a/defaultmodules/weather/providers/envcanada.js +++ b/defaultmodules/weather/providers/envcanada.js @@ -31,7 +31,7 @@ class EnvCanadaProvider { this.currentHour = null; // Track current hour for URL updates } - async initialize () { + initialize () { this.#validateConfig(); this.#initializeFetcher(); } diff --git a/defaultmodules/weather/providers/openweathermap.js b/defaultmodules/weather/providers/openweathermap.js index c75e1f92..119df32b 100644 --- a/defaultmodules/weather/providers/openweathermap.js +++ b/defaultmodules/weather/providers/openweathermap.js @@ -28,7 +28,7 @@ class OpenWeatherMapProvider { this.locationName = null; } - async initialize () { + initialize () { // Validate callbacks exist if (typeof this.onErrorCallback !== "function") { throw new Error("setCallbacks() must be called before initialize()"); diff --git a/defaultmodules/weather/providers/pirateweather.js b/defaultmodules/weather/providers/pirateweather.js index fbf14403..539eb877 100644 --- a/defaultmodules/weather/providers/pirateweather.js +++ b/defaultmodules/weather/providers/pirateweather.js @@ -24,7 +24,7 @@ class PirateweatherProvider { this.onErrorCallback = onErrorCallback; } - async initialize () { + initialize () { if (!this.config.apiKey) { Log.error("[pirateweather] No API key configured"); if (this.onErrorCallback) { diff --git a/defaultmodules/weather/providers/smhi.js b/defaultmodules/weather/providers/smhi.js index 26deb54e..70ffba8f 100644 --- a/defaultmodules/weather/providers/smhi.js +++ b/defaultmodules/weather/providers/smhi.js @@ -29,7 +29,7 @@ class SMHIProvider { this.onErrorCallback = null; } - async initialize () { + initialize () { try { // SMHI requires max 6 decimal places validateCoordinates(this.config, 6); diff --git a/defaultmodules/weather/providers/ukmetofficedatahub.js b/defaultmodules/weather/providers/ukmetofficedatahub.js index b55a6f2c..715458b0 100644 --- a/defaultmodules/weather/providers/ukmetofficedatahub.js +++ b/defaultmodules/weather/providers/ukmetofficedatahub.js @@ -35,7 +35,7 @@ class UkMetOfficeDataHubProvider { this.onErrorCallback = onErrorCallback; } - async initialize () { + initialize () { if (!this.config.apiKey || this.config.apiKey === "YOUR_API_KEY_HERE") { Log.error("[ukmetofficedatahub] No API key configured"); if (this.onErrorCallback) { diff --git a/defaultmodules/weather/providers/weatherapi.js b/defaultmodules/weather/providers/weatherapi.js index 46aef7f8..93f2f319 100644 --- a/defaultmodules/weather/providers/weatherapi.js +++ b/defaultmodules/weather/providers/weatherapi.js @@ -25,7 +25,7 @@ class WeatherAPIProvider { this.onErrorCallback = null; } - async initialize () { + initialize () { this.#validateConfig(); this.#initializeFetcher(); } diff --git a/defaultmodules/weather/providers/weatherbit.js b/defaultmodules/weather/providers/weatherbit.js index 14a64476..32c261fe 100644 --- a/defaultmodules/weather/providers/weatherbit.js +++ b/defaultmodules/weather/providers/weatherbit.js @@ -27,7 +27,7 @@ class WeatherbitProvider { this.onErrorCallback = onErrorCallback; } - async initialize () { + initialize () { if (!this.config.apiKey || this.config.apiKey === "YOUR_API_KEY_HERE") { Log.error("[weatherbit] No API key configured"); if (this.onErrorCallback) { diff --git a/defaultmodules/weather/providers/weatherflow.js b/defaultmodules/weather/providers/weatherflow.js index e4ba4a20..4797fa02 100644 --- a/defaultmodules/weather/providers/weatherflow.js +++ b/defaultmodules/weather/providers/weatherflow.js @@ -31,7 +31,7 @@ class WeatherFlowProvider { /** * Initialize the provider */ - async initialize () { + initialize () { if (!this.config.token || this.config.token === "YOUR_API_TOKEN_HERE") { Log.error("[weatherflow] No API token configured. Get one at https://tempestwx.com/"); if (this.onErrorCallback) { diff --git a/eslint.config.mjs b/eslint.config.mjs index ee3af12c..8e1ed23c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -75,6 +75,7 @@ export default defineConfig([ "object-shorthand": ["error", "methods"], "one-var": "off", "prefer-template": "error", + "require-await": "error", "sort-keys": "off" } }, diff --git a/js/loader.js b/js/loader.js index b6db8386..53283d59 100644 --- a/js/loader.js +++ b/js/loader.js @@ -193,7 +193,7 @@ const Loader = (function () { * @param {string} fileName Path of the file we want to load. * @returns {Promise} resolved when the file is loaded */ - const loadFile = async function (fileName) { + const loadFile = function (fileName) { const extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1); let script, stylesheet; @@ -267,10 +267,10 @@ const Loader = (function () { * @param {Module} module The module that calls the loadFile function. * @returns {Promise} resolved when the file is loaded */ - async loadFileForModule (fileName, module) { + loadFileForModule (fileName, module) { if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) { Log.log(`File already loaded: ${fileName}`); - return; + return Promise.resolve(); } if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) { diff --git a/js/module.js b/js/module.js index a600c50c..176ef980 100644 --- a/js/module.js +++ b/js/module.js @@ -43,7 +43,7 @@ const Module = Class.extend({ /** * Called when the module is started. */ - async start () { + start () { Log.info(`Starting module: ${this.name}`); }, diff --git a/serveronly/watcher.js b/serveronly/watcher.js index 0f75a5fb..95b62c2a 100644 --- a/serveronly/watcher.js +++ b/serveronly/watcher.js @@ -145,10 +145,10 @@ function notifyClientsToReload () { * Restart the server process * @param {string} reason The reason for the restart */ -async function restartServer (reason) { +function restartServer (reason) { if (restartTimer) clearTimeout(restartTimer); - restartTimer = setTimeout(async () => { + restartTimer = setTimeout(() => { Log.info(reason); if (child) { diff --git a/tests/e2e/clientonly_spec.js b/tests/e2e/clientonly_spec.js index 6df9a3c3..e19d4eee 100644 --- a/tests/e2e/clientonly_spec.js +++ b/tests/e2e/clientonly_spec.js @@ -133,7 +133,7 @@ describe("Clientonly parameter handling", () => { }); describe("Display environment check", () => { - it("should fail without DISPLAY or WAYLAND_DISPLAY when connecting to valid server", async () => { + it("should fail without DISPLAY or WAYLAND_DISPLAY when connecting to valid server", () => { // This test needs a running server to get past the connection phase // Without DISPLAY, it should fail with display error // For now, we just verify it fails (connection error comes first without server) @@ -159,7 +159,7 @@ describe("Clientonly with running server", () => { await delay(2000); }); - afterAll(async () => { + afterAll(() => { if (serverProcess && serverProcess.pid) { try { process.kill(-serverProcess.pid); diff --git a/tests/e2e/config_variables_spec.js b/tests/e2e/config_variables_spec.js index f3bf6505..4fb9cedd 100644 --- a/tests/e2e/config_variables_spec.js +++ b/tests/e2e/config_variables_spec.js @@ -9,19 +9,19 @@ describe("config with variables and secrets", () => { await helpers.stopApplication(); }); - it("config.language should be \"de\"", async () => { + it("config.language should be \"de\"", () => { expect(config.language).toBe("de"); }); - it("config.loglevel should be [\"ERROR\", \"LOG\", \"WARN\", \"INFO\"]", async () => { + it("config.loglevel should be [\"ERROR\", \"LOG\", \"WARN\", \"INFO\"]", () => { expect(config.logLevel).toStrictEqual(["ERROR", "LOG", "WARN", "INFO"]); }); - it("config.ipWhitelist should be [\"::ffff:127.0.0.1\", \"::1\", \"127.0.0.1\"]", async () => { + it("config.ipWhitelist should be [\"::ffff:127.0.0.1\", \"::1\", \"127.0.0.1\"]", () => { expect(config.ipWhitelist).toStrictEqual(["::ffff:127.0.0.1", "::1", "127.0.0.1"]); }); - it("config.timeFormat should be 12", async () => { + it("config.timeFormat should be 12", () => { expect(config.timeFormat).toBe(12); // default is 24 }); diff --git a/tests/e2e/modules/newsfeed_spec.js b/tests/e2e/modules/newsfeed_spec.js index 4a86d4af..0d4c161e 100644 --- a/tests/e2e/modules/newsfeed_spec.js +++ b/tests/e2e/modules/newsfeed_spec.js @@ -2,7 +2,7 @@ const fs = require("node:fs"); const { expect } = require("playwright/test"); const helpers = require("../helpers/global-setup"); -const runTests = async () => { +const runTests = () => { let page; describe("Default configuration", () => { diff --git a/tests/unit/classes/translator_spec.js b/tests/unit/classes/translator_spec.js index 383f823b..2f4de653 100644 --- a/tests/unit/classes/translator_spec.js +++ b/tests/unit/classes/translator_spec.js @@ -93,7 +93,7 @@ describe("Translator", () => { Translator.coreTranslationsFallback = coreTranslationsFallback; }; - it("should return custom module translation", async () => { + it("should return custom module translation", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); @@ -104,7 +104,7 @@ describe("Translator", () => { expect(translation).toBe("Hallo fewieden"); }); - it("should return core translation", async () => { + it("should return core translation", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); let translation = Translator.translate({ name: "MMM-Module" }, "FOO"); @@ -113,28 +113,28 @@ describe("Translator", () => { expect(translation).toBe("Bar Lorem Ipsum"); }); - it("should return custom module translation fallback", async () => { + it("should return custom module translation fallback", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "A key"); expect(translation).toBe("A translation"); }); - it("should return core translation fallback", async () => { + it("should return core translation fallback", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "Fallback"); expect(translation).toBe("core fallback"); }); - it("should return translation with placeholder for missing variables", async () => { + it("should return translation with placeholder for missing variables", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}"); expect(translation).toBe("Hallo {username}"); }); - it("should return key if no translation was found", async () => { + it("should return key if no translation was found", () => { const { Translator } = createTranslationTestEnvironment(); setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "MISSING"); diff --git a/tests/unit/functions/server_functions_spec.js b/tests/unit/functions/server_functions_spec.js index 91ee3cac..8fd0a014 100644 --- a/tests/unit/functions/server_functions_spec.js +++ b/tests/unit/functions/server_functions_spec.js @@ -163,7 +163,7 @@ describe("server_functions tests", () => { expect(corsResponse.set.mock.calls[2][1]).toBe("value2"); }); - it("Gets User-Agent from configuration", async () => { + it("Gets User-Agent from configuration", () => { const previousConfig = global.config; global.config = {}; let userAgent; diff --git a/tests/unit/functions/updatenotification_spec.js b/tests/unit/functions/updatenotification_spec.js index 09b39b18..f6617e4b 100644 --- a/tests/unit/functions/updatenotification_spec.js +++ b/tests/unit/functions/updatenotification_spec.js @@ -320,7 +320,7 @@ describe("Updatenotification", () => { describe("custom module", () => { const moduleName = "MMM-Fuel"; - beforeEach(async () => { + beforeEach(() => { gitRemoteOut = `origin\thttps://github.com/fewieden/${moduleName}.git (fetch)\norigin\thttps://github.com/fewieden/${moduleName}.git (push)\n`; gitRevParseOut = "9d8310163da94441073a93cead711ba43e8888d0"; gitStatusOut = "## master...origin/master"; diff --git a/tests/unit/helpers/global-setup.js b/tests/unit/helpers/global-setup.js index 132a02bb..d4a49778 100644 --- a/tests/unit/helpers/global-setup.js +++ b/tests/unit/helpers/global-setup.js @@ -1,3 +1,3 @@ -module.exports = async () => { +module.exports = () => { process.env.TZ = "UTC"; }; diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index efea6e28..e5d01632 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -7,7 +7,7 @@ describe("Default modules utils tests", () => { describe("formatTime", () => { const time = new Date(); - beforeEach(async () => { + beforeEach(() => { time.setHours(13, 13); }); diff --git a/tests/unit/modules/default/weather/providers/envcanada_spec.js b/tests/unit/modules/default/weather/providers/envcanada_spec.js index 85533c51..63086710 100644 --- a/tests/unit/modules/default/weather/providers/envcanada_spec.js +++ b/tests/unit/modules/default/weather/providers/envcanada_spec.js @@ -67,10 +67,10 @@ describe("EnvCanadaProvider", () => { expect(provider.config.type).toBe("current"); }); - it("should throw error if siteCode or provCode missing", async () => { + it("should throw error if siteCode or provCode missing", () => { const provider = new EnvCanadaProvider({ siteCode: "", provCode: "" }); provider.setCallbacks(vi.fn(), vi.fn()); - await expect(provider.initialize()).rejects.toThrow("siteCode and provCode are required"); + expect(() => provider.initialize()).toThrow("siteCode and provCode are required"); }); }); diff --git a/tests/unit/modules/default/weather/providers/openweathermap_spec.js b/tests/unit/modules/default/weather/providers/openweathermap_spec.js index 63039784..7b27fd71 100644 --- a/tests/unit/modules/default/weather/providers/openweathermap_spec.js +++ b/tests/unit/modules/default/weather/providers/openweathermap_spec.js @@ -70,9 +70,9 @@ describe("OpenWeatherMapProvider", () => { expect(provider.fetcher).toBeNull(); }); - it("should throw if setCallbacks not called before initialize", async () => { + it("should throw if setCallbacks not called before initialize", () => { const provider = new OpenWeatherMapProvider({ apiKey: "test" }); - await expect(provider.initialize()).rejects.toThrow("setCallbacks"); + expect(() => provider.initialize()).toThrow("setCallbacks"); }); });