build: properly workaround Ajv TS type definitions bug
[e-mobility-charging-stations-simulator.git] / src / utils / Configuration.ts
CommitLineData
d972af76 1import { type FSWatcher, readFileSync, watch } from 'node:fs';
4354af5a 2import { dirname, join } from 'node:path';
10687422 3import { env } from 'node:process';
130783a7 4import { fileURLToPath } from 'node:url';
8114d10e
JB
5
6import chalk from 'chalk';
088ee3c1 7import merge from 'just-merge';
8114d10e 8
a418c77b 9import {
4354af5a
JB
10 buildPerformanceUriFilePath,
11 checkWorkerElementsPerWorker,
12 checkWorkerProcessType,
13 getDefaultPerformanceStorageUri,
14 handleFileException,
a418c77b 15 logPrefix,
a6ef1ece
JB
16} from './ConfigurationUtils.js';
17import { Constants } from './Constants.js';
18import { hasOwnProp, isCFEnvironment, isUndefined, once } from './Utils.js';
83e00df1 19import {
268a74bb 20 ApplicationProtocol,
83e00df1 21 type ConfigurationData,
5d049829 22 ConfigurationSection,
268a74bb 23 FileType,
3d48c1c1 24 type LogConfiguration,
83e00df1
JB
25 type StationTemplateUrl,
26 type StorageConfiguration,
268a74bb 27 StorageType,
e7aeea18 28 SupervisionUrlDistribution,
83e00df1
JB
29 type UIServerConfiguration,
30 type WorkerConfiguration,
a6ef1ece 31} from '../types/index.js';
769d3b10
JB
32import {
33 DEFAULT_ELEMENT_START_DELAY,
34 DEFAULT_POOL_MAX_SIZE,
35 DEFAULT_POOL_MIN_SIZE,
36 DEFAULT_WORKER_START_DELAY,
37 WorkerProcessType,
a6ef1ece 38} from '../worker/index.js';
7dde0b73 39
e7c0fce0
JB
40type ConfigurationSectionType =
41 | LogConfiguration
42 | StorageConfiguration
43 | WorkerConfiguration
44 | UIServerConfiguration;
45
268a74bb 46export class Configuration {
6501eda9
JB
47 public static configurationChangeCallback: () => Promise<void>;
48
d972af76
JB
49 private static configurationFile = join(
50 dirname(fileURLToPath(import.meta.url)),
e7aeea18 51 'assets',
5edd8ba0 52 'config.json',
e7aeea18 53 );
10068088 54
1b4ccee3 55 private static configurationFileReloading = false;
c7db8ecb 56 private static configurationData?: ConfigurationData;
3602e107 57 private static configurationFileWatcher?: FSWatcher;
974efe6c
JB
58 private static configurationSectionCache = new Map<
59 ConfigurationSection,
e7c0fce0 60 ConfigurationSectionType
5d049829
JB
61 >([
62 [ConfigurationSection.log, Configuration.buildLogSection()],
63 [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()],
64 [ConfigurationSection.worker, Configuration.buildWorkerSection()],
65 [ConfigurationSection.uiServer, Configuration.buildUIServerSection()],
66 ]);
974efe6c 67
d5bd1c00
JB
68 private constructor() {
69 // This is intentional
70 }
71
e7c0fce0
JB
72 public static getConfigurationSection<T extends ConfigurationSectionType>(
73 sectionName: ConfigurationSection,
74 ): T {
81b9a105
JB
75 if (!Configuration.isConfigurationSectionCached(sectionName)) {
76 Configuration.cacheConfigurationSection(sectionName);
c1c97db8 77 }
5d049829
JB
78 return Configuration.configurationSectionCache.get(sectionName) as T;
79 }
80
5d049829 81 public static getStationTemplateUrls(): StationTemplateUrl[] | undefined {
5f742aac
JB
82 const checkDeprecatedConfigurationKeysOnce = once(
83 Configuration.checkDeprecatedConfigurationKeys.bind(Configuration),
0898a067 84 Configuration,
5f742aac
JB
85 );
86 checkDeprecatedConfigurationKeysOnce();
5d049829
JB
87 return Configuration.getConfigurationData()?.stationTemplateUrls;
88 }
89
5d049829 90 public static getSupervisionUrls(): string | string[] | undefined {
a37fc6dc
JB
91 if (
92 !isUndefined(
1c9de2b9 93 Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData],
a37fc6dc
JB
94 )
95 ) {
5d049829 96 Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![
a37fc6dc 97 'supervisionURLs' as keyof ConfigurationData
5d049829
JB
98 ] as string | string[];
99 }
100 return Configuration.getConfigurationData()?.supervisionUrls;
101 }
102
103 public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined {
5d049829
JB
104 return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution')
105 ? Configuration.getConfigurationData()?.supervisionUrlDistribution
106 : SupervisionUrlDistribution.ROUND_ROBIN;
107 }
108
c831d2bc 109 public static workerPoolInUse(): boolean {
1d8f226b 110 return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes(
eb979012
JB
111 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
112 .processType!,
c831d2bc
JB
113 );
114 }
115
116 public static workerDynamicPoolInUse(): boolean {
eb979012
JB
117 return (
118 Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
119 .processType === WorkerProcessType.dynamicPool
120 );
c831d2bc
JB
121 }
122
81b9a105
JB
123 private static isConfigurationSectionCached(sectionName: ConfigurationSection): boolean {
124 return Configuration.configurationSectionCache.has(sectionName);
125 }
126
127 private static cacheConfigurationSection(sectionName: ConfigurationSection): void {
128 switch (sectionName) {
129 case ConfigurationSection.log:
130 Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection());
131 break;
132 case ConfigurationSection.performanceStorage:
133 Configuration.configurationSectionCache.set(
134 sectionName,
135 Configuration.buildPerformanceStorageSection(),
136 );
137 break;
138 case ConfigurationSection.worker:
139 Configuration.configurationSectionCache.set(
140 sectionName,
141 Configuration.buildWorkerSection(),
142 );
143 break;
144 case ConfigurationSection.uiServer:
145 Configuration.configurationSectionCache.set(
146 sectionName,
147 Configuration.buildUIServerSection(),
148 );
149 break;
150 default:
151 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
152 throw new Error(`Unknown configuration section '${sectionName}'`);
153 }
154 }
155
5d049829 156 private static buildUIServerSection(): UIServerConfiguration {
675fa8e3 157 let uiServerConfiguration: UIServerConfiguration = {
b803eefa 158 enabled: false,
1f7fa4de 159 type: ApplicationProtocol.WS,
c127bd64 160 options: {
adbddcb4
JB
161 host: Constants.DEFAULT_UI_SERVER_HOST,
162 port: Constants.DEFAULT_UI_SERVER_PORT,
c127bd64 163 },
6a49ad23 164 };
f74e97ac 165 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) {
598c886d
JB
166 uiServerConfiguration = merge<UIServerConfiguration>(
167 uiServerConfiguration,
f74e97ac 168 Configuration.getConfigurationData()!.uiServer!,
598c886d 169 );
6a49ad23 170 }
9bf0ef23 171 if (isCFEnvironment() === true) {
72092cfc 172 delete uiServerConfiguration.options?.host;
10687422 173 uiServerConfiguration.options!.port = parseInt(env.PORT!);
b803eefa 174 }
5d049829 175 return uiServerConfiguration;
6a49ad23
JB
176 }
177
5d049829 178 private static buildPerformanceStorageSection(): StorageConfiguration {
6a49ad23
JB
179 let storageConfiguration: StorageConfiguration = {
180 enabled: false,
181 type: StorageType.JSON_FILE,
4354af5a 182 uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE),
6a49ad23 183 };
f74e97ac 184 if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
e7aeea18 185 storageConfiguration = {
1ba1e8fb 186 ...storageConfiguration,
f74e97ac
JB
187 ...Configuration.getConfigurationData()?.performanceStorage,
188 ...(Configuration.getConfigurationData()?.performanceStorage?.type ===
189 StorageType.JSON_FILE &&
190 Configuration.getConfigurationData()?.performanceStorage?.uri && {
4354af5a 191 uri: buildPerformanceUriFilePath(
f74e97ac 192 new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname,
e8044a69 193 ),
f682b2dc 194 }),
72f041bd 195 };
72f041bd 196 }
5d049829 197 return storageConfiguration;
7dde0b73
JB
198 }
199
5d049829 200 private static buildLogSection(): LogConfiguration {
3d48c1c1
JB
201 const defaultLogConfiguration: LogConfiguration = {
202 enabled: true,
203 file: 'logs/combined.log',
204 errorFile: 'logs/error.log',
205 statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL,
206 level: 'info',
207 format: 'simple',
208 rotate: true,
209 };
210 const deprecatedLogConfiguration: LogConfiguration = {
f74e97ac
JB
211 ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && {
212 enabled: Configuration.getConfigurationData()?.logEnabled,
3d48c1c1 213 }),
f74e97ac
JB
214 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && {
215 file: Configuration.getConfigurationData()?.logFile,
3d48c1c1 216 }),
f74e97ac
JB
217 ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && {
218 errorFile: Configuration.getConfigurationData()?.logErrorFile,
3d48c1c1 219 }),
f74e97ac
JB
220 ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && {
221 statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval,
3d48c1c1 222 }),
f74e97ac
JB
223 ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && {
224 level: Configuration.getConfigurationData()?.logLevel,
3d48c1c1 225 }),
f74e97ac
JB
226 ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && {
227 console: Configuration.getConfigurationData()?.logConsole,
3d48c1c1 228 }),
f74e97ac
JB
229 ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && {
230 format: Configuration.getConfigurationData()?.logFormat,
3d48c1c1 231 }),
f74e97ac
JB
232 ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && {
233 rotate: Configuration.getConfigurationData()?.logRotate,
3d48c1c1 234 }),
f74e97ac
JB
235 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && {
236 maxFiles: Configuration.getConfigurationData()?.logMaxFiles,
3d48c1c1 237 }),
f74e97ac
JB
238 ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && {
239 maxSize: Configuration.getConfigurationData()?.logMaxSize,
3d48c1c1
JB
240 }),
241 };
242 const logConfiguration: LogConfiguration = {
243 ...defaultLogConfiguration,
244 ...deprecatedLogConfiguration,
f74e97ac
JB
245 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) &&
246 Configuration.getConfigurationData()?.log),
3d48c1c1 247 };
5d049829 248 return logConfiguration;
3d48c1c1
JB
249 }
250
5d049829 251 private static buildWorkerSection(): WorkerConfiguration {
3602e107
JB
252 const defaultWorkerConfiguration: WorkerConfiguration = {
253 processType: WorkerProcessType.workerSet,
254 startDelay: DEFAULT_WORKER_START_DELAY,
255 elementsPerWorker: 'auto',
256 elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
257 poolMinSize: DEFAULT_POOL_MIN_SIZE,
258 poolMaxSize: DEFAULT_POOL_MAX_SIZE,
259 };
260 const deprecatedWorkerConfiguration: WorkerConfiguration = {
261 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
262 processType: Configuration.getConfigurationData()?.workerProcess,
263 }),
264 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && {
265 startDelay: Configuration.getConfigurationData()?.workerStartDelay,
266 }),
267 ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
268 elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker,
269 }),
270 ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
271 elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay,
272 }),
273 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
274 poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize,
275 }),
276 ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && {
277 poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize,
278 }),
279 };
280 hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') &&
281 delete Configuration.getConfigurationData()?.workerPoolStrategy;
282 const workerConfiguration: WorkerConfiguration = {
283 ...defaultWorkerConfiguration,
284 ...deprecatedWorkerConfiguration,
285 ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) &&
286 Configuration.getConfigurationData()?.worker),
287 };
4354af5a
JB
288 checkWorkerProcessType(workerConfiguration.processType!);
289 checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker);
3602e107
JB
290 return workerConfiguration;
291 }
292
3602e107 293 private static checkDeprecatedConfigurationKeys() {
3602e107
JB
294 // connection timeout
295 Configuration.warnDeprecatedConfigurationKey(
296 'autoReconnectTimeout',
297 undefined,
298 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
299 );
300 Configuration.warnDeprecatedConfigurationKey(
301 'connectionTimeout',
302 undefined,
303 "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead",
304 );
305 // connection retries
306 Configuration.warnDeprecatedConfigurationKey(
307 'autoReconnectMaxRetries',
308 undefined,
309 'Use it in charging station template instead',
310 );
311 // station template url(s)
312 Configuration.warnDeprecatedConfigurationKey(
313 'stationTemplateURLs',
314 undefined,
315 "Use 'stationTemplateUrls' instead",
316 );
317 !isUndefined(
1c9de2b9 318 Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData],
3602e107
JB
319 ) &&
320 (Configuration.getConfigurationData()!.stationTemplateUrls =
321 Configuration.getConfigurationData()![
322 'stationTemplateURLs' as keyof ConfigurationData
323 ] as StationTemplateUrl[]);
1c9de2b9 324 Configuration.getConfigurationData()?.stationTemplateUrls.forEach(
3602e107 325 (stationTemplateUrl: StationTemplateUrl) => {
1c9de2b9 326 if (!isUndefined(stationTemplateUrl?.['numberOfStation' as keyof StationTemplateUrl])) {
3602e107 327 console.error(
4354af5a 328 `${chalk.green(logPrefix())} ${chalk.red(
3602e107
JB
329 `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`,
330 )}`,
331 );
332 }
333 },
334 );
335 // supervision url(s)
336 Configuration.warnDeprecatedConfigurationKey(
337 'supervisionURLs',
338 undefined,
339 "Use 'supervisionUrls' instead",
340 );
341 // supervision urls distribution
342 Configuration.warnDeprecatedConfigurationKey(
343 'distributeStationToTenantEqually',
344 undefined,
345 "Use 'supervisionUrlDistribution' instead",
346 );
347 Configuration.warnDeprecatedConfigurationKey(
348 'distributeStationsToTenantsEqually',
349 undefined,
350 "Use 'supervisionUrlDistribution' instead",
351 );
352 // worker section
e7aeea18 353 Configuration.warnDeprecatedConfigurationKey(
e80bc579 354 'useWorkerPool',
1895299d 355 undefined,
974efe6c 356 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
cf2a5d9b
JB
357 );
358 Configuration.warnDeprecatedConfigurationKey(
359 'workerProcess',
1895299d 360 undefined,
974efe6c 361 `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`,
cf2a5d9b
JB
362 );
363 Configuration.warnDeprecatedConfigurationKey(
364 'workerStartDelay',
1895299d 365 undefined,
974efe6c 366 `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`,
cf2a5d9b
JB
367 );
368 Configuration.warnDeprecatedConfigurationKey(
369 'chargingStationsPerWorker',
1895299d 370 undefined,
974efe6c 371 `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`,
cf2a5d9b
JB
372 );
373 Configuration.warnDeprecatedConfigurationKey(
374 'elementStartDelay',
1895299d 375 undefined,
974efe6c 376 `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead`,
cf2a5d9b
JB
377 );
378 Configuration.warnDeprecatedConfigurationKey(
379 'workerPoolMinSize',
1895299d 380 undefined,
974efe6c 381 `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`,
e7aeea18 382 );
e7aeea18 383 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 384 'workerPoolSize',
1895299d 385 undefined,
974efe6c 386 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
e7aeea18 387 );
cf2a5d9b 388 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 389 'workerPoolMaxSize',
1895299d 390 undefined,
974efe6c 391 `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`,
cf2a5d9b
JB
392 );
393 Configuration.warnDeprecatedConfigurationKey(
1d8f226b 394 'workerPoolStrategy',
1895299d 395 undefined,
974efe6c 396 `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`,
cf2a5d9b 397 );
eda9c451
JB
398 Configuration.warnDeprecatedConfigurationKey(
399 'poolStrategy',
974efe6c 400 ConfigurationSection.worker,
eda9c451
JB
401 'Not publicly exposed to end users',
402 );
1d8f226b
JB
403 if (
404 Configuration.getConfigurationData()?.worker?.processType ===
405 ('staticPool' as WorkerProcessType)
406 ) {
407 console.error(
4354af5a 408 `${chalk.green(logPrefix())} ${chalk.red(
1d8f226b
JB
409 `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`,
410 )}`,
411 );
412 }
3602e107
JB
413 // log section
414 Configuration.warnDeprecatedConfigurationKey(
415 'logEnabled',
416 undefined,
417 `Use '${ConfigurationSection.log}' section to define the logging enablement instead`,
418 );
419 Configuration.warnDeprecatedConfigurationKey(
420 'logFile',
421 undefined,
422 `Use '${ConfigurationSection.log}' section to define the log file instead`,
423 );
424 Configuration.warnDeprecatedConfigurationKey(
425 'logErrorFile',
426 undefined,
427 `Use '${ConfigurationSection.log}' section to define the log error file instead`,
428 );
429 Configuration.warnDeprecatedConfigurationKey(
430 'logConsole',
431 undefined,
432 `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`,
433 );
434 Configuration.warnDeprecatedConfigurationKey(
435 'logStatisticsInterval',
436 undefined,
437 `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`,
438 );
439 Configuration.warnDeprecatedConfigurationKey(
440 'logLevel',
441 undefined,
442 `Use '${ConfigurationSection.log}' section to define the log level instead`,
443 );
444 Configuration.warnDeprecatedConfigurationKey(
445 'logFormat',
446 undefined,
447 `Use '${ConfigurationSection.log}' section to define the log format instead`,
448 );
449 Configuration.warnDeprecatedConfigurationKey(
450 'logRotate',
451 undefined,
452 `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`,
453 );
454 Configuration.warnDeprecatedConfigurationKey(
455 'logMaxFiles',
456 undefined,
457 `Use '${ConfigurationSection.log}' section to define the log maximum files instead`,
458 );
459 Configuration.warnDeprecatedConfigurationKey(
460 'logMaxSize',
461 undefined,
462 `Use '${ConfigurationSection.log}' section to define the log maximum size instead`,
463 );
464 // performanceStorage section
465 Configuration.warnDeprecatedConfigurationKey(
466 'URI',
467 ConfigurationSection.performanceStorage,
468 "Use 'uri' instead",
469 );
470 // uiServer section
471 if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) {
472 console.error(
4354af5a 473 `${chalk.green(logPrefix())} ${chalk.red(
3602e107
JB
474 `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`,
475 )}`,
b5b2c3e8
JB
476 );
477 }
7dde0b73 478 }
eb3937cb 479
e7aeea18
JB
480 private static warnDeprecatedConfigurationKey(
481 key: string,
482 sectionName?: string,
5edd8ba0 483 logMsgToAppend = '',
e7aeea18 484 ) {
e7aeea18
JB
485 if (
486 sectionName &&
1c9de2b9
JB
487 !isUndefined(
488 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData],
489 ) &&
f74e97ac 490 !isUndefined(
a37fc6dc 491 (
1c9de2b9 492 Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] as Record<
a37fc6dc
JB
493 string,
494 unknown
495 >
1c9de2b9 496 )?.[key],
f74e97ac 497 )
e7aeea18
JB
498 ) {
499 console.error(
4354af5a 500 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
501 `Deprecated configuration key '${key}' usage in section '${sectionName}'${
502 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
503 }`,
504 )}`,
e7aeea18 505 );
a37fc6dc 506 } else if (
1c9de2b9 507 !isUndefined(Configuration.getConfigurationData()?.[key as keyof ConfigurationData])
a37fc6dc 508 ) {
e7aeea18 509 console.error(
4354af5a 510 `${chalk.green(logPrefix())} ${chalk.red(
c5e52a07
JB
511 `Deprecated configuration key '${key}' usage${
512 logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
5edd8ba0
JB
513 }`,
514 )}`,
e7aeea18 515 );
eb3937cb
JB
516 }
517 }
518
c7db8ecb 519 private static getConfigurationData(): ConfigurationData | undefined {
f74e97ac 520 if (!Configuration.configurationData) {
23132a44 521 try {
f74e97ac 522 Configuration.configurationData = JSON.parse(
5edd8ba0 523 readFileSync(Configuration.configurationFile, 'utf8'),
e7aeea18 524 ) as ConfigurationData;
1f8f6332
JB
525 if (!Configuration.configurationFileWatcher) {
526 Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher();
527 }
23132a44 528 } catch (error) {
4354af5a 529 handleFileException(
a95873d8 530 Configuration.configurationFile,
7164966d
JB
531 FileType.Configuration,
532 error as NodeJS.ErrnoException,
4354af5a 533 logPrefix(),
e7aeea18 534 );
23132a44 535 }
eb3937cb 536 }
f74e97ac 537 return Configuration.configurationData;
eb3937cb 538 }
963ee397 539
d972af76 540 private static getConfigurationFileWatcher(): FSWatcher | undefined {
23132a44 541 try {
d972af76 542 return watch(Configuration.configurationFile, (event, filename): void => {
1b4ccee3
JB
543 if (
544 !Configuration.configurationFileReloading &&
545 filename!.trim()!.length > 0 &&
546 event === 'change'
547 ) {
548 Configuration.configurationFileReloading = true;
549 const consoleWarnOnce = once(console.warn, this);
550 consoleWarnOnce(
4354af5a 551 `${chalk.green(logPrefix())} ${chalk.yellow(
ab7a96fa
JB
552 `${FileType.Configuration} ${this.configurationFile} file have changed, reload`,
553 )}`,
554 );
c7db8ecb 555 delete Configuration.configurationData;
974efe6c 556 Configuration.configurationSectionCache.clear();
9bf0ef23 557 if (!isUndefined(Configuration.configurationChangeCallback)) {
1b4ccee3
JB
558 Configuration.configurationChangeCallback()
559 .catch((error) => {
560 throw typeof error === 'string' ? new Error(error) : error;
561 })
562 .finally(() => {
563 Configuration.configurationFileReloading = false;
564 });
41d95b76
JB
565 } else {
566 Configuration.configurationFileReloading = false;
3ec10737 567 }
23132a44
JB
568 }
569 });
570 } catch (error) {
4354af5a 571 handleFileException(
a95873d8 572 Configuration.configurationFile,
7164966d
JB
573 FileType.Configuration,
574 error as NodeJS.ErrnoException,
4354af5a 575 logPrefix(),
e7aeea18 576 );
23132a44 577 }
ded13d97 578 }
7dde0b73 579}