Cleanup more callback things (#3051)

Looks quite stable on my computers, so maybe we give it a try?

---------

Co-authored-by: veeck <michael@veeck.de>
This commit is contained in:
Veeck 2023-02-26 18:36:47 +01:00 committed by GitHub
parent b5b61246e6
commit 1b2785cc56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1243 additions and 1244 deletions

View File

@ -37,10 +37,7 @@ _This release is scheduled to be released on 2023-04-01._
- Update dates in Calendar widgets every minute
- Cleanup jest coverage for patches
- Update `stylelint` dependencies, switch to `stylelint-config-standard` and handle `stylelint` issues
- Convert load callbacks to async/await
- Convert module start to async/await
- Convert translator callbacks to async/await
- Convert app-start/-stop callbacks to async/awaits
- Convert lots of callbacks to async/await
### Fixed

View File

@ -152,9 +152,8 @@ function App() {
* Loads a specific module.
*
* @param {string} module The name of the module (including subpath).
* @param {Function} callback Function to be called after loading
*/
function loadModule(module, callback) {
function loadModule(module) {
const elements = module.split("/");
const moduleName = elements[elements.length - 1];
let moduleFolder = `${__dirname}/../modules/${module}`;
@ -199,39 +198,37 @@ function App() {
m.setPath(path.resolve(moduleFolder));
nodeHelpers.push(m);
m.loaded(callback);
} else {
callback();
m.loaded();
}
}
/**
* Loads all modules.
*
* @param {Module[]} modules All modules to be loaded
* @param {Function} callback Function to be called after loading
* @param {string[]} modules All modules to be loaded
*/
function loadModules(modules, callback) {
Log.log("Loading module helpers ...");
async function loadModules(modules) {
return new Promise((resolve) => {
Log.log("Loading module helpers ...");
/**
*
*/
function loadNextModule() {
if (modules.length > 0) {
const nextModule = modules[0];
loadModule(nextModule, function () {
/**
*
*/
function loadNextModule() {
if (modules.length > 0) {
const nextModule = modules[0];
loadModule(nextModule);
modules = modules.slice(1);
loadNextModule();
});
} else {
// All modules are loaded
Log.log("All module helpers loaded.");
callback();
} else {
// All modules are loaded
Log.log("All module helpers loaded.");
resolve();
}
}
}
loadNextModule();
loadNextModule();
});
}
/**
@ -272,43 +269,41 @@ function App() {
Log.setLogLevel(config.logLevel);
let modules = [];
for (const module of config.modules) {
if (!modules.includes(module.module) && !module.disabled) {
modules.push(module.module);
}
}
await loadModules(modules);
loadModules(modules, async function () {
httpServer = new Server(config);
const { app, io } = await httpServer.open();
Log.log("Server started ...");
httpServer = new Server(config);
const { app, io } = await httpServer.open();
Log.log("Server started ...");
const nodePromises = [];
for (let nodeHelper of nodeHelpers) {
nodeHelper.setExpressApp(app);
nodeHelper.setSocketIO(io);
const nodePromises = [];
for (let nodeHelper of nodeHelpers) {
nodeHelper.setExpressApp(app);
nodeHelper.setSocketIO(io);
try {
nodePromises.push(nodeHelper.start());
} catch (error) {
Log.error(`Error when starting node_helper for module ${nodeHelper.name}:`);
Log.error(error);
}
try {
nodePromises.push(nodeHelper.start());
} catch (error) {
Log.error(`Error when starting node_helper for module ${nodeHelper.name}:`);
Log.error(error);
}
}
const results = await Promise.allSettled(nodePromises);
const results = await Promise.allSettled(nodePromises);
// Log errors that happened during async node_helper startup
results.forEach((result) => {
if (result.status === "rejected") {
Log.error(result.reason);
}
});
Log.log("Sockets connected & modules started ...");
// Log errors that happened during async node_helper startup
results.forEach((result) => {
if (result.status === "rejected") {
Log.error(result.reason);
}
});
Log.log("Sockets connected & modules started ...");
return config;
};

View File

@ -16,37 +16,9 @@ const Loader = (function () {
/* Private Methods */
/**
* Loops thru all modules and requests load for every module.
* Loops through all modules and requests start for every module.
*/
const loadModules = function () {
let moduleData = getModuleData();
const loadNextModule = function () {
if (moduleData.length > 0) {
const nextModule = moduleData[0];
loadModule(nextModule, function () {
moduleData = moduleData.slice(1);
loadNextModule();
});
} else {
// All modules loaded. Load custom.css
// This is done after all the modules so we can
// overwrite all the defined styles.
loadFile(config.customCss).then(() => {
// custom.css loaded. Start all modules.
startModules();
});
}
};
loadNextModule();
};
/**
* Loops thru all modules and requests start for every module.
*/
const startModules = function () {
const startModules = async function () {
const modulePromises = [];
for (const module of moduleObjects) {
try {
@ -57,25 +29,25 @@ const Loader = (function () {
}
}
Promise.allSettled(modulePromises).then((results) => {
// Log errors that happened during async node_helper startup
results.forEach((result) => {
if (result.status === "rejected") {
Log.error(result.reason);
}
});
const results = await Promise.allSettled(modulePromises);
// Notify core of loaded modules.
MM.modulesStarted(moduleObjects);
// Starting modules also hides any modules that have requested to be initially hidden
for (const thisModule of moduleObjects) {
if (thisModule.data.hiddenOnStartup) {
Log.info("Initially hiding " + thisModule.name);
thisModule.hide();
}
// Log errors that happened during async node_helper startup
results.forEach((result) => {
if (result.status === "rejected") {
Log.error(result.reason);
}
});
// Notify core of loaded modules.
MM.modulesStarted(moduleObjects);
// Starting modules also hides any modules that have requested to be initially hidden
for (const thisModule of moduleObjects) {
if (thisModule.data.hiddenOnStartup) {
Log.info("Initially hiding " + thisModule.name);
thisModule.hide();
}
}
};
/**
@ -130,32 +102,30 @@ const Loader = (function () {
};
/**
* Load modules via ajax request and create module objects.s
* Load modules via ajax request and create module objects.
*
* @param {object} module Information about the module we want to load.
* @param {Function} callback Function called when done.
* @returns {Promise<void>} resolved when module is loaded
*/
const loadModule = function (module, callback) {
const loadModule = async function (module) {
const url = module.path + module.file;
const afterLoad = function () {
/**
* @returns {Promise<void>}
*/
const afterLoad = async function () {
const moduleObject = Module.create(module.name);
if (moduleObject) {
bootstrapModule(module, moduleObject, function () {
callback();
});
} else {
callback();
await bootstrapModule(module, moduleObject);
}
};
if (loadedModuleFiles.indexOf(url) !== -1) {
afterLoad();
await afterLoad();
} else {
loadFile(url).then(() => {
loadedModuleFiles.push(url);
afterLoad();
});
await loadFile(url);
loadedModuleFiles.push(url);
await afterLoad();
}
};
@ -164,24 +134,21 @@ const Loader = (function () {
*
* @param {object} module Information about the module we want to load.
* @param {Module} mObj Modules instance.
* @param {Function} callback Function called when done.
*/
const bootstrapModule = function (module, mObj, callback) {
const bootstrapModule = async function (module, mObj) {
Log.info("Bootstrapping module: " + module.name);
mObj.setData(module);
mObj.loadScripts().then(() => {
Log.log("Scripts loaded for: " + module.name);
mObj.loadStyles().then(() => {
Log.log("Styles loaded for: " + module.name);
mObj.loadTranslations().then(() => {
Log.log("Translations loaded for: " + module.name);
moduleObjects.push(mObj);
callback();
});
});
});
await mObj.loadScripts();
Log.log("Scripts loaded for: " + module.name);
await mObj.loadStyles();
Log.log("Styles loaded for: " + module.name);
await mObj.loadTranslations();
Log.log("Translations loaded for: " + module.name);
moduleObjects.push(mObj);
};
/**
@ -235,8 +202,28 @@ const Loader = (function () {
/**
* Load all modules as defined in the config.
*/
loadModules: function () {
loadModules();
loadModules: async function () {
let moduleData = getModuleData();
/**
* @returns {Promise<void>} when all modules are loaded
*/
const loadNextModule = async function () {
if (moduleData.length > 0) {
const nextModule = moduleData[0];
await loadModule(nextModule);
moduleData = moduleData.slice(1);
await loadNextModule();
} else {
// All modules loaded. Load custom.css
// This is done after all the modules so we can
// overwrite all the defined styles.
await loadFile(config.customCss);
// custom.css loaded. Start all modules.
await startModules();
}
};
await loadNextModule();
},
/**
@ -250,7 +237,7 @@ const Loader = (function () {
loadFileForModule: async function (fileName, module) {
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
Log.log("File already loaded: " + fileName);
return Promise.resolve();
return;
}
if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) {

View File

@ -479,13 +479,14 @@ const MM = (function () {
/**
* Main init method.
*/
init: function () {
init: async function () {
Log.info("Initializing MagicMirror².");
loadConfig();
Log.setLogLevel(config.logLevel);
Translator.loadCoreTranslations(config.language).then(() => Loader.loadModules());
await Translator.loadCoreTranslations(config.language);
await Loader.loadModules();
},
/**

View File

@ -25,7 +25,7 @@ const Module = Class.extend({
// visibility when hiding and showing module.
lockStrings: [],
// Storage of the nunjuck Environment,
// Storage of the nunjucks Environment,
// This should not be referenced directly.
// Use the nunjucksEnvironment() to get it.
_nunjucksEnvironment: null,
@ -302,7 +302,7 @@ const Module = Class.extend({
/**
* Load all translations.
*/
async loadTranslations() {
loadTranslations: async function () {
const translations = this.getTranslations() || {};
const language = config.language.toLowerCase();

View File

@ -13,9 +13,8 @@ const NodeHelper = Class.extend({
Log.log("Initializing new module helper ...");
},
loaded(callback) {
loaded() {
Log.log(`Module helper loaded: ${this.name}`);
callback();
},
start() {

View File

@ -9,6 +9,9 @@ const path = require("path");
const ipfilter = require("express-ipfilter").IpFilter;
const fs = require("fs");
const helmet = require("helmet");
const socketio = require("socket.io");
const http = require("http");
const https = require("https");
const Log = require("logger");
const Utils = require("./utils.js");
@ -38,11 +41,11 @@ function Server(config) {
key: fs.readFileSync(config.httpsPrivateKey),
cert: fs.readFileSync(config.httpsCertificate)
};
server = require("https").Server(options, app);
server = https.Server(options, app);
} else {
server = require("http").Server(app);
server = http.Server(app);
}
const io = require("socket.io")(server, {
const io = socketio(server, {
cors: {
origin: /.*$/,
credentials: true

2191
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -51,31 +51,31 @@
"devDependencies": {
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jsdoc": "^39.8.0",
"eslint-plugin-jsdoc": "^40.0.0",
"eslint-plugin-prettier": "^4.2.1",
"express-basic-auth": "^1.2.1",
"husky": "^8.0.3",
"jest": "^29.4.2",
"jest": "^29.4.3",
"jsdom": "^21.1.0",
"lodash": "^4.17.21",
"playwright": "^1.30.0",
"playwright": "^1.31.1",
"prettier": "^2.8.4",
"pretty-quick": "^3.1.3",
"sinon": "^15.0.1",
"stylelint": "^15.1.0",
"stylelint": "^15.2.0",
"stylelint-config-standard": "^30.0.1",
"stylelint-prettier": "^2.0.0",
"stylelint-prettier": "^3.0.0",
"suncalc": "^1.9.0"
},
"optionalDependencies": {
"electron": "^22.2.0"
"electron": "^22.3.1"
},
"dependencies": {
"colors": "^1.4.0",
"console-stamp": "^3.1.0",
"console-stamp": "^3.1.1",
"digest-fetch": "^2.0.1",
"envsub": "^4.1.0",
"eslint": "^8.33.0",
"eslint": "^8.35.0",
"express": "^4.18.2",
"express-ipfilter": "^1.3.1",
"feedme": "^2.0.2",
@ -86,7 +86,7 @@
"moment": "^2.29.4",
"node-fetch": "^2.6.9",
"node-ical": "^0.15.3",
"socket.io": "^4.6.0"
"socket.io": "^4.6.1"
},
"_moduleAliases": {
"node_helper": "js/node_helper.js",

View File

@ -13,7 +13,7 @@ describe("Electron app environment", () => {
it("should open browserwindow", async () => {
const module = await helpers.getElement("#module_0_helloworld");
expect(await module.textContent()).toContain("Test Display Header");
expect(await global.electronApp.windows().length).toBe(1);
expect(global.electronApp.windows().length).toBe(1);
});
});

18
vendor/package-lock.json generated vendored
View File

@ -9,7 +9,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^6.3.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.40",
"moment-timezone": "^0.5.41",
"nunjucks": "^3.2.3",
"suncalc": "^1.9.0",
"weathericons": "^2.1.0"
@ -51,11 +51,11 @@
}
},
"node_modules/moment-timezone": {
"version": "0.5.40",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz",
"integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==",
"version": "0.5.41",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz",
"integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==",
"dependencies": {
"moment": ">= 2.9.0"
"moment": "^2.29.4"
},
"engines": {
"node": "*"
@ -123,11 +123,11 @@
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"moment-timezone": {
"version": "0.5.40",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz",
"integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==",
"version": "0.5.41",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz",
"integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==",
"requires": {
"moment": ">= 2.9.0"
"moment": "^2.29.4"
}
},
"nunjucks": {

2
vendor/package.json vendored
View File

@ -12,7 +12,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^6.3.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.40",
"moment-timezone": "^0.5.41",
"nunjucks": "^3.2.3",
"suncalc": "^1.9.0",
"weathericons": "^2.1.0"