From aa9c992f2ed28ab68666fc3a014921867f112b09 Mon Sep 17 00:00:00 2001
From: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
Date: Wed, 22 Nov 2023 14:28:09 -0800
Subject: [PATCH] Part 2 of eslint prefer-const (#1958)

* Part 2 of eslint prefer-const

* Release notes

* Update
---
 .eslintrc.js                                  |   9 ++
 packages/loot-core/init-node.js               |   8 +-
 .../src/client/SpreadsheetProvider.tsx        |   6 +-
 .../loot-core/src/client/actions/account.ts   |  10 +-
 .../loot-core/src/client/actions/budgets.ts   |  14 +-
 .../loot-core/src/client/actions/modals.ts    |   2 +-
 .../loot-core/src/client/actions/prefs.ts     |   6 +-
 .../loot-core/src/client/actions/queries.ts   |  14 +-
 packages/loot-core/src/client/actions/sync.ts |  10 +-
 .../src/client/data-hooks/filters.tsx         |   2 +-
 .../src/client/data-hooks/schedules.tsx       |   8 +-
 packages/loot-core/src/client/queries.ts      |   6 +-
 .../src/client/query-helpers.test.ts          |  78 +++++-----
 .../loot-core/src/client/query-helpers.ts     |  34 ++---
 packages/loot-core/src/client/query-hooks.tsx |  16 +-
 .../loot-core/src/client/reducers/account.ts  |   4 +-
 .../loot-core/src/client/reducers/budgets.ts  |  18 +--
 .../loot-core/src/client/reducers/queries.ts  |   6 +-
 .../loot-core/src/client/shared-listeners.ts  |   8 +-
 .../src/client/update-notification.ts         |   4 +-
 .../loot-core/src/mocks/arbitrary-schema.ts   |  12 +-
 packages/loot-core/src/mocks/budget.ts        | 142 +++++++++---------
 packages/loot-core/src/mocks/redux.tsx        |   2 +-
 packages/loot-core/src/mocks/setup.ts         |   8 +-
 packages/loot-core/src/mocks/spreadsheet.ts   |  10 +-
 packages/loot-core/src/mocks/util.ts          |   2 +-
 .../client/fetch/__mocks__/index.web.ts       |   6 +-
 .../platform/client/fetch/index.browser.ts    |  14 +-
 .../src/platform/client/fetch/index.web.ts    |  15 +-
 .../src/platform/client/undo/index.web.ts     |   6 +-
 .../server/asyncStorage/index.electron.ts     |   2 +-
 .../platform/server/asyncStorage/index.web.ts |  54 +++----
 .../server/connection/index.electron.ts       |   6 +-
 .../platform/server/connection/index.web.ts   |  10 +-
 .../src/platform/server/fs/index.electron.ts  |   2 +-
 .../src/platform/server/fs/index.web.test.ts  |  18 +--
 .../src/platform/server/fs/index.web.ts       |  56 +++----
 .../src/platform/server/fs/path-join.web.ts   |   8 +-
 .../platform/server/indexeddb/index.web.ts    |  18 +--
 .../platform/server/sqlite/index.electron.ts  |  10 +-
 .../platform/server/sqlite/index.web.test.ts  |  10 +-
 .../src/platform/server/sqlite/index.web.ts   |   8 +-
 packages/loot-core/src/shared/arithmetic.ts   |  18 ++-
 packages/loot-core/src/shared/async.test.ts   |   2 +-
 packages/loot-core/src/shared/async.ts        |   4 +-
 packages/loot-core/src/shared/categories.ts   |   2 +-
 packages/loot-core/src/shared/errors.ts       |   2 +-
 packages/loot-core/src/shared/months.ts       |   8 +-
 packages/loot-core/src/shared/query.ts        |  12 +-
 packages/loot-core/src/shared/rules.ts        |   8 +-
 packages/loot-core/src/shared/schedules.ts    |  34 ++---
 packages/loot-core/src/shared/test-helpers.ts |  12 +-
 .../loot-core/src/shared/transactions.test.ts |  30 ++--
 packages/loot-core/src/shared/transactions.ts |  44 +++---
 packages/loot-core/src/shared/util.ts         |  56 +++----
 .../loot-core/webpack/webpack.api.config.js   |   4 +-
 .../webpack/webpack.browser.config.js         |   4 +-
 .../webpack/webpack.desktop.config.js         |   6 +-
 upcoming-release-notes/1958.md                |   6 +
 59 files changed, 476 insertions(+), 458 deletions(-)
 create mode 100644 upcoming-release-notes/1958.md

diff --git a/.eslintrc.js b/.eslintrc.js
index 78a6e37d1..19255cf7b 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -239,6 +239,7 @@ module.exports = {
         './packages/api/app/**/*',
         './packages/crdt/**/*',
         './packages/desktop-client/src/*',
+        // './packages/desktop-client/src/components/**/*',
         './packages/desktop-client/src/hooks/**/*',
         './packages/desktop-client/src/icons/**/*',
         './packages/desktop-client/src/style/**/*',
@@ -246,6 +247,14 @@ module.exports = {
         './packages/desktop-client/src/util/**/*',
         './packages/desktop-electron/**/*',
         './packages/eslint-plugin-actual/**/*',
+        './packages/loot-core/*',
+        './packages/loot-core/src/client/**/*',
+        './packages/loot-core/src/mocks/**/*',
+        './packages/loot-core/src/platform/**/*',
+        // './packages/loot-core/src/server/**/*',
+        './packages/loot-core/src/shared/**/*',
+        './packages/loot-core/src/types/**/*',
+        './packages/loot-core/webpack/**/*',
       ],
       rules: {
         'prefer-const': 'warn',
diff --git a/packages/loot-core/init-node.js b/packages/loot-core/init-node.js
index a426a73b2..3470eccd0 100644
--- a/packages/loot-core/init-node.js
+++ b/packages/loot-core/init-node.js
@@ -9,8 +9,8 @@ import bundle from './lib-dist/bundle.desktop.js';
 global.fetch = fetch;
 
 async function init(budgetPath) {
-  let dir = dirname(budgetPath);
-  let budgetId = basename(budgetPath);
+  const dir = dirname(budgetPath);
+  const budgetId = basename(budgetPath);
   await bundle.initEmbedded('0.0.147', true, dir);
   await bundle.lib.send('load-budget', { id: budgetId });
 
@@ -18,8 +18,8 @@ async function init(budgetPath) {
 }
 
 async function run() {
-  let { send } = await init('/tmp/_test-budget');
-  let accounts = await send('accounts-get');
+  const { send } = await init('/tmp/_test-budget');
+  const accounts = await send('accounts-get');
 
   await send('transaction-add', {
     date: '2022-03-20',
diff --git a/packages/loot-core/src/client/SpreadsheetProvider.tsx b/packages/loot-core/src/client/SpreadsheetProvider.tsx
index bb30cda95..2823fe123 100644
--- a/packages/loot-core/src/client/SpreadsheetProvider.tsx
+++ b/packages/loot-core/src/client/SpreadsheetProvider.tsx
@@ -65,8 +65,8 @@ function makeSpreadsheet() {
       binding =
         typeof binding === 'string' ? { name: binding, value: null } : binding;
 
-      let resolvedName = `${sheetName}!${binding.name}`;
-      let cleanup = this.observeCell(resolvedName, cb);
+      const resolvedName = `${sheetName}!${binding.name}`;
+      const cleanup = this.observeCell(resolvedName, cb);
 
       // Always synchronously call with the existing value if it has one.
       // This is a display optimization to avoid flicker. The LRU cache
@@ -118,7 +118,7 @@ function makeSpreadsheet() {
 }
 
 export function SpreadsheetProvider({ children }) {
-  let spreadsheet = useMemo(() => makeSpreadsheet(), []);
+  const spreadsheet = useMemo(() => makeSpreadsheet(), []);
 
   useEffect(() => {
     return spreadsheet.listen();
diff --git a/packages/loot-core/src/client/actions/account.ts b/packages/loot-core/src/client/actions/account.ts
index cecc9d8f3..7d937ae65 100644
--- a/packages/loot-core/src/client/actions/account.ts
+++ b/packages/loot-core/src/client/actions/account.ts
@@ -83,7 +83,7 @@ export function connectAccounts(
   offbudgetIds,
 ) {
   return async (dispatch: Dispatch) => {
-    let ids = await send('accounts-connect', {
+    const ids = await send('accounts-connect', {
       institution,
       publicToken,
       accountIds,
@@ -103,7 +103,7 @@ export function connectGoCardlessAccounts(
   offbudgetIds,
 ) {
   return async (dispatch: Dispatch) => {
-    let ids = await send('gocardless-accounts-connect', {
+    const ids = await send('gocardless-accounts-connect', {
       institution,
       publicToken,
       accountIds,
@@ -122,7 +122,7 @@ export function syncAccounts(id: string) {
     }
 
     if (id) {
-      let account = getState().queries.accounts.find(a => a.id === id);
+      const account = getState().queries.accounts.find(a => a.id === id);
       dispatch(setAccountsSyncing(account.name));
     } else {
       dispatch(setAccountsSyncing('__all'));
@@ -133,7 +133,7 @@ export function syncAccounts(id: string) {
     dispatch(setAccountsSyncing(null));
 
     if (id) {
-      let error = errors.find(error => error.accountId === id);
+      const error = errors.find(error => error.accountId === id);
 
       if (error) {
         // We only want to mark the account as having problem if it
@@ -209,7 +209,7 @@ export function parseTransactions(filepath, options) {
 
 export function importTransactions(id, transactions) {
   return async (dispatch: Dispatch) => {
-    let {
+    const {
       errors = [],
       added,
       updated,
diff --git a/packages/loot-core/src/client/actions/budgets.ts b/packages/loot-core/src/client/actions/budgets.ts
index 3247472f4..c35ca100e 100644
--- a/packages/loot-core/src/client/actions/budgets.ts
+++ b/packages/loot-core/src/client/actions/budgets.ts
@@ -50,14 +50,14 @@ export function loadBudget(id: string, loadingText = '', options = {}) {
     dispatch(setAppState({ loadingText }));
 
     // Loading a budget may fail
-    let { error } = await send('load-budget', { id, ...options });
+    const { error } = await send('load-budget', { id, ...options });
 
     if (error) {
-      let message = getSyncError(error, id);
+      const message = getSyncError(error, id);
       if (error === 'out-of-sync-migrations' || error === 'out-of-sync-data') {
         // confirm is not available on iOS
         if (typeof window.confirm !== 'undefined') {
-          let showBackups = window.confirm(
+          const showBackups = window.confirm(
             message +
               ' Make sure the app is up-to-date. Do you want to load a backup?',
           );
@@ -103,7 +103,7 @@ export function closeBudget() {
 
 export function closeBudgetUI() {
   return async (dispatch: Dispatch, getState: GetState) => {
-    let prefs = getState().prefs.local;
+    const prefs = getState().prefs.local;
     if (prefs && prefs.id) {
       dispatch({ type: constants.CLOSE_BUDGET });
     }
@@ -164,7 +164,7 @@ export function importBudget(
 
 export function uploadBudget(id: string) {
   return async (dispatch: Dispatch) => {
-    let { error } = await send('upload-budget', { id });
+    const { error } = await send('upload-budget', { id });
     if (error) {
       return { error };
     }
@@ -190,14 +190,14 @@ export function downloadBudget(cloudFileId: string, { replace = false } = {}) {
   return async (dispatch: Dispatch) => {
     dispatch(setAppState({ loadingText: 'Downloading...' }));
 
-    let { id, error } = await send('download-budget', {
+    const { id, error } = await send('download-budget', {
       fileId: cloudFileId,
       replace,
     });
 
     if (error) {
       if (error.reason === 'decrypt-failure') {
-        let opts = {
+        const opts = {
           hasExistingKey: error.meta && error.meta.isMissingKey,
           cloudFileId,
           onSuccess: () => {
diff --git a/packages/loot-core/src/client/actions/modals.ts b/packages/loot-core/src/client/actions/modals.ts
index 7e50ba652..7abf5e78a 100644
--- a/packages/loot-core/src/client/actions/modals.ts
+++ b/packages/loot-core/src/client/actions/modals.ts
@@ -37,7 +37,7 @@ export function replaceModal<M extends ModalType>(
   options?: FinanceModals[M],
 ): ReplaceModalAction {
   // @ts-expect-error TS is unable to determine that `name` and `options` match
-  let modal: M = { name, options };
+  const modal: M = { name, options };
   return { type: constants.REPLACE_MODAL, modal };
 }
 
diff --git a/packages/loot-core/src/client/actions/prefs.ts b/packages/loot-core/src/client/actions/prefs.ts
index d309e0173..ac6cf98b9 100644
--- a/packages/loot-core/src/client/actions/prefs.ts
+++ b/packages/loot-core/src/client/actions/prefs.ts
@@ -7,10 +7,10 @@ import type { Dispatch, GetState } from './types';
 
 export function loadPrefs() {
   return async (dispatch: Dispatch, getState: GetState) => {
-    let prefs = await send('load-prefs');
+    const prefs = await send('load-prefs');
 
     // Remove any modal state if switching between budgets
-    let currentPrefs = getState().prefs.local;
+    const currentPrefs = getState().prefs.local;
     if (prefs && prefs.id && !currentPrefs) {
       dispatch(closeModal());
     }
@@ -37,7 +37,7 @@ export function savePrefs(prefs: Partial<prefs.LocalPrefs>) {
 
 export function loadGlobalPrefs() {
   return async (dispatch: Dispatch, getState: GetState) => {
-    let globalPrefs = await send('load-global-prefs');
+    const globalPrefs = await send('load-global-prefs');
     dispatch({
       type: constants.SET_PREFS,
       prefs: getState().prefs.local,
diff --git a/packages/loot-core/src/client/actions/queries.ts b/packages/loot-core/src/client/actions/queries.ts
index ab50be723..0482d17da 100644
--- a/packages/loot-core/src/client/actions/queries.ts
+++ b/packages/loot-core/src/client/actions/queries.ts
@@ -136,7 +136,7 @@ export function createCategory(
   isIncome: boolean,
 ) {
   return async (dispatch: Dispatch) => {
-    let id = await send('category-create', {
+    const id = await send('category-create', {
       name,
       groupId,
       isIncome,
@@ -148,7 +148,7 @@ export function createCategory(
 
 export function deleteCategory(id: string, transferId?: string) {
   return async (dispatch: Dispatch) => {
-    let { error } = await send('category-delete', { id, transferId });
+    const { error } = await send('category-delete', { id, transferId });
 
     if (error) {
       switch (error) {
@@ -198,7 +198,7 @@ export function moveCategoryGroup(id, targetId) {
 
 export function createGroup(name) {
   return async (dispatch: Dispatch) => {
-    let id = await send('category-group-create', { name });
+    const id = await send('category-group-create', { name });
     dispatch(getCategories());
     return id;
   };
@@ -227,7 +227,7 @@ export function deleteGroup(id, transferId?) {
 
 export function getPayees() {
   return async (dispatch: Dispatch) => {
-    let payees = await send('payees-get');
+    const payees = await send('payees-get');
     dispatch({
       type: constants.LOAD_PAYEES,
       payees,
@@ -267,7 +267,7 @@ export function updateAccount(account) {
 
 export function createAccount(name, balance, offBudget) {
   return async (dispatch: Dispatch) => {
-    let id = await send('account-create', { name, balance, offBudget });
+    const id = await send('account-create', { name, balance, offBudget });
     await dispatch(getAccounts());
     await dispatch(getPayees());
     return id;
@@ -321,8 +321,8 @@ export function forceCloseAccount(accountId) {
   return closeAccount(accountId, null, null, true);
 }
 
-let _undo = throttle(() => send('undo'), 100);
-let _redo = throttle(() => send('redo'), 100);
+const _undo = throttle(() => send('undo'), 100);
+const _redo = throttle(() => send('redo'), 100);
 
 let _undoEnabled = true;
 export function setUndoEnabled(flag: boolean) {
diff --git a/packages/loot-core/src/client/actions/sync.ts b/packages/loot-core/src/client/actions/sync.ts
index 4838c1e43..c2b03d22a 100644
--- a/packages/loot-core/src/client/actions/sync.ts
+++ b/packages/loot-core/src/client/actions/sync.ts
@@ -8,7 +8,7 @@ import type { Dispatch, GetState } from './types';
 
 export function resetSync() {
   return async (dispatch: Dispatch) => {
-    let { error } = await send('sync-reset');
+    const { error } = await send('sync-reset');
 
     if (error) {
       alert(getUploadError(error));
@@ -40,7 +40,7 @@ export function sync() {
   return async (dispatch: Dispatch, getState: GetState) => {
     const prefs = getState().prefs.local;
     if (prefs && prefs.id) {
-      let result = await send('sync');
+      const result = await send('sync');
       if ('error' in result) {
         return { error: result.error };
       }
@@ -56,16 +56,16 @@ export function syncAndDownload(accountId) {
     // clients have already made, so that imported transactions can be
     // reconciled against them. Otherwise, two clients will each add
     // new transactions from the bank and create duplicate ones.
-    let syncState = await dispatch(sync());
+    const syncState = await dispatch(sync());
     if (syncState.error) {
       return { error: syncState.error };
     }
 
-    let hasDownloaded = await dispatch(syncAccounts(accountId));
+    const hasDownloaded = await dispatch(syncAccounts(accountId));
 
     if (hasDownloaded) {
       // Sync again afterwards if new transactions were created
-      let syncState = await dispatch(sync());
+      const syncState = await dispatch(sync());
       if (syncState.error) {
         return { error: syncState.error };
       }
diff --git a/packages/loot-core/src/client/data-hooks/filters.tsx b/packages/loot-core/src/client/data-hooks/filters.tsx
index b7b65060d..60f9999e8 100644
--- a/packages/loot-core/src/client/data-hooks/filters.tsx
+++ b/packages/loot-core/src/client/data-hooks/filters.tsx
@@ -4,7 +4,7 @@ import q from '../query-helpers';
 import { useLiveQuery } from '../query-hooks';
 
 function toJS(rows) {
-  let filters = rows.map(row => {
+  const filters = rows.map(row => {
     return {
       ...row.fields,
       id: row.id,
diff --git a/packages/loot-core/src/client/data-hooks/schedules.tsx b/packages/loot-core/src/client/data-hooks/schedules.tsx
index a11bb1c7f..a139658c9 100644
--- a/packages/loot-core/src/client/data-hooks/schedules.tsx
+++ b/packages/loot-core/src/client/data-hooks/schedules.tsx
@@ -11,7 +11,7 @@ export type ScheduleStatuses = Map<ScheduleEntity['id'], ScheduleStatusType>;
 function loadStatuses(schedules: ScheduleEntity[], onData) {
   return liveQuery(getHasTransactionsQuery(schedules), onData, {
     mapper: data => {
-      let hasTrans = new Set(data.filter(Boolean).map(row => row.schedule));
+      const hasTrans = new Set(data.filter(Boolean).map(row => row.schedule));
 
       return new Map(
         schedules.map(s => [
@@ -35,9 +35,9 @@ export function useSchedules({
 
   useEffect(() => {
     const query = q('schedules').select('*');
-    let scheduleQuery, statusQuery;
+    let statusQuery;
 
-    scheduleQuery = liveQuery(
+    const scheduleQuery = liveQuery(
       transform ? transform(query) : query,
       async (schedules: ScheduleEntity[]) => {
         if (scheduleQuery) {
@@ -65,7 +65,7 @@ export function useSchedules({
   return data;
 }
 
-let SchedulesContext = createContext(null);
+const SchedulesContext = createContext(null);
 
 export function SchedulesProvider({ transform, children }) {
   const data = useSchedules({ transform });
diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts
index 0da752bb3..1ed3d615b 100644
--- a/packages/loot-core/src/client/queries.ts
+++ b/packages/loot-core/src/client/queries.ts
@@ -49,7 +49,7 @@ export function getAccountFilter(accountId, field = 'account') {
 export function makeTransactionsQuery(accountId) {
   let query = q('transactions').options({ splits: 'grouped' });
 
-  let filter = getAccountFilter(accountId);
+  const filter = getAccountFilter(accountId);
   if (filter) {
     query = query.filter(filter);
   }
@@ -58,7 +58,7 @@ export function makeTransactionsQuery(accountId) {
 }
 
 export function makeTransactionSearchQuery(currentQuery, search, dateFormat) {
-  let amount = currencyToAmount(search);
+  const amount = currencyToAmount(search);
 
   // Support various date formats
   let parsedDate;
@@ -130,7 +130,7 @@ export function offbudgetAccountBalance() {
   };
 }
 
-let uncategorizedQuery = q('transactions').filter({
+const uncategorizedQuery = q('transactions').filter({
   'account.offbudget': false,
   category: null,
   $or: [
diff --git a/packages/loot-core/src/client/query-helpers.test.ts b/packages/loot-core/src/client/query-helpers.test.ts
index 13b42a06f..c42bbdbec 100644
--- a/packages/loot-core/src/client/query-helpers.test.ts
+++ b/packages/loot-core/src/client/query-helpers.test.ts
@@ -11,7 +11,7 @@ function wait(n) {
 
 function isCountQuery(query) {
   if (query.selectExpressions.length === 1) {
-    let select = query.selectExpressions[0];
+    const select = query.selectExpressions[0];
     return select.result && select.result.$count === '*';
   }
 
@@ -29,8 +29,8 @@ function selectData(data, selectExpressions) {
 }
 
 function limitOffset(data, limit, offset) {
-  let start = offset != null ? offset : 0;
-  let end = limit != null ? limit : data.length;
+  const start = offset != null ? offset : 0;
+  const end = limit != null ? limit : data.length;
   return data.slice(start, start + end);
 }
 
@@ -40,13 +40,13 @@ function runPagedQuery(query, data) {
   }
 
   if (query.filterExpressions.length > 0) {
-    let filter = query.filterExpressions[0];
+    const filter = query.filterExpressions[0];
     if (filter.id != null) {
       return [data.find(row => row.id === filter.id)];
     }
 
     if (filter.date != null) {
-      let op = Object.keys(filter.date)[0];
+      const op = Object.keys(filter.date)[0];
 
       return limitOffset(
         data
@@ -95,7 +95,7 @@ function initPagingServer(
   dataLength,
   { delay, eventType = 'select' }: { delay?: number; eventType?: string } = {},
 ) {
-  let data = [];
+  const data = [];
   for (let i = 0; i < dataLength; i++) {
     data.push({ id: i, date: subDays('2020-05-01', Math.floor(i / 5)) });
   }
@@ -123,19 +123,19 @@ describe('query helpers', () => {
   it('runQuery runs a query', async () => {
     initServer({ query: query => ({ data: query, dependencies: [] }) });
 
-    let query = q('transactions').select('*');
-    let { data } = await runQuery(query);
+    const query = q('transactions').select('*');
+    const { data } = await runQuery(query);
     expect(data).toEqual(query.serialize());
   });
 
   ['liveQuery', 'pagedQuery'].forEach(queryType => {
-    let doQuery = queryType === 'liveQuery' ? liveQuery : pagedQuery;
+    const doQuery = queryType === 'liveQuery' ? liveQuery : pagedQuery;
 
     it(`${queryType} runs and subscribes to a query`, async () => {
       initBasicServer();
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
       doQuery(query, data => tracer.event('data', data));
 
       await tracer.expect('server-query');
@@ -151,14 +151,14 @@ describe('query helpers', () => {
       initBasicServer();
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
       doQuery(query, data => tracer.event('data', data), { onlySync: true });
 
       await tracer.expect('server-query');
       await tracer.expect('data', ['*']);
       serverPush('sync-event', { type: 'applied', tables: ['transactions'] });
 
-      let p = Promise.race([tracer.wait('server-query'), wait(100)]);
+      const p = Promise.race([tracer.wait('server-query'), wait(100)]);
       expect(await p).toEqual('wait(100)');
     });
 
@@ -166,7 +166,7 @@ describe('query helpers', () => {
       initBasicServer();
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
       doQuery(query, data => tracer.event('data', data), { onlySync: true });
 
       await tracer.expect('server-query');
@@ -189,8 +189,8 @@ describe('query helpers', () => {
       });
 
       tracer.start();
-      let query = q('transactions').select('*');
-      let lq = doQuery(query, data => tracer.event('data', data), {
+      const query = q('transactions').select('*');
+      const lq = doQuery(query, data => tracer.event('data', data), {
         onlySync: true,
       });
 
@@ -217,7 +217,7 @@ describe('query helpers', () => {
       initBasicServer();
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
 
       doQuery(query, data => tracer.event('data', data), { onlySync: true });
 
@@ -238,7 +238,7 @@ describe('query helpers', () => {
       initBasicServer(500);
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
 
       doQuery(query, data => tracer.event('data', data), { onlySync: true });
 
@@ -260,9 +260,9 @@ describe('query helpers', () => {
       initBasicServer();
       tracer.start();
 
-      let query = q('transactions').select('*');
+      const query = q('transactions').select('*');
 
-      let lq = doQuery(query, data => tracer.event('data', data));
+      const lq = doQuery(query, data => tracer.event('data', data));
 
       await tracer.expect('server-query');
       await tracer.expect('data', ['*']);
@@ -271,17 +271,17 @@ describe('query helpers', () => {
       serverPush('sync-event', { type: 'success', tables: ['transactions'] });
 
       // Wait a bit and make sure nothing comes through
-      let p = Promise.race([tracer.expect('server-query'), wait(100)]);
+      const p = Promise.race([tracer.expect('server-query'), wait(100)]);
       expect(await p).toEqual('wait(100)');
     });
   });
 
   it('pagedQuery makes requests in pages', async () => {
-    let data = initPagingServer(1502);
+    const data = initPagingServer(1502);
     tracer.start();
 
-    let query = q('transactions').select('id');
-    let paged = pagedQuery(query, data => tracer.event('data', data), {
+    const query = q('transactions').select('id');
+    const paged = pagedQuery(query, data => tracer.event('data', data), {
       onPageData: data => tracer.event('page-data', data),
     });
 
@@ -332,15 +332,15 @@ describe('query helpers', () => {
 
     await paged.fetchNext();
     // Wait a bit and make sure nothing comes through
-    let p = Promise.race([tracer.expect('server-query'), wait(100)]);
+    const p = Promise.race([tracer.expect('server-query'), wait(100)]);
     expect(await p).toEqual('wait(100)');
   });
 
   it('pagedQuery allows customizing page count', async () => {
-    let data = initPagingServer(50);
+    const data = initPagingServer(50);
     tracer.start();
 
-    let query = q('transactions').select('id');
+    const query = q('transactions').select('id');
     pagedQuery(query, data => tracer.event('data', data), {
       pageCount: 10,
     });
@@ -356,8 +356,8 @@ describe('query helpers', () => {
     initPagingServer(1000, { delay: 200 });
     tracer.start();
 
-    let query = q('transactions').select('id');
-    let paged = pagedQuery(query, data => tracer.event('data', data));
+    const query = q('transactions').select('id');
+    const paged = pagedQuery(query, data => tracer.event('data', data));
 
     await tracer.expect('server-query', [{ result: { $count: '*' } }]);
     await tracer.expect('server-query', ['id']);
@@ -372,16 +372,16 @@ describe('query helpers', () => {
     await tracer.expect('data', data => {});
 
     // Wait a bit and make sure nothing comes through
-    let p = Promise.race([tracer.expect('server-query'), wait(200)]);
+    const p = Promise.race([tracer.expect('server-query'), wait(200)]);
     expect(await p).toEqual('wait(200)');
   });
 
   it('pagedQuery refetches all paged data on update', async () => {
-    let data = initPagingServer(500, { delay: 200 });
+    const data = initPagingServer(500, { delay: 200 });
     tracer.start();
 
-    let query = q('transactions').select('id');
-    let paged = pagedQuery(query, data => tracer.event('data', data), {
+    const query = q('transactions').select('id');
+    const paged = pagedQuery(query, data => tracer.event('data', data), {
       pageCount: 20,
       onPageData: data => tracer.event('page-data', data),
     });
@@ -414,9 +414,9 @@ describe('query helpers', () => {
   });
 
   it('pagedQuery reruns `fetchNext` if data changed underneath it', async () => {
-    let data = initPagingServer(500, { delay: 10 });
-    let query = q('transactions').select('id');
-    let paged = pagedQuery(query, data => tracer.event('data', data), {
+    const data = initPagingServer(500, { delay: 10 });
+    const query = q('transactions').select('id');
+    const paged = pagedQuery(query, data => tracer.event('data', data), {
       pageCount: 20,
       onPageData: data => tracer.event('page-data', data),
     });
@@ -457,9 +457,9 @@ describe('query helpers', () => {
   });
 
   it('pagedQuery fetches up to a specific row', async () => {
-    let data = initPagingServer(500, { delay: 10, eventType: 'all' });
-    let query = q('transactions').select(['id', 'date']);
-    let paged = pagedQuery(query, data => tracer.event('data', data), {
+    const data = initPagingServer(500, { delay: 10, eventType: 'all' });
+    const query = q('transactions').select(['id', 'date']);
+    const paged = pagedQuery(query, data => tracer.event('data', data), {
       pageCount: 20,
       onPageData: data => tracer.event('page-data', data),
     });
@@ -467,7 +467,7 @@ describe('query helpers', () => {
 
     tracer.start();
 
-    let item = data.find(row => row.id === 300);
+    const item = data.find(row => row.id === 300);
     paged.refetchUpToRow(item.id, { field: 'date', order: 'desc' });
 
     await tracer.expect(
diff --git a/packages/loot-core/src/client/query-helpers.ts b/packages/loot-core/src/client/query-helpers.ts
index 4ff9e457f..72a7156e3 100644
--- a/packages/loot-core/src/client/query-helpers.ts
+++ b/packages/loot-core/src/client/query-helpers.ts
@@ -8,13 +8,13 @@ export async function runQuery(query) {
 }
 
 export function liveQuery(query, onData?, opts?): LiveQuery {
-  let q = new LiveQuery(query, onData, opts);
+  const q = new LiveQuery(query, onData, opts);
   q.run();
   return q;
 }
 
 export function pagedQuery(query, onData?, opts?): PagedQuery {
-  let q = new PagedQuery(query, onData, opts);
+  const q = new PagedQuery(query, onData, opts);
   q.run();
   return q;
 }
@@ -90,10 +90,10 @@ export class LiveQuery {
     // backend. could give a query an id which makes it cacheable via
     // an LRU cache
 
-    let reqId = Math.random();
+    const reqId = Math.random();
     this.inflightRequestId = reqId;
 
-    let { data, dependencies } = await makeRequest();
+    const { data, dependencies } = await makeRequest();
 
     // Regardless if this request was cancelled or not, save the
     // dependencies. The query can't change so all requests will
@@ -105,7 +105,7 @@ export class LiveQuery {
     // Only fire events if this hasn't been cancelled and if we're
     // still subscribed (`this._subscribe` will exist)
     if (this.inflightRequestId === reqId && this._unsubscribe) {
-      let prevData = this.mappedData;
+      const prevData = this.mappedData;
       this.data = data;
       this.mappedData = this.mapData(data);
       this.onData(this.mappedData, prevData);
@@ -124,7 +124,7 @@ export class LiveQuery {
         // always update to all changes.
         //
         // TODO: errors?
-        let syncTypes = this.onlySync ? ['success'] : ['applied', 'success'];
+        const syncTypes = this.onlySync ? ['success'] : ['applied', 'success'];
 
         if (syncTypes.indexOf(type) !== -1) {
           this.onUpdate(tables);
@@ -169,7 +169,7 @@ export class LiveQuery {
   };
 
   optimisticUpdate = (dataFunc, mappedDataFunc) => {
-    let prevMappedData = this.mappedData;
+    const prevMappedData = this.mappedData;
     this._optimisticUpdate(dataFunc, mappedDataFunc);
     this.onData(this.mappedData, prevMappedData);
   };
@@ -232,12 +232,12 @@ class PagedQuery extends LiveQuery {
       // Also fetch the total count
       this._fetchCount();
 
-      let orderDesc = getPrimaryOrderBy(this.query, defaultOrderBy);
+      const orderDesc = getPrimaryOrderBy(this.query, defaultOrderBy);
       if (orderDesc == null) {
         throw new Error(`refetchUpToRow requires a query with orderBy`);
       }
 
-      let { field, order } = orderDesc;
+      const { field, order } = orderDesc;
 
       let result = await runQuery(this.query.filter({ id }).select(field));
       if (result.data.length === 0) {
@@ -246,7 +246,7 @@ class PagedQuery extends LiveQuery {
         // data that we don't need
         return;
       }
-      let fullRow = result.data[0];
+      const fullRow = result.data[0];
 
       result = await runQuery(
         this.query.filter({
@@ -255,7 +255,7 @@ class PagedQuery extends LiveQuery {
           },
         }),
       );
-      let data = result.data;
+      const data = result.data;
 
       // Load in an extra page to make room for the UI to show some
       // data after it
@@ -285,10 +285,10 @@ class PagedQuery extends LiveQuery {
       await this.runPromise;
     }
 
-    let previousData = this.data;
+    const previousData = this.data;
 
     if (!this.done) {
-      let { data } = await runQuery(
+      const { data } = await runQuery(
         this.query.limit(this.pageCount).offset(previousData.length),
       );
 
@@ -305,8 +305,8 @@ class PagedQuery extends LiveQuery {
           this.done = data.length < this.pageCount;
           this.data = this.data.concat(data);
 
-          let prevData = this.mappedData;
-          let mapped = this.mapData(data);
+          const prevData = this.mappedData;
+          const mapped = this.mapData(data);
           this.mappedData = this.mappedData.concat(mapped);
           this.onPageData(mapped);
           this.onData(this.mappedData, prevData);
@@ -326,8 +326,8 @@ class PagedQuery extends LiveQuery {
   };
 
   optimisticUpdate = (dataFunc, mappedDataFunc) => {
-    let prevData = this.data;
-    let prevMappedData = this.mappedData;
+    const prevData = this.data;
+    const prevMappedData = this.mappedData;
 
     this._optimisticUpdate(dataFunc, mappedDataFunc);
     this.totalCount += this.data.length - prevData.length;
diff --git a/packages/loot-core/src/client/query-hooks.tsx b/packages/loot-core/src/client/query-hooks.tsx
index 671ebf6d6..329f466b7 100644
--- a/packages/loot-core/src/client/query-hooks.tsx
+++ b/packages/loot-core/src/client/query-hooks.tsx
@@ -12,12 +12,12 @@ import { type Query } from '../shared/query';
 import { liveQuery, LiveQuery } from './query-helpers';
 
 function makeContext(queryState, opts, QueryClass) {
-  let query = new QueryClass(queryState, null, opts);
-  let Context = createContext(null);
+  const query = new QueryClass(queryState, null, opts);
+  const Context = createContext(null);
 
   function Provider({ children }) {
-    let [data, setData] = useState(query.getData());
-    let value = useMemo(() => ({ data, query }), [data, query]);
+    const [data, setData] = useState(query.getData());
+    const value = useMemo(() => ({ data, query }), [data, query]);
 
     useEffect(() => {
       if (query.getNumListeners() !== 0) {
@@ -26,7 +26,7 @@ function makeContext(queryState, opts, QueryClass) {
         );
       }
 
-      let unlisten = query.addListener(data => setData(data));
+      const unlisten = query.addListener(data => setData(data));
 
       // Start the query if it hasn't run yet. Most likely it's not
       // running, however the user can freely start the query early if
@@ -50,7 +50,7 @@ function makeContext(queryState, opts, QueryClass) {
   }
 
   function useQuery() {
-    let queryData = useContext(Context);
+    const queryData = useContext(Context);
     if (queryData == null) {
       throw new Error(
         '`useQuery` tried to access a query that hasn’t been run. You need to put its `Provider` in a parent component',
@@ -70,8 +70,8 @@ export function liveQueryContext(query, opts?) {
 }
 
 export function useLiveQuery(makeQuery: () => Query, deps: DependencyList) {
-  let [data, setData] = useState(null);
-  let query = useMemo(makeQuery, deps);
+  const [data, setData] = useState(null);
+  const query = useMemo(makeQuery, deps);
 
   useEffect(() => {
     let live = liveQuery(query, async data => {
diff --git a/packages/loot-core/src/client/reducers/account.ts b/packages/loot-core/src/client/reducers/account.ts
index dc78b508f..5e713db7e 100644
--- a/packages/loot-core/src/client/reducers/account.ts
+++ b/packages/loot-core/src/client/reducers/account.ts
@@ -18,7 +18,7 @@ export default function update(
         accountsSyncing: action.name,
       };
     case constants.ACCOUNT_SYNC_STATUS: {
-      let failedAccounts = new Map(state.failedAccounts);
+      const failedAccounts = new Map(state.failedAccounts);
       if (action.failed) {
         failedAccounts.set(action.id, {
           type: action.errorType,
@@ -31,7 +31,7 @@ export default function update(
       return { ...state, failedAccounts };
     }
     case constants.ACCOUNT_SYNC_FAILURES: {
-      let failures = new Map();
+      const failures = new Map();
       action.syncErrors.forEach(error => {
         failures.set(error.id, {
           type: error.type,
diff --git a/packages/loot-core/src/client/reducers/budgets.ts b/packages/loot-core/src/client/reducers/budgets.ts
index e37484b88..1d8625d9b 100644
--- a/packages/loot-core/src/client/reducers/budgets.ts
+++ b/packages/loot-core/src/client/reducers/budgets.ts
@@ -7,12 +7,12 @@ import type { BudgetsState } from '../state-types/budgets';
 
 function sortFiles(arr: File[]) {
   arr.sort((x, y) => {
-    let name1 = x.name.toLowerCase();
-    let name2 = y.name.toLowerCase();
+    const name1 = x.name.toLowerCase();
+    const name2 = y.name.toLowerCase();
     let i = name1 < name2 ? -1 : name1 > name2 ? 1 : 0;
     if (i === 0) {
-      let xId = x.state === 'remote' ? x.cloudFileId : x.id;
-      let yId = x.state === 'remote' ? x.cloudFileId : x.id;
+      const xId = x.state === 'remote' ? x.cloudFileId : x.id;
+      const yId = x.state === 'remote' ? x.cloudFileId : x.id;
       i = xId < yId ? -1 : xId > yId ? 1 : 0;
     }
     return i;
@@ -32,10 +32,10 @@ function reconcileFiles(
   remoteFiles: RemoteFile[] | null,
 ): File[] {
   console.log({ localFiles, remoteFiles });
-  let reconciled = new Set();
+  const reconciled = new Set();
 
-  let files = localFiles.map((localFile): File & { deleted: boolean } => {
-    let { cloudFileId, groupId } = localFile;
+  const files = localFiles.map((localFile): File & { deleted: boolean } => {
+    const { cloudFileId, groupId } = localFile;
     if (cloudFileId && groupId) {
       // This is the case where for some reason getting the files from
       // the server failed. We don't want to scare the user, just show
@@ -51,7 +51,7 @@ function reconcileFiles(
         };
       }
 
-      let remote = remoteFiles.find(f => localFile.cloudFileId === f.fileId);
+      const remote = remoteFiles.find(f => localFile.cloudFileId === f.fileId);
       if (remote) {
         // Mark reconciled
         reconciled.add(remote.fileId);
@@ -93,7 +93,7 @@ function reconcileFiles(
     }
   });
 
-  let sorted = sortFiles(
+  const sorted = sortFiles(
     files
       .concat(
         (remoteFiles || [])
diff --git a/packages/loot-core/src/client/reducers/queries.ts b/packages/loot-core/src/client/reducers/queries.ts
index 8cd82fb88..f44c6ca86 100644
--- a/packages/loot-core/src/client/reducers/queries.ts
+++ b/packages/loot-core/src/client/reducers/queries.ts
@@ -89,7 +89,7 @@ export default function update(
 export const getAccountsById = memoizeOne(accounts => groupById(accounts));
 export const getPayeesById = memoizeOne(payees => groupById(payees));
 export const getCategoriesById = memoizeOne(categoryGroups => {
-  let res = {};
+  const res = {};
   categoryGroups.forEach(group => {
     group.categories.forEach(cat => {
       res[cat.id] = cat;
@@ -99,11 +99,11 @@ export const getCategoriesById = memoizeOne(categoryGroups => {
 });
 
 export const getActivePayees = memoizeOne((payees, accounts) => {
-  let accountsById = getAccountsById(accounts);
+  const accountsById = getAccountsById(accounts);
 
   return payees.filter(payee => {
     if (payee.transfer_acct) {
-      let account = accountsById[payee.transfer_acct];
+      const account = accountsById[payee.transfer_acct];
       return account != null && !account.closed;
     }
     return true;
diff --git a/packages/loot-core/src/client/shared-listeners.ts b/packages/loot-core/src/client/shared-listeners.ts
index a46f1490c..d26c8ed89 100644
--- a/packages/loot-core/src/client/shared-listeners.ts
+++ b/packages/loot-core/src/client/shared-listeners.ts
@@ -6,9 +6,9 @@ export function listenForSyncEvent(actions, store) {
   let attemptedSyncRepair = false;
 
   listen('sync-event', info => {
-    let { type, subtype, meta, tables } = info;
+    const { type, subtype, meta, tables } = info;
 
-    let prefs = store.getState().prefs.local;
+    const prefs = store.getState().prefs.local;
     if (!prefs || !prefs.id) {
       // Do nothing if no budget is loaded
       return;
@@ -46,7 +46,7 @@ export function listenForSyncEvent(actions, store) {
       }
     } else if (type === 'error') {
       let notif: Notification | null = null;
-      let learnMore =
+      const learnMore =
         '[Learn more](https://actualbudget.org/docs/getting-started/sync/#debugging-sync-issues)';
       const githubIssueLink =
         'https://github.com/actualbudget/actual/issues/new?assignees=&labels=bug&template=bug-report.yml&title=%5BBug%5D%3A+';
@@ -174,7 +174,7 @@ export function listenForSyncEvent(actions, store) {
           // the server does not match the local one. This can mean a
           // few things depending on the state, and we try to show an
           // appropriate message and call to action to fix it.
-          let { cloudFileId } = store.getState().prefs.local;
+          const { cloudFileId } = store.getState().prefs.local;
 
           notif = {
             title: 'Syncing has been reset on this cloud file',
diff --git a/packages/loot-core/src/client/update-notification.ts b/packages/loot-core/src/client/update-notification.ts
index 3057a1c6f..d66b8c1c0 100644
--- a/packages/loot-core/src/client/update-notification.ts
+++ b/packages/loot-core/src/client/update-notification.ts
@@ -5,8 +5,8 @@ export default async function checkForUpdateNotification(
   loadPrefs,
   savePrefs,
 ) {
-  let latestVersion = await getLatestVersion();
-  let isOutdated = await getIsOutdated(latestVersion);
+  const latestVersion = await getLatestVersion();
+  const isOutdated = await getIsOutdated(latestVersion);
   if (
     !isOutdated ||
     (await loadPrefs())['flags.updateNotificationShownForVersion'] ===
diff --git a/packages/loot-core/src/mocks/arbitrary-schema.ts b/packages/loot-core/src/mocks/arbitrary-schema.ts
index 97ec3113d..23aace50d 100644
--- a/packages/loot-core/src/mocks/arbitrary-schema.ts
+++ b/packages/loot-core/src/mocks/arbitrary-schema.ts
@@ -52,8 +52,8 @@ export function typeArbitrary(typeDesc, name?) {
 }
 
 export function flattenSortTransactions(arr) {
-  let flattened = arr.reduce((list, trans) => {
-    let { subtransactions, ...fields } = trans;
+  const flattened = arr.reduce((list, trans) => {
+    const { subtransactions, ...fields } = trans;
 
     if (subtransactions.length > 0) {
       list.push({
@@ -101,7 +101,7 @@ function tableArbitrary<
   extraArbs?: E,
   requiredKeys: Array<Extract<keyof T | keyof E, string>> = [],
 ) {
-  let arb = fc.record(
+  const arb = fc.record(
     {
       ...Object.fromEntries<T>(
         Object.entries(tableSchema).map(([name, field]) => {
@@ -128,11 +128,11 @@ export function makeTransaction({
   splitFreq = 1,
   payeeIds,
 }: { splitFreq?: number; payeeIds?: string[] } = {}) {
-  let payeeField = payeeIds
+  const payeeField = payeeIds
     ? { payee: fc.oneof(...payeeIds.map(id => fc.constant(id))) }
     : null;
 
-  let subtrans = tableArbitrary(schema.transactions, payeeField);
+  const subtrans = tableArbitrary(schema.transactions, payeeField);
 
   return tableArbitrary(
     schema.transactions,
@@ -150,7 +150,7 @@ export function makeTransaction({
 export const makeTransactionArray = (
   options: { minLength?; maxLength?; splitFreq?; payeeIds? } = {},
 ) => {
-  let { minLength, maxLength, ...transOpts } = options;
+  const { minLength, maxLength, ...transOpts } = options;
   return fc
     .array(makeTransaction(transOpts), { minLength, maxLength })
     .map(arr => flattenSortTransactions(arr));
diff --git a/packages/loot-core/src/mocks/budget.ts b/packages/loot-core/src/mocks/budget.ts
index 2993722a6..8c308b339 100644
--- a/packages/loot-core/src/mocks/budget.ts
+++ b/packages/loot-core/src/mocks/budget.ts
@@ -44,13 +44,13 @@ function getStartingBalanceCat(categories) {
 }
 
 function extractCommonThings(payees, groups) {
-  let incomePayee = payees.find(p => p.name === 'Deposit');
-  let expensePayees = payees.filter(
+  const incomePayee = payees.find(p => p.name === 'Deposit');
+  const expensePayees = payees.filter(
     p => p.name !== 'Deposit' && p.name !== 'Starting Balance',
   );
-  let expenseGroup = groups.find(g => g.is_income === 0);
-  let incomeGroup = groups.find(g => g.is_income === 1);
-  let categories = expenseGroup.categories.filter(
+  const expenseGroup = groups.find(g => g.is_income === 0);
+  const incomeGroup = groups.find(g => g.is_income === 1);
+  const categories = expenseGroup.categories.filter(
     c =>
       [
         'Food',
@@ -74,7 +74,7 @@ function extractCommonThings(payees, groups) {
 }
 
 async function fillPrimaryChecking(handlers, account, payees, groups) {
-  let {
+  const {
     incomePayee,
     expensePayees,
     incomeGroup,
@@ -82,9 +82,9 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
     billCategories,
     billPayees,
   } = extractCommonThings(payees, groups);
-  let numTransactions = integer(100, 200);
+  const numTransactions = integer(100, 200);
 
-  let transactions = [];
+  const transactions = [];
   for (let i = 0; i < numTransactions; i++) {
     let payee;
     if (random() < 0.09) {
@@ -107,7 +107,7 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
       amount = integer(0, random() < 0.05 ? -8000 : -700);
     }
 
-    let transaction: TransactionEntity = {
+    const transaction: TransactionEntity = {
       amount,
       payee: payee.id,
       account: account.id,
@@ -117,8 +117,8 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
     transactions.push(transaction);
 
     if (random() < 0.2) {
-      let a = Math.round(transaction.amount / 3);
-      let pick = () =>
+      const a = Math.round(transaction.amount / 3);
+      const pick = () =>
         payee === incomePayee
           ? incomeGroup.categories.find(c => c.name === 'Income').id
           : pickRandom(expenseCategories).id;
@@ -133,15 +133,15 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
     }
   }
 
-  let earliestMonth = monthUtils.monthFromDate(
+  const earliestMonth = monthUtils.monthFromDate(
     transactions[transactions.length - 1].date,
   );
-  let months = monthUtils.rangeInclusive(
+  const months = monthUtils.rangeInclusive(
     earliestMonth,
     monthUtils.currentMonth(),
   );
-  let currentDay = monthUtils.currentDay();
-  for (let month of months) {
+  const currentDay = monthUtils.currentDay();
+  for (const month of months) {
     let date = monthUtils.addDays(month, 12);
     if (monthUtils.isBefore(date, currentDay)) {
       transactions.push({
@@ -220,11 +220,11 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
 }
 
 async function fillChecking(handlers, account, payees, groups) {
-  let { incomePayee, expensePayees, incomeGroup, expenseCategories } =
+  const { incomePayee, expensePayees, incomeGroup, expenseCategories } =
     extractCommonThings(payees, groups);
-  let numTransactions = integer(20, 40);
+  const numTransactions = integer(20, 40);
 
-  let transactions = [];
+  const transactions = [];
   for (let i = 0; i < numTransactions; i++) {
     let payee;
     if (random() < 0.04) {
@@ -240,7 +240,7 @@ async function fillChecking(handlers, account, payees, groups) {
       category = pickRandom(expenseCategories);
     }
 
-    let amount =
+    const amount =
       payee.name === 'Deposit' ? integer(50000, 70000) : integer(0, -10000);
 
     transactions.push({
@@ -268,16 +268,16 @@ async function fillChecking(handlers, account, payees, groups) {
 }
 
 async function fillInvestment(handlers, account, payees, groups) {
-  let { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
+  const { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
 
-  let numTransactions = integer(10, 30);
+  const numTransactions = integer(10, 30);
 
-  let transactions = [];
+  const transactions = [];
   for (let i = 0; i < numTransactions; i++) {
-    let payee = incomePayee;
-    let category = incomeGroup.categories.find(c => c.name === 'Income');
+    const payee = incomePayee;
+    const category = incomeGroup.categories.find(c => c.name === 'Income');
 
-    let amount = integer(10000, 20000);
+    const amount = integer(10000, 20000);
 
     transactions.push({
       amount,
@@ -304,12 +304,12 @@ async function fillInvestment(handlers, account, payees, groups) {
 }
 
 async function fillSavings(handlers, account, payees, groups) {
-  let { incomePayee, expensePayees, incomeGroup, expenseCategories } =
+  const { incomePayee, expensePayees, incomeGroup, expenseCategories } =
     extractCommonThings(payees, groups);
 
-  let numTransactions = integer(15, 40);
+  const numTransactions = integer(15, 40);
 
-  let transactions = [];
+  const transactions = [];
   for (let i = 0; i < numTransactions; i++) {
     let payee;
     if (random() < 0.3) {
@@ -317,11 +317,11 @@ async function fillSavings(handlers, account, payees, groups) {
     } else {
       payee = pickRandom(expensePayees);
     }
-    let category =
+    const category =
       payee === incomePayee
         ? incomeGroup.categories.find(c => c.name === 'Income')
         : pickRandom(expenseCategories);
-    let amount =
+    const amount =
       payee === incomePayee ? integer(10000, 80000) : integer(-10000, -2000);
 
     transactions.push({
@@ -349,13 +349,13 @@ async function fillSavings(handlers, account, payees, groups) {
 }
 
 async function fillMortgage(handlers, account, payees, groups) {
-  let { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
+  const { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
 
-  let numTransactions = integer(7, 10);
-  let amount = integer(100000, 200000);
-  let category = incomeGroup.categories.find(c => c.name === 'Income');
+  const numTransactions = integer(7, 10);
+  const amount = integer(100000, 200000);
+  const category = incomeGroup.categories.find(c => c.name === 'Income');
 
-  let transactions = [
+  const transactions = [
     {
       amount: integer(-3000, -3500) * 100 * 100,
       payee: payees.find(p => p.name === 'Starting Balance').id,
@@ -367,7 +367,7 @@ async function fillMortgage(handlers, account, payees, groups) {
     },
   ];
   for (let i = 0; i < numTransactions; i++) {
-    let payee = incomePayee;
+    const payee = incomePayee;
 
     transactions.push({
       amount,
@@ -386,12 +386,12 @@ async function fillMortgage(handlers, account, payees, groups) {
 }
 
 async function fillOther(handlers, account, payees, groups) {
-  let { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
+  const { incomePayee, incomeGroup } = extractCommonThings(payees, groups);
 
-  let numTransactions = integer(3, 6);
-  let category = incomeGroup.categories.find(c => c.name === 'Income');
+  const numTransactions = integer(3, 6);
+  const category = incomeGroup.categories.find(c => c.name === 'Income');
 
-  let transactions: TransactionEntity[] = [
+  const transactions: TransactionEntity[] = [
     {
       amount: integer(3250, 3700) * 100 * 100,
       payee: payees.find(p => p.name === 'Starting Balance').id,
@@ -403,8 +403,8 @@ async function fillOther(handlers, account, payees, groups) {
     },
   ];
   for (let i = 0; i < numTransactions; i++) {
-    let payee = incomePayee;
-    let amount = integer(4, 9) * 100 * 100;
+    const payee = incomePayee;
+    const amount = integer(4, 9) * 100 * 100;
 
     transactions.push({
       amount,
@@ -422,14 +422,14 @@ async function fillOther(handlers, account, payees, groups) {
 }
 
 async function createBudget(accounts, payees, groups) {
-  let primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
-  let earliestDate = (
+  const primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
+  const earliestDate = (
     await db.first(
       `SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
        WHERE a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`,
     )
   ).date;
-  let earliestPrimaryDate = (
+  const earliestPrimaryDate = (
     await db.first(
       `SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
        WHERE a.id = ? AND a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`,
@@ -437,13 +437,13 @@ async function createBudget(accounts, payees, groups) {
     )
   ).date;
 
-  let start = monthUtils.monthFromDate(db.fromDateRepr(earliestDate));
-  let end = monthUtils.currentMonth();
-  let months = monthUtils.rangeInclusive(start, end);
+  const start = monthUtils.monthFromDate(db.fromDateRepr(earliestDate));
+  const end = monthUtils.currentMonth();
+  const months = monthUtils.rangeInclusive(start, end);
 
   function category(name) {
-    for (let group of groups) {
-      let cat = group.categories.find(c => c.name === name);
+    for (const group of groups) {
+      const cat = group.categories.find(c => c.name === name);
       if (cat) {
         return cat;
       }
@@ -455,7 +455,7 @@ async function createBudget(accounts, payees, groups) {
   }
 
   function setBudgetIfSpent(month, cat) {
-    let spent: number = sheet.getCellValue(
+    const spent: number = sheet.getCellValue(
       monthUtils.sheetForMonth(month),
       `sum-amount-${cat.id}`,
     ) as number;
@@ -467,7 +467,7 @@ async function createBudget(accounts, payees, groups) {
 
   await runMutator(() =>
     batchMessages(async () => {
-      for (let month of months) {
+      for (const month of months) {
         if (
           month >=
           monthUtils.monthFromDate(db.fromDateRepr(earliestPrimaryDate))
@@ -509,18 +509,18 @@ async function createBudget(accounts, payees, groups) {
   await runMutator(() =>
     batchMessages(async () => {
       let prevSaved = 0;
-      for (let month of months) {
+      for (const month of months) {
         if (
           month >=
             monthUtils.monthFromDate(db.fromDateRepr(earliestPrimaryDate)) &&
           month <= monthUtils.currentMonth()
         ) {
-          let sheetName = monthUtils.sheetForMonth(month);
-          let toBudget: number = sheet.getCellValue(
+          const sheetName = monthUtils.sheetForMonth(month);
+          const toBudget: number = sheet.getCellValue(
             sheetName,
             'to-budget',
           ) as number;
-          let available = toBudget - prevSaved;
+          const available = toBudget - prevSaved;
 
           if (available - 403000 > 0) {
             setBudget(month, category('Savings'), available - 403000);
@@ -537,8 +537,8 @@ async function createBudget(accounts, payees, groups) {
 
   await sheet.waitOnSpreadsheet();
 
-  let sheetName = monthUtils.sheetForMonth(monthUtils.currentMonth());
-  let toBudget: number = sheet.getCellValue(sheetName, 'to-budget') as number;
+  const sheetName = monthUtils.sheetForMonth(monthUtils.currentMonth());
+  const toBudget: number = sheet.getCellValue(sheetName, 'to-budget') as number;
   if (toBudget < 0) {
     await addTransactions(primaryAccount.id, [
       {
@@ -567,7 +567,7 @@ export async function createTestBudget(handlers) {
   await db.runQuery('DELETE FROM categories;');
   await db.runQuery('DELETE FROM category_groups');
 
-  let accounts: { name: string; offBudget?: 1; id?: string }[] = [
+  const accounts: { name: string; offBudget?: 1; id?: string }[] = [
     { name: 'Bank of America' },
     { name: 'Ally Savings' },
     { name: 'Capital One Checking' },
@@ -579,13 +579,13 @@ export async function createTestBudget(handlers) {
   ];
   await runMutator(() =>
     batchMessages(async () => {
-      for (let account of accounts) {
+      for (const account of accounts) {
         account.id = await handlers['account-create'](account);
       }
     }),
   );
 
-  let payees: Array<PayeeEntity & { bill?: boolean }> = [
+  const payees: Array<PayeeEntity & { bill?: boolean }> = [
     { name: 'Starting Balance' },
     { name: 'Kroger' },
     { name: 'Publix' },
@@ -602,13 +602,13 @@ export async function createTestBudget(handlers) {
 
   await runMutator(() =>
     batchMessages(async () => {
-      for (let payee of payees) {
+      for (const payee of payees) {
         payee.id = await handlers['payee-create']({ name: payee.name });
       }
     }),
   );
 
-  let categoryGroups: Array<CategoryGroupEntity> = [
+  const categoryGroups: Array<CategoryGroupEntity> = [
     {
       name: 'Usual Expenses',
       categories: [
@@ -644,13 +644,13 @@ export async function createTestBudget(handlers) {
   ];
 
   await runMutator(async () => {
-    for (let group of categoryGroups) {
+    for (const group of categoryGroups) {
       group.id = await handlers['category-group-create']({
         name: group.name,
         isIncome: group.is_income ? 1 : 0,
       });
 
-      for (let category of group.categories) {
+      for (const category of group.categories) {
         category.id = await handlers['category-create']({
           ...category,
           isIncome: category.is_income ? 1 : 0,
@@ -660,13 +660,13 @@ export async function createTestBudget(handlers) {
     }
   });
 
-  let allGroups = (await runHandler(handlers['get-categories'])).grouped;
+  const allGroups = (await runHandler(handlers['get-categories'])).grouped;
 
   setSyncingMode('import');
 
   await runMutator(() =>
     batchMessages(async () => {
-      for (let account of accounts) {
+      for (const account of accounts) {
         if (account.name === 'Bank of America') {
           await fillPrimaryChecking(handlers, account, payees, allGroups);
         } else if (
@@ -699,22 +699,22 @@ export async function createTestBudget(handlers) {
   // This might happen depending on the transactions added, but we
   // don't want to show that as it'd be weird. We modify the latest
   // deposit transaction to force it to be positive
-  let primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
-  let { data: primaryBalance } = await aqlQuery(
+  const primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
+  const { data: primaryBalance } = await aqlQuery(
     q('transactions')
       .filter({ account: primaryAccount.id })
       .calculate({ $sum: '$amount' })
       .serialize(),
   );
   if (primaryBalance < 0) {
-    let { data: results } = await aqlQuery(
+    const { data: results } = await aqlQuery(
       q('transactions')
         .filter({ account: primaryAccount.id, amount: { $gt: 0 } })
         .limit(1)
         .select(['id', 'amount'])
         .serialize(),
     );
-    let lastDeposit = results[0];
+    const lastDeposit = results[0];
 
     await runHandler(handlers['transaction-update'], {
       ...lastDeposit,
diff --git a/packages/loot-core/src/mocks/redux.tsx b/packages/loot-core/src/mocks/redux.tsx
index f47efeabb..569c6ae82 100644
--- a/packages/loot-core/src/mocks/redux.tsx
+++ b/packages/loot-core/src/mocks/redux.tsx
@@ -6,7 +6,7 @@ import thunk from 'redux-thunk';
 
 import reducers from '../client/reducers';
 
-let appReducer = combineReducers(reducers);
+const appReducer = combineReducers(reducers);
 let store = null;
 
 export function resetStore() {
diff --git a/packages/loot-core/src/mocks/setup.ts b/packages/loot-core/src/mocks/setup.ts
index 431b0a478..ec4293a7d 100644
--- a/packages/loot-core/src/mocks/setup.ts
+++ b/packages/loot-core/src/mocks/setup.ts
@@ -29,7 +29,7 @@ process.on('unhandledRejection', reason => {
 global.IS_TESTING = true;
 
 let _time = 123456789;
-let _oldDateNow = global.Date.now;
+const _oldDateNow = global.Date.now;
 global.Date.now = () => _time;
 
 global.restoreDateNow = () => (global.Date.now = _oldDateNow);
@@ -99,7 +99,7 @@ global.getDatabaseDump = async function (tables) {
     }),
   );
 
-  let grouped = {};
+  const grouped = {};
   data.forEach(table => (grouped[table[0]] = table[1]));
   return grouped;
 };
@@ -110,13 +110,13 @@ global.getDatabaseDump = async function (tables) {
 
 global.emptyDatabase = function (avoidUpdate) {
   return async () => {
-    let path = ':memory:';
+    const path = ':memory:';
     // let path = `/tmp/foo-${Math.random()}.sqlite`;
     // console.log('Using db ' + path);
 
     await sqlite.init();
 
-    let memoryDB = await sqlite.openDatabase(path);
+    const memoryDB = await sqlite.openDatabase(path);
     sqlite.execQuery(
       memoryDB,
       nativeFs.readFileSync(__dirname + '/../server/sql/init.sql', 'utf8'),
diff --git a/packages/loot-core/src/mocks/spreadsheet.ts b/packages/loot-core/src/mocks/spreadsheet.ts
index a83cc3f5f..7a79ed62d 100644
--- a/packages/loot-core/src/mocks/spreadsheet.ts
+++ b/packages/loot-core/src/mocks/spreadsheet.ts
@@ -3,9 +3,9 @@ function makeSpreadsheet() {
   return {
     observers: [],
     _getNode(sheetName, name) {
-      let resolvedName = `${sheetName}!${name}`;
+      const resolvedName = `${sheetName}!${name}`;
 
-      let existing = cells[resolvedName];
+      const existing = cells[resolvedName];
       if (existing) {
         return existing;
       }
@@ -23,14 +23,14 @@ function makeSpreadsheet() {
     },
 
     bind(sheetName, binding, fields, cb) {
-      let { name } = binding;
-      let resolvedName = `${sheetName}!${name}`;
+      const { name } = binding;
+      const resolvedName = `${sheetName}!${name}`;
       if (!this.observers[resolvedName]) {
         this.observers[resolvedName] = [];
       }
       this.observers[resolvedName].push(cb);
 
-      let node = this._getNode(sheetName, name);
+      const node = this._getNode(sheetName, name);
       cb(node);
 
       // bind returns a function which unsubscribes itself. In this mock
diff --git a/packages/loot-core/src/mocks/util.ts b/packages/loot-core/src/mocks/util.ts
index 0525babc6..484c1252e 100644
--- a/packages/loot-core/src/mocks/util.ts
+++ b/packages/loot-core/src/mocks/util.ts
@@ -42,7 +42,7 @@ export function debugDOM(node) {
         str += node.textContent + '\n';
       }
 
-      for (let child of node.childNodes) {
+      for (const child of node.childNodes) {
         str += debugDOM(child, indent + 2);
       }
     }
diff --git a/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts b/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
index b486938ce..bbb71b502 100644
--- a/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
+++ b/packages/loot-core/src/platform/client/fetch/__mocks__/index.web.ts
@@ -5,10 +5,10 @@ let serverHandler = null;
 
 export const initServer: T.InitServer = handlers => {
   serverHandler = msg => {
-    let { name, args, catchErrors } = msg;
+    const { name, args, catchErrors } = msg;
     if (handlers[name]) {
       return Promise.resolve().then(() => {
-        let promise = handlers[name](args);
+        const promise = handlers[name](args);
 
         if (catchErrors) {
           return promise.then(
@@ -57,7 +57,7 @@ export const listen = (name, cb) => {
   listeners.get(name).push(cb);
 
   return () => {
-    let arr = listeners.get(name);
+    const arr = listeners.get(name);
     listeners.set(
       name,
       arr.filter(cb_ => cb_ !== cb),
diff --git a/packages/loot-core/src/platform/client/fetch/index.browser.ts b/packages/loot-core/src/platform/client/fetch/index.browser.ts
index 8280b8493..47482e7c8 100644
--- a/packages/loot-core/src/platform/client/fetch/index.browser.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.browser.ts
@@ -5,8 +5,8 @@ import * as undo from '../undo';
 
 import type * as T from '.';
 
-let replyHandlers = new Map();
-let listeners = new Map();
+const replyHandlers = new Map();
+const listeners = new Map();
 let messageQueue = [];
 
 let globalWorker = null;
@@ -65,7 +65,7 @@ function handleMessage(msg) {
     const listens = listeners.get(name);
     if (listens) {
       for (let i = 0; i < listens.length; i++) {
-        let stop = listens[i](args);
+        const stop = listens[i](args);
         if (stop === true) {
           break;
         }
@@ -86,7 +86,7 @@ function connectWorker(worker, onOpen, onError) {
   globalWorker = worker;
 
   worker.onmessage = event => {
-    let msg = event.data;
+    const msg = event.data;
 
     // The worker implementation implements its own concept of a
     // 'connect' event because the worker is immediately
@@ -148,10 +148,10 @@ export const send: T.Send = function (
   { catchErrors = false } = {},
 ) {
   return new Promise((resolve, reject) => {
-    let id = uuidv4();
+    const id = uuidv4();
 
     replyHandlers.set(id, { resolve, reject });
-    let message = {
+    const message = {
       id,
       name,
       args,
@@ -178,7 +178,7 @@ export const listen: T.Listen = function (name, cb) {
   listeners.get(name).push(cb);
 
   return () => {
-    let arr = listeners.get(name);
+    const arr = listeners.get(name);
     listeners.set(
       name,
       arr.filter(cb_ => cb_ !== cb),
diff --git a/packages/loot-core/src/platform/client/fetch/index.web.ts b/packages/loot-core/src/platform/client/fetch/index.web.ts
index b993bfc4b..cdf9e2c5f 100644
--- a/packages/loot-core/src/platform/client/fetch/index.web.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.web.ts
@@ -4,8 +4,8 @@ import * as undo from '../undo';
 
 import type * as T from '.';
 
-let replyHandlers = new Map();
-let listeners = new Map();
+const replyHandlers = new Map();
+const listeners = new Map();
 let messageQueue = [];
 let socketClient = null;
 let activePort = null;
@@ -16,7 +16,7 @@ function connectSocket(port, onOpen) {
     return;
   }
 
-  let client = new WebSocket('ws://localhost:' + port);
+  const client = new WebSocket('ws://localhost:' + port);
   socketClient = client;
   activePort = port;
 
@@ -32,7 +32,8 @@ function connectSocket(port, onOpen) {
       const { id } = msg;
       replyHandlers.delete(id);
     } else if (msg.type === 'reply') {
-      let { id, result, mutated, undoTag } = msg;
+      let { result } = msg;
+      const { id, mutated, undoTag } = msg;
 
       // Check if the result is a serialized buffer, and if so
       // convert it to a Uint8Array. This is only needed when working
@@ -58,7 +59,7 @@ function connectSocket(port, onOpen) {
       const listens = listeners.get(name);
       if (listens) {
         for (let i = 0; i < listens.length; i++) {
-          let stop = listens[i](args);
+          const stop = listens[i](args);
           if (stop === true) {
             break;
           }
@@ -96,7 +97,7 @@ export const send: T.Send = function (
   { catchErrors = false } = {},
 ) {
   return new Promise((resolve, reject) => {
-    let id = uuidv4();
+    const id = uuidv4();
     replyHandlers.set(id, { resolve, reject });
 
     if (socketClient) {
@@ -135,7 +136,7 @@ export const listen: T.Listen = function (name, cb) {
   listeners.get(name).push(cb);
 
   return () => {
-    let arr = listeners.get(name);
+    const arr = listeners.get(name);
     if (arr) {
       listeners.set(
         name,
diff --git a/packages/loot-core/src/platform/client/undo/index.web.ts b/packages/loot-core/src/platform/client/undo/index.web.ts
index 3718a85c3..15c3300b0 100644
--- a/packages/loot-core/src/platform/client/undo/index.web.ts
+++ b/packages/loot-core/src/platform/client/undo/index.web.ts
@@ -4,10 +4,10 @@ import type * as T from '.';
 
 // List of recently used states. We don't use a true MRU structure
 // because our needs are simple and we also do some custom reordering.
-let HISTORY_SIZE = 40;
+const HISTORY_SIZE = 40;
 let UNDO_STATE_MRU: T.UndoState[] = [];
 
-let currentUndoState: T.UndoState = {
+const currentUndoState: T.UndoState = {
   url: null,
   openModal: null,
   selectedItems: null,
@@ -27,7 +27,7 @@ export const getTaggedState: T.GetTaggedState = function (id) {
 };
 
 export const snapshot: T.Snapshot = function () {
-  let tagged = { ...currentUndoState, id: uuidv4() };
+  const tagged = { ...currentUndoState, id: uuidv4() };
   UNDO_STATE_MRU.unshift(tagged);
   UNDO_STATE_MRU = UNDO_STATE_MRU.slice(0, HISTORY_SIZE);
   return tagged.id;
diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts b/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts
index 7551acb07..22f3b8a02 100644
--- a/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts
+++ b/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts
@@ -5,7 +5,7 @@ import * as lootFs from '../fs';
 
 import * as T from '.';
 
-let getStorePath = () => join(lootFs.getDataDir(), 'global-store.json');
+const getStorePath = () => join(lootFs.getDataDir(), 'global-store.json');
 let store;
 let persisted = true;
 
diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.web.ts b/packages/loot-core/src/platform/server/asyncStorage/index.web.ts
index b81081c71..6af2b01ae 100644
--- a/packages/loot-core/src/platform/server/asyncStorage/index.web.ts
+++ b/packages/loot-core/src/platform/server/asyncStorage/index.web.ts
@@ -11,13 +11,13 @@ function commit(trans) {
 }
 
 export const getItem: T.GetItem = async function (key) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readonly');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readonly');
+  const objectStore = transaction.objectStore('asyncStorage');
 
   return new Promise((resolve, reject) => {
-    let req = objectStore.get(key);
+    const req = objectStore.get(key);
     req.onerror = e => reject(e);
     req.onsuccess = e => resolve(e.target.result);
     commit(transaction);
@@ -25,13 +25,13 @@ export const getItem: T.GetItem = async function (key) {
 };
 
 export const setItem: T.SetItem = async function (key, value) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readwrite');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readwrite');
+  const objectStore = transaction.objectStore('asyncStorage');
 
   new Promise((resolve, reject) => {
-    let req = objectStore.put(value, key);
+    const req = objectStore.put(value, key);
     req.onerror = e => reject(e);
     req.onsuccess = e => resolve(undefined);
     commit(transaction);
@@ -39,13 +39,13 @@ export const setItem: T.SetItem = async function (key, value) {
 };
 
 export const removeItem: T.RemoveItem = async function (key) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readwrite');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readwrite');
+  const objectStore = transaction.objectStore('asyncStorage');
 
   return new Promise((resolve, reject) => {
-    let req = objectStore.delete(key);
+    const req = objectStore.delete(key);
     req.onerror = e => reject(e);
     req.onsuccess = e => resolve(undefined);
     commit(transaction);
@@ -53,15 +53,15 @@ export const removeItem: T.RemoveItem = async function (key) {
 };
 
 export const multiGet: T.MultiGet = async function (keys) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readonly');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readonly');
+  const objectStore = transaction.objectStore('asyncStorage');
 
-  let promise = Promise.all(
+  const promise = Promise.all(
     keys.map(key => {
       return new Promise<[string, string]>((resolve, reject) => {
-        let req = objectStore.get(key);
+        const req = objectStore.get(key);
         req.onerror = e => reject(e);
         req.onsuccess = e => resolve([key, e.target.result]);
       });
@@ -73,15 +73,15 @@ export const multiGet: T.MultiGet = async function (keys) {
 };
 
 export const multiSet: T.MultiSet = async function (keyValues) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readwrite');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readwrite');
+  const objectStore = transaction.objectStore('asyncStorage');
 
-  let promise = Promise.all(
+  const promise = Promise.all(
     keyValues.map(([key, value]) => {
       return new Promise((resolve, reject) => {
-        let req = objectStore.put(value, key);
+        const req = objectStore.put(value, key);
         req.onerror = e => reject(e);
         req.onsuccess = e => resolve(undefined);
       });
@@ -93,15 +93,15 @@ export const multiSet: T.MultiSet = async function (keyValues) {
 };
 
 export const multiRemove: T.MultiRemove = async function (keys) {
-  let db = await getDatabase();
+  const db = await getDatabase();
 
-  let transaction = db.transaction(['asyncStorage'], 'readwrite');
-  let objectStore = transaction.objectStore('asyncStorage');
+  const transaction = db.transaction(['asyncStorage'], 'readwrite');
+  const objectStore = transaction.objectStore('asyncStorage');
 
-  let promise = Promise.all(
+  const promise = Promise.all(
     keys.map(key => {
       return new Promise((resolve, reject) => {
-        let req = objectStore.delete(key);
+        const req = objectStore.delete(key);
         req.onerror = e => reject(e);
         req.onsuccess = e => resolve(undefined);
       });
diff --git a/packages/loot-core/src/platform/server/connection/index.electron.ts b/packages/loot-core/src/platform/server/connection/index.electron.ts
index faf0ffea5..92206ff41 100644
--- a/packages/loot-core/src/platform/server/connection/index.electron.ts
+++ b/packages/loot-core/src/platform/server/connection/index.electron.ts
@@ -25,13 +25,13 @@ export const init: T.Init = function (socketName, handlers) {
     ws.on('error', console.error);
 
     ws.on('message', data => {
-      let msg = JSON.parse(data);
+      const msg = JSON.parse(data);
 
       if (ws.readyState !== 1) {
         return;
       }
 
-      let { id, name, args, undoTag, catchErrors } = msg;
+      const { id, name, args, undoTag, catchErrors } = msg;
 
       if (handlers[name]) {
         runHandler(handlers[name], args, { undoTag, name }).then(
@@ -60,7 +60,7 @@ export const init: T.Init = function (socketName, handlers) {
             if (ws.readyState !== 1) {
               return;
             }
-            let error = coerceError(nativeError);
+            const error = coerceError(nativeError);
 
             if (name.startsWith('api/')) {
               // The API is newer and does automatically forward
diff --git a/packages/loot-core/src/platform/server/connection/index.web.ts b/packages/loot-core/src/platform/server/connection/index.web.ts
index 0cafd1e71..8b8bcd5b4 100644
--- a/packages/loot-core/src/platform/server/connection/index.web.ts
+++ b/packages/loot-core/src/platform/server/connection/index.web.ts
@@ -4,7 +4,7 @@ import { captureException } from '../../exceptions';
 import type * as T from '.';
 
 function getGlobalObject() {
-  let obj =
+  const obj =
     typeof window !== 'undefined'
       ? window
       : typeof self !== 'undefined'
@@ -35,14 +35,14 @@ export const init: T.Init = function (serverChn, handlers) {
   serverChannel.addEventListener(
     'message',
     e => {
-      let data = e.data;
-      let msg = typeof data === 'string' ? JSON.parse(data) : data;
+      const data = e.data;
+      const msg = typeof data === 'string' ? JSON.parse(data) : data;
 
       if (msg.type && (msg.type === 'init' || msg.type.startsWith('__'))) {
         return;
       }
 
-      let { id, name, args, undoTag, catchErrors } = msg;
+      const { id, name, args, undoTag, catchErrors } = msg;
 
       if (handlers[name]) {
         runHandler(handlers[name], args, { undoTag, name }).then(
@@ -60,7 +60,7 @@ export const init: T.Init = function (serverChn, handlers) {
             });
           },
           nativeError => {
-            let error = coerceError(nativeError);
+            const error = coerceError(nativeError);
 
             if (name.startsWith('api/')) {
               // The API is newer and does automatically forward
diff --git a/packages/loot-core/src/platform/server/fs/index.electron.ts b/packages/loot-core/src/platform/server/fs/index.electron.ts
index 8e5df8d61..58313cb88 100644
--- a/packages/loot-core/src/platform/server/fs/index.electron.ts
+++ b/packages/loot-core/src/platform/server/fs/index.electron.ts
@@ -136,7 +136,7 @@ export const removeDir = dirpath => {
 
 export const removeDirRecursively = async dirpath => {
   if (await exists(dirpath)) {
-    for (let file of await listDir(dirpath)) {
+    for (const file of await listDir(dirpath)) {
       const fullpath = join(dirpath, file);
       if (fs.statSync(fullpath).isDirectory()) {
         await removeDirRecursively(fullpath);
diff --git a/packages/loot-core/src/platform/server/fs/index.web.test.ts b/packages/loot-core/src/platform/server/fs/index.web.test.ts
index a77a51889..d83f08f32 100644
--- a/packages/loot-core/src/platform/server/fs/index.web.test.ts
+++ b/packages/loot-core/src/platform/server/fs/index.web.test.ts
@@ -30,9 +30,9 @@ describe('web filesystem', () => {
     expect(await readFile('/documents/foo.txt')).toBe('hello');
 
     // Binary file
-    let str = 'hello, world';
-    let buf = new ArrayBuffer(str.length * 2);
-    let view = new Uint16Array(buf);
+    const str = 'hello, world';
+    const buf = new ArrayBuffer(str.length * 2);
+    const view = new Uint16Array(buf);
     for (let i = 0, strLen = str.length; i < strLen; i++) {
       view[i] = str.charCodeAt(i);
     }
@@ -40,8 +40,8 @@ describe('web filesystem', () => {
     await writeFile('/documents/foo.bin', buf);
     expect(await readFile('/documents/foo.bin')).toBe('hello, world');
 
-    let db = await idb.openDatabase();
-    let { store } = await idb.getStore(db, 'files');
+    const db = await idb.openDatabase();
+    const { store } = await idb.getStore(db, 'files');
 
     // Make sure they are in idb
     expect(await idb.get(store, '/documents/foo.txt')).toEqual({
@@ -75,8 +75,8 @@ describe('web filesystem', () => {
   });
 
   test('files are restored from idb', async () => {
-    let db = await idb.openDatabase();
-    let { store } = await idb.getStore(db, 'files');
+    const db = await idb.openDatabase();
+    const { store } = await idb.getStore(db, 'files');
     idb.set(store, { filepath: '/documents/ok.txt', contents: 'oh yeah' });
     idb.set(store, {
       filepath: '/documents/deep/nested/file/ok.txt',
@@ -94,8 +94,8 @@ describe('web filesystem', () => {
     expect(await exists('/documents/deep')).toBe(true);
     expect(await readFile('/documents/deep/nested/file/ok.txt')).toBe('deeper');
 
-    let FS = sqlite._getModule().FS;
-    let { node } = FS.lookupPath('/documents/deep/nested/db.sqlite');
+    const FS = sqlite._getModule().FS;
+    const { node } = FS.lookupPath('/documents/deep/nested/db.sqlite');
     expect(node.link).toBe(
       '/blocked/' + pathToId('/documents/deep/nested/db.sqlite'),
     );
diff --git a/packages/loot-core/src/platform/server/fs/index.web.ts b/packages/loot-core/src/platform/server/fs/index.web.ts
index d7685b82c..45da2f6f6 100644
--- a/packages/loot-core/src/platform/server/fs/index.web.ts
+++ b/packages/loot-core/src/platform/server/fs/index.web.ts
@@ -9,7 +9,7 @@ import join from './path-join';
 
 let FS = null;
 let BFS = null;
-let NO_PERSIST = false;
+const NO_PERSIST = false;
 
 export const bundledDatabasePath = '/default-db.sqlite';
 export const migrationsPath = '/migrations';
@@ -36,9 +36,9 @@ function _exists(filepath) {
 }
 
 function _mkdirRecursively(dir) {
-  let parts = dir.split('/').filter(str => str !== '');
+  const parts = dir.split('/').filter(str => str !== '');
   let path = '';
-  for (let part of parts) {
+  for (const part of parts) {
     path += '/' + part;
     if (!_exists(path)) {
       FS.mkdir(path);
@@ -80,8 +80,8 @@ async function _readFile(filepath, opts?: { encoding?: string }) {
     }
 
     // Grab contents from IDB
-    let { store } = idb.getStore(await idb.getDatabase(), 'files');
-    let item = await idb.get(store, filepath);
+    const { store } = idb.getStore(await idb.getDatabase(), 'files');
+    const item = await idb.get(store, filepath);
 
     if (item == null) {
       throw new Error('File does not exist: ' + filepath);
@@ -102,7 +102,7 @@ async function _readFile(filepath, opts?: { encoding?: string }) {
 
 function resolveLink(path) {
   try {
-    let { node } = FS.lookupPath(path, { follow: false });
+    const { node } = FS.lookupPath(path, { follow: false });
     return node.link ? FS.readlink(path) : path;
   } catch (e) {
     return path;
@@ -121,10 +121,10 @@ async function _writeFile(filepath, contents) {
   _createFile(filepath);
 
   if (!NO_PERSIST && filepath.startsWith('/documents')) {
-    let isDb = filepath.endsWith('.sqlite');
+    const isDb = filepath.endsWith('.sqlite');
 
     // Write to IDB
-    let { store } = idb.getStore(await idb.getDatabase(), 'files');
+    const { store } = idb.getStore(await idb.getDatabase(), 'files');
 
     if (isDb) {
       // We never write the contents of the database to idb ourselves.
@@ -149,16 +149,16 @@ async function _writeFile(filepath, contents) {
 
 async function _removeFile(filepath) {
   if (!NO_PERSIST && filepath.startsWith('/documents')) {
-    let isDb = filepath.endsWith('.sqlite');
+    const isDb = filepath.endsWith('.sqlite');
 
     // Remove from IDB
-    let { store } = idb.getStore(await idb.getDatabase(), 'files');
+    const { store } = idb.getStore(await idb.getDatabase(), 'files');
     await idb.del(store, filepath);
 
     // If this is the database, is has been symlinked and we want to
     // remove the actual contents
     if (isDb) {
-      let linked = resolveLink(filepath);
+      const linked = resolveLink(filepath);
       // Be resilient to fs corruption: don't throw an error by trying
       // to remove a file that doesn't exist. For some reason the db
       // file is gone? It's ok, just ignore it
@@ -174,14 +174,14 @@ async function _removeFile(filepath) {
 
 // Load files from the server that should exist by default
 async function populateDefaultFilesystem() {
-  let index = await (
+  const index = await (
     await fetch(process.env.PUBLIC_URL + 'data-file-index.txt')
   ).text();
-  let files = index
+  const files = index
     .split('\n')
     .map(name => name.trim())
     .filter(name => name !== '');
-  let fetchFile = url => fetch(url).then(res => res.arrayBuffer());
+  const fetchFile = url => fetch(url).then(res => res.arrayBuffer());
 
   // This is hardcoded. We know we must create the migrations
   // directory, it's not worth complicating the index to support
@@ -191,28 +191,28 @@ async function populateDefaultFilesystem() {
 
   await Promise.all(
     files.map(async file => {
-      let contents = await fetchFile(process.env.PUBLIC_URL + 'data/' + file);
+      const contents = await fetchFile(process.env.PUBLIC_URL + 'data/' + file);
       _writeFile('/' + file, contents);
     }),
   );
 }
 
 export const populateFileHeirarchy = async function () {
-  let { store } = idb.getStore(await idb.getDatabase(), 'files');
-  let req = store.getAllKeys();
-  let paths: string[] = await new Promise((resolve, reject) => {
+  const { store } = idb.getStore(await idb.getDatabase(), 'files');
+  const req = store.getAllKeys();
+  const paths: string[] = await new Promise((resolve, reject) => {
     req.onsuccess = e => resolve(e.target.result);
     req.onerror = e => reject(e);
   });
 
-  for (let path of paths) {
+  for (const path of paths) {
     _mkdirRecursively(basename(path));
     _createFile(path);
   }
 };
 
 export const init = async function () {
-  let Module = _getModule();
+  const Module = _getModule();
   FS = Module.FS;
 
   // When a user "uploads" a file, we just put it in memory in this
@@ -234,7 +234,7 @@ export const init = async function () {
   // the blocked fs enough. Additionally, we don't populate the
   // default files in testing.
   if (process.env.NODE_ENV !== 'test') {
-    let backend = new IndexedDBBackend(() => {
+    const backend = new IndexedDBBackend(() => {
       connection.send('fallback-write-error');
     });
     BFS = new SQLiteFS(FS, backend);
@@ -249,12 +249,12 @@ export const init = async function () {
 };
 
 export const basename = function (filepath) {
-  let parts = filepath.split('/');
+  const parts = filepath.split('/');
   return parts.slice(0, -1).join('/');
 };
 
 export const listDir = async function (filepath) {
-  let paths = FS.readdir(filepath);
+  const paths = FS.readdir(filepath);
   return paths.filter(p => p !== '.' && p !== '..');
 };
 
@@ -267,14 +267,14 @@ export const mkdir = async function (filepath) {
 };
 
 export const size = async function (filepath) {
-  let attrs = FS.stat(resolveLink(filepath));
+  const attrs = FS.stat(resolveLink(filepath));
   return attrs.size;
 };
 
 export const copyFile = async function (frompath, topath) {
   // TODO: This reads the whole file into memory, but that's probably
   // not a problem. This could be optimized
-  let contents = await _readFile(frompath);
+  const contents = await _readFile(frompath);
   return _writeFile(topath, contents);
 };
 
@@ -296,10 +296,10 @@ export const removeDir = async function (filepath) {
 
 export const removeDirRecursively = async function (dirpath) {
   if (await exists(dirpath)) {
-    for (let file of await listDir(dirpath)) {
-      let fullpath = join(dirpath, file);
+    for (const file of await listDir(dirpath)) {
+      const fullpath = join(dirpath, file);
       // `true` here means to not follow symlinks
-      let attr = FS.stat(fullpath, true);
+      const attr = FS.stat(fullpath, true);
 
       if (FS.isDir(attr.mode)) {
         await removeDirRecursively(fullpath);
diff --git a/packages/loot-core/src/platform/server/fs/path-join.web.ts b/packages/loot-core/src/platform/server/fs/path-join.web.ts
index cbad0db6d..7c36099fb 100644
--- a/packages/loot-core/src/platform/server/fs/path-join.web.ts
+++ b/packages/loot-core/src/platform/server/fs/path-join.web.ts
@@ -25,7 +25,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
           res.charCodeAt(res.length - 2) !== 46 /*.*/
         ) {
           if (res.length > 2) {
-            let lastSlashIndex = res.lastIndexOf('/');
+            const lastSlashIndex = res.lastIndexOf('/');
             if (lastSlashIndex !== res.length - 1) {
               if (lastSlashIndex === -1) {
                 res = '';
@@ -70,8 +70,8 @@ function normalizeStringPosix(path, allowAboveRoot) {
 function normalizePath(path) {
   if (path.length === 0) return '.';
 
-  let isAbsolute = path.charCodeAt(0) === 47; /*/*/
-  let trailingSeparator = path.charCodeAt(path.length - 1) === 47; /*/*/
+  const isAbsolute = path.charCodeAt(0) === 47; /*/*/
+  const trailingSeparator = path.charCodeAt(path.length - 1) === 47; /*/*/
 
   // Normalize the path
   path = normalizeStringPosix(path, !isAbsolute);
@@ -87,7 +87,7 @@ const join: T.Join = (...args) => {
   if (args.length === 0) return '.';
   let joined;
   for (let i = 0; i < args.length; ++i) {
-    let arg = args[i];
+    const arg = args[i];
     if (arg.length > 0) {
       if (joined === undefined) joined = arg;
       else joined += '/' + arg;
diff --git a/packages/loot-core/src/platform/server/indexeddb/index.web.ts b/packages/loot-core/src/platform/server/indexeddb/index.web.ts
index d657a53f7..58f37165a 100644
--- a/packages/loot-core/src/platform/server/indexeddb/index.web.ts
+++ b/packages/loot-core/src/platform/server/indexeddb/index.web.ts
@@ -5,12 +5,12 @@ let openedDb = _openDatabase();
 // The web version uses IndexedDB to store data
 function _openDatabase() {
   return new Promise((resolve, reject) => {
-    let dbVersion = 9;
-    let openRequest = indexedDB.open('actual', dbVersion);
+    const dbVersion = 9;
+    const openRequest = indexedDB.open('actual', dbVersion);
 
     openRequest.onupgradeneeded = function (e) {
       // @ts-expect-error EventTarget needs refinement
-      let db: IDBDatabase = e.target.result;
+      const db: IDBDatabase = e.target.result;
 
       // Remove old stores
       if (db.objectStoreNames.contains('filesystem')) {
@@ -40,7 +40,7 @@ function _openDatabase() {
 
     openRequest.onsuccess = function (e) {
       // @ts-expect-error EventTarget needs refinement
-      let db = e.target.result;
+      const db = e.target.result;
 
       db.onversionchange = () => {
         // TODO: Notify the user somehow
@@ -51,7 +51,7 @@ function _openDatabase() {
         console.log('Database error: ' + (event.target && event.target.error));
 
         if (event.target && event.target.error) {
-          let e = event.target.error;
+          const e = event.target.error;
           if (e.name === 'QuotaExceededError') {
             // Don't try to get the sized used -- too brittle. Is there
             // a better way to do it?
@@ -94,13 +94,13 @@ function _openDatabase() {
 }
 
 export const getStore: T.GetStore = function (db, name) {
-  let trans = db.transaction([name], 'readwrite');
+  const trans = db.transaction([name], 'readwrite');
   return { trans, store: trans.objectStore(name) };
 };
 
 export const get: T.Get = async function (store, key, mapper = x => x) {
   return new Promise((resolve, reject) => {
-    let req = store.get(key);
+    const req = store.get(key);
     req.onsuccess = e => {
       resolve(mapper(req.result));
     };
@@ -110,7 +110,7 @@ export const get: T.Get = async function (store, key, mapper = x => x) {
 
 export const set: T.Set = async function (store, item) {
   return new Promise((resolve, reject) => {
-    let req = store.put(item);
+    const req = store.put(item);
     req.onsuccess = e => resolve(undefined);
     req.onerror = e => reject(e);
   });
@@ -118,7 +118,7 @@ export const set: T.Set = async function (store, item) {
 
 export const del: T.Del = async function (store, key) {
   return new Promise((resolve, reject) => {
-    let req = store.delete(key);
+    const req = store.delete(key);
     req.onsuccess = e => resolve(undefined);
     req.onerror = e => reject(e);
   });
diff --git a/packages/loot-core/src/platform/server/sqlite/index.electron.ts b/packages/loot-core/src/platform/server/sqlite/index.electron.ts
index 2e928f60f..146e8ac19 100644
--- a/packages/loot-core/src/platform/server/sqlite/index.electron.ts
+++ b/packages/loot-core/src/platform/server/sqlite/index.electron.ts
@@ -38,7 +38,7 @@ export function runQuery(
 
   if (fetchAll) {
     try {
-      let result = stmt.all(...params);
+      const result = stmt.all(...params);
       return result;
     } catch (e) {
       console.log('error', sql);
@@ -46,7 +46,7 @@ export function runQuery(
     }
   } else {
     try {
-      let info = stmt.run(...params);
+      const info = stmt.run(...params);
       return { changes: info.changes, insertId: info.lastInsertRowid };
     } catch (e) {
       // console.log('error', sql);
@@ -95,7 +95,7 @@ export async function asyncTransaction(
 }
 
 export function openDatabase(pathOrBuffer: string | Buffer) {
-  let db = new SQL(pathOrBuffer);
+  const db = new SQL(pathOrBuffer);
   // Define Unicode-aware LOWER and UPPER implementation.
   // This is necessary because better-sqlite3 uses SQLite build without ICU support.
   db.function('UNICODE_LOWER', { deterministic: true }, (arg: string | null) =>
@@ -114,11 +114,11 @@ export function closeDatabase(db: SQL.Database) {
 export async function exportDatabase(db: SQL.Database) {
   // electron does not support better-sqlite serialize since v21
   // save to file and read in the raw data.
-  let name = `backup-for-export-${uuidv4()}.db`;
+  const name = `backup-for-export-${uuidv4()}.db`;
 
   await db.backup(name);
 
-  let data = await readFile(name, 'binary');
+  const data = await readFile(name, 'binary');
   await removeFile(name);
 
   return data;
diff --git a/packages/loot-core/src/platform/server/sqlite/index.web.test.ts b/packages/loot-core/src/platform/server/sqlite/index.web.test.ts
index 09eaadae0..06790bdc6 100644
--- a/packages/loot-core/src/platform/server/sqlite/index.web.test.ts
+++ b/packages/loot-core/src/platform/server/sqlite/index.web.test.ts
@@ -13,13 +13,13 @@ beforeAll(() => {
   return init();
 });
 
-let initSQL = `
+const initSQL = `
 CREATE TABLE numbers (id TEXT PRIMARY KEY, number INTEGER);
 `;
 
 describe('Web sqlite', () => {
   it('should rollback transactions', async () => {
-    let db = await openDatabase();
+    const db = await openDatabase();
     execQuery(db, initSQL);
 
     runQuery(db, "INSERT INTO numbers (id, number) VALUES ('id1', 4)");
@@ -29,7 +29,7 @@ describe('Web sqlite', () => {
     // @ts-expect-error Property 'number' does not exist on type 'unknown'
     expect(rows[0].number).toBe(4);
 
-    let consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+    const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
     expect(() => {
       transaction(db, () => {
         runQuery(db, "INSERT INTO numbers (id, number) VALUES ('id2', 5)");
@@ -48,7 +48,7 @@ describe('Web sqlite', () => {
   });
 
   it('should support nested transactions', async () => {
-    let db = await openDatabase();
+    const db = await openDatabase();
     execQuery(db, initSQL);
 
     runQuery(db, "INSERT INTO numbers (id, number) VALUES ('id1', 4)");
@@ -63,7 +63,7 @@ describe('Web sqlite', () => {
       runQuery(db, "INSERT INTO numbers (id, number) VALUES ('id3', 6)");
 
       // Only this transaction should fail
-      let consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+      const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
       expect(() => {
         transaction(db, () => {
           runQuery(db, "INSERT INTO numbers (id, number) VALUES ('id4', 7)");
diff --git a/packages/loot-core/src/platform/server/sqlite/index.web.ts b/packages/loot-core/src/platform/server/sqlite/index.web.ts
index b9b39f456..e804b1b41 100644
--- a/packages/loot-core/src/platform/server/sqlite/index.web.ts
+++ b/packages/loot-core/src/platform/server/sqlite/index.web.ts
@@ -56,12 +56,12 @@ export function runQuery(db, sql, params = [], fetchAll = false) {
     verifyParamTypes(sql, params);
   }
 
-  let stmt = typeof sql === 'string' ? db.prepare(sql) : sql;
+  const stmt = typeof sql === 'string' ? db.prepare(sql) : sql;
 
   if (fetchAll) {
     try {
       stmt.bind(params);
-      let rows = [];
+      const rows = [];
 
       while (stmt.step()) {
         rows.push(stmt.getAsObject());
@@ -156,11 +156,11 @@ export async function openDatabase(pathOrBuffer?: string | Buffer) {
     if (typeof pathOrBuffer !== 'string') {
       db = new SQL.Database(pathOrBuffer);
     } else {
-      let path = pathOrBuffer;
+      const path = pathOrBuffer;
       if (path !== ':memory:') {
         if (typeof SharedArrayBuffer === 'undefined') {
           // @ts-expect-error FS missing in sql.js types
-          let stream = SQL.FS.open(SQL.FS.readlink(path), 'a+');
+          const stream = SQL.FS.open(SQL.FS.readlink(path), 'a+');
           await stream.node.contents.readIfFallback();
           // @ts-expect-error FS missing in sql.js types
           SQL.FS.close(stream);
diff --git a/packages/loot-core/src/shared/arithmetic.ts b/packages/loot-core/src/shared/arithmetic.ts
index 4dd2b8955..2bf0eaa88 100644
--- a/packages/loot-core/src/shared/arithmetic.ts
+++ b/packages/loot-core/src/shared/arithmetic.ts
@@ -15,7 +15,7 @@ function next(state) {
     return null;
   }
 
-  let ch = char(state);
+  const ch = char(state);
   state.index++;
   return ch;
 }
@@ -31,7 +31,7 @@ function nextOperator(state, op) {
 
 function parsePrimary(state) {
   // We only support numbers
-  let isNegative = char(state) === '-';
+  const isNegative = char(state) === '-';
   if (isNegative) {
     next(state);
   }
@@ -44,7 +44,7 @@ function parsePrimary(state) {
   // and we should do more strict parsing
   let numberStr = '';
   while (char(state) && char(state).match(/[0-9,.]/)) {
-    let thousandsSep = getNumberFormat().separator === ',' ? '.' : ',';
+    const thousandsSep = getNumberFormat().separator === ',' ? '.' : ',';
 
     // Don't include the thousands separator
     if (char(state) === thousandsSep) {
@@ -58,14 +58,16 @@ function parsePrimary(state) {
     fail(state, 'Unexpected character');
   }
 
-  let number = parseFloat(numberStr.replace(getNumberFormat().separator, '.'));
+  const number = parseFloat(
+    numberStr.replace(getNumberFormat().separator, '.'),
+  );
   return isNegative ? -number : number;
 }
 
 function parseParens(state) {
   if (char(state) === '(') {
     next(state);
-    let expr = parseOperator(state);
+    const expr = parseOperator(state);
 
     if (char(state) !== ')') {
       fail(state, 'Unbalanced parentheses');
@@ -91,10 +93,10 @@ function makeOperatorParser(...ops) {
 }
 
 // These operators go from high to low order of precedence
-let parseOperator = makeOperatorParser('^', '/', '*', '-', '+');
+const parseOperator = makeOperatorParser('^', '/', '*', '-', '+');
 
 function parse(expression) {
-  let state = { str: expression.replace(/\s/g, ''), index: 0 };
+  const state = { str: expression.replace(/\s/g, ''), index: 0 };
   return parseOperator(state);
 }
 
@@ -103,7 +105,7 @@ function evaluate(ast) {
     return ast;
   }
 
-  let { left, right, op } = ast;
+  const { left, right, op } = ast;
 
   switch (op) {
     case '+':
diff --git a/packages/loot-core/src/shared/async.test.ts b/packages/loot-core/src/shared/async.test.ts
index 3f545b5da..31280cbc4 100644
--- a/packages/loot-core/src/shared/async.test.ts
+++ b/packages/loot-core/src/shared/async.test.ts
@@ -119,7 +119,7 @@ describe('async', () => {
       return {};
     });
 
-    let results = await Promise.all([fn(), fn(), fn()]);
+    const results = await Promise.all([fn(), fn(), fn()]);
 
     // It should only have been called once
     expect(timesCalled).toBe(1);
diff --git a/packages/loot-core/src/shared/async.ts b/packages/loot-core/src/shared/async.ts
index a534631cd..718204f36 100644
--- a/packages/loot-core/src/shared/async.ts
+++ b/packages/loot-core/src/shared/async.ts
@@ -1,7 +1,7 @@
 export function sequential<T extends (...args: unknown[]) => unknown>(
   fn: T,
 ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
-  let sequenceState = {
+  const sequenceState = {
     running: null,
     queue: [],
   };
@@ -47,7 +47,7 @@ export function once<T extends (...args: unknown[]) => Promise<unknown>>(
   fn: T,
 ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
   let promise = null;
-  let onceFn = (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {
+  const onceFn = (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {
     if (!promise) {
       promise = fn(...args).finally(() => {
         promise = null;
diff --git a/packages/loot-core/src/shared/categories.ts b/packages/loot-core/src/shared/categories.ts
index 3cd2503ad..54784b45e 100644
--- a/packages/loot-core/src/shared/categories.ts
+++ b/packages/loot-core/src/shared/categories.ts
@@ -61,7 +61,7 @@ export function moveCategoryGroup(categoryGroups, id, targetId) {
     return categoryGroups;
   }
 
-  let moveGroup = categoryGroups.find(g => g.id === id);
+  const moveGroup = categoryGroups.find(g => g.id === id);
 
   categoryGroups = categoryGroups.reduce((groups, group) => {
     if (group.id === targetId) {
diff --git a/packages/loot-core/src/shared/errors.ts b/packages/loot-core/src/shared/errors.ts
index 1f62874da..f7fc58536 100644
--- a/packages/loot-core/src/shared/errors.ts
+++ b/packages/loot-core/src/shared/errors.ts
@@ -51,7 +51,7 @@ export function getDownloadError({ reason, meta, fileName }) {
       );
 
     default:
-      let info = meta && meta.fileId ? `, fileId: ${meta.fileId}` : '';
+      const info = meta && meta.fileId ? `, fileId: ${meta.fileId}` : '';
       return (
         'Something went wrong trying to download that file, sorry! ' +
         'Visit https://actualbudget.org/contact/ for support. ' +
diff --git a/packages/loot-core/src/shared/months.ts b/packages/loot-core/src/shared/months.ts
index 651333707..c87d8694d 100644
--- a/packages/loot-core/src/shared/months.ts
+++ b/packages/loot-core/src/shared/months.ts
@@ -61,7 +61,7 @@ export function _parse(value: DateLike): Date {
     // shifted backwards or forwards, doing date logic will stay
     // within the day we want.
 
-    let [year, month, day] = value.split('-');
+    const [year, month, day] = value.split('-');
     if (day != null) {
       return new Date(parseInt(year), parseInt(month) - 1, parseInt(day), 12);
     } else if (month != null) {
@@ -282,7 +282,7 @@ export const getDayMonthFormat = memoizeOne((format: string) => {
 });
 
 export const getDayMonthRegex = memoizeOne((format: string) => {
-  let regex = format
+  const regex = format
     .replace(/y+/g, '')
     .replace(/[^\w]$/, '')
     .replace(/^[^\w]/, '')
@@ -302,7 +302,7 @@ export const getMonthYearFormat = memoizeOne((format: string) => {
 });
 
 export const getMonthYearRegex = memoizeOne((format: string) => {
-  let regex = format
+  const regex = format
     .replace(/d+/g, '')
     .replace(/[^\w]$/, '')
     .replace(/^[^\w]/, '')
@@ -317,7 +317,7 @@ export const getShortYearFormat = memoizeOne((format: string) => {
 });
 
 export const getShortYearRegex = memoizeOne((format: string) => {
-  let regex = format
+  const regex = format
     .replace(/[^\w]$/, '')
     .replace(/^[^\w]/, '')
     .replace(/d+/g, '\\d{1,2}')
diff --git a/packages/loot-core/src/shared/query.ts b/packages/loot-core/src/shared/query.ts
index f37dee7bd..b54ab392f 100644
--- a/packages/loot-core/src/shared/query.ts
+++ b/packages/loot-core/src/shared/query.ts
@@ -38,7 +38,7 @@ export class Query {
   }
 
   unfilter(exprs) {
-    let exprSet = new Set(exprs);
+    const exprSet = new Set(exprs);
     return new Query({
       ...this.state,
       filterExpressions: this.state.filterExpressions.filter(
@@ -52,13 +52,13 @@ export class Query {
       exprs = [exprs];
     }
 
-    let query = new Query({ ...this.state, selectExpressions: exprs });
+    const query = new Query({ ...this.state, selectExpressions: exprs });
     query.state.calculation = false;
     return query;
   }
 
   calculate(expr) {
-    let query = this.select({ result: expr });
+    const query = this.select({ result: expr });
     query.state.calculation = true;
     return query;
   }
@@ -115,7 +115,7 @@ export class Query {
 }
 
 export function getPrimaryOrderBy(query, defaultOrderBy) {
-  let orderExprs = query.serialize().orderExpressions;
+  const orderExprs = query.serialize().orderExpressions;
   if (orderExprs.length === 0) {
     if (defaultOrderBy) {
       return { order: 'asc', ...defaultOrderBy };
@@ -123,12 +123,12 @@ export function getPrimaryOrderBy(query, defaultOrderBy) {
     return null;
   }
 
-  let firstOrder = orderExprs[0];
+  const firstOrder = orderExprs[0];
   if (typeof firstOrder === 'string') {
     return { field: firstOrder, order: 'asc' };
   }
   // Handle this form: { field: 'desc' }
-  let [field] = Object.keys(firstOrder);
+  const [field] = Object.keys(firstOrder);
   return { field, order: firstOrder[field] };
 }
 
diff --git a/packages/loot-core/src/shared/rules.ts b/packages/loot-core/src/shared/rules.ts
index 3857aff31..0bab72945 100644
--- a/packages/loot-core/src/shared/rules.ts
+++ b/packages/loot-core/src/shared/rules.ts
@@ -170,11 +170,11 @@ export function parse(item) {
       return { ...item, value: parsed };
     }
     case 'string': {
-      let parsed = item.value == null ? '' : item.value;
+      const parsed = item.value == null ? '' : item.value;
       return { ...item, value: parsed };
     }
     case 'boolean': {
-      let parsed = item.value;
+      const parsed = item.value;
       return { ...item, value: parsed };
     }
     default:
@@ -194,11 +194,11 @@ export function unparse({ error, inputKey, ...item }) {
       return { ...item, value: unparsed };
     }
     case 'string': {
-      let unparsed = item.value == null ? '' : item.value;
+      const unparsed = item.value == null ? '' : item.value;
       return { ...item, value: unparsed };
     }
     case 'boolean': {
-      let unparsed = item.value == null ? false : item.value;
+      const unparsed = item.value == null ? false : item.value;
       return { ...item, value: unparsed };
     }
     default:
diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts
index a6396b93b..8c1d816eb 100644
--- a/packages/loot-core/src/shared/schedules.ts
+++ b/packages/loot-core/src/shared/schedules.ts
@@ -4,7 +4,7 @@ import * as monthUtils from './months';
 import q from './query';
 
 export function getStatus(nextDate, completed, hasTrans) {
-  let today = monthUtils.currentDay();
+  const today = monthUtils.currentDay();
 
   if (completed) {
     return 'completed';
@@ -22,8 +22,8 @@ export function getStatus(nextDate, completed, hasTrans) {
 }
 
 export function getHasTransactionsQuery(schedules) {
-  let filters = schedules.map(schedule => {
-    let dateCond = schedule._conditions.find(c => c.field === 'date');
+  const filters = schedules.map(schedule => {
+    const dateCond = schedule._conditions.find(c => c.field === 'date');
     return {
       $and: {
         schedule: schedule.id,
@@ -51,7 +51,7 @@ function makeNumberSuffix(num) {
 }
 
 function prettyDayName(day) {
-  let days = {
+  const days = {
     SU: 'Sunday',
     MO: 'Monday',
     TU: 'Tuesday',
@@ -64,9 +64,9 @@ function prettyDayName(day) {
 }
 
 export function getRecurringDescription(config) {
-  let interval = config.interval || 1;
+  const interval = config.interval || 1;
 
-  let weekendSolveSuffix = config.skipWeekend
+  const weekendSolveSuffix = config.skipWeekend
     ? ` (${config.weekendSolveMode} weekend) `
     : '';
 
@@ -92,9 +92,9 @@ export function getRecurringDescription(config) {
         // sort would put them first
         let patterns = [...config.patterns]
           .sort((p1, p2) => {
-            let typeOrder =
+            const typeOrder =
               (p1.type === 'day' ? 1 : 0) - (p2.type === 'day' ? 1 : 0);
-            let valOrder = p1.value - p2.value;
+            const valOrder = p1.value - p2.value;
 
             if (typeOrder === 0) {
               return valOrder;
@@ -108,12 +108,12 @@ export function getRecurringDescription(config) {
 
         desc += ' on the ';
 
-        let strs = [];
+        const strs = [];
 
-        let uniqueDays = new Set(patterns.map(p => p.type));
-        let isSameDay = uniqueDays.size === 1 && !uniqueDays.has('day');
+        const uniqueDays = new Set(patterns.map(p => p.type));
+        const isSameDay = uniqueDays.size === 1 && !uniqueDays.has('day');
 
-        for (let pattern of patterns) {
+        for (const pattern of patterns) {
           if (pattern.type === 'day') {
             if (pattern.value === -1) {
               strs.push('last day');
@@ -122,7 +122,7 @@ export function getRecurringDescription(config) {
               strs.push(makeNumberSuffix(pattern.value));
             }
           } else {
-            let dayName = isSameDay ? '' : ' ' + prettyDayName(pattern.type);
+            const dayName = isSameDay ? '' : ' ' + prettyDayName(pattern.type);
 
             if (pattern.value === -1) {
               // Example: last Monday
@@ -163,7 +163,7 @@ export function getRecurringDescription(config) {
 }
 
 export function recurConfigToRSchedule(config) {
-  let base: IRuleOptions = {
+  const base: IRuleOptions = {
     start: monthUtils.parseDate(config.start),
     frequency: config.frequency.toUpperCase(),
     byHourOfDay: [12],
@@ -173,7 +173,7 @@ export function recurConfigToRSchedule(config) {
     base.interval = config.interval;
   }
 
-  let abbrevDay = name => name.slice(0, 2).toUpperCase();
+  const abbrevDay = name => name.slice(0, 2).toUpperCase();
 
   switch (config.frequency) {
     case 'daily':
@@ -184,8 +184,8 @@ export function recurConfigToRSchedule(config) {
       return [base];
     case 'monthly':
       if (config.patterns && config.patterns.length > 0) {
-        let days = config.patterns.filter(p => p.type === 'day');
-        let dayNames = config.patterns.filter(p => p.type !== 'day');
+        const days = config.patterns.filter(p => p.type === 'day');
+        const dayNames = config.patterns.filter(p => p.type !== 'day');
 
         return [
           days.length > 0 && { ...base, byDayOfMonth: days.map(p => p.value) },
diff --git a/packages/loot-core/src/shared/test-helpers.ts b/packages/loot-core/src/shared/test-helpers.ts
index 574d92599..5cda80135 100644
--- a/packages/loot-core/src/shared/test-helpers.ts
+++ b/packages/loot-core/src/shared/test-helpers.ts
@@ -2,8 +2,8 @@ export let tracer = null;
 
 function timeout(promise, n) {
   let resolve;
-  let timeoutPromise = new Promise(_ => (resolve = _));
-  let timer = setTimeout(() => resolve(`timeout(${n})`), n);
+  const timeoutPromise = new Promise(_ => (resolve = _));
+  const timer = setTimeout(() => resolve(`timeout(${n})`), n);
 
   return Promise.race([
     promise.then(res => {
@@ -19,12 +19,12 @@ export function resetTracer() {
 }
 
 export function execTracer() {
-  let queue = [];
+  const queue = [];
   let hasStarted = false;
   let waitingFor = null;
   let ended = false;
 
-  let log = false;
+  const log = false;
 
   return {
     event(name: string, data?: unknown) {
@@ -108,7 +108,7 @@ export function execTracer() {
           `Expected event “${name}” but none found - has it happened yet?`,
         );
       } else if (queue[0].name === name) {
-        let entry = queue.shift();
+        const entry = queue.shift();
 
         if (typeof data === 'function') {
           data(entry.data);
@@ -135,7 +135,7 @@ export function execTracer() {
 
     end() {
       if (hasStarted && queue.length !== 0) {
-        let str = queue.map(x => JSON.stringify(x));
+        const str = queue.map(x => JSON.stringify(x));
         throw new Error(
           'Event tracer ended with existing events: ' + str.join('\n\n'),
         );
diff --git a/packages/loot-core/src/shared/transactions.test.ts b/packages/loot-core/src/shared/transactions.test.ts
index 7aca05f27..2ff17433f 100644
--- a/packages/loot-core/src/shared/transactions.test.ts
+++ b/packages/loot-core/src/shared/transactions.test.ts
@@ -23,7 +23,7 @@ function makeTransaction(data) {
 }
 
 function makeSplitTransaction(data, children) {
-  let parent = makeTransaction({ ...data, is_parent: true });
+  const parent = makeTransaction({ ...data, is_parent: true });
   return [parent, ...children.map(t => makeChild(parent, t))];
 }
 
@@ -33,12 +33,12 @@ function splitError(amount) {
 
 describe('Transactions', () => {
   test('updating a transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ amount: 5000 }),
       makeTransaction({ id: 't1', amount: 4000 }),
       makeTransaction({ amount: 3000 }),
     ];
-    let { data, diff } = updateTransaction(transactions, {
+    const { data, diff } = updateTransaction(transactions, {
       id: 't1',
       amount: 5000,
     });
@@ -56,11 +56,11 @@ describe('Transactions', () => {
   });
 
   test('updating does nothing if value not changed', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ id: 't1', amount: 5000 }),
       makeTransaction({ amount: 3000 }),
     ];
-    let { data, diff } = updateTransaction(transactions, {
+    const { data, diff } = updateTransaction(transactions, {
       id: 't1',
       amount: 5000,
     });
@@ -72,12 +72,12 @@ describe('Transactions', () => {
   });
 
   test('deleting a transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ amount: 5000 }),
       makeTransaction({ id: 't1', amount: 4000 }),
       makeTransaction({ amount: 3000 }),
     ];
-    let { data, diff } = deleteTransaction(transactions, 't1');
+    const { data, diff } = deleteTransaction(transactions, 't1');
 
     expect(diff).toEqual({
       added: [],
@@ -91,11 +91,11 @@ describe('Transactions', () => {
   });
 
   test('splitting a transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ id: 't1', amount: 5000 }),
       makeTransaction({ amount: 3000 }),
     ];
-    let { data, diff } = splitTransaction(transactions, 't1');
+    const { data, diff } = splitTransaction(transactions, 't1');
     expect(data.find(d => d.subtransactions)).toBeFalsy();
 
     expect(diff).toEqual({
@@ -121,7 +121,7 @@ describe('Transactions', () => {
   });
 
   test('adding a split transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ amount: 2001 }),
       ...makeSplitTransaction({ id: 't1', amount: 2500 }, [
         { id: 't2', amount: 2000 },
@@ -133,7 +133,7 @@ describe('Transactions', () => {
     expect(transactions.filter(t => t.parent_id === 't1').length).toBe(2);
 
     // Should be able to pass in any id from the split trans
-    let { data, diff } = addSplitTransaction(transactions, 't1');
+    const { data, diff } = addSplitTransaction(transactions, 't1');
     expect(data.find(d => d.subtransactions)).toBeFalsy();
 
     expect(data.filter(t => t.parent_id === 't1').length).toBe(3);
@@ -152,7 +152,7 @@ describe('Transactions', () => {
   });
 
   test('updating a split transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ amount: 2001 }),
       ...makeSplitTransaction({ id: 't1', amount: 2500 }, [
         { id: 't2', amount: 2000 },
@@ -160,7 +160,7 @@ describe('Transactions', () => {
       ]),
       makeTransaction({ amount: 3002 }),
     ];
-    let { data, diff } = updateTransaction(transactions, {
+    const { data, diff } = updateTransaction(transactions, {
       id: 't2',
       amount: 2200,
     });
@@ -177,7 +177,7 @@ describe('Transactions', () => {
   });
 
   test('deleting a split transaction works', () => {
-    let transactions = [
+    const transactions = [
       makeTransaction({ amount: 2001 }),
       ...makeSplitTransaction({ id: 't1', amount: 2500 }, [
         { id: 't2', amount: 2000 },
@@ -185,7 +185,7 @@ describe('Transactions', () => {
       ]),
       makeTransaction({ amount: 3002 }),
     ];
-    let { data, diff } = deleteTransaction(transactions, 't2');
+    const { data, diff } = deleteTransaction(transactions, 't2');
 
     expect(diff).toEqual({
       added: [],
diff --git a/packages/loot-core/src/shared/transactions.ts b/packages/loot-core/src/shared/transactions.ts
index 967532554..5e99b43d4 100644
--- a/packages/loot-core/src/shared/transactions.ts
+++ b/packages/loot-core/src/shared/transactions.ts
@@ -12,7 +12,7 @@ function num(n) {
 }
 
 function SplitTransactionError(total, parent) {
-  let difference = num(parent.amount) - total;
+  const difference = num(parent.amount) - total;
 
   return {
     type: 'SplitTransactionError',
@@ -22,7 +22,7 @@ function SplitTransactionError(total, parent) {
 }
 
 export function makeChild(parent, data) {
-  let prefix = parent.id === 'temp' ? 'temp' : '';
+  const prefix = parent.id === 'temp' ? 'temp' : '';
 
   return {
     amount: 0,
@@ -61,7 +61,7 @@ function findParentIndex(transactions, idx) {
   // are always before children, which is enforced in the db layer.
   // Walk backwards and find the last parent;
   while (idx >= 0) {
-    let trans = transactions[idx];
+    const trans = transactions[idx];
     if (trans.is_parent) {
       return idx;
     }
@@ -71,7 +71,7 @@ function findParentIndex(transactions, idx) {
 }
 
 function getSplit(transactions, parentIndex) {
-  let split = [transactions[parentIndex]];
+  const split = [transactions[parentIndex]];
   let curr = parentIndex + 1;
   while (curr < transactions.length && transactions[curr].is_child) {
     split.push(transactions[curr]);
@@ -81,14 +81,14 @@ function getSplit(transactions, parentIndex) {
 }
 
 export function ungroupTransactions(transactions) {
-  let x = transactions.reduce((list, parent) => {
-    let { subtransactions, ...trans } = parent;
-    subtransactions = subtransactions || [];
+  const x = transactions.reduce((list, parent) => {
+    const { subtransactions, ...trans } = parent;
+    const _subtransactions = subtransactions || [];
 
     list.push(trans);
 
-    for (let i = 0; i < subtransactions.length; i++) {
-      list.push(subtransactions[i]);
+    for (let i = 0; i < _subtransactions.length; i++) {
+      list.push(_subtransactions[i]);
     }
     return list;
   }, []);
@@ -111,24 +111,24 @@ export function applyTransactionDiff(groupedTrans, diff) {
 }
 
 function replaceTransactions(transactions, id, func) {
-  let idx = transactions.findIndex(t => t.id === id);
-  let trans = transactions[idx];
-  let transactionsCopy = [...transactions];
+  const idx = transactions.findIndex(t => t.id === id);
+  const trans = transactions[idx];
+  const transactionsCopy = [...transactions];
 
   if (idx === -1) {
     throw new Error('Tried to edit unknown transaction id: ' + id);
   }
 
   if (trans.is_parent || trans.is_child) {
-    let parentIndex = findParentIndex(transactions, idx);
+    const parentIndex = findParentIndex(transactions, idx);
     if (parentIndex == null) {
       console.log('Cannot find parent index');
       return { diff: { deleted: [], updated: [] } };
     }
 
-    let split = getSplit(transactions, parentIndex);
+    const split = getSplit(transactions, parentIndex);
     let grouped = func(groupTransaction(split));
-    let newSplit = ungroupTransaction(grouped);
+    const newSplit = ungroupTransaction(grouped);
 
     let diff;
     if (newSplit == null) {
@@ -144,8 +144,8 @@ function replaceTransactions(transactions, id, func) {
 
     return { data: transactionsCopy, newTransaction: grouped, diff };
   } else {
-    let grouped = func(trans);
-    let newTrans = ungroupTransaction(grouped) || [];
+    const grouped = func(trans);
+    const newTrans = ungroupTransaction(grouped) || [];
     if (grouped) {
       grouped.subtransactions = grouped.subtransactions || [];
     }
@@ -164,7 +164,7 @@ export function addSplitTransaction(transactions, id) {
     if (!trans.is_parent) {
       return trans;
     }
-    let prevSub = last(trans.subtransactions);
+    const prevSub = last(trans.subtransactions);
     trans.subtransactions.push(
       makeChild(trans, {
         amount: 0,
@@ -178,8 +178,8 @@ export function addSplitTransaction(transactions, id) {
 export function updateTransaction(transactions, transaction) {
   return replaceTransactions(transactions, transaction.id, trans => {
     if (trans.is_parent) {
-      let parent = trans.id === transaction.id ? transaction : trans;
-      let sub = trans.subtransactions.map(t => {
+      const parent = trans.id === transaction.id ? transaction : trans;
+      const sub = trans.subtransactions.map(t => {
         // Make sure to update the children to reflect the updated
         // properties (if the parent updated)
 
@@ -216,7 +216,7 @@ export function deleteTransaction(transactions, id) {
           error: null,
         };
       } else {
-        let sub = trans.subtransactions.filter(t => t.id !== id);
+        const sub = trans.subtransactions.filter(t => t.id !== id);
         return recalculateSplit({ ...trans, subtransactions: sub });
       }
     } else {
@@ -244,7 +244,7 @@ export function realizeTempTransactions(transactions) {
   let parent = transactions.find(t => !t.is_child);
   parent = { ...parent, id: uuidv4() };
 
-  let children = transactions.filter(t => t.is_child);
+  const children = transactions.filter(t => t.is_child);
   return [
     parent,
     ...children.map(child => ({
diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts
index 6e744c8a7..cf587db47 100644
--- a/packages/loot-core/src/shared/util.ts
+++ b/packages/loot-core/src/shared/util.ts
@@ -10,7 +10,7 @@ export function getChangedValues(obj1, obj2) {
   let hasChanged = false;
 
   for (let i = 0; i < keys.length; i++) {
-    let key = keys[i];
+    const key = keys[i];
 
     if (obj1[key] !== obj2[key]) {
       diff[key] = obj2[key];
@@ -24,7 +24,7 @@ export function getChangedValues(obj1, obj2) {
 export function hasFieldsChanged(obj1, obj2, fields) {
   let changed = false;
   for (let i = 0; i < fields.length; i++) {
-    let field = fields[i];
+    const field = fields[i];
     if (obj1[field] !== obj2[field]) {
       changed = true;
       break;
@@ -65,12 +65,12 @@ export function applyChanges(changes, items) {
 }
 
 export function partitionByField(data, field) {
-  let res = new Map();
+  const res = new Map();
   for (let i = 0; i < data.length; i++) {
-    let item = data[i];
-    let key = item[field];
+    const item = data[i];
+    const key = item[field];
 
-    let items = res.get(key) || [];
+    const items = res.get(key) || [];
     items.push(item);
 
     res.set(key, items);
@@ -79,11 +79,11 @@ export function partitionByField(data, field) {
 }
 
 export function groupBy<T, K extends keyof T>(data: T[], field: K) {
-  let res = new Map<T[K], T[]>();
+  const res = new Map<T[K], T[]>();
   for (let i = 0; i < data.length; i++) {
-    let item = data[i];
-    let key = item[field];
-    let existing = res.get(key) || [];
+    const item = data[i];
+    const key = item[field];
+    const existing = res.get(key) || [];
     res.set(key, existing.concat([item]));
   }
   return res;
@@ -94,26 +94,26 @@ export function groupBy<T, K extends keyof T>(data: T[], field: K) {
 // different API and we need to go through and update everywhere that
 // uses it.
 function _groupById(data) {
-  let res = new Map();
+  const res = new Map();
   for (let i = 0; i < data.length; i++) {
-    let item = data[i];
+    const item = data[i];
     res.set(item.id, item);
   }
   return res;
 }
 
 export function diffItems(items, newItems) {
-  let grouped = _groupById(items);
-  let newGrouped = _groupById(newItems);
-  let added = [];
-  let updated = [];
+  const grouped = _groupById(items);
+  const newGrouped = _groupById(newItems);
+  const added = [];
+  const updated = [];
 
-  let deleted = items
+  const deleted = items
     .filter(item => !newGrouped.has(item.id))
     .map(item => ({ id: item.id }));
 
   newItems.forEach(newItem => {
-    let item = grouped.get(newItem.id);
+    const item = grouped.get(newItem.id);
     if (!item) {
       added.push(newItem);
     } else {
@@ -128,9 +128,9 @@ export function diffItems(items, newItems) {
 }
 
 export function groupById(data) {
-  let res = {};
+  const res = {};
   for (let i = 0; i < data.length; i++) {
-    let item = data[i];
+    const item = data[i];
     res[item.id] = item;
   }
   return res;
@@ -169,8 +169,8 @@ export function getIn(map, keys) {
 }
 
 export function fastSetMerge(set1, set2) {
-  let finalSet = new Set(set1);
-  let iter = set2.values();
+  const finalSet = new Set(set1);
+  const iter = set2.values();
   let value = iter.next();
   while (!value.done) {
     finalSet.add(value.value);
@@ -295,7 +295,7 @@ export function amountToCurrency(n) {
 }
 
 export function currencyToAmount(str) {
-  let amount = parseFloat(
+  const amount = parseFloat(
     str
       .replace(getNumberFormat().regex, '')
       .replace(getNumberFormat().separator, '.'),
@@ -304,12 +304,12 @@ export function currencyToAmount(str) {
 }
 
 export function currencyToInteger(str) {
-  let amount = currencyToAmount(str);
+  const amount = currencyToAmount(str);
   return amount == null ? null : amountToInteger(amount);
 }
 
 export function stringToInteger(str) {
-  let amount = parseInt(str.replace(/[^-0-9.,]/g, ''));
+  const amount = parseInt(str.replace(/[^-0-9.,]/g, ''));
   if (!isNaN(amount)) {
     return amount;
   }
@@ -341,13 +341,13 @@ export function looselyParseAmount(amount) {
     amount = amount.replace('(', '-').replace(')', '');
   }
 
-  let m = amount.match(/[.,][^.,]*$/);
+  const m = amount.match(/[.,][^.,]*$/);
   if (!m || m.index === 0) {
     return safeNumber(parseFloat(extractNumbers(amount)));
   }
 
-  let left = extractNumbers(amount.slice(0, m.index));
-  let right = extractNumbers(amount.slice(m.index + 1));
+  const left = extractNumbers(amount.slice(0, m.index));
+  const right = extractNumbers(amount.slice(m.index + 1));
 
   return safeNumber(parseFloat(left + '.' + right));
 }
diff --git a/packages/loot-core/webpack/webpack.api.config.js b/packages/loot-core/webpack/webpack.api.config.js
index 56604239f..9db83f1e7 100644
--- a/packages/loot-core/webpack/webpack.api.config.js
+++ b/packages/loot-core/webpack/webpack.api.config.js
@@ -1,6 +1,6 @@
-let path = require('path');
+const path = require('path');
 
-let config = require('./webpack.desktop.config');
+const config = require('./webpack.desktop.config');
 
 config.resolve.extensions = [
   '.api.js',
diff --git a/packages/loot-core/webpack/webpack.browser.config.js b/packages/loot-core/webpack/webpack.browser.config.js
index e5024f618..6b9a70c6e 100644
--- a/packages/loot-core/webpack/webpack.browser.config.js
+++ b/packages/loot-core/webpack/webpack.browser.config.js
@@ -1,7 +1,7 @@
-let path = require('path');
+const path = require('path');
 
 const TerserPlugin = require('terser-webpack-plugin');
-let webpack = require('webpack');
+const webpack = require('webpack');
 const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 
 /** @type {webpack.Configuration} */
diff --git a/packages/loot-core/webpack/webpack.desktop.config.js b/packages/loot-core/webpack/webpack.desktop.config.js
index 9a509a45d..a0f9b2c6d 100644
--- a/packages/loot-core/webpack/webpack.desktop.config.js
+++ b/packages/loot-core/webpack/webpack.desktop.config.js
@@ -1,9 +1,9 @@
-let path = require('path');
+const path = require('path');
 
-let webpack = require('webpack');
+const webpack = require('webpack');
 const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 
-let browser = require('./webpack.browser.config');
+const browser = require('./webpack.browser.config');
 
 /** @type {webpack.Configuration} */
 module.exports = {
diff --git a/upcoming-release-notes/1958.md b/upcoming-release-notes/1958.md
new file mode 100644
index 000000000..0919a55dc
--- /dev/null
+++ b/upcoming-release-notes/1958.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [joel-jeremy]
+---
+
+ESLint prefer-const rule
-- 
GitLab