From 8e1630e8bf21c7fe47b87d52bee83add03533cd4 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Thu, 2 Apr 2026 08:56:27 +0200 Subject: [PATCH] refactor: enable ESLint rule "no-unused-vars" and handle related issues (#4080) In PR #4072 GitHub Bot complained about an unused var. Instead of just removing that one, I checked why ESLint hadn't complained about it: We had disabled the rule for it. So I enabled rule and resolved the issues that ESLint then detected. Related to #4073 --- defaultmodules/calendar/calendar.js | 2 +- defaultmodules/calendar/calendarfetcher.js | 1 - defaultmodules/calendar/calendarfetcherutils.js | 2 +- defaultmodules/compliments/compliments.js | 2 +- defaultmodules/newsfeed/newsfeed.js | 2 +- defaultmodules/updatenotification/git_helper.js | 2 +- defaultmodules/updatenotification/update_helper.js | 4 ++-- defaultmodules/weather/providers/yr.js | 2 +- defaultmodules/weather/weather.js | 2 +- eslint.config.mjs | 1 - js/animateCSS.js | 3 ++- js/app.js | 6 +++--- js/electron.js | 2 +- js/http_fetcher.js | 2 +- js/ip_access_control.js | 4 ++-- js/loader.js | 1 + js/logger.js | 2 +- js/main.js | 4 ++-- js/server.js | 2 +- js/socketclient.js | 1 + js/translator.js | 2 +- js/utils.js | 4 ++-- serveronly/watcher.js | 4 ++-- tests/e2e/translations_spec.js | 6 +++--- tests/electron/helpers/global-setup.js | 4 ++-- tests/electron/modules/calendar_spec.js | 1 + tests/mocks/testNotification/testNotification.js | 6 +++--- tests/unit/modules/default/weather/providers/yr_spec.js | 1 - 28 files changed, 38 insertions(+), 37 deletions(-) diff --git a/defaultmodules/calendar/calendar.js b/defaultmodules/calendar/calendar.js index cfc62543..e70cbb85 100644 --- a/defaultmodules/calendar/calendar.js +++ b/defaultmodules/calendar/calendar.js @@ -167,7 +167,7 @@ Module.register("calendar", { this.selfUpdate(); }, - notificationReceived (notification, payload, sender) { + notificationReceived (notification, payload) { if (notification === "FETCH_CALENDAR") { this.sendSocketNotification(notification, { url: payload.url, id: this.identifier }); } diff --git a/defaultmodules/calendar/calendarfetcher.js b/defaultmodules/calendar/calendarfetcher.js index 557f9994..809b924c 100644 --- a/defaultmodules/calendar/calendarfetcher.js +++ b/defaultmodules/calendar/calendarfetcher.js @@ -1,6 +1,5 @@ const ical = require("node-ical"); const Log = require("logger"); -const { Agent } = require("undici"); const CalendarFetcherUtils = require("./calendarfetcherutils"); const HTTPFetcher = require("#http_fetcher"); diff --git a/defaultmodules/calendar/calendarfetcherutils.js b/defaultmodules/calendar/calendarfetcherutils.js index 7234514d..437c081e 100644 --- a/defaultmodules/calendar/calendarfetcherutils.js +++ b/defaultmodules/calendar/calendarfetcherutils.js @@ -62,7 +62,7 @@ const CalendarFetcherUtils = { // Subtract 1 second so that events that start on the middle of the night will not repeat. .subtract(1, "seconds"); - Object.entries(data).forEach(([key, event]) => { + Object.values(data).forEach((event) => { if (event.type !== "VEVENT") { return; } diff --git a/defaultmodules/compliments/compliments.js b/defaultmodules/compliments/compliments.js index a20810a9..2ded7c19 100644 --- a/defaultmodules/compliments/compliments.js +++ b/defaultmodules/compliments/compliments.js @@ -308,7 +308,7 @@ Module.register("compliments", { }, // Override notification handler. - notificationReceived (notification, payload, sender) { + notificationReceived (notification, payload) { if (notification === "CURRENTWEATHER_TYPE") { this.currentWeatherType = payload.type; } diff --git a/defaultmodules/newsfeed/newsfeed.js b/defaultmodules/newsfeed/newsfeed.js index b2490696..cbf1aefa 100644 --- a/defaultmodules/newsfeed/newsfeed.js +++ b/defaultmodules/newsfeed/newsfeed.js @@ -411,7 +411,7 @@ Module.register("newsfeed", { } }, - notificationReceived (notification, payload, sender) { + notificationReceived (notification) { const before = this.activeItem; if (notification === "MODULE_DOM_CREATED" && this.config.hideLoading) { this.hide(); diff --git a/defaultmodules/updatenotification/git_helper.js b/defaultmodules/updatenotification/git_helper.js index 0444c950..a9c577b7 100644 --- a/defaultmodules/updatenotification/git_helper.js +++ b/defaultmodules/updatenotification/git_helper.js @@ -51,7 +51,7 @@ class GitHelper { // Folder has .git and has at least one git remote, watch this folder this.gitRepos.push({ module: moduleName, folder: moduleFolder }); } - } catch (err) { + } catch { // Error when directory .git doesn't exist or doesn't have any remotes // This module is not managed with git, skip } diff --git a/defaultmodules/updatenotification/update_helper.js b/defaultmodules/updatenotification/update_helper.js index 34bf289c..7fcec31f 100644 --- a/defaultmodules/updatenotification/update_helper.js +++ b/defaultmodules/updatenotification/update_helper.js @@ -113,7 +113,7 @@ class Updater { Log.info(`Updating ${module.name}...`); return new Promise((resolve) => { - Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout, stderr) => { + Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout) => { if (error) { Log.error(`exec error: ${error}`); Result.error = true; @@ -143,7 +143,7 @@ class Updater { pm2Restart () { Log.info("[PM2] restarting MagicMirror..."); const pm2 = require("pm2"); - pm2.restart(this.PM2Id, (err, proc) => { + pm2.restart(this.PM2Id, (err) => { if (err) { Log.error("[PM2] restart Error", err); } diff --git a/defaultmodules/weather/providers/yr.js b/defaultmodules/weather/providers/yr.js index 0de8b519..9b44571f 100644 --- a/defaultmodules/weather/providers/yr.js +++ b/defaultmodules/weather/providers/yr.js @@ -314,7 +314,7 @@ class YrProvider { // Convert collected data to forecast objects const days = []; - for (const [dateStr, data] of dailyData) { + for (const data of dailyData.values()) { const stellarInfo = this.#getStellarInfoForDate(data.date); const dayData = { diff --git a/defaultmodules/weather/weather.js b/defaultmodules/weather/weather.js index cf0170a7..732958e6 100644 --- a/defaultmodules/weather/weather.js +++ b/defaultmodules/weather/weather.js @@ -1,4 +1,4 @@ -/* global WeatherProvider, WeatherUtils, WeatherObject, formatTime */ +/* global WeatherUtils, WeatherObject, formatTime */ Module.register("weather", { // Default module config. diff --git a/eslint.config.mjs b/eslint.config.mjs index fcde3a68..05232e45 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -68,7 +68,6 @@ export default defineConfig([ "no-throw-literal": "error", "no-undefined": "off", "no-unneeded-ternary": "error", - "no-unused-vars": "off", "no-useless-return": "error", "no-warning-comments": "off", "object-shorthand": ["error", "methods"], diff --git a/js/animateCSS.js b/js/animateCSS.js index 8721553e..eacb51d3 100644 --- a/js/animateCSS.js +++ b/js/animateCSS.js @@ -1,4 +1,5 @@ /* enumeration of animations in Array **/ + const AnimateCSSIn = [ // Attention seekers "bounce", @@ -155,4 +156,4 @@ function removeAnimateCSS (element, animation) { node.classList.remove("animate__animated", animationName); node.style.removeProperty("--animate-duration"); } -if (typeof window === "undefined") module.exports = { AnimateCSSIn, AnimateCSSOut }; +if (typeof window === "undefined") module.exports = { AnimateCSSIn, AnimateCSSOut, addAnimateCSS, removeAnimateCSS }; diff --git a/js/app.js b/js/app.js index 04bda122..398a1313 100644 --- a/js/app.js +++ b/js/app.js @@ -84,7 +84,7 @@ function App () { try { fs.accessSync(moduleFile, fs.constants.R_OK); - } catch (e) { + } catch { Log.warn(`No ${moduleFile} found for module: ${moduleName}.`); } @@ -93,7 +93,7 @@ function App () { let loadHelper = true; try { fs.accessSync(helperPath, fs.constants.R_OK); - } catch (e) { + } catch { loadHelper = false; Log.log(`No helper found for module: ${moduleName}.`); } @@ -188,7 +188,7 @@ function App () { try { fs.renameSync(`${global.root_path}/css/custom.css`, `${global.root_path}/${env.customCss}`); Log.warn(`WARNING! Your custom css file was moved from ${global.root_path}/css/custom.css to ${global.root_path}/${env.customCss}`); - } catch (err) { + } catch { Log.warn("WARNING! Your custom css file is currently located in the css folder. Please move it to the config folder!"); } } diff --git a/js/electron.js b/js/electron.js index 9b4465a1..ac48dd4b 100644 --- a/js/electron.js +++ b/js/electron.js @@ -115,7 +115,7 @@ function createWindow () { } // simulate mouse move to hide black cursor on start - mainWindow.webContents.on("dom-ready", (event) => { + mainWindow.webContents.on("dom-ready", () => { mainWindow.webContents.sendInputEvent({ type: "mouseMove", x: 0, y: 0 }); }); diff --git a/js/http_fetcher.js b/js/http_fetcher.js index f72b4a3b..830b08bd 100644 --- a/js/http_fetcher.js +++ b/js/http_fetcher.js @@ -300,7 +300,7 @@ class HTTPFetcher extends EventEmitter { try { const urlObj = new URL(this.url); shortUrl = `${urlObj.origin}${urlObj.pathname}${urlObj.search.length > 50 ? "?..." : urlObj.search}`; - } catch (urlError) { + } catch { // If URL parsing fails, use original URL } diff --git a/js/ip_access_control.js b/js/ip_access_control.js index be1f1063..0fef1a4e 100644 --- a/js/ip_access_control.js +++ b/js/ip_access_control.js @@ -22,12 +22,12 @@ function isAllowed (clientIp, whitelist) { // Single IP address - let ipaddr.process normalize both const allowedAddr = ipaddr.process(entry); return addr.toString() === allowedAddr.toString(); - } catch (err) { + } catch { Log.warn(`Invalid whitelist entry: ${entry}`); return false; } }); - } catch (err) { + } catch { Log.warn(`Failed to parse client IP: ${clientIp}`); return false; } diff --git a/js/loader.js b/js/loader.js index 53283d59..31cf7cc0 100644 --- a/js/loader.js +++ b/js/loader.js @@ -1,5 +1,6 @@ /* global defaultModules, vendor */ +// eslint-disable-next-line no-unused-vars const Loader = (function () { /* Create helper variables */ diff --git a/js/logger.js b/js/logger.js index b4b55228..28d6db52 100644 --- a/js/logger.js +++ b/js/logger.js @@ -29,7 +29,7 @@ return styleText("gray", parentDir === "js" ? `[${baseName}]` : `[${parentDir}]`); } } - } catch (err) { /* ignore */ } + } catch { /* ignore */ } return styleText("gray", "[unknown]"); }; diff --git a/js/main.js b/js/main.js index 359f71df..0e20c41e 100644 --- a/js/main.js +++ b/js/main.js @@ -1,4 +1,4 @@ -/* global Loader, defaults, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, io */ +/* global Loader, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, io */ const MM = (function () { let modules = []; @@ -408,7 +408,7 @@ const MM = (function () { updateWrapperStates(); // Waiting for DOM-changes done in updateWrapperStates before we can start the animation. - const dummy = moduleWrapper.parentElement.parentElement.offsetHeight; + void moduleWrapper.parentElement.parentElement.offsetHeight; moduleWrapper.style.opacity = 1; if (haveAnimateName) { diff --git a/js/server.js b/js/server.js index 9922958c..081e9089 100644 --- a/js/server.js +++ b/js/server.js @@ -98,7 +98,7 @@ function Server (configObj) { } let directories = ["/config", "/css", "/favicon.svg", "/defaultmodules", "/modules", "/node_modules/animate.css", "/node_modules/@fontsource", "/node_modules/@fortawesome", "/translations", "/tests/configs", "/tests/mocks"]; - for (const [key, value] of Object.entries(vendor)) { + for (const value of Object.values(vendor)) { const dirArr = value.split("/"); if (dirArr[0] === "node_modules") directories.push(`/${dirArr[0]}/${dirArr[1]}`); } diff --git a/js/socketclient.js b/js/socketclient.js index 73fdbf08..0c3a7c66 100644 --- a/js/socketclient.js +++ b/js/socketclient.js @@ -1,5 +1,6 @@ /* global io */ +// eslint-disable-next-line no-unused-vars const MMSocket = function (moduleName) { if (typeof moduleName !== "string") { throw new Error("Please set the module name for the MMSocket."); diff --git a/js/translator.js b/js/translator.js index 497c4268..b1b52ec7 100644 --- a/js/translator.js +++ b/js/translator.js @@ -17,7 +17,7 @@ const Translator = (function () { throw new Error(`Unexpected response status: ${response.status}`); } return await response.json(); - } catch (exception) { + } catch { Log.error(`Loading json file =${file} failed`); return null; } diff --git a/js/utils.js b/js/utils.js index f1c80ba2..e947bee9 100644 --- a/js/utils.js +++ b/js/utils.js @@ -55,7 +55,7 @@ const getModulePositions = () => { try { fs.writeFileSync(discoveredPositionsJSFilename, `const modulePositions=${JSON.stringify(modulePositions)}`); } - catch (error) { + catch { Log.error("unable to write js/positions.js with the discovered module positions\nmake the MagicMirror/js folder writeable by the user starting MagicMirror"); } } @@ -111,7 +111,7 @@ const loadConfig = () => { try { fs.accessSync(templateFile, fs.constants.F_OK); Log.warn("config.js.template files are deprecated and not used anymore. You can use variables inside config.js so copy the template file content into config.js if needed."); - } catch (error) { + } catch { // no action } diff --git a/serveronly/watcher.js b/serveronly/watcher.js index c73be294..ab8aa0a1 100644 --- a/serveronly/watcher.js +++ b/serveronly/watcher.js @@ -35,7 +35,7 @@ function getServerConfig () { port: global.mmPort || config.port || 8080, address: config.address || "localhost" }; - } catch (err) { + } catch { serverConfig = { port: 8080, address: "localhost" }; } @@ -248,7 +248,7 @@ try { Log.warn(`Watch target is not a file (directories not supported): ${targetPath}`); } } -} catch (err) { +} catch { // Config file might not exist or be invalid, use fallback targets Log.warn("Could not load watchTargets from config."); } diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 12a364d0..42a07391 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -76,7 +76,7 @@ describe("translations", () => { it("should load translation file", async () => { const { Translator, Module, config } = dom.window; config.language = "en"; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); @@ -89,7 +89,7 @@ describe("translations", () => { it("should load translation + fallback file", async () => { const { Translator, Module } = dom.window; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); @@ -104,7 +104,7 @@ describe("translations", () => { it("should load translation fallback file", async () => { const { Translator, Module, config } = dom.window; config.language = "--"; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); diff --git a/tests/electron/helpers/global-setup.js b/tests/electron/helpers/global-setup.js index 91c542d0..8df47c49 100644 --- a/tests/electron/helpers/global-setup.js +++ b/tests/electron/helpers/global-setup.js @@ -70,7 +70,7 @@ exports.stopApplication = async (timeout = 10000) => { if (electronProcess && !electronProcess.killed) { electronProcess.kill("SIGKILL"); } - } catch (error) { + } catch { // Ignore errors caused by Playwright already tearing down the connection } }; @@ -80,7 +80,7 @@ exports.stopApplication = async (timeout = 10000) => { app.close(), new Promise((_, reject) => setTimeout(() => reject(new Error("Electron close timeout")), timeout)) ]); - } catch (error) { + } catch { killElectron(); } }; diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 1be60c4d..c37f3a43 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -26,6 +26,7 @@ describe("Calendar module", () => { * Use this for debugging broken tests, it will console log the text of the calendar module * @returns {Promise} */ + // eslint-disable-next-line no-unused-vars const logAllText = async () => { expect(global.page).not.toBeNull(); const loc = await global.page.locator(".calendar .event"); diff --git a/tests/mocks/testNotification/testNotification.js b/tests/mocks/testNotification/testNotification.js index 735af025..a18cc026 100644 --- a/tests/mocks/testNotification/testNotification.js +++ b/tests/mocks/testNotification/testNotification.js @@ -42,9 +42,9 @@ Module.register("testNotification", { let tableRow = document.createElement("tr"); table.appendChild(tableRow); - let tablecol1 = this.maketd(tableRow, col1); - let tablecol2 = this.maketd(tableRow, col2); - let tablecol3 = this.maketd(tableRow, col3); + this.maketd(tableRow, col1); + this.maketd(tableRow, col2); + this.maketd(tableRow, col3); return tableRow; }, diff --git a/tests/unit/modules/default/weather/providers/yr_spec.js b/tests/unit/modules/default/weather/providers/yr_spec.js index 4602a8d8..64598f25 100644 --- a/tests/unit/modules/default/weather/providers/yr_spec.js +++ b/tests/unit/modules/default/weather/providers/yr_spec.js @@ -15,7 +15,6 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } import yrData from "../../../../../mocks/weather_yr.json" with { type: "json" }; const YR_FORECAST_URL = "https://api.met.no/weatherapi/locationforecast/**"; -const YR_SUNRISE_URL = "https://api.met.no/weatherapi/sunrise/**"; // Fixed time: 30 minutes after the first timeseries entry (2026-02-06T21:00:00Z) // This ensures timeseries[0] is always chosen as the closest past entry.