Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f5642da
Upgrade eslint-config-expensify to 4.0.5
roryabraham Jun 19, 2026
bd03a87
Remove redundant es/* operator overrides from ESLint config
roryabraham Jun 19, 2026
ec40a22
Upgrade eslint-config-expensify to 4.0.6
roryabraham Jun 19, 2026
285686c
Run prettier on eslint.config.mjs
roryabraham Jun 19, 2026
08e566b
Merge branch 'main' into rory-eslint-config-expensify-4
roryabraham Jun 26, 2026
cc19068
Remove redundant eslint.config.mjs overrides
roryabraham Jun 26, 2026
bea5370
Apply autofix for mechanical lint rules
roryabraham Jun 26, 2026
7dac669
Add ESLint overrides for seatbelt cleanup
roryabraham Jun 26, 2026
26ae966
Fix lodash import paths in lib storage code
roryabraham Jun 26, 2026
7549048
Fix NoopProvider unused param and Onyx loop-func
roryabraham Jun 26, 2026
bd34cf7
Fix remaining seatbelt violations outside no-unsafe-*
roryabraham Jun 26, 2026
a6f1d3c
Merge remote-tracking branch 'origin/main' into rory-eslint-config-ex…
roryabraham Jul 1, 2026
b01ee08
Fix TypeScript build errors blocking npm ci
roryabraham Jul 1, 2026
2cb18e1
Fix remaining CI failures after eslint-config-expensify 4 upgrade
roryabraham Jul 1, 2026
f0a51a2
Fix lint and typecheck CI after eslint-config-expensify 4 upgrade
roryabraham Jul 1, 2026
eaee7bc
Fix lint CI by syncing seatbelt with eslint-config-expensify 4 rules
roryabraham Jul 1, 2026
62e9276
Remove unused dot-notation eslint-disable directives in tests
roryabraham Jul 1, 2026
b949a8e
Use @types/lodash.bindall instead of hand-rolled declaration
roryabraham Jul 1, 2026
364e50e
Remove test ESLint overrides and fix violations in code
roryabraham Jul 2, 2026
c30508e
Fix typecheck errors in test harness casts
roryabraham Jul 3, 2026
a2561d5
Run prettier on test harness files
roryabraham Jul 3, 2026
d32edbd
Remove unused SQLiteProvider naming-convention override
roryabraham Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 99 additions & 104 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
import expensify from 'eslint-config-expensify';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import browserConfig from 'eslint-config-expensify/browser';
import reactConfig from 'eslint-config-expensify/react';
import scriptsConfig from 'eslint-config-expensify/scripts';
import tsExpensifyConfig from 'eslint-config-expensify/typescript';
import jestConfig from 'eslint-config-expensify/jest';
import prettierConfig from 'eslint-config-prettier';
import seatbelt from 'eslint-seatbelt';
import rulesdir from 'eslint-plugin-rulesdir';
import {defineConfig, globalIgnores} from 'eslint/config';
import globals from 'globals';
import path from 'node:path';
import {createRequire} from 'node:module';
import {fileURLToPath} from 'node:url';

export default [
...expensify,
prettierConfig,
{
ignores: ['dist/**', 'node_modules/**', '.github/**', '*.d.ts', '*.config.js', '*.config.cjs', 'tests/types/**/*.ts'],
},
const dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const expensifyConfigDirectory = path.dirname(require.resolve('eslint-config-expensify'));
rulesdir.RULES_DIR = path.resolve(expensifyConfigDirectory, 'eslint-plugin-expensify');

