Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: consolidate utility modules and remove external dependencies #689

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"ignore": [
"src/renderer/ethers.js",
"src/main/util/*.js",
"*genesis-l2.json"
"*genesis-l2.json",
"src/renderer/utils/browserLanguageDetector.js"
]
}
}
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nice-node",
"version": "6.4.4-alpha",
"version": "6.4.3-alpha",
"description": "Run a node at home, the easy way.",
"homepage": "https://nicenode.xyz",
"productName": "NiceNode",
Expand Down Expand Up @@ -65,7 +65,7 @@
"@wdio/mocha-framework": "^9.0.8",
"@wdio/spec-reporter": "^9.0.8",
"cross-env": "^7.0.3",
"electron": "^34.2.0",
"electron": "^34.3.0",
"electron-devtools-installer": "^3.2.0",
"electron-extension-installer": "^1.2.0",
"electron-mock-ipc": "^0.3.12",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/node/childProcess.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type SpawnOptions, spawn } from 'node:child_process';
import * as url from 'node:url';
import sleep from 'await-sleep';
import { describe, expect, it } from 'vitest';
import { sleep } from '../../main/utils/sleep.js';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

Expand Down
4 changes: 2 additions & 2 deletions src/main/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { app } from 'electron';

import logger from './logger';

import du from 'du';
import calculateDiskUsage from './utils/diskUsage';

