From 4415c04a88902e9f4380462b828afc65f48e106e Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 21 Jun 2023 16:08:39 +0530 Subject: [PATCH] chore: enable typescript eslint rules - refactor code to satisfy rules (batch 1) - use ensuredir in build to prevent ENOENT --- .eslintrc.js | 31 ++++--- build/scripts/build.mjs | 4 +- fyo/core/authHandler.ts | 7 +- fyo/core/docHandler.ts | 8 +- fyo/core/types.ts | 22 ++--- fyo/demux/config.ts | 15 ++- fyo/index.ts | 17 ++-- fyo/telemetry/telemetry.ts | 13 +-- main.ts | 9 +- main/contactMothership.ts | 5 +- main/getLanguageMap.ts | 11 ++- main/helpers.ts | 10 +- main/registerIpcMainActionListeners.ts | 98 +++++++++++++------- main/registerIpcMainMessageListeners.ts | 4 +- src/App.vue | 10 +- src/errorHandling.ts | 20 ++-- src/importer.ts | 2 +- src/pages/DatabaseSelector.vue | 12 +-- src/renderer.ts | 11 ++- src/renderer/registerIpcRendererListeners.ts | 2 +- src/router.ts | 6 +- src/setup/setupInstance.ts | 2 +- src/shims-tsx.d.ts | 4 +- src/utils/doc.ts | 2 +- src/utils/export.ts | 2 +- src/utils/index.ts | 4 +- src/utils/initialization.ts | 10 +- src/utils/interactive.ts | 2 +- src/utils/ipcCalls.ts | 9 ++ src/utils/language.ts | 2 +- src/utils/misc.ts | 6 +- src/utils/printTemplates.ts | 12 +-- src/utils/refs.ts | 5 +- src/utils/search.ts | 6 +- src/utils/shortcuts.ts | 6 +- src/utils/sidebarConfig.ts | 18 ++-- src/utils/types.ts | 2 +- src/utils/ui.ts | 4 +- src/utils/vueUtils.ts | 2 +- utils/config.ts | 3 +- utils/types.ts | 5 +- 41 files changed, 240 insertions(+), 183 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2b09c43c..4d377aaa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,23 +1,32 @@ module.exports = { root: true, - env: { node: true, + browser: true, + es2020: true, }, - rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'no-console': 'warn', + 'no-debugger': 'warn', 'arrow-body-style': 'off', - 'prefer-arrow-callback': 'off', + 'prefer-arrow-callback': 'warn', 'vue/no-mutating-props': 'off', 'vue/multi-word-component-names': 'off', 'vue/no-useless-template-attributes': 'off', + 'vue/one-component-per-file': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-misused-promises': 'off', }, - - parserOptions: { - parser: '@typescript-eslint/parser', - }, - - extends: ['plugin:vue/vue3-essential', '@vue/typescript'], + parser: '@typescript-eslint/parser', + parserOptions: { project: true, tsconfigRootDir: __dirname }, + plugins: ['@typescript-eslint'], + extends: [ + 'plugin:vue/vue3-strongly-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + ignorePatterns: ['.eslintrc.js'], }; diff --git a/build/scripts/build.mjs b/build/scripts/build.mjs index eb782061..eb059f7c 100644 --- a/build/scripts/build.mjs +++ b/build/scripts/build.mjs @@ -24,9 +24,9 @@ await packageApp(); function updatePaths() { fs.removeSync(buildDirPath); - fs.mkdirSync(buildDirPath); + fs.ensureDirSync(buildDirPath); fs.removeSync(packageDirPath); - fs.mkdirSync(packageDirPath); + fs.ensureDirSync(packageDirPath); fs.ensureDirSync(path.join(buildDirPath, 'node_modules')); } diff --git a/fyo/core/authHandler.ts b/fyo/core/authHandler.ts index 13e1c203..22325cb1 100644 --- a/fyo/core/authHandler.ts +++ b/fyo/core/authHandler.ts @@ -58,7 +58,10 @@ export class AuthHandler { return { ...this.#config }; } - init() {} + init() { + return null; + } + async login(email: string, password: string) { if (email === 'Administrator') { this.#session.user = 'Administrator'; @@ -107,8 +110,6 @@ export class AuthHandler { // TODO: Implement this with auth flow } - async purgeCache() {} - #getServerURL() { return this.#config.serverURL || ''; } diff --git a/fyo/core/docHandler.ts b/fyo/core/docHandler.ts index 6630bf2a..2cb9b310 100644 --- a/fyo/core/docHandler.ts +++ b/fyo/core/docHandler.ts @@ -28,7 +28,7 @@ export class DocHandler { this.observer = new Observable(); } - async purgeCache() { + purgeCache() { this.init(); } @@ -82,10 +82,10 @@ export class DocHandler { getNewDoc( schemaName: string, data: DocValueMap | RawValueMap = {}, - cacheDoc: boolean = true, + cacheDoc = true, schema?: Schema, Model?: typeof Doc, - isRawValueMap: boolean = true + isRawValueMap = true ): Doc { if (!this.models[schemaName] && Model) { this.models[schemaName] = Model; @@ -153,7 +153,7 @@ export class DocHandler { // propagate change to `docs` doc.on('change', (params: unknown) => { - this.docs!.trigger('change', params); + this.docs.trigger('change', params); }); doc.on('afterSync', () => { diff --git a/fyo/core/types.ts b/fyo/core/types.ts index 8530f0be..513605a5 100644 --- a/fyo/core/types.ts +++ b/fyo/core/types.ts @@ -1,8 +1,8 @@ -import { Doc } from 'fyo/model/doc'; -import { Money } from 'pesa'; -import { RawValue } from 'schemas/types'; -import { AuthDemuxBase } from 'utils/auth/types'; -import { DatabaseDemuxBase } from 'utils/db/types'; +import type { Doc } from 'fyo/model/doc'; +import type { Money } from 'pesa'; +import type { RawValue } from 'schemas/types'; +import type { AuthDemuxBase } from 'utils/auth/types'; +import type { DatabaseDemuxBase } from 'utils/db/types'; export type Attachment = { name: string; type: string; data: string }; export type DocValue = @@ -31,12 +31,12 @@ export type DatabaseDemuxConstructor = new ( export type AuthDemuxConstructor = new (isElectron?: boolean) => AuthDemuxBase; -export enum ConfigKeys { - Files = 'files', - LastSelectedFilePath = 'lastSelectedFilePath', - Language = 'language', - DeviceId = 'deviceId', -} +export type ConfigMap = { + files: ConfigFile[]; + lastSelectedFilePath: null | string; + language: string + deviceId: string +}; export interface ConfigFile { id: string; diff --git a/fyo/demux/config.ts b/fyo/demux/config.ts index 51ba7c4c..22691c08 100644 --- a/fyo/demux/config.ts +++ b/fyo/demux/config.ts @@ -1,11 +1,12 @@ import type Store from 'electron-store'; +import { ConfigMap } from 'fyo/core/types'; export class Config { config: Map | Store; constructor(isElectron: boolean) { this.config = new Map(); if (isElectron) { - const Config: typeof Store = require('electron-store'); + const Config = require('electron-store') as typeof Store; this.config = new Config(); } } @@ -23,15 +24,19 @@ export class Config { } } - get(key: string, defaultValue?: unknown): unknown { - return this.config.get(key) ?? defaultValue; + get( + key: K, + defaultValue?: ConfigMap[K] + ): ConfigMap[K] | undefined { + const value = this.config.get(key) as ConfigMap[K] | undefined; + return value ?? defaultValue; } - set(key: string, value: unknown) { + set(key: K, value: ConfigMap[K]) { this.config.set(key, value); } - delete(key: string) { + delete(key: keyof ConfigMap) { this.config.delete(key); } diff --git a/fyo/index.ts b/fyo/index.ts index a53bbbd3..d9de79bc 100644 --- a/fyo/index.ts +++ b/fyo/index.ts @@ -35,7 +35,7 @@ export class Fyo { doc: DocHandler; db: DatabaseHandler; - _initialized: boolean = false; + _initialized = false; errorLog: ErrorLog[] = []; temp?: Record; @@ -94,7 +94,7 @@ export class Fyo { return format(value, field, doc ?? null, this); } - async setIsElectron() { + setIsElectron() { try { this.isElectron = Boolean(require('electron')); } catch { @@ -105,7 +105,7 @@ export class Fyo { async initializeAndRegister( models: ModelMap = {}, regionalModels: ModelMap = {}, - force: boolean = false + force = false ) { if (this._initialized && !force) return; @@ -121,8 +121,8 @@ export class Fyo { // temp params while calling routes this.temp = {}; - await this.doc.init(); - await this.auth.init(); + this.doc.init(); + this.auth.init(); await this.db.init(); } @@ -189,14 +189,14 @@ export class Fyo { let value: DocValue | Doc[]; try { doc = await this.doc.getDoc(schemaName, name); - value = doc.get(fieldname!); + value = doc.get(fieldname); } catch (err) { value = undefined; } if (value === undefined && schemaName === name) { const sv = await this.db.getSingleValues({ - fieldname: fieldname!, + fieldname: fieldname, parent: schemaName, }); @@ -221,8 +221,7 @@ export class Fyo { this.errorLog = []; this.temp = {}; await this.db.purgeCache(); - await this.auth.purgeCache(); - await this.doc.purgeCache(); + this.doc.purgeCache(); } store = { diff --git a/fyo/telemetry/telemetry.ts b/fyo/telemetry/telemetry.ts index bb84fcd2..7c5b77af 100644 --- a/fyo/telemetry/telemetry.ts +++ b/fyo/telemetry/telemetry.ts @@ -1,5 +1,4 @@ import { Fyo } from 'fyo'; -import { ConfigKeys } from 'fyo/core/types'; import { Noun, Telemetry, Verb } from './types'; /** @@ -28,8 +27,8 @@ import { Noun, Telemetry, Verb } from './types'; */ export class TelemetryManager { - #url: string = ''; - #token: string = ''; + #url = ''; + #token = ''; #started = false; fyo: Fyo; @@ -108,16 +107,12 @@ export class TelemetryManager { noun: Noun, more?: Record ): Telemetry { - const countryCode = this.fyo.singles.SystemSettings?.countryCode as - | string - | undefined; - + const countryCode = this.fyo.singles.SystemSettings?.countryCode; return { country: countryCode ?? '', language: this.fyo.store.language, deviceId: - this.fyo.store.deviceId || - (this.fyo.config.get(ConfigKeys.DeviceId) as string), + this.fyo.store.deviceId || (this.fyo.config.get('deviceId') ?? '-'), instanceId: this.fyo.store.instanceId, version: this.fyo.store.appVersion, openCount: this.fyo.store.openCount, diff --git a/main.ts b/main.ts index ba716bd1..36b4a6c2 100644 --- a/main.ts +++ b/main.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line require('source-map-support').install({ handleUncaughtException: false, environment: 'node', @@ -22,10 +23,10 @@ import registerIpcMainMessageListeners from './main/registerIpcMainMessageListen import registerProcessListeners from './main/registerProcessListeners'; export class Main { - title: string = 'Frappe Books'; + title = 'Frappe Books'; icon: string; - winURL: string = ''; + winURL = ''; checkedForUpdate = false; mainWindow: BrowserWindow | null = null; @@ -130,9 +131,9 @@ export class Main { this.registerAppProtocol(); } - this.mainWindow!.loadURL(this.winURL); + this.mainWindow.loadURL(this.winURL); if (this.isDevelopment && !this.isTest) { - this.mainWindow!.webContents.openDevTools(); + this.mainWindow.webContents.openDevTools(); } this.setMainWindowListeners(); diff --git a/main/contactMothership.ts b/main/contactMothership.ts index 24ecace3..f411eb92 100644 --- a/main/contactMothership.ts +++ b/main/contactMothership.ts @@ -4,6 +4,7 @@ import fetch from 'node-fetch'; import path from 'path'; import { Creds } from 'utils/types'; import { rendererLog } from './helpers'; +import type { Main } from 'main'; export function getUrlAndTokenString(): Creds { const inProduction = app.isPackaged; @@ -42,7 +43,7 @@ export function getUrlAndTokenString(): Creds { }; } -export async function sendError(body: string) { +export async function sendError(body: string, main: Main) { const { errorLogUrl, tokenString } = getUrlAndTokenString(); const headers = { Authorization: tokenString, @@ -51,6 +52,6 @@ export async function sendError(body: string) { }; await fetch(errorLogUrl, { method: 'POST', headers, body }).catch((err) => { - rendererLog(err); + rendererLog(main, err); }); } diff --git a/main/getLanguageMap.ts b/main/getLanguageMap.ts index 23cd0b40..917d3682 100644 --- a/main/getLanguageMap.ts +++ b/main/getLanguageMap.ts @@ -16,8 +16,7 @@ import fs from 'fs/promises'; import path from 'path'; import { parseCSV } from 'utils/csvParser'; import { LanguageMap } from 'utils/types'; - -const fetch = require('node-fetch').default; +import fetch from 'node-fetch'; const VALENTINES_DAY = 1644796800000; @@ -100,7 +99,7 @@ async function fetchContentsFromApi(code: string) { return null; } - const resJson = await res.json(); + const resJson = (await res.json()) as { content: string }; return Buffer.from(resJson.content, 'base64').toString(); } @@ -138,7 +137,9 @@ async function getLastUpdated(code: string): Promise { return new Date(VALENTINES_DAY); } - const resJson = await res.json(); + const resJson = (await res.json()) as { + commit: { author: { date: string } }; + }[]; try { return new Date(resJson[0].commit.author.date); } catch { @@ -187,7 +188,7 @@ async function storeFile(code: string, contents: string) { async function errorHandledFetch(url: string) { try { - return (await fetch(url)) as Response; + return await fetch(url); } catch { return null; } diff --git a/main/helpers.ts b/main/helpers.ts index 24022450..3ec78849 100644 --- a/main/helpers.ts +++ b/main/helpers.ts @@ -1,6 +1,6 @@ import { constants } from 'fs'; import fs from 'fs/promises'; -import { ConfigFile, ConfigKeys } from 'fyo/core/types'; +import { ConfigFile } from 'fyo/core/types'; import { Main } from 'main'; import config from 'utils/config'; import { BackendResponse } from 'utils/ipc/types'; @@ -8,7 +8,7 @@ import { IPC_CHANNELS } from 'utils/messages'; import type { ConfigFilesWithModified } from 'utils/types'; export async function setAndGetCleanedConfigFiles() { - const files = config.get(ConfigKeys.Files, []) as ConfigFile[]; + const files = config.get('files', []); const cleanedFileMap: Map = new Map(); for (const file of files) { @@ -30,7 +30,7 @@ export async function setAndGetCleanedConfigFiles() { } const cleanedFiles = Array.from(cleanedFileMap.values()); - config.set(ConfigKeys.Files, cleanedFiles); + config.set('files', cleanedFiles); return cleanedFiles; } @@ -50,7 +50,9 @@ export async function getConfigFilesWithModified(files: ConfigFile[]) { return filesWithModified; } -export async function getErrorHandledReponse(func: () => Promise) { +export async function getErrorHandledReponse( + func: () => Promise | unknown +) { const response: BackendResponse = {}; try { diff --git a/main/registerIpcMainActionListeners.ts b/main/registerIpcMainActionListeners.ts index 8cc1a3dd..7e34d979 100644 --- a/main/registerIpcMainActionListeners.ts +++ b/main/registerIpcMainActionListeners.ts @@ -1,4 +1,11 @@ -import { app, dialog, ipcMain } from 'electron'; +import { + MessageBoxOptions, + OpenDialogOptions, + SaveDialogOptions, + app, + dialog, + ipcMain, +} from 'electron'; import { autoUpdater } from 'electron-updater'; import fs from 'fs/promises'; import path from 'path'; @@ -20,39 +27,60 @@ import { import { saveHtmlAsPdf } from './saveHtmlAsPdf'; export default function registerIpcMainActionListeners(main: Main) { - ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => { - return await dialog.showOpenDialog(main.mainWindow!, options); - }); - - ipcMain.handle(IPC_ACTIONS.GET_SAVE_FILEPATH, async (event, options) => { - return await dialog.showSaveDialog(main.mainWindow!, options); - }); - - ipcMain.handle(IPC_ACTIONS.GET_DIALOG_RESPONSE, async (event, options) => { - if (main.isDevelopment || main.isLinux) { - Object.assign(options, { icon: main.icon }); + ipcMain.handle( + IPC_ACTIONS.GET_OPEN_FILEPATH, + async (_, options: OpenDialogOptions) => { + return await dialog.showOpenDialog(main.mainWindow!, options); } + ); - return await dialog.showMessageBox(main.mainWindow!, options); - }); + ipcMain.handle( + IPC_ACTIONS.GET_SAVE_FILEPATH, + async (_, options: SaveDialogOptions) => { + return await dialog.showSaveDialog(main.mainWindow!, options); + } + ); - ipcMain.handle(IPC_ACTIONS.SHOW_ERROR, async (event, { title, content }) => { - return await dialog.showErrorBox(title, content); - }); + ipcMain.handle( + IPC_ACTIONS.GET_DIALOG_RESPONSE, + async (_, options: MessageBoxOptions) => { + if (main.isDevelopment || main.isLinux) { + Object.assign(options, { icon: main.icon }); + } + + return await dialog.showMessageBox(main.mainWindow!, options); + } + ); + + ipcMain.handle( + IPC_ACTIONS.SHOW_ERROR, + (_, { title, content }: { title: string; content: string }) => { + return dialog.showErrorBox(title, content); + } + ); ipcMain.handle( IPC_ACTIONS.SAVE_HTML_AS_PDF, - async (event, html, savePath, width: number, height: number) => { + async ( + _, + html: string, + savePath: string, + width: number, + height: number + ) => { return await saveHtmlAsPdf(html, savePath, app, width, height); } ); - ipcMain.handle(IPC_ACTIONS.SAVE_DATA, async (event, data, savePath) => { - return await fs.writeFile(savePath, data, { encoding: 'utf-8' }); - }); + ipcMain.handle( + IPC_ACTIONS.SAVE_DATA, + async (_, data: string, savePath: string) => { + return await fs.writeFile(savePath, data, { encoding: 'utf-8' }); + } + ); - ipcMain.handle(IPC_ACTIONS.SEND_ERROR, (event, bodyJson) => { - sendError(bodyJson); + ipcMain.handle(IPC_ACTIONS.SEND_ERROR, (_, bodyJson: string) => { + sendError(bodyJson, main); }); ipcMain.handle(IPC_ACTIONS.CHECK_FOR_UPDATES, async () => { @@ -72,7 +100,7 @@ export default function registerIpcMainActionListeners(main: Main) { main.checkedForUpdate = true; }); - ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (event, code) => { + ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (_, code: string) => { const obj = { languageMap: {}, success: true, message: '' }; try { obj.languageMap = await getLanguageMap(code); @@ -117,20 +145,20 @@ export default function registerIpcMainActionListeners(main: Main) { } ); - ipcMain.handle(IPC_ACTIONS.GET_CREDS, async (event) => { + ipcMain.handle(IPC_ACTIONS.GET_CREDS, () => { return getUrlAndTokenString(); }); - ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath) => { + ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath: string) => { return getErrorHandledReponse(async () => await fs.unlink(filePath)); }); - ipcMain.handle(IPC_ACTIONS.GET_DB_LIST, async (_) => { + ipcMain.handle(IPC_ACTIONS.GET_DB_LIST, async () => { const files = await setAndGetCleanedConfigFiles(); return await getConfigFilesWithModified(files); }); - ipcMain.handle(IPC_ACTIONS.GET_ENV, async (_) => { + ipcMain.handle(IPC_ACTIONS.GET_ENV, () => { return { isDevelopment: main.isDevelopment, platform: process.platform, @@ -149,7 +177,7 @@ export default function registerIpcMainActionListeners(main: Main) { ipcMain.handle( IPC_ACTIONS.DB_CREATE, async (_, dbPath: string, countryCode: string) => { - return await getErrorHandledReponse(async function dbFunc() { + return await getErrorHandledReponse(async () => { return await databaseManager.createNewDatabase(dbPath, countryCode); }); } @@ -158,7 +186,7 @@ export default function registerIpcMainActionListeners(main: Main) { ipcMain.handle( IPC_ACTIONS.DB_CONNECT, async (_, dbPath: string, countryCode?: string) => { - return await getErrorHandledReponse(async function dbFunc() { + return await getErrorHandledReponse(async () => { return await databaseManager.connectToDatabase(dbPath, countryCode); }); } @@ -167,7 +195,7 @@ export default function registerIpcMainActionListeners(main: Main) { ipcMain.handle( IPC_ACTIONS.DB_CALL, async (_, method: DatabaseMethod, ...args: unknown[]) => { - return await getErrorHandledReponse(async function dbFunc() { + return await getErrorHandledReponse(async () => { return await databaseManager.call(method, ...args); }); } @@ -176,15 +204,15 @@ export default function registerIpcMainActionListeners(main: Main) { ipcMain.handle( IPC_ACTIONS.DB_BESPOKE, async (_, method: string, ...args: unknown[]) => { - return await getErrorHandledReponse(async function dbFunc() { + return await getErrorHandledReponse(async () => { return await databaseManager.callBespoke(method, ...args); }); } ); - ipcMain.handle(IPC_ACTIONS.DB_SCHEMA, async (_) => { - return await getErrorHandledReponse(async function dbFunc() { - return await databaseManager.getSchemaMap(); + ipcMain.handle(IPC_ACTIONS.DB_SCHEMA, async () => { + return await getErrorHandledReponse(() => { + return databaseManager.getSchemaMap(); }); }); } diff --git a/main/registerIpcMainMessageListeners.ts b/main/registerIpcMainMessageListeners.ts index b62b1c33..c3400447 100644 --- a/main/registerIpcMainMessageListeners.ts +++ b/main/registerIpcMainMessageListeners.ts @@ -20,11 +20,11 @@ export default function registerIpcMainMessageListeners(main: Main) { main.mainWindow!.reload(); }); - ipcMain.on(IPC_MESSAGES.OPEN_EXTERNAL, (_, link) => { + ipcMain.on(IPC_MESSAGES.OPEN_EXTERNAL, (_, link: string) => { shell.openExternal(link); }); - ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath) => { + ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (_, filePath: string) => { return shell.showItemInFolder(filePath); }); } diff --git a/src/App.vue b/src/App.vue index fc1471ff..c509da3e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -36,7 +36,6 @@