export default defineConfig([
seatbelt.configs.enable,
globalIgnores(['dist/**', 'node_modules/**', '**/*.d.ts', '**/*.config.js', '**/*.config.cjs', 'tests/types/**/*.ts', 'cpp/**', 'bench-results/**', '.github/**']),
...browserConfig,
...reactConfig,
...tsExpensifyConfig,
...jestConfig,
...scriptsConfig,
{
// Overwriting this for now because web-e will conflict with this
files: ['**/*.js', '**/*.jsx'],
plugins: {
rulesdir,
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.website.js', '.desktop.js', '.native.js', '.ios.js', '.android.js', '.config.js', '.ts', '.tsx'],
},
seatbelt: {
seatbeltFile: path.join(dirname, 'eslint.seatbelt.tsv'),
threadsafe: true,
},
},
rules: {
'react/jsx-filename-extension': [1, {extensions: ['.js']}],
'rulesdir/no-multiple-onyx-in-file': 'off',
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
},
{
files: ['**/*.ts', '**/*.tsx'],
plugins: {
'@typescript-eslint': tsPlugin,
},
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
},
},
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
settings: {
'import/resolver': {
typescript: {
Expand All @@ -54,88 +47,90 @@ export default [
},
},
rules: {
...tsPlugin.configs.recommended.rules,
...tsPlugin.configs.stylistic.rules,
'rulesdir/prefer-underscore-method': 'off',
'react/jsx-props-no-spreading': 'off',
'react/prop-types': 'off',
'react/require-default-props': 'off',
'react/jsx-filename-extension': ['error', {extensions: ['.tsx', '.jsx']}],
'import/no-unresolved': 'error',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-unused-vars': ['error', {argsIgnorePattern: '^_', caughtErrors: 'none'}],
'@typescript-eslint/consistent-type-imports': ['error', {prefer: 'type-imports'}],
'@typescript-eslint/consistent-type-exports': ['error', {fixMixedExportsWithInlineTypeSpecifier: false}],
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/array-type': ['error', {default: 'array-simple'}],
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'rulesdir/no-multiple-onyx-in-file': 'off',
'valid-jsdoc': 'off',
'rulesdir/prefer-import-module-contents': 'off',
'es/no-optional-chaining': 'off',
'es/no-nullish-coalescing-operators': 'off',
// Disable JSDoc type rules for TypeScript files (TypeScript provides the types)
'jsdoc/require-param': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/check-param-names': 'off',
'jsdoc/check-tag-names': 'off',
'jsdoc/check-types': 'off',
'no-func-assign': 'off',
'no-loop-func': 'off',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
'rulesdir/prefer-onyx-connect-in-libs': 'off',
},
},
{
files: ['tests/**/*.{js,jsx,ts,tsx}', 'jestSetup.js', 'lib/**/__mocks__/**/*.{js,ts}'],
files: ['.prettierrc.cjs', 'jest-sequencer.js', 'jestSetup.js', 'jest-test-environment.ts', 'buildDocs.ts'],
languageOptions: {
globals: {
jest: 'readonly',
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
fail: 'readonly',
},
globals: globals.node,
},
},
{
files: ['lib/OnyxUtils.ts'],
rules: {
'@lwc/lwc/no-async-await': 'off',
'no-await-in-loop': 'off',
'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
'import/extensions': 'off',
'@typescript-eslint/no-require-imports': 'off',
'no-restricted-imports': 'off',
'@typescript-eslint/no-use-before-define': 'off',
},
},
// Flat config replaces the whole naming-convention array, so this repeats eslint-config-expensify/typescript
// and adds objectLiteralProperty exceptions for Onyx key strings (underscores) and numeric collection IDs.
{
files: ['tests/**/*'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['variable', 'property'],
format: null,
filter: {
regex: '^__esModule$',
match: true,
},
},
{
selector: ['variable', 'property'],
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
filter: {
regex: '^private_[a-z][a-zA-Z0-9]*$',
match: false,
},
},
{
selector: 'objectLiteralProperty',
format: null,
filter: {
regex: '_',
match: true,
},
},
{
selector: 'objectLiteralProperty',
format: null,
filter: {
regex: '^[0-9]+$',
match: true,
},
},
{
selector: 'function',
format: ['camelCase', 'PascalCase'],
},
{
selector: ['typeLike', 'enumMember'],
format: ['PascalCase'],
},
{
selector: ['parameter', 'method'],
format: ['camelCase', 'PascalCase'],
leadingUnderscore: 'allow',
},
],
},
},
{
files: ['**/*.native.ts', '**/*.native.tsx'],
files: ['tests/**/*', 'jestSetup.js', 'lib/**/__mocks__/**/*'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
'import/extensions': 'off',
},
},
{
files: ['lib/storage/providers/MemoryOnlyProvider.ts'],
languageOptions: {
globals: {
jest: 'readonly',
...globals.jest,
},
},
},
];
prettierConfig,
]);
107 changes: 107 additions & 0 deletions eslint.seatbelt.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# eslint-seatbelt temporarily allowed errors
# docs: https://github.com/justjake/eslint-seatbelt#readme

