Skip to content

Commit c3c2f0b

Browse files
committed
Merge branch 'main' into VickyStash/poc/remove-batching
# Conflicts: # lib/OnyxUtils.ts
2 parents fd10fea + 9bd7266 commit c3c2f0b

30 files changed

+1782
-1017
lines changed

.eslintignore

Lines changed: 0 additions & 6 deletions
This file was deleted.

.eslintrc.js

Lines changed: 0 additions & 82 deletions
This file was deleted.
File renamed without changes.

eslint.config.mjs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import expensify from 'eslint-config-expensify';
2+
import tsPlugin from '@typescript-eslint/eslint-plugin';
3+
import tsParser from '@typescript-eslint/parser';
4+
import prettierConfig from 'eslint-config-prettier';
5+
6+
export default [
7+
...expensify,
8+
prettierConfig,
9+
{
10+
ignores: ['dist/**', 'node_modules/**', '.github/**', '*.d.ts', '*.config.js', '*.config.cjs', 'tests/types/**/*.ts'],
11+
},
12+
{
13+
// Overwriting this for now because web-e will conflict with this
14+
files: ['**/*.js', '**/*.jsx'],
15+
settings: {
16+
'import/resolver': {
17+
node: {
18+
extensions: ['.js', '.website.js', '.desktop.js', '.native.js', '.ios.js', '.android.js', '.config.js', '.ts', '.tsx'],
19+
},
20+
},
21+
},
22+
rules: {
23+
'react/jsx-filename-extension': [1, {extensions: ['.js']}],
24+
'rulesdir/no-multiple-onyx-in-file': 'off',
25+
'import/extensions': [
26+
'error',
27+
'ignorePackages',
28+
{
29+
js: 'never',
30+
jsx: 'never',
31+
ts: 'never',
32+
tsx: 'never',
33+
},
34+
],
35+
},
36+
},
37+
{
38+
files: ['**/*.ts', '**/*.tsx'],
39+
plugins: {
40+
'@typescript-eslint': tsPlugin,
41+
},
42+
languageOptions: {
43+
parser: tsParser,
44+
parserOptions: {
45+
project: './tsconfig.json',
46+
},
47+
},
48+
settings: {
49+
'import/resolver': {
50+
typescript: {
51+
alwaysTryTypes: true,
52+
project: './tsconfig.json',
53+
},
54+
},
55+
},
56+
rules: {
57+
...tsPlugin.configs.recommended.rules,
58+
...tsPlugin.configs.stylistic.rules,
59+
'rulesdir/prefer-underscore-method': 'off',
60+
'react/jsx-props-no-spreading': 'off',
61+
'react/require-default-props': 'off',
62+
'react/jsx-filename-extension': ['error', {extensions: ['.tsx', '.jsx']}],
63+
'import/no-unresolved': 'error',
64+
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
65+
'no-use-before-define': 'off',
66+
'@typescript-eslint/no-use-before-define': 'off',
67+
'@typescript-eslint/no-unused-vars': ['error', {argsIgnorePattern: '^_', caughtErrors: 'none'}],
68+
'@typescript-eslint/consistent-type-imports': ['error', {prefer: 'type-imports'}],
69+
'@typescript-eslint/consistent-type-exports': ['error', {fixMixedExportsWithInlineTypeSpecifier: false}],
70+
'@typescript-eslint/no-non-null-assertion': 'off',
71+
'@typescript-eslint/array-type': ['error', {default: 'array-simple'}],
72+
'@typescript-eslint/consistent-type-definitions': 'off',
73+
'@typescript-eslint/no-empty-object-type': 'off',
74+
'rulesdir/no-multiple-onyx-in-file': 'off',
75+
'valid-jsdoc': 'off',
76+
'rulesdir/prefer-import-module-contents': 'off',
77+
'es/no-optional-chaining': 'off',
78+
'es/no-nullish-coalescing-operators': 'off',
79+
// Disable JSDoc type rules for TypeScript files (TypeScript provides the types)
80+
'jsdoc/require-param': 'off',
81+
'jsdoc/require-param-type': 'off',
82+
'jsdoc/check-param-names': 'off',
83+
'jsdoc/check-tag-names': 'off',
84+
'jsdoc/check-types': 'off',
85+
'no-func-assign': 'off',
86+
'no-loop-func': 'off',
87+
'no-redeclare': 'off',
88+
'@typescript-eslint/no-redeclare': 'error',
89+
'import/extensions': [
90+
'error',
91+
'ignorePackages',
92+
{
93+
js: 'never',
94+
jsx: 'never',
95+
ts: 'never',
96+
tsx: 'never',
97+
},
98+
],
99+
'rulesdir/prefer-onyx-connect-in-libs': 'off',
100+
},
101+
},
102+
{
103+
files: ['tests/**/*.{js,jsx,ts,tsx}', 'jestSetup.js', 'lib/**/__mocks__/**/*.{js,ts}'],
104+
languageOptions: {
105+
globals: {
106+
jest: 'readonly',
107+
describe: 'readonly',
108+
it: 'readonly',
109+
test: 'readonly',
110+
expect: 'readonly',
111+
beforeEach: 'readonly',
112+
afterEach: 'readonly',
113+
beforeAll: 'readonly',
114+
afterAll: 'readonly',
115+
fail: 'readonly',
116+
},
117+
},
118+
rules: {
119+
'@lwc/lwc/no-async-await': 'off',
120+
'no-await-in-loop': 'off',
121+
'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
122+
'import/extensions': 'off',
123+
'@typescript-eslint/no-require-imports': 'off',
124+
'no-restricted-imports': 'off',
125+
},
126+
},
127+
{
128+
files: ['**/*.native.ts', '**/*.native.tsx'],
129+
rules: {
130+
'@typescript-eslint/no-require-imports': 'off',
131+
},
132+
},
133+
{
134+
files: ['lib/storage/providers/MemoryOnlyProvider.ts'],
135+
languageOptions: {
136+
globals: {
137+
jest: 'readonly',
138+
},
139+
},
140+
},
141+
];

