@luxfi/eslint-config 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +29 -0
- package/base.js +177 -0
- package/biome-supported.js +241 -0
- package/lib.js +41 -0
- package/load.js +5 -0
- package/native.js +230 -0
- package/package.json +57 -0
- package/restrictedImports.js +301 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright 2022 Uniswap Labs
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# ESLint
|
|
2
|
+
|
|
3
|
+
## Custom Rules
|
|
4
|
+
|
|
5
|
+
### Prelude
|
|
6
|
+
|
|
7
|
+
In most cases, a [core](https://eslint.org/docs/latest/rules/) or community rule will suffice for your use case, as these rules tend to be robust and performant; use a custom rule when these rules cover too little for your specific use case (ie not robust/applicable) or too much (ie not performant).
|
|
8
|
+
|
|
9
|
+
### Steps
|
|
10
|
+
|
|
11
|
+
1. Ensure there is no available rule that sufficiently covers your use case
|
|
12
|
+
2. Create two files in `plugins/`: `<your-lint-name>.js` and `<your-lint-name>.test.js`
|
|
13
|
+
- align naming with [ESLint core naming conventions](https://eslint.org/docs/latest/contribute/core-rules#rule-naming-conventions)
|
|
14
|
+
3. Create your lint rule using steps 2-4 [here](https://eslint.org/docs/latest/extend/custom-rule-tutorial#step-2-stub-out-the-rule-file)
|
|
15
|
+
- it's much easier to find the node names (eg, JSXText, JSXExpressionContainer) for the visitor methods using [this explorer](https://astexplorer.net/)
|
|
16
|
+
4. [Add tests](https://eslint.org/docs/latest/extend/custom-rule-tutorial#step-6-write-the-test)
|
|
17
|
+
5. Import new rule to [`universe/eslint-local-rules`](../../eslint-local-rules.js)
|
|
18
|
+
6. Add your shiny new rule to [`native.js`](./native.js), [`base.js`](./base.js), and – if it's a react lint rule - [`react.js`](./react.js)
|
|
19
|
+
7. **Test, test, test**; cover all your bases
|
|
20
|
+
- remember that this rule will checked against nearly every single LOC in the codebase
|
|
21
|
+
8. Profile with `TIMING=1 bun g:lint` and ensure your rule is **performant**
|
|
22
|
+
- rules should generally not exceed 25 ms (~1%) for each package
|
|
23
|
+
|
|
24
|
+
### Tips and resources
|
|
25
|
+
|
|
26
|
+
- For configurable lists (eg greenlisted elements), lean towards passing a variable as a rule config rather than using a const, so as to avoid having to update the rule itself as the codebase evolves
|
|
27
|
+
- Avoid traversing children in a custom rule to avoid performance issues. Ideally, you target the nodes directly
|
|
28
|
+
- As with any lint rule, make sure the tradeoff in DevX (eg slower lint times, more styling considerations) is worth the benefits
|
|
29
|
+
- Utilize the [ESLint docs](https://eslint.org/docs/latest/extend/custom-rules)
|
package/base.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// The ESLint browser environment defines all browser globals as valid,
|
|
2
|
+
// even though most people don't know some of them exist (e.g. `name` or `status`).
|
|
3
|
+
// This is dangerous as it hides accidentally undefined variables.
|
|
4
|
+
// We blocklist the globals that we deem potentially confusing.
|
|
5
|
+
// To use them, explicitly reference them, e.g. `window.name` or `window.status`.
|
|
6
|
+
const restrictedGlobals = require('confusing-browser-globals')
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
env: {
|
|
10
|
+
es6: true,
|
|
11
|
+
node: true,
|
|
12
|
+
},
|
|
13
|
+
plugins: [
|
|
14
|
+
'import',
|
|
15
|
+
'unused-imports',
|
|
16
|
+
'check-file',
|
|
17
|
+
'local-rules',
|
|
18
|
+
'react',
|
|
19
|
+
'react-hooks',
|
|
20
|
+
'security',
|
|
21
|
+
'no-unsanitized',
|
|
22
|
+
'@typescript-eslint',
|
|
23
|
+
],
|
|
24
|
+
extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react-hooks/recommended'],
|
|
25
|
+
parserOptions: {
|
|
26
|
+
ecmaVersion: 2020,
|
|
27
|
+
sourceType: 'module',
|
|
28
|
+
},
|
|
29
|
+
rules: {
|
|
30
|
+
// Imports and file naming
|
|
31
|
+
'check-file/no-index': ['error', { ignoreMiddleExtensions: true }],
|
|
32
|
+
'unused-imports/no-unused-imports': 'error',
|
|
33
|
+
|
|
34
|
+
// Basics
|
|
35
|
+
'react/display-name': 'error',
|
|
36
|
+
'no-shadow': 'off',
|
|
37
|
+
'no-ex-assign': 'error',
|
|
38
|
+
'no-eval': 'error',
|
|
39
|
+
'guard-for-in': 'error',
|
|
40
|
+
'no-extra-boolean-cast': 'error',
|
|
41
|
+
'object-shorthand': ['error', 'always'],
|
|
42
|
+
'consistent-return': ['error', { treatUndefinedAsUnspecified: false }],
|
|
43
|
+
'max-lines': ['error', 500], // cap file length
|
|
44
|
+
// Disallow unnecessary curly braces in JSX props and children
|
|
45
|
+
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
|
|
46
|
+
'max-params': ['error', { max: 2 }],
|
|
47
|
+
|
|
48
|
+
// Rules within the standard React plugin
|
|
49
|
+
'react/no-danger': 'error',
|
|
50
|
+
'react/no-danger-with-children': 'error',
|
|
51
|
+
'react/no-unsafe': 'error',
|
|
52
|
+
|
|
53
|
+
// Security Linting
|
|
54
|
+
// Mozilla's No Unsanitized - https://github.com/mozilla/eslint-plugin-no-unsanitized
|
|
55
|
+
'no-unsanitized/method': 'error',
|
|
56
|
+
'no-unsanitized/property': 'error',
|
|
57
|
+
// Generic Security Linting - https://www.npmjs.com/package/eslint-plugin-security
|
|
58
|
+
'security/detect-unsafe-regex': 'error',
|
|
59
|
+
'security/detect-buffer-noassert': 'error',
|
|
60
|
+
'security/detect-child-process': 'error',
|
|
61
|
+
'security/detect-disable-mustache-escape': 'error',
|
|
62
|
+
'security/detect-eval-with-expression': 'error',
|
|
63
|
+
'security/detect-non-literal-regexp': 'error',
|
|
64
|
+
'security/detect-pseudoRandomBytes': 'error',
|
|
65
|
+
'security/detect-new-buffer': 'error',
|
|
66
|
+
|
|
67
|
+
// Globals.
|
|
68
|
+
// The Extension config overrides this rule, so make sure to verify if we need to
|
|
69
|
+
// update `apps/extension/.eslintrc.js` if you make any changes here.
|
|
70
|
+
'no-restricted-globals': ['error'].concat(restrictedGlobals, [
|
|
71
|
+
{
|
|
72
|
+
name: 'chrome',
|
|
73
|
+
message:
|
|
74
|
+
'Direct `chrome` access is restricted to prevent accidental usage in the wrong context. Use `getChrome()` or `getChromeWithThrow()` instead.',
|
|
75
|
+
},
|
|
76
|
+
]),
|
|
77
|
+
|
|
78
|
+
// Custom Rules
|
|
79
|
+
'local-rules/no-unwrapped-t': ['error', { blockedElements: ['Flex', 'AnimatedFlex', 'TouchableArea', 'Trace'] }],
|
|
80
|
+
|
|
81
|
+
'@typescript-eslint/no-unused-vars': [
|
|
82
|
+
'error',
|
|
83
|
+
{
|
|
84
|
+
args: 'all',
|
|
85
|
+
argsIgnorePattern: '^_',
|
|
86
|
+
caughtErrors: 'all',
|
|
87
|
+
caughtErrorsIgnorePattern: 'e',
|
|
88
|
+
destructuredArrayIgnorePattern: '^_',
|
|
89
|
+
varsIgnorePattern: '^_',
|
|
90
|
+
ignoreRestSiblings: true,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
overrides: [
|
|
95
|
+
// All Typescript Files
|
|
96
|
+
{
|
|
97
|
+
files: ['*.ts', '*.tsx'],
|
|
98
|
+
parser: '@typescript-eslint/parser',
|
|
99
|
+
plugins: ['@typescript-eslint/eslint-plugin'],
|
|
100
|
+
extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/typescript'],
|
|
101
|
+
settings: {
|
|
102
|
+
'import/parsers': {
|
|
103
|
+
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
104
|
+
},
|
|
105
|
+
'import/resolver': {
|
|
106
|
+
typescript: {
|
|
107
|
+
alwaysTryTypes: true,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
rules: {
|
|
112
|
+
'local-rules/prevent-this-method-destructure': 'error',
|
|
113
|
+
'local-rules/enforce-query-options-result': [
|
|
114
|
+
'error',
|
|
115
|
+
{
|
|
116
|
+
importPath: 'utilities/src/reactQuery/queryOptions',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
curly: 'error',
|
|
120
|
+
|
|
121
|
+
// Disable dot-notation to allow TypeScript's noPropertyAccessFromIndexSignature
|
|
122
|
+
'dot-notation': 'off',
|
|
123
|
+
'@typescript-eslint/dot-notation': 'off',
|
|
124
|
+
|
|
125
|
+
'@typescript-eslint/prefer-enum-initializers': 'error',
|
|
126
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
127
|
+
'@typescript-eslint/ban-ts-comment': 'off',
|
|
128
|
+
'@typescript-eslint/ban-ts-ignore': 'off',
|
|
129
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
130
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
131
|
+
'@typescript-eslint/no-unnecessary-condition': [
|
|
132
|
+
'error',
|
|
133
|
+
{
|
|
134
|
+
allowConstantLoopConditions: true,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
// Non-Test Typescript Files
|
|
140
|
+
{
|
|
141
|
+
files: ['*.ts', '*.tsx'],
|
|
142
|
+
excludedFiles: ['*.test.ts', '*.test.tsx'],
|
|
143
|
+
rules: {
|
|
144
|
+
'no-console': 'error',
|
|
145
|
+
'local-rules/no-hex-string-casting': 'error',
|
|
146
|
+
'react/forbid-elements': [
|
|
147
|
+
'error',
|
|
148
|
+
{
|
|
149
|
+
forbid: [
|
|
150
|
+
{
|
|
151
|
+
element: 'div',
|
|
152
|
+
message: 'Please avoid using div when possible, even in web code! Use `Flex` or Fragments (`<>`).',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
// Test Files
|
|
160
|
+
{
|
|
161
|
+
files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)', '*.e2e.js'],
|
|
162
|
+
env: {
|
|
163
|
+
jest: true,
|
|
164
|
+
'jest/globals': true,
|
|
165
|
+
},
|
|
166
|
+
extends: ['plugin:jest/recommended'],
|
|
167
|
+
plugins: ['jest'],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
// Allow test files to exceed max-lines limit
|
|
171
|
+
files: ['**/*.test.ts', '**/*.test.tsx', '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
|
|
172
|
+
rules: {
|
|
173
|
+
'max-lines': 'off',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Below are the rules, we still need to migrate to biome
|
|
2
|
+
//
|
|
3
|
+
// Rules that can be migrated to an inspired rule using --include-inspired:
|
|
4
|
+
//
|
|
5
|
+
// - no-cond-assign
|
|
6
|
+
// - no-labels
|
|
7
|
+
// - object-shorthand
|
|
8
|
+
// - @typescript-eslint/ban-ts-comment
|
|
9
|
+
// - @typescript-eslint/explicit-function-return-type
|
|
10
|
+
// - @typescript-eslint/naming-convention
|
|
11
|
+
// - @typescript-eslint/no-empty-interface
|
|
12
|
+
// - @typescript-eslint/no-this-alias
|
|
13
|
+
// - jest/no-focused-tests
|
|
14
|
+
// - jest/no-standalone-expect
|
|
15
|
+
// - react/jsx-curly-brace-presence
|
|
16
|
+
// - react/jsx-no-target-blank
|
|
17
|
+
//
|
|
18
|
+
// Rules that can be migrated to a nursery rule using --include-nursery:
|
|
19
|
+
//
|
|
20
|
+
// - max-params
|
|
21
|
+
// - @typescript-eslint/no-floating-promises
|
|
22
|
+
// - @typescript-eslint/no-non-null-asserted-optional-chain
|
|
23
|
+
// - @typescript-eslint/no-unnecessary-condition
|
|
24
|
+
//
|
|
25
|
+
// Stylistic rules that the formatter may support (manual migration required):
|
|
26
|
+
//
|
|
27
|
+
// - eol-last
|
|
28
|
+
// - jsx-quotes
|
|
29
|
+
// - keyword-spacing
|
|
30
|
+
// - new-parens
|
|
31
|
+
// - no-extra-semi
|
|
32
|
+
// - no-floating-decimal
|
|
33
|
+
// - no-mixed-spaces-and-tabs
|
|
34
|
+
// - semi-spacing
|
|
35
|
+
// - space-infix-ops
|
|
36
|
+
// - space-unary-ops
|
|
37
|
+
//
|
|
38
|
+
// Unsupported rules:
|
|
39
|
+
//
|
|
40
|
+
// - complexity
|
|
41
|
+
// - consistent-return
|
|
42
|
+
// - consistent-this
|
|
43
|
+
// - handle-callback-err
|
|
44
|
+
// - max-depth
|
|
45
|
+
// - max-lines
|
|
46
|
+
// - max-nested-callbacks
|
|
47
|
+
// - no-caller
|
|
48
|
+
// - no-catch-shadow
|
|
49
|
+
// - no-delete-var
|
|
50
|
+
// - no-div-regex
|
|
51
|
+
// - no-extend-native
|
|
52
|
+
// - no-extra-bind
|
|
53
|
+
// - no-implied-eval
|
|
54
|
+
// - no-invalid-regexp
|
|
55
|
+
// - no-iterator
|
|
56
|
+
// - no-mixed-requires
|
|
57
|
+
// - no-negated-in-lhs
|
|
58
|
+
// - no-new
|
|
59
|
+
// - no-new-func
|
|
60
|
+
// - no-new-object
|
|
61
|
+
// - no-new-require
|
|
62
|
+
// - no-octal
|
|
63
|
+
// - no-path-concat
|
|
64
|
+
// - no-proto
|
|
65
|
+
// - no-restricted-modules
|
|
66
|
+
// - no-restricted-syntax
|
|
67
|
+
// - no-return-assign
|
|
68
|
+
// - no-script-url
|
|
69
|
+
// - prefer-spread
|
|
70
|
+
// - @jambit/typed-redux-saga/delegate-effects
|
|
71
|
+
// - @jambit/typed-redux-saga/use-typed-effects
|
|
72
|
+
// - @typescript-eslint/func-call-spacing
|
|
73
|
+
// - @typescript-eslint/no-duplicate-enum-values
|
|
74
|
+
// - @typescript-eslint/no-shadow
|
|
75
|
+
// - @typescript-eslint/no-unsafe-return
|
|
76
|
+
// - @typescript-eslint/no-unused-expressions
|
|
77
|
+
// - @typescript-eslint/triple-slash-reference
|
|
78
|
+
// - check-file/no-index
|
|
79
|
+
// - eslint-comments/no-aggregating-enable
|
|
80
|
+
// - eslint-comments/no-unlimited-disable
|
|
81
|
+
// - eslint-comments/no-unused-disable
|
|
82
|
+
// - eslint-comments/no-unused-enable
|
|
83
|
+
// - import/no-unused-modules
|
|
84
|
+
// - jest/expect-expect
|
|
85
|
+
// - jest/no-alias-methods
|
|
86
|
+
// - jest/no-commented-out-tests
|
|
87
|
+
// - jest/no-deprecated-functions
|
|
88
|
+
// - jest/no-identical-title
|
|
89
|
+
// - jest/no-interpolation-in-snapshots
|
|
90
|
+
// - jest/no-jasmine-globals
|
|
91
|
+
// - jest/no-mocks-import
|
|
92
|
+
// - jest/no-test-prefixes
|
|
93
|
+
// - jest/valid-expect
|
|
94
|
+
// - jest/valid-expect-in-promise
|
|
95
|
+
// - jest/valid-title
|
|
96
|
+
// - local-rules/enforce-query-options-result
|
|
97
|
+
// - local-rules/no-hex-string-casting
|
|
98
|
+
// - local-rules/no-unwrapped-t
|
|
99
|
+
// - local-rules/prevent-this-method-destructure
|
|
100
|
+
// - no-relative-import-paths/no-relative-import-paths
|
|
101
|
+
// - no-unsanitized/method
|
|
102
|
+
// - no-unsanitized/property
|
|
103
|
+
// - react/jsx-no-undef
|
|
104
|
+
// - react/jsx-sort-props
|
|
105
|
+
// - react/jsx-uses-react
|
|
106
|
+
// - react/jsx-uses-vars
|
|
107
|
+
// - react/no-deprecated
|
|
108
|
+
// - react/no-did-mount-set-state
|
|
109
|
+
// - react/no-did-update-set-state
|
|
110
|
+
// - react/no-direct-mutation-state
|
|
111
|
+
// - react/no-find-dom-node
|
|
112
|
+
// - react/no-is-mounted
|
|
113
|
+
// - react/no-render-return-value
|
|
114
|
+
// - react/no-string-refs
|
|
115
|
+
// - react/no-unescaped-entities
|
|
116
|
+
// - react/no-unsafe
|
|
117
|
+
// - react/no-unstable-nested-components
|
|
118
|
+
// - react/require-render-return
|
|
119
|
+
// - react/self-closing-comp
|
|
120
|
+
// - react-native/no-unused-styles
|
|
121
|
+
// - react-native/sort-styles
|
|
122
|
+
// - rulesdir/i18n
|
|
123
|
+
// - rulesdir/no-redux-modals
|
|
124
|
+
// - security/detect-buffer-noassert
|
|
125
|
+
// - security/detect-child-process
|
|
126
|
+
// - security/detect-disable-mustache-escape
|
|
127
|
+
// - security/detect-eval-with-expression
|
|
128
|
+
// - security/detect-new-buffer
|
|
129
|
+
// - security/detect-non-literal-regexp
|
|
130
|
+
// - security/detect-pseudoRandomBytes
|
|
131
|
+
// - security/detect-unsafe-regex
|
|
132
|
+
//
|
|
133
|
+
// Paritally migrated!
|
|
134
|
+
// - @typescript-eslint/no-restricted-imports - biome doesn't have allowTypeImports param.
|
|
135
|
+
// So, keep this eslint rule for @lux/smart-order-router and react-native related imports
|
|
136
|
+
|
|
137
|
+
// Rules that have been migrated to Biome and should be disabled in ESLint
|
|
138
|
+
// by overriding them inside .eslintrc.* files, so eslint does not check them
|
|
139
|
+
module.exports = {
|
|
140
|
+
// Standard ESLint rules
|
|
141
|
+
curly: 'off',
|
|
142
|
+
'dot-notation': 'off',
|
|
143
|
+
eqeqeq: 'off',
|
|
144
|
+
'for-direction': 'off',
|
|
145
|
+
'no-alert': 'off',
|
|
146
|
+
'no-async-promise-executor': 'off',
|
|
147
|
+
'no-bitwise': 'off',
|
|
148
|
+
'no-case-declarations': 'off',
|
|
149
|
+
'no-class-assign': 'off',
|
|
150
|
+
'no-compare-neg-zero': 'off',
|
|
151
|
+
'no-console': 'off',
|
|
152
|
+
'no-control-regex': 'off',
|
|
153
|
+
'no-debugger': 'off',
|
|
154
|
+
'no-dupe-else-if': 'off',
|
|
155
|
+
'no-duplicate-case': 'off',
|
|
156
|
+
'no-empty-character-class': 'off',
|
|
157
|
+
'no-empty-pattern': 'off',
|
|
158
|
+
'no-eval': 'off',
|
|
159
|
+
'no-ex-assign': 'off',
|
|
160
|
+
'no-extra-boolean-cast': 'off',
|
|
161
|
+
'no-fallthrough': 'off',
|
|
162
|
+
'no-global-assign': 'off',
|
|
163
|
+
'no-irregular-whitespace': 'off',
|
|
164
|
+
'no-label-var': 'off',
|
|
165
|
+
'no-lone-blocks': 'off',
|
|
166
|
+
'no-misleading-character-class': 'off',
|
|
167
|
+
'no-new-wrappers': 'off',
|
|
168
|
+
'no-nonoctal-decimal-escape': 'off',
|
|
169
|
+
'no-octal-escape': 'off',
|
|
170
|
+
'no-prototype-builtins': 'off',
|
|
171
|
+
'no-regex-spaces': 'off',
|
|
172
|
+
'no-restricted-globals': 'off',
|
|
173
|
+
'no-self-assign': 'off',
|
|
174
|
+
'no-self-compare': 'off',
|
|
175
|
+
'no-sequences': 'off',
|
|
176
|
+
'no-shadow-restricted-names': 'off',
|
|
177
|
+
'no-sparse-arrays': 'off',
|
|
178
|
+
'no-undef-init': 'off',
|
|
179
|
+
'no-unsafe-finally': 'off',
|
|
180
|
+
'no-unsafe-optional-chaining': 'off',
|
|
181
|
+
'no-unused-labels': 'off',
|
|
182
|
+
'no-useless-backreference': 'off',
|
|
183
|
+
'no-useless-catch': 'off',
|
|
184
|
+
'no-useless-escape': 'off',
|
|
185
|
+
'no-var': 'off',
|
|
186
|
+
'no-void': 'off',
|
|
187
|
+
'no-with': 'off',
|
|
188
|
+
'prefer-const': 'off',
|
|
189
|
+
'prefer-rest-params': 'off',
|
|
190
|
+
radix: 'off',
|
|
191
|
+
'require-yield': 'off',
|
|
192
|
+
'use-isnan': 'off',
|
|
193
|
+
'valid-typeof': 'off',
|
|
194
|
+
yoda: 'off',
|
|
195
|
+
|
|
196
|
+
// TypeScript ESLint rules
|
|
197
|
+
'@typescript-eslint/ban-types': 'off',
|
|
198
|
+
'@typescript-eslint/no-array-constructor': 'off',
|
|
199
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
200
|
+
'@typescript-eslint/no-extra-non-null-assertion': 'off',
|
|
201
|
+
'@typescript-eslint/no-loss-of-precision': 'off',
|
|
202
|
+
'@typescript-eslint/no-misused-new': 'off',
|
|
203
|
+
'@typescript-eslint/no-namespace': 'off',
|
|
204
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
205
|
+
'@typescript-eslint/no-restricted-imports': [
|
|
206
|
+
'error',
|
|
207
|
+
{
|
|
208
|
+
paths: [
|
|
209
|
+
{
|
|
210
|
+
name: '@uniswap/smart-order-router',
|
|
211
|
+
message: 'Only import types, unless you are in the client-side SOR, to preserve lazy-loading.',
|
|
212
|
+
allowTypeImports: true,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
'@typescript-eslint/no-unnecessary-type-constraint': 'off',
|
|
218
|
+
'@typescript-eslint/no-unsafe-declaration-merging': 'off',
|
|
219
|
+
'@typescript-eslint/no-unused-vars': 'off',
|
|
220
|
+
'@typescript-eslint/prefer-as-const': 'off',
|
|
221
|
+
'@typescript-eslint/prefer-enum-initializers': 'off',
|
|
222
|
+
|
|
223
|
+
// Jest rules
|
|
224
|
+
'jest/no-done-callback': 'off',
|
|
225
|
+
|
|
226
|
+
// React rules
|
|
227
|
+
'react/forbid-elements': 'off',
|
|
228
|
+
'react/jsx-key': 'off',
|
|
229
|
+
'react/jsx-no-comment-textnodes': 'off',
|
|
230
|
+
'react/jsx-no-duplicate-props': 'off',
|
|
231
|
+
'react/no-children-prop': 'off',
|
|
232
|
+
'react/no-danger': 'off',
|
|
233
|
+
'react/no-danger-with-children': 'off',
|
|
234
|
+
|
|
235
|
+
// React Hooks rules
|
|
236
|
+
'react-hooks/exhaustive-deps': 'off',
|
|
237
|
+
'react-hooks/rules-of-hooks': 'off',
|
|
238
|
+
|
|
239
|
+
// Unused imports
|
|
240
|
+
'unused-imports/no-unused-imports': 'off',
|
|
241
|
+
}
|
package/lib.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const biomeSupportedRules = require('./biome-supported')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
root: true,
|
|
5
|
+
extends: ['@luxfi/eslint-config/native', '@luxfi/eslint-config/webPlatform'],
|
|
6
|
+
ignorePatterns: [
|
|
7
|
+
'node_modules',
|
|
8
|
+
'.turbo',
|
|
9
|
+
'.eslintrc.js',
|
|
10
|
+
'vitest.config.ts',
|
|
11
|
+
'codegen.ts',
|
|
12
|
+
'.nx',
|
|
13
|
+
'scripts',
|
|
14
|
+
'dist',
|
|
15
|
+
'src/**/__generated__',
|
|
16
|
+
],
|
|
17
|
+
parserOptions: {
|
|
18
|
+
project: 'tsconfig.lint.json',
|
|
19
|
+
ecmaFeatures: {
|
|
20
|
+
jsx: true,
|
|
21
|
+
},
|
|
22
|
+
ecmaVersion: 2018,
|
|
23
|
+
sourceType: 'module',
|
|
24
|
+
},
|
|
25
|
+
overrides: [
|
|
26
|
+
{
|
|
27
|
+
files: ['**'],
|
|
28
|
+
rules: {
|
|
29
|
+
// Disable all ESLint rules that have been migrated to Biome
|
|
30
|
+
...biomeSupportedRules,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
files: ['src/index.ts'],
|
|
35
|
+
rules: {
|
|
36
|
+
'check-file/no-index': 'off',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
rules: {},
|
|
41
|
+
}
|
package/load.js
ADDED
package/native.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// this allows us to use es6, es2017, es2018 syntax (const, spread operators outside of array literals, etc.)
|
|
2
|
+
/* eslint-env es6, es2017, es2018 */
|
|
3
|
+
|
|
4
|
+
const { native: restrictedImports } = require('@luxfi/eslint-config/restrictedImports')
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
root: true,
|
|
8
|
+
parser: '@typescript-eslint/parser',
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaFeatures: {
|
|
11
|
+
jsx: true,
|
|
12
|
+
modules: true,
|
|
13
|
+
experimentalObjectRestSpread: true,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
extends: [
|
|
17
|
+
require.resolve('./base.js'),
|
|
18
|
+
'eslint:recommended',
|
|
19
|
+
'@react-native-community',
|
|
20
|
+
'plugin:jest/recommended',
|
|
21
|
+
'plugin:@typescript-eslint/recommended',
|
|
22
|
+
],
|
|
23
|
+
plugins: [
|
|
24
|
+
'jest',
|
|
25
|
+
'no-relative-import-paths',
|
|
26
|
+
'react',
|
|
27
|
+
'react-native',
|
|
28
|
+
'@typescript-eslint',
|
|
29
|
+
'@jambit/typed-redux-saga',
|
|
30
|
+
'check-file',
|
|
31
|
+
'local-rules',
|
|
32
|
+
],
|
|
33
|
+
rules: {
|
|
34
|
+
// Platform specific restricted imports
|
|
35
|
+
'@typescript-eslint/no-restricted-imports': ['error', restrictedImports],
|
|
36
|
+
|
|
37
|
+
// Disable dot-notation to allow TypeScript's noPropertyAccessFromIndexSignature
|
|
38
|
+
'dot-notation': 'off',
|
|
39
|
+
'@typescript-eslint/dot-notation': 'off',
|
|
40
|
+
|
|
41
|
+
// Complexity Rules
|
|
42
|
+
'max-depth': ['error', 4], // prevent deeply nested code paths which are hard to read
|
|
43
|
+
'max-nested-callbacks': ['error', 3],
|
|
44
|
+
complexity: ['error', 20], // restrict cyclomatic complexity (number of linearly independent paths)
|
|
45
|
+
|
|
46
|
+
// disable prettier linting, as we format with biome:
|
|
47
|
+
'prettier/prettier': 0,
|
|
48
|
+
semi: 0,
|
|
49
|
+
quotes: 0,
|
|
50
|
+
'comma-dangle': 0,
|
|
51
|
+
'no-trailing-spaces': 0,
|
|
52
|
+
|
|
53
|
+
// gui encourages inline styles and makes them fast
|
|
54
|
+
'react-native/no-inline-styles': 'off',
|
|
55
|
+
|
|
56
|
+
'@typescript-eslint/no-unused-expressions': [
|
|
57
|
+
2,
|
|
58
|
+
{
|
|
59
|
+
allowShortCircuit: true,
|
|
60
|
+
allowTernary: true,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
'@typescript-eslint/naming-convention': [
|
|
64
|
+
2,
|
|
65
|
+
{
|
|
66
|
+
selector: 'enumMember',
|
|
67
|
+
format: ['PascalCase'],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
// Required for e2e use cases
|
|
71
|
+
'jest/no-export': 'off',
|
|
72
|
+
'jest/valid-describe-callback': 'off',
|
|
73
|
+
'jest/valid-title': [
|
|
74
|
+
2,
|
|
75
|
+
{
|
|
76
|
+
// jest expect string titles, but we use function names in the codebase
|
|
77
|
+
ignoreTypeOfDescribeName: true,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
// Required for e2e use cases
|
|
81
|
+
'jest/expect-expect': [0, { assertFunctionNames: ['expect', 'expectSaga'] }],
|
|
82
|
+
// Required for exception catching tests
|
|
83
|
+
'jest/no-conditional-expect': 'off',
|
|
84
|
+
'jest/no-disabled-tests': 'off',
|
|
85
|
+
'react-hooks/exhaustive-deps': [
|
|
86
|
+
'error',
|
|
87
|
+
{
|
|
88
|
+
// https://docs.swmansion.com/react-native-reanimated/docs/guides/web-support/
|
|
89
|
+
additionalHooks: '(useAnimatedStyle|useDerivedValue|useAnimatedProps)',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
'no-restricted-syntax': [
|
|
93
|
+
'error',
|
|
94
|
+
{
|
|
95
|
+
selector:
|
|
96
|
+
"CallExpression[callee.property.name='sendMessage'][callee.object.property.name='tabs'][callee.object.object.name='chrome']",
|
|
97
|
+
message:
|
|
98
|
+
'Please use a message channel from apps/extension/src/background/messagePassing/messageChannels.ts instead of chrome.tabs.sendMessage.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
selector:
|
|
102
|
+
"CallExpression[callee.property.name='sendMessage'][callee.object.property.name='runtime'][callee.object.object.name='chrome']",
|
|
103
|
+
message:
|
|
104
|
+
'Please use a message channel from apps/extension/src/background/messagePassing/messageChannels.ts instead of chrome.runtime.sendMessage.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
selector:
|
|
108
|
+
"CallExpression[callee.property.name='addListener'][callee.object.property.name='onMessage'][callee.object.object.property.name='runtime'][callee.object.object.object.name='chrome']",
|
|
109
|
+
message:
|
|
110
|
+
'Please use a message channel from apps/extension/src/background/messagePassing/messageChannels.ts instead of chrome.runtime.onMessage.addListener.',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
selector:
|
|
114
|
+
"CallExpression[callee.property.name='removeListener'][callee.object.property.name='onMessage'][callee.object.object.property.name='runtime'][callee.object.object.object.name='chrome']",
|
|
115
|
+
message:
|
|
116
|
+
'Please use a message channel from apps/extension/src/background/messagePassing/messageChannels.ts instead of chrome.runtime.onMessage.removeListener.',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
selector: "CallExpression[callee.object.name='z'][callee.property.name='any']",
|
|
120
|
+
message: 'Avoid using z.any() in favor of more precise custom types, unless absolutely necessary.',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
// React Plugin
|
|
124
|
+
// Overrides rules from @react-native-community:
|
|
125
|
+
// https://github.com/facebook/react-native/blob/3cf0291008dfeed4d967ebb95bdccbe2d52c5b81/pkgs/eslint-config-react-native-community/index.js#L287
|
|
126
|
+
'react/jsx-sort-props': [
|
|
127
|
+
2,
|
|
128
|
+
{
|
|
129
|
+
callbacksLast: true,
|
|
130
|
+
shorthandFirst: true,
|
|
131
|
+
ignoreCase: false,
|
|
132
|
+
noSortAlphabetically: true,
|
|
133
|
+
reservedFirst: true,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
// React-Native Plugin
|
|
137
|
+
// Overrides rules from @react-native-community:
|
|
138
|
+
// https://github.com/facebook/react-native/blob/3cf0291008dfeed4d967ebb95bdccbe2d52c5b81/pkgs/eslint-config-react-native-community/index.js#L313
|
|
139
|
+
'react-native/no-unused-styles': 'error',
|
|
140
|
+
'react-native/sort-styles': 'error',
|
|
141
|
+
|
|
142
|
+
// To be shared, requires notable fixing to share
|
|
143
|
+
'react/no-unstable-nested-components': 'error',
|
|
144
|
+
|
|
145
|
+
// Same but can't be shared for some reason
|
|
146
|
+
'react/react-in-jsx-scope': 'off',
|
|
147
|
+
'consistent-return': ['error', { treatUndefinedAsUnspecified: false }],
|
|
148
|
+
|
|
149
|
+
// Requires some investigation to move
|
|
150
|
+
// https://stackoverflow.com/questions/63961803/eslint-says-all-enums-in-typescript-app-are-already-declared-in-the-upper-scope
|
|
151
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
152
|
+
'@typescript-eslint/no-shadow': 'error',
|
|
153
|
+
|
|
154
|
+
// use throughout the app when importing devtools, or in test files
|
|
155
|
+
'@typescript-eslint/no-var-requires': 'off',
|
|
156
|
+
'@typescript-eslint/no-require-imports': 'off',
|
|
157
|
+
'max-params': ['error', { max: 2 }],
|
|
158
|
+
},
|
|
159
|
+
overrides: [
|
|
160
|
+
{
|
|
161
|
+
files: ['**/utils/haptics/**'],
|
|
162
|
+
rules: {
|
|
163
|
+
'@typescript-eslint/no-restricted-imports': [
|
|
164
|
+
'error',
|
|
165
|
+
{
|
|
166
|
+
paths: restrictedImports.paths.filter((rule) => rule.name !== 'expo-haptics'),
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
// enable these rules specifically for TypeScript files
|
|
173
|
+
files: ['*.ts', '*.mts', '*.cts', '*.tsx'],
|
|
174
|
+
rules: {
|
|
175
|
+
'@typescript-eslint/explicit-function-return-type': ['error', { allowedNames: ['useEffect'] }],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
// TypeScript rules for non-test files (can be a bit more strict)
|
|
180
|
+
files: ['*.ts', '*.mts', '*.cts', '*.tsx'],
|
|
181
|
+
excludedFiles: ['migrations.ts', './**/*.test.ts', './**/*.test.tsx', './test/**'],
|
|
182
|
+
rules: {
|
|
183
|
+
'@typescript-eslint/prefer-enum-initializers': 'error',
|
|
184
|
+
'@typescript-eslint/no-unsafe-return': 'error',
|
|
185
|
+
'@typescript-eslint/no-non-null-assertion': 'error',
|
|
186
|
+
'@typescript-eslint/explicit-function-return-type': 'warn',
|
|
187
|
+
'@typescript-eslint/no-empty-interface': 'warn',
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
// ignore return type in saga files given return types are unwieldy and tied
|
|
191
|
+
// to implementation details.
|
|
192
|
+
{
|
|
193
|
+
files: ['*saga*.ts', '*Saga.ts', 'handleDeepLink.ts'],
|
|
194
|
+
rules: {
|
|
195
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
// Typescript only files
|
|
199
|
+
{
|
|
200
|
+
files: ['./**/*.ts', './**/*.tsx'],
|
|
201
|
+
excludedFiles: ['./**/*.test.ts', './**/*.test.tsx'],
|
|
202
|
+
rules: {
|
|
203
|
+
// enforce saga imports from typed-redux-saga
|
|
204
|
+
'@jambit/typed-redux-saga/use-typed-effects': 'error',
|
|
205
|
+
'@jambit/typed-redux-saga/delegate-effects': 'error',
|
|
206
|
+
'no-console': 'error',
|
|
207
|
+
// React Native specific: Percentage transforms crash on Android
|
|
208
|
+
'local-rules/no-transform-percentage-strings': 'error',
|
|
209
|
+
'react/forbid-elements': [
|
|
210
|
+
'error',
|
|
211
|
+
{
|
|
212
|
+
forbid: [
|
|
213
|
+
{
|
|
214
|
+
element: 'div',
|
|
215
|
+
message: 'Please avoid using div when possible, even in web code! Use `Flex` or Fragments (`<>`).',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
// Allow more depth for testing files
|
|
223
|
+
{
|
|
224
|
+
files: ['./**/*.test.ts', './**/*.test.tsx'],
|
|
225
|
+
rules: {
|
|
226
|
+
'max-nested-callbacks': ['error', 4],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@luxfi/eslint-config",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lux ESLint config",
|
|
5
|
+
"repository": "https://github.com/Lux/eslint-config",
|
|
6
|
+
"homepage": "https://github.com/Lux/eslint-config#readme",
|
|
7
|
+
"bugs": "https://github.com/Lux/eslint-config/issues",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"lint": "nx lint eslint-config",
|
|
11
|
+
"lint:fix": "nx lint:fix eslint-config",
|
|
12
|
+
"test": "nx test eslint-config"
|
|
13
|
+
},
|
|
14
|
+
"nx": {
|
|
15
|
+
"includedScripts": []
|
|
16
|
+
},
|
|
17
|
+
"main": "base.js",
|
|
18
|
+
"files": ["restrictedImports.js", "base.js", "react.js", "native.js", "load.js", "lib.js", "biome-supported.js"],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@jambit/eslint-plugin-typed-redux-saga": "0.4.0",
|
|
21
|
+
"@react-native-community/eslint-config": "3.2.0",
|
|
22
|
+
"@rushstack/eslint-patch": "1.5.1",
|
|
23
|
+
"@typescript-eslint/eslint-plugin": "6.20.0",
|
|
24
|
+
"@typescript-eslint/parser": "6.20.0",
|
|
25
|
+
"@typescript-eslint/utils": "6.20.0",
|
|
26
|
+
"eslint-config-turbo": "2.5.6",
|
|
27
|
+
"eslint-import-resolver-typescript": "3.6.3",
|
|
28
|
+
"eslint-plugin-check-file": "2.8.0",
|
|
29
|
+
"eslint-plugin-detox": "1.0.0",
|
|
30
|
+
"eslint-plugin-ft-flow": "2.0.3",
|
|
31
|
+
"eslint-plugin-import": "2.27.5",
|
|
32
|
+
"eslint-plugin-jest": "27.9.0",
|
|
33
|
+
"eslint-plugin-local-rules": "3.0.2",
|
|
34
|
+
"eslint-plugin-no-relative-import-paths": "1.5.2",
|
|
35
|
+
"eslint-plugin-no-unsanitized": "4.0.1",
|
|
36
|
+
"eslint-plugin-react": "7.34.1",
|
|
37
|
+
"eslint-plugin-react-hooks": "4.6.0",
|
|
38
|
+
"eslint-plugin-react-native": "4.1.0",
|
|
39
|
+
"eslint-plugin-security": "1.5.0",
|
|
40
|
+
"eslint-plugin-spellcheck": "0.0.20",
|
|
41
|
+
"eslint-plugin-storybook": "0.8.0",
|
|
42
|
+
"eslint-plugin-unused-imports": "2.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"eslint": "8.57.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@babel/core": "7.26.0",
|
|
49
|
+
"@babel/preset-env": "7.26.0",
|
|
50
|
+
"@babel/preset-typescript": "7.26.0",
|
|
51
|
+
"@types/jest": "29.5.14",
|
|
52
|
+
"babel-jest": "29.7.0",
|
|
53
|
+
"eslint": "8.57.1",
|
|
54
|
+
"jest": "29.7.0",
|
|
55
|
+
"typescript": "5.8.3"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// Rules that should apply to all cases
|
|
2
|
+
const sharedRules = {
|
|
3
|
+
paths: [
|
|
4
|
+
{
|
|
5
|
+
name: '@hanzogui/core',
|
|
6
|
+
message: "Please import from '@hanzo/gui' directly to prevent mismatches.",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: '@uniswap/sdk-core',
|
|
10
|
+
importNames: ['ChainId'],
|
|
11
|
+
message: "Don't use ChainId from @lux/sdk-core. Use the UniverseChainId from universe/lux.",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'utilities/src/telemetry/trace/Trace',
|
|
15
|
+
message: "Please use the Trace in 'lux/src/features/telemetry/Trace' for app level usage!",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'utilities/src/telemetry/analytics/analytics',
|
|
19
|
+
message:
|
|
20
|
+
'Please only use this for initialization, tests, flushing, and internal usage. Otherwise use `pkgs/lux/src/features/telemetry`',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: '@uniswap/analytics',
|
|
24
|
+
importNames: ['sendAnalyticsEvent'],
|
|
25
|
+
message: "Please use the typed `sendAnalyticsEvent` in 'lux/src/features/telemetry/send'?",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'expo-localization',
|
|
29
|
+
message:
|
|
30
|
+
'Avoid using due to issue with unsupported locales. Use utilities/src/device/locales.ts getDeviceLocales instead',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'lux/src/features/dataApi/balances/balances',
|
|
34
|
+
importNames: ['usePortfolioValueModifiers'],
|
|
35
|
+
message:
|
|
36
|
+
'Use the wrapper hooks `usePortfolioTotalValue`, `useAccountListData` or `usePortfolioBalances` instead of `usePortfolioValueModifiers` directly.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'lux/src/features/dataApi/balances/balancesRest',
|
|
40
|
+
importNames: ['useRESTPortfolioTotalValue'],
|
|
41
|
+
message:
|
|
42
|
+
'Use the wrapper hooks `usePortfolioTotalValue`, `useAccountListData` or `usePortfolioBalances` instead of `useRESTPortfolioTotalValue` directly.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'i18next',
|
|
46
|
+
importNames: ['t'],
|
|
47
|
+
message:
|
|
48
|
+
'Please avoid direct imports of t, using `useTranslation` and `i18n.t` when absolutely needed outside of a React context',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'utilities/src/format/localeBased',
|
|
52
|
+
message: 'Use via `useLocalizationContext` instead.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'lux/src/features/fiatCurrency/conversion',
|
|
56
|
+
importNames: ['useFiatConverter'],
|
|
57
|
+
message: 'Use via `useLocalizationContext` instead.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'lux/src/features/language/formatter',
|
|
61
|
+
importNames: ['useLocalizedFormatter'],
|
|
62
|
+
message: 'Use via `useLocalizationContext` instead.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'lux/src/features/chains/hooks/useOrderedChainIds',
|
|
66
|
+
importNames: ['useOrderedChainIds'],
|
|
67
|
+
message: 'Use `useEnabledChains` instead, which returns the ordered chains that are currently enabled.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'ui/src/hooks/useDeviceInsets',
|
|
71
|
+
importNames: ['useDeviceInsets'],
|
|
72
|
+
message: 'Use `useAppInsets` instead.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'react-native-device-info',
|
|
76
|
+
importNames: ['getUniqueId'],
|
|
77
|
+
message: 'Not supported for web/extension, use `getUniqueId` from `utilities/src/device/getUniqueId` instead.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'lodash',
|
|
81
|
+
message:
|
|
82
|
+
"Use specific imports (e.g. `import isEqual from 'lodash/isEqual'`) to avoid pulling in all of lodash to web to keep bundle size down!",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'lux/src/features/chains/chainInfo',
|
|
86
|
+
importNames: ['UNIVERSE_CHAIN_INFO'],
|
|
87
|
+
message: 'Use useChainInfo or helpers in pkgs/lux/src/features/chains/utils.ts when possible!',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'lux/src/features/settings/selectors',
|
|
91
|
+
importNames: ['selectIsTestnetModeEnabled'],
|
|
92
|
+
message: 'Use `useEnabledChains` instead.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'api/src/clients/graphql/__generated__/react-hooks',
|
|
96
|
+
importNames: ['useAccountListQuery'],
|
|
97
|
+
message: 'Use `useAccountListData` instead.',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'api/src/clients/graphql/__generated__/react-hooks',
|
|
101
|
+
importNames: ['usePortfolioBalancesQuery'],
|
|
102
|
+
message: 'Use `usePortfolioBalances` instead.',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'wallet/src/data/apollo/usePersistedApolloClient',
|
|
106
|
+
importNames: ['usePersistedApolloClient'],
|
|
107
|
+
message:
|
|
108
|
+
"This hook should only be used once at the top level where the React app is initialized . You can use `import { useApolloClient } from '@apollo/client'` to get the default apollo client from the provider elsewhere in React. If you need access to apollo outside of React, you can use `import { apolloClientRef } from 'wallet/src/data/apollo/usePersistedApolloClient''`.",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'statsig-react',
|
|
112
|
+
message: 'Import from internal module lux/src/features/gating instead',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'wallet/src/components/ErrorBoundary/restart',
|
|
116
|
+
message: 'Use `wallet/src/components/ErrorBoundary/restartApp` instead.',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
patterns: [
|
|
120
|
+
{
|
|
121
|
+
group: ['ui/src/assets/icons/*.svg'],
|
|
122
|
+
message:
|
|
123
|
+
'Please do not import SVG files directly from `ui/src/assets/icons/*.svg`. Use generated icon components instead, e.g., `ui/src/components/icons/{iconName}`.',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Rules that should apply to native code only
|
|
129
|
+
const nativeRules = {
|
|
130
|
+
paths: [
|
|
131
|
+
// Shared rules
|
|
132
|
+
...sharedRules.paths,
|
|
133
|
+
// Should attempt sharing in the future
|
|
134
|
+
{
|
|
135
|
+
name: '@ethersproject',
|
|
136
|
+
message: "Please import from 'ethers' directly to support tree-shaking.",
|
|
137
|
+
},
|
|
138
|
+
// Native specific pkgs/restrictions
|
|
139
|
+
{
|
|
140
|
+
name: 'statsig-react-native',
|
|
141
|
+
message: 'Import from internal module lux/src/features/gating instead',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'react-native-safe-area-context',
|
|
145
|
+
importNames: ['useSafeAreaInsets'],
|
|
146
|
+
message: 'Use our internal `useAppInsets` hook instead.',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'react-native',
|
|
150
|
+
importNames: ['Switch'],
|
|
151
|
+
message: 'Use our custom Switch component instead.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'react-native',
|
|
155
|
+
importNames: ['Keyboard'],
|
|
156
|
+
message:
|
|
157
|
+
'Please use dismissNativeKeyboard() instead for dismissals. addListener is okay to ignore this import for!',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: '@gorhom/bottom-sheet',
|
|
161
|
+
importNames: ['BottomSheetTextInput'],
|
|
162
|
+
message: 'Use our internal `BottomSheetTextInput` wrapper from `/lux/src/components/modals/Modal`.',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'expo-haptics',
|
|
166
|
+
message:
|
|
167
|
+
"Use our internal `HapticFeedback` wrapper instead: `import { HapticFeedback } from 'pkgs/lux/src/features/settings/useHapticFeedback/types'`",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'react-router',
|
|
171
|
+
message: 'Do not import react-router in native code. Use react-navigation instead.',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
patterns: sharedRules.patterns,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const reactNativeRuleMessage =
|
|
178
|
+
"React Native modules should not be imported outside of .native.ts files unless they are only types (import type { ... }). If the file isn't used outside of native usage, add it to the excluded files in webPlatform.js."
|
|
179
|
+
|
|
180
|
+
const reactNative = {
|
|
181
|
+
patterns: [
|
|
182
|
+
{
|
|
183
|
+
group: [
|
|
184
|
+
'*react-native*',
|
|
185
|
+
// The following are allowed to be imported in cross-platform code.
|
|
186
|
+
'!react-native-reanimated',
|
|
187
|
+
'!react-native-image-colors',
|
|
188
|
+
'!@testing-library/react-native',
|
|
189
|
+
'!@react-native-community/netinfo',
|
|
190
|
+
'!react-native-localize',
|
|
191
|
+
],
|
|
192
|
+
allowTypeImports: true,
|
|
193
|
+
message: reactNativeRuleMessage,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Rules that should apply to any code that's run on the web (interface) platform
|
|
199
|
+
const webPlatformRules = {
|
|
200
|
+
// paths: [],
|
|
201
|
+
// patterns: [],
|
|
202
|
+
paths: [
|
|
203
|
+
...sharedRules.paths,
|
|
204
|
+
{
|
|
205
|
+
name: 'ethers',
|
|
206
|
+
message: "Please import from '@ethersproject/module' directly to support tree-shaking.",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'ui/src/components/icons',
|
|
210
|
+
message:
|
|
211
|
+
'Please import icons directly from their respective files, e.g. `ui/src/components/icons/SpecificIcon`. This is to avoid importing the entire icons folder when only some icons are needed, which increases bundle size',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'ui/src/components/modal/AdaptiveWebModal',
|
|
215
|
+
message:
|
|
216
|
+
'Please import Modal from `lux/src/components/modals/Modal` instead. Modal uses AdaptiveWebModal under the hood but has extra logic for handling animation, mounting, and dismounting.',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
patterns: [...sharedRules.patterns, ...reactNative.patterns],
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const extensionRules = {
|
|
223
|
+
paths: [
|
|
224
|
+
// Allow general icon path in extension
|
|
225
|
+
...webPlatformRules.paths.filter((p) => p.name !== 'ui/src/components/icons'),
|
|
226
|
+
],
|
|
227
|
+
patterns: [
|
|
228
|
+
// Remove react native rules for extension
|
|
229
|
+
...webPlatformRules.patterns.filter((p) => p.message !== reactNativeRuleMessage),
|
|
230
|
+
],
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Rules that should apply to the web interface only
|
|
234
|
+
const interfaceRules = {
|
|
235
|
+
paths: [
|
|
236
|
+
...webPlatformRules.paths,
|
|
237
|
+
{
|
|
238
|
+
name: '@playwright/test',
|
|
239
|
+
message: 'Import test and expect from playwright/fixtures instead.',
|
|
240
|
+
importNames: ['test', 'expect'],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'i18next',
|
|
244
|
+
importNames: ['i18n'],
|
|
245
|
+
message: 'Import from `lux/src/i18n` instead.',
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'styled-components',
|
|
249
|
+
message: 'Styled components is deprecated, please use Flex or styled from "ui/src" instead.',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'api/src/clients/graphql/__generated__/react-hooks',
|
|
253
|
+
importNames: ['useActivityWebQuery'],
|
|
254
|
+
message: 'Import cached/subscription-based activity hooks from `AssetActivityProvider` instead.',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: '@uniswap/smart-order-router',
|
|
258
|
+
message: 'Only import types, unless you are in the client-side SOR, to preserve lazy-loading.',
|
|
259
|
+
allowTypeImports: true,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'moment',
|
|
263
|
+
// tree-shaking for moment is not configured because it degrades performance - see craco.config.cjs.
|
|
264
|
+
message: 'moment is not configured for tree-shaking. If you use it, update the Webpack configuration.',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'react-helmet-async',
|
|
268
|
+
// default package's esm export is broken, but the explicit cjs export works.
|
|
269
|
+
message: `Import from 'react-helmet-async/lib/index' instead.`,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'zustand',
|
|
273
|
+
importNames: ['default'],
|
|
274
|
+
message: 'Default import from zustand is deprecated. Import `{ create }` instead.',
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'utilities/src/platform',
|
|
278
|
+
importNames: ['isIOS', 'isAndroid'],
|
|
279
|
+
message: 'Importing isIOS and isAndroid from platform is not allowed. Use isWebIOS and isWebAndroid instead.',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'wagmi',
|
|
283
|
+
importNames: ['useChainId', 'useAccount', 'useConnect', 'useDisconnect', 'useBlockNumber', 'useWatchBlockNumber'],
|
|
284
|
+
message:
|
|
285
|
+
'Import wrapped utilities from internal hooks instead: useAccount from `hooks/useAccount`, useConnect from `hooks/useConnect`, useDisconnect from `hooks/useDisconnect`, useBlockNumber from `hooks/useBlockNumber`.',
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
patterns: webPlatformRules.patterns,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Universal
|
|
292
|
+
exports.shared = sharedRules
|
|
293
|
+
|
|
294
|
+
// Platform
|
|
295
|
+
exports.native = nativeRules
|
|
296
|
+
exports.webPlatform = webPlatformRules
|
|
297
|
+
exports.reactNative = reactNative
|
|
298
|
+
|
|
299
|
+
// App Specific
|
|
300
|
+
exports.interface = interfaceRules
|
|
301
|
+
exports.extension = extensionRules
|