"buildDocs.ts" "import/no-extraneous-dependencies" 1
"jest-test-environment.ts" "import/no-extraneous-dependencies" 1
"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-assignment" 1
"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-member-access" 1
"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"lib/Onyx.ts" "@typescript-eslint/no-unsafe-type-assertion" 6
"lib/OnyxConnectionManager.ts" "@typescript-eslint/no-unsafe-type-assertion" 7
"lib/OnyxConnectionManager.ts" "@typescript-eslint/non-nullable-type-assertion-style" 3
"lib/OnyxKeys.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"lib/OnyxMerge/index.native.ts" "@typescript-eslint/no-unsafe-type-assertion" 3
"lib/OnyxMerge/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 2
"lib/OnyxSnapshotCache.ts" "@typescript-eslint/no-unsafe-type-assertion" 2
"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-argument" 6
"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-assignment" 6
"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-call" 2
"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-member-access" 5
"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 31
"lib/OnyxUtils.ts" "@typescript-eslint/non-nullable-type-assertion-style" 1
"lib/Str.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"lib/storage/providers/IDBKeyValProvider/index.ts" "@typescript-eslint/no-unsafe-return" 1
"lib/storage/providers/IDBKeyValProvider/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 3
"lib/storage/providers/MemoryOnlyProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 5
"lib/storage/providers/NoopProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"lib/storage/providers/SQLiteProvider.ts" "@typescript-eslint/no-unsafe-return" 3
"lib/storage/providers/SQLiteProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 3
# useLiveRef is deprecated but still used by useOnyx; refs assignment during render is intentional
"lib/useLiveRef.ts" "@typescript-eslint/no-deprecated" 1
"lib/useLiveRef.ts" "react-hooks/refs" 1
"lib/useOnyx.ts" "@typescript-eslint/no-deprecated" 1
"lib/useOnyx.ts" "@typescript-eslint/no-unsafe-type-assertion" 2
"lib/utils.ts" "@typescript-eslint/no-unsafe-type-assertion" 5
"tests/perf-test/Onyx.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-non-null-assertion" 1
"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-unsafe-assignment" 1
"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-unsafe-member-access" 1
"tests/perf-test/OnyxConnectionManager.perf-test.ts" "@typescript-eslint/no-non-null-assertion" 1
"tests/perf-test/OnyxSnapshotCache.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/perf-test/OnyxUtils.perf-test.ts" "@typescript-eslint/no-misused-promises" 1
"tests/perf-test/OnyxUtils.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"tests/perf-test/useOnyx.perf-test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2
"tests/perf-test/utils.perf-test.ts" "@typescript-eslint/no-unsafe-call" 1
"tests/perf-test/utils.perf-test.ts" "@typescript-eslint/no-unsafe-member-access" 3
"tests/unit/DevToolsTest.ts" "@typescript-eslint/no-unsafe-assignment" 2
"tests/unit/DevToolsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 9
"tests/utils/onyxConnectionManagerTestHarness.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-unsafe-return" 1
"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2
"tests/unit/OnyxSnapshotCacheTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 7
"tests/unit/collectionHydrationTest.ts" "@typescript-eslint/no-unsafe-assignment" 3
"tests/unit/collectionHydrationTest.ts" "@typescript-eslint/no-unsafe-member-access" 7
"tests/unit/mocks/sqliteMock.ts" "@typescript-eslint/no-unsafe-type-assertion" 7
"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-non-null-assertion" 3
"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-assignment" 7
"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-member-access" 6
"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2
"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1
"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1
"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/unit/onyxClearWebStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1
"tests/unit/onyxClearWebStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1
"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1
"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1
"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
"tests/unit/onyxTest.ts" "@typescript-eslint/no-non-null-assertion" 1
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-assignment" 9
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-call" 2
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-member-access" 31
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-return" 1
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5
"tests/unit/onyxTest.ts" "import/no-extraneous-dependencies" 2
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/await-thenable" 6
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-assignment" 10
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-call" 2
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 20
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-return" 1
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3
"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unsafe-member-access" 2
"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-member-access" 2
"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-return" 1
"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/unit/storage/tryOrDegradePerformanceTest.ts" "@typescript-eslint/no-unsafe-assignment" 2
"tests/unit/storage/tryOrDegradePerformanceTest.ts" "@typescript-eslint/no-unsafe-member-access" 1
"tests/unit/useOnyxTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 21
"tests/unit/utilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 3
"lib/StateMachine.ts" "@typescript-eslint/no-unsafe-type-assertion" 2
"lib/storage/__mocks__/index.ts" "@typescript-eslint/no-unsafe-argument" 1
"lib/storage/index.ts" "no-nested-ternary" 1
"lib/storage/providers/IDBKeyValProvider/index.ts" "rulesdir/prefer-at" 1
"lib/storage/providers/NoopProvider.ts" "no-void" 1
"lib/utils.ts" "@typescript-eslint/no-redundant-type-constituents" 1
"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-non-null-assertion" 1
"tests/unit/onyxTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 4
"tests/unit/onyxTest.ts" "rulesdir/prefer-at" 11
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-non-null-assertion" 2
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 21
"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/unit/storage/providers/createStoreTest.ts" "@typescript-eslint/no-unsafe-assignment" 21
"tests/unit/storage/providers/createStoreTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 2
"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/unbound-method" 2
"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "rulesdir/prefer-at" 4
"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 5
"tests/unit/storage/providers/SQLiteProviderTest.ts" "rulesdir/prefer-at" 1
Loading
Loading