lib/GlobalSettings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function addGlobalSettingsChangeListener(listener: (settings: GlobalSettings) =>
1717
}
1818

1919
function notifyListeners() {
20-
listeners.forEach((listener) => listener(globalSettings));
20+
for (const listener of listeners) listener(globalSettings);
2121
}
2222

2323
function setPerformanceMetricsEnabled(enabled: boolean) {

lib/Onyx.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
320320
// 2. Any keys with a default state (because they need to remain in Onyx as their default, and setting them
321321
// to null would cause unknown behavior)
322322
// 2.1 However, if a default key was explicitly set to null, we need to reset it to the default value
323-
allKeys.forEach((key) => {
323+
for (const key of allKeys) {
324324
const isKeyToPreserve = keysToPreserve.includes(key);
325325
const isDefaultKey = key in defaultKeyStates;
326326

@@ -354,22 +354,22 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
354354
}
355355

356356
if (isKeyToPreserve || isDefaultKey) {
357-
return;
357+
continue;
358358
}
359359

360360
// If it isn't preserved and doesn't have a default, we'll remove it
361361
keysToBeClearedFromStorage.push(key);
362-
});
362+
}
363363

364364
const updatePromises: Array<Promise<void>> = [];
365365

366366
// Notify the subscribers for each key/value group so they can receive the new values
367-
Object.entries(keyValuesToResetIndividually).forEach(([key, value]) => {
367+
for (const [key, value] of Object.entries(keyValuesToResetIndividually)) {
368368
updatePromises.push(OnyxUtils.scheduleSubscriberUpdate(key, value));
369-
});
370-
Object.entries(keyValuesToResetAsCollection).forEach(([key, value]) => {
369+
}
370+
for (const [key, value] of Object.entries(keyValuesToResetAsCollection)) {
371371
updatePromises.push(OnyxUtils.scheduleNotifyCollectionSubscribers(key, value));
372-
});
372+
}
373373

374374
const defaultKeyValuePairs = Object.entries(
375375
Object.keys(defaultKeyStates)
@@ -382,7 +382,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
382382
);
383383

384384
// Remove only the items that we want cleared from storage, and reset others to default
385-
keysToBeClearedFromStorage.forEach((key) => cache.drop(key));
385+
for (const key of keysToBeClearedFromStorage) cache.drop(key);
386386
return Storage.removeItems(keysToBeClearedFromStorage)
387387
.then(() => connectionManager.refreshSessionID())
388388
.then(() => Storage.multiSet(defaultKeyValuePairs))
@@ -404,7 +404,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
404404
*/
405405
function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<void> {
406406
// First, validate the Onyx object is in the format we expect
407-
data.forEach(({onyxMethod, key, value}) => {
407+
for (const {onyxMethod, key, value} of data) {
408408
if (!Object.values(OnyxUtils.METHOD).includes(onyxMethod)) {
409409
throw new Error(`Invalid onyxMethod ${onyxMethod} in Onyx update.`);
410410
}
@@ -416,7 +416,7 @@ function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<vo
416416
} else if (onyxMethod !== OnyxUtils.METHOD.CLEAR && typeof key !== 'string') {
417417
throw new Error(`Invalid ${typeof key} key provided in Onyx update. Onyx key must be of type string.`);
418418
}
419-
});
419+
}
420420

421421
// The queue of operations within a single `update` call in the format of <item key - list of operations updating the item>.
422422
// This allows us to batch the operations per item and merge them into one operation in the order they were requested.
@@ -441,7 +441,7 @@ function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<vo
441441
const promises: Array<() => Promise<void>> = [];
442442
let clearPromise: Promise<void> = Promise.resolve();
443443