logger.info(`App data dir: ${app.getPath('appData')}`);
logger.info(`User data dir: ${app.getPath('userData')}`);
Expand Down Expand Up @@ -124,7 +124,7 @@ export const getSystemDiskSize = async (): Promise<number> => {
export const tryCalcDiskSpace = async (dirPath: string) => {
let diskUsedInGBs;
try {
diskUsedInGBs = (await du(dirPath)) * 1e-9;
diskUsedInGBs = (await calculateDiskUsage(dirPath)) * 1e-9;
} catch (err) {
console.info(
`Cannot calculate disk usage at ${dirPath}. Could be changing files.`,
Expand Down
2 changes: 1 addition & 1 deletion src/main/i18nMain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import i18n from 'i18next';
import I18nextCLILanguageDetector from 'i18next-cli-language-detector';
import I18nextCLILanguageDetector from './utils/i18nextCLILanguageDetector.js';

import { getMenuBuilder } from './main';
import { getSettings, setLanguage } from './state/settings';
Expand Down
8 changes: 7 additions & 1 deletion src/main/nn-auto-updater/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
/**
* Uses TypedEmitter implementation based on tiny-typed-emitter
* @see https://github.com/binier/tiny-typed-emitter
* @license MIT
*/

import EventEmitter from 'node:events';
import { type AutoUpdater, autoUpdater as _autoUpdater } from 'electron';
import { TypedEmitter } from 'tiny-typed-emitter';
import { checkForUpdates } from './../updater';
import { TypedEmitter } from './typed-emitter.js';

import { isLinux } from '../platform';
import { findPackageManager } from './findPackageManager';
Expand Down
32 changes: 32 additions & 0 deletions src/main/nn-auto-updater/typed-emitter.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Based on tiny-typed-emitter
* @see https://github.com/binier/tiny-typed-emitter
* @license MIT
*/

export type ListenerSignature<L> = {
[E in keyof L]: (...args: any[]) => any;
};

export type DefaultListener = {
[k: string]: (...args: any[]) => any;
};

export class TypedEmitter<L extends ListenerSignature<L> = DefaultListener> {
static defaultMaxListeners: number;
addListener<U extends keyof L>(event: U, listener: L[U]): this;
prependListener<U extends keyof L>(event: U, listener: L[U]): this;
prependOnceListener<U extends keyof L>(event: U, listener: L[U]): this;
removeListener<U extends keyof L>(event: U, listener: L[U]): this;
removeAllListeners(event?: keyof L): this;
once<U extends keyof L>(event: U, listener: L[U]): this;
on<U extends keyof L>(event: U, listener: L[U]): this;
off<U extends keyof L>(event: U, listener: L[U]): this;
emit<U extends keyof L>(event: U, ...args: Parameters<L[U]>): boolean;
eventNames<U extends keyof L>(): U[];
listenerCount(type: keyof L): number;
listeners<U extends keyof L>(type: U): L[U][];
rawListeners<U extends keyof L>(type: U): L[U][];
getMaxListeners(): number;
setMaxListeners(n: number): this;
}
2 changes: 2 additions & 0 deletions src/main/nn-auto-updater/typed-emitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.TypedEmitter = require('node:events').EventEmitter;
2 changes: 1 addition & 1 deletion src/main/podman/install/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import installOnLinux from './installOnLinux';
import installOnMac from './installOnMac';
import installOnWindows from './installOnWindows';

export const PODMAN_LATEST_VERSION = '5.4.0';
export const PODMAN_LATEST_VERSION = '5.2.4';
export const PODMAN_MIN_VERSION = '4.3.0';

const installPodman = async (): Promise<any> => {
Expand Down
17 changes: 11 additions & 6 deletions src/main/podman/uninstall/uninstallOnLinux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,21 @@ const uninstallOnLinux = async (): Promise<boolean | { error: string }> => {
// Stop and remove all containers
await execAwait('podman stop -a', { log: true });
await execAwait('podman rm -af', { log: true });

// Remove all pods
await execAwait('podman pod rm -af', { log: true });

// Remove all images
await execAwait('podman rmi -af', { log: true });

logger.info('Successfully cleaned up all podman containers, pods and images');

logger.info(
'Successfully cleaned up all podman containers, pods and images',
);
} catch (cleanupErr) {
logger.error('Error during container cleanup, continuing with uninstall:', cleanupErr);
logger.error(
'Error during container cleanup, continuing with uninstall:',
cleanupErr,
);
}

// Remove podman configuration folders
Expand All @@ -45,7 +50,7 @@ const uninstallOnLinux = async (): Promise<boolean | { error: string }> => {
`${userHome}/.ssh/*podman*`,
'/usr/local/podman',
];

try {
const rmCommand = `rm -rf ${foldersToDelete.join(' ')}`;
await execAwait(rmCommand, {
Expand Down
16 changes: 8 additions & 8 deletions src/main/updater.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import sleep from 'await-sleep';
import { type BrowserWindow, app, dialog } from 'electron';
import { autoUpdateLogger } from './logger';
import { autoUpdateLogger } from './logger.js';
import { sleep } from './utils/sleep.js';

import { reportEvent } from './events';
import i18nMain from './i18nMain';
import { setFullQuitForNextQuit } from './main';
import { autoUpdater } from './nn-auto-updater/main';
import { reportEvent } from './events.js';
import i18nMain from './i18nMain.js';
import { setFullQuitForNextQuit } from './main.js';
import { autoUpdater } from './nn-auto-updater/main.js';
// import { getSetIsPreReleaseUpdatesEnabled } from './state/settings';

let notifyUserIfNoUpdateAvailable: boolean;
Expand All @@ -15,7 +15,7 @@ const t = i18nMain.getFixedT(null, 'updater');
const logger = autoUpdateLogger;

const initUpdateHandlers = (browserWindow: BrowserWindow) => {
autoUpdater.on('error', (error) => {
autoUpdater.on('error', (error: Error) => {
logger.error('autoUpdater:::::::::error', error);
});

Expand All @@ -42,7 +42,7 @@ const initUpdateHandlers = (browserWindow: BrowserWindow) => {
}
});

autoUpdater.on('update-downloaded', (...args) => {
autoUpdater.on('update-downloaded', (...args: unknown[]) => {
logger.info('autoUpdater:::::::::update-downloaded args: ', args);
logger.info('Calling autoUpdater.quitAndInstall()');
try {
Expand Down
64 changes: 64 additions & 0 deletions src/main/utils/diskUsage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Inspired by node-du (https://github.com/rvagg/node-du)
*
* Original License (MIT):
* Copyright (c) 2012 Rod Vagg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import fs from 'node:fs/promises';
import path from 'node:path';

/**
* Calculates the size of a directory or file recursively
* @param dir Path to directory or file
* @param options Optional settings (disk: boolean to use block size instead of file size)
* @returns Promise<number> Total size in bytes
*/
async function calculateDiskUsage(
dir: string,
options: { disk?: boolean } = {},
): Promise<number> {
const stat = await fs.lstat(dir);

// Base size calculation
const size = options.disk ? 512 * stat.blocks : stat.size;

if (!stat.isDirectory()) {
return size;
}

try {
const files = await fs.readdir(dir);
const sizes = await Promise.all(
files.map(
(file) =>
calculateDiskUsage(path.join(dir, file), options).catch(() => 0), // Handle permission errors or other issues for individual files
),
);

return sizes.reduce((total, s) => total + s, size);
} catch (err) {
// If we can't read the directory, return the current known size
return size;
}
}

export default calculateDiskUsage;
87 changes: 87 additions & 0 deletions src/main/utils/i18nextCLILanguageDetector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { InitOptions, LanguageDetectorModule, Services } from 'i18next';

export class I18nextCLILanguageDetector implements LanguageDetectorModule {
static type = 'languageDetector' as const;

type = I18nextCLILanguageDetector.type;
services!: Services;
detectorOptions?: object;
i18nextOptions!: InitOptions;

init(
services: Services,
detectorOptions: object,
i18nextOptions: InitOptions,
): void {
this.services = services;
this.detectorOptions = detectorOptions;
this.i18nextOptions = i18nextOptions;
}

detect(): string | string[] | undefined {
const shellLocale =
process.env.LC_ALL ??
process.env.LC_MESSAGES ??
process.env.LANG ??
process.env.LANGUAGE;

const language = this._getShellLanguage(shellLocale);
if (language != null) {
return language;
}

if (Array.isArray(this.i18nextOptions.fallbackLng)) {
return [...this.i18nextOptions.fallbackLng];
}

if (typeof this.i18nextOptions.fallbackLng === 'string') {
return this.i18nextOptions.fallbackLng;
}

return undefined;
}

cacheUserLanguage(): void {
return;
}

/**
* @see http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html
*/
private _getShellLanguage(lc?: string): string | string[] | undefined {
if (lc == null) return;

const languages = lc
.split(':')
.map((language) =>
language
// Get `en_US` part from `en_US.UTF-8`
.split('.')[0]
// transforms `en_US` to `en-US`
.replace('_', '-'),
)
.filter((language) =>
this.services.languageUtils.isSupportedCode(language),
)
.map((language) =>
this.services.languageUtils.formatLanguageCode(language),
);

// https://unix.stackexchange.com/questions/87745/what-does-lc-all-c-do
if (languages.some((l) => l === 'C')) {
return;
}

if (languages.length === 1 && languages[0] === '') {
return;
}

if (languages.length === 1) {
return languages[0];
}

return languages;
}
}

export default I18nextCLILanguageDetector;
11 changes: 11 additions & 0 deletions src/main/utils/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Inspired by https://github.com/sorenlouv/await-sleep
* Original code has no explicit license
*
* Creates a promise that resolves after the specified number of milliseconds
* @param ms The number of milliseconds to sleep
* @returns A promise that resolves after the specified delay
*/
export const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
Loading
Loading