Skip to content

Commit adf7003

Browse files
committed
add ConfigError
1 parent c2ba7d2 commit adf7003

3 files changed

Lines changed: 27 additions & 14 deletions

File tree

js/app.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const { setGlobalDispatcher, Agent } = require("undici");
1414

1515
const Server = require("./server");
1616
const Utils = require("./utils");
17+
const { ConfigError } = require("./utils");
1718

1819
const { getEnvVarsAsObj } = require("#server_functions");
1920
// common timeout value, provide environment override in case
@@ -258,7 +259,12 @@ function App () {
258259
Log.log("Sockets connected & modules started ...");
259260

260261
return global.config;
261-
} catch {
262+
} catch (err) {
263+
// planned ConfigErrors already logged their message before throwing
264+
if (!(err instanceof ConfigError)) {
265+
Log.error("Unexpected error during startup:", err);
266+
}
267+
262268
const int32 = new Int32Array(new SharedArrayBuffer(4));
263269
// wait 1000ms before exiting so that child processes (e.g. systeminformation) have some additional time
264270
Atomics.wait(int32, 0, 0, 1000);

js/utils.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ const { getConfigFilePath } = require("#server_functions");
1414

1515
const linter = new Linter({ configType: "flat" });
1616

17+
class ConfigError extends Error {
18+
constructor (message) {
19+
super(message);
20+
this.name = "ConfigError";
21+
}
22+
}
23+
1724
const requireFromString = (src) => {
1825
const m = new module.constructor();
1926
m._compile(src, "");
@@ -172,7 +179,7 @@ const loadConfig = () => {
172179
} else {
173180
Log.error(`Cannot access config file: ${configFilename}\n${error.message}`);
174181
}
175-
throw new Error("process.exit:1", { cause: error });
182+
throw new ConfigError("");
176183
}
177184
};
178185

@@ -219,7 +226,7 @@ const checkConfigFile = (configObject) => {
219226
errorMessage += `\nLine ${error.line} column ${error.column}: ${error.message}`;
220227
}
221228
Log.error(errorMessage);
222-
throw new Error("process.exit:1");
229+
throw new ConfigError("");
223230
}
224231
};
225232

@@ -241,27 +248,27 @@ const validateModulePositions = (data) => {
241248
// `modules` always exists (defaults.js provides a default array), but guard against it being overridden with a non-array value
242249
if (data.modules !== undefined && !Array.isArray(data.modules)) {
243250
Log.error("This module configuration contains errors:\nmodules must be an array");
244-
throw new Error("process.exit:1");
251+
throw new ConfigError("");
245252
}
246253

247254
// Validate each module entry
248255
for (const [index, mod] of (data.modules ?? []).entries()) {
249256
// Each module entry must be an object so we can safely inspect its fields
250257
if (mod === null || typeof mod !== "object" || Array.isArray(mod)) {
251258
Log.error(`This module configuration contains errors:\n${JSON.stringify(mod, null, 2)}\nmodule entry must be an object`);
252-
throw new Error("process.exit:1");
259+
throw new ConfigError("");
253260
}
254261

255262
// `module` (the module name) is required and must be a string
256263
if (typeof mod.module !== "string") {
257264
Log.error(`This module configuration contains errors:\n${JSON.stringify(mod, null, 2)}\nmodule: must be a string`);
258-
throw new Error("process.exit:1");
265+
throw new ConfigError("");
259266
}
260267

261268
// `position` is optional, but must be a string when provided
262269
if (mod.position !== undefined && typeof mod.position !== "string") {
263270
Log.error(`This module configuration contains errors:\n${JSON.stringify(mod, null, 2)}\nposition: must be a string`);
264-
throw new Error("process.exit:1");
271+
throw new ConfigError("");
265272
}
266273

267274
// `position` is optional, but when set it must match a known region
@@ -274,4 +281,4 @@ const validateModulePositions = (data) => {
274281
Log.info(styleText("green", "Your modules structure configuration doesn't contain errors :)"));
275282
};
276283

277-
module.exports = { loadConfig, getModulePositions, moduleHasValidPosition, getAvailableModulePositions, checkConfigFile };
284+
module.exports = { loadConfig, getModulePositions, moduleHasValidPosition, getAvailableModulePositions, checkConfigFile, ConfigError };

tests/unit/classes/utils_spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const fs = require("node:fs");
22

33
const Log = require("../../../js/logger");
4-
const { checkConfigFile } = require("../../../js/utils");
4+
const { checkConfigFile, ConfigError } = require("../../../js/utils");
55

66
const createConfigObject = (modules) => ({
77
configFilename: "config.js",
@@ -14,13 +14,13 @@ const runCheck = (modules) => {
1414
};
1515

1616
const expectExitForModules = (modules) => {
17-
vi.spyOn(process, "exit").mockImplementation((code) => {
18-
throw new Error(`process.exit:${code}`);
17+
vi.spyOn(process, "exit").mockImplementation(() => {
18+
throw new ConfigError("");
1919
});
2020

2121
expect(() => {
2222
runCheck(modules);
23-
}).toThrow("process.exit:1");
23+
}).toThrow(ConfigError);
2424
};
2525

2626
describe("utils", () => {
@@ -69,8 +69,8 @@ describe("utils", () => {
6969
});
7070

7171
it("warns for unknown positions without exiting", () => {
72-
const exitSpy = vi.spyOn(process, "exit").mockImplementation((code) => {
73-
throw new Error(`process.exit:${code}`);
72+
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => {
73+
throw new ConfigError("");
7474
});
7575

7676
expect(() => {

0 commit comments

Comments
 (0)