444-
data.forEach(({onyxMethod, key, value}) => {
444+
for (const {onyxMethod, key, value} of data) {
445445
const handlers: Record<OnyxMethodMap[keyof OnyxMethodMap], (k: typeof key, v: typeof value) => void> = {
446446
[OnyxUtils.METHOD.SET]: enqueueSetOperation,
447447
[OnyxUtils.METHOD.MERGE]: enqueueMergeOperation,
@@ -456,28 +456,30 @@ function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<vo
456456
const collectionKeys = Object.keys(collection);
457457
if (OnyxUtils.doAllCollectionItemsBelongToSameParent(key, collectionKeys)) {
458458
const mergedCollection: OnyxInputKeyValueMapping = collection;
459-
collectionKeys.forEach((collectionKey) => enqueueMergeOperation(collectionKey, mergedCollection[collectionKey]));
459+
for (const collectionKey of collectionKeys) enqueueMergeOperation(collectionKey, mergedCollection[collectionKey]);
460460
}
461461
},
462462
[OnyxUtils.METHOD.SET_COLLECTION]: (k, v) => promises.push(() => setCollection(k as TKey, v as OnyxSetCollectionInput<TKey>)),
463-
[OnyxUtils.METHOD.MULTI_SET]: (k, v) => Object.entries(v as Partial<OnyxInputKeyValueMapping>).forEach(([entryKey, entryValue]) => enqueueSetOperation(entryKey, entryValue)),
463+
[OnyxUtils.METHOD.MULTI_SET]: (k, v) => {
464+
for (const [entryKey, entryValue] of Object.entries(v as Partial<OnyxInputKeyValueMapping>)) enqueueSetOperation(entryKey, entryValue);
465+
},
464466
[OnyxUtils.METHOD.CLEAR]: () => {
465467
clearPromise = clear();
466468
},
467469
};
468470

469471
handlers[onyxMethod](key, value);
470-
});
472+
}
471473

472474
// Group all the collection-related keys and update each collection in a single `mergeCollection` call.
473475
// This is needed to prevent multiple `mergeCollection` calls for the same collection and `merge` calls for the individual items of the said collection.
474476
// This way, we ensure there is no race condition in the queued updates of the same key.
475-
OnyxUtils.getCollectionKeys().forEach((collectionKey) => {
477+
for (const collectionKey of OnyxUtils.getCollectionKeys()) {
476478
const collectionItemKeys = Object.keys(updateQueue).filter((key) => OnyxUtils.isKeyMatch(collectionKey, key));
477479
if (collectionItemKeys.length <= 1) {
478480
// If there are no items of this collection in the updateQueue, we should skip it.
479481
// If there is only one item, we should update it individually, therefore retain it in the updateQueue.
480-
return;
482+
continue;
481483
}
482484

483485
const batchedCollectionUpdates = collectionItemKeys.reduce(
@@ -521,19 +523,19 @@ function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<vo
521523
if (!utils.isEmptyObject(batchedCollectionUpdates.set)) {
522524
promises.push(() => OnyxUtils.partialSetCollection({collectionKey, collection: batchedCollectionUpdates.set as OnyxSetCollectionInput<OnyxKey>}));
523525
}
524-
});
526+
}
525527

526-
Object.entries(updateQueue).forEach(([key, operations]) => {
528+
for (const [key, operations] of Object.entries(updateQueue)) {
527529
if (operations[0] === null) {
528530
const batchedChanges = OnyxUtils.mergeChanges(operations).result;
529531
promises.push(() => set(key, batchedChanges));
530-
return;
532+
continue;
531533
}
532534

533-
operations.forEach((operation) => {
535+
for (const operation of operations) {
534536
promises.push(() => merge(key, operation));
535-
});
536-
});
537+
}
538+
}
537539

538540
const snapshotPromises = OnyxUtils.updateSnapshots(data, merge);
539541

@@ -578,7 +580,6 @@ const Onyx = {
578580

579581
function applyDecorators() {
580582
// We are reassigning the functions directly so that internal function calls are also decorated
581-
/* eslint-disable rulesdir/prefer-actions-set-data */
582583
// @ts-expect-error Reassign
583584
connect = decorateWithMetrics(connect, 'Onyx.connect');
584585
// @ts-expect-error Reassign
@@ -595,7 +596,6 @@ function applyDecorators() {
595596
update = decorateWithMetrics(update, 'Onyx.update');
596597
// @ts-expect-error Reassign
597598
clear = decorateWithMetrics(clear, 'Onyx.clear');
598-
/* eslint-enable rulesdir/prefer-actions-set-data */
599599
}
600600

601601
export default Onyx;

0 commit comments

Comments
 (0)