@blumintinc/eslint-plugin-blumint 1.12.6 → 1.14.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/README.md +171 -99
- package/lib/index.js +300 -17
- package/lib/rules/array-methods-this-context.js +20 -2
- package/lib/rules/avoid-utils-directory.js +11 -2
- package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
- package/lib/rules/consistent-callback-naming.js +1 -1
- package/lib/rules/dynamic-https-errors.d.ts +3 -1
- package/lib/rules/dynamic-https-errors.js +48 -11
- package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
- package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
- package/lib/rules/enforce-assert-throws.d.ts +3 -1
- package/lib/rules/enforce-assert-throws.js +493 -51
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
- package/lib/rules/enforce-callable-types.js +4 -4
- package/lib/rules/enforce-callback-memo.js +143 -18
- package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
- package/lib/rules/enforce-console-error.d.ts +3 -0
- package/lib/rules/enforce-console-error.js +278 -0
- package/lib/rules/enforce-css-media-queries.js +24 -46
- package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
- package/lib/rules/enforce-dynamic-file-naming.js +53 -28
- package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-early-destructuring.d.ts +2 -0
- package/lib/rules/enforce-early-destructuring.js +980 -0
- package/lib/rules/enforce-empty-object-check.d.ts +11 -0
- package/lib/rules/enforce-empty-object-check.js +502 -0
- package/lib/rules/enforce-exported-function-types.js +4 -4
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
- package/lib/rules/enforce-firestore-facade.js +349 -157
- package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
- package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.d.ts +4 -1
- package/lib/rules/enforce-global-constants.js +240 -12
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.d.ts +2 -1
- package/lib/rules/enforce-memoize-async.js +92 -27
- package/lib/rules/enforce-memoize-getters.d.ts +3 -0
- package/lib/rules/enforce-memoize-getters.js +147 -0
- package/lib/rules/enforce-microdiff.js +3 -2
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +9 -7
- package/lib/rules/enforce-props-argument-name.d.ts +2 -7
- package/lib/rules/enforce-props-argument-name.js +165 -151
- package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
- package/lib/rules/enforce-props-naming-consistency.js +145 -0
- package/lib/rules/enforce-querykey-ts.d.ts +9 -0
- package/lib/rules/enforce-querykey-ts.js +274 -0
- package/lib/rules/enforce-safe-stringify.js +1 -1
- package/lib/rules/enforce-serializable-params.js +3 -3
- package/lib/rules/enforce-singular-type-names.js +10 -6
- package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
- package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
- package/lib/rules/enforce-storage-context.d.ts +9 -0
- package/lib/rules/enforce-storage-context.js +646 -0
- package/lib/rules/enforce-timestamp-now.js +23 -7
- package/lib/rules/enforce-transform-memoization.d.ts +4 -0
- package/lib/rules/enforce-transform-memoization.js +416 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
- package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
- package/lib/rules/enforce-unique-cursor-headers.js +365 -0
- package/lib/rules/enforce-verb-noun-naming.js +90 -14
- package/lib/rules/ensure-pointer-events-none.js +28 -2
- package/lib/rules/export-if-in-doubt.js +27 -4
- package/lib/rules/extract-global-constants.js +92 -53
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
- package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
- package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
- package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- package/lib/rules/generic-starts-with-t.js +8 -2
- package/lib/rules/global-const-style.js +50 -19
- package/lib/rules/jsdoc-above-field.d.ts +11 -0
- package/lib/rules/jsdoc-above-field.js +208 -0
- package/lib/rules/key-only-outermost-element.js +21 -3
- package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
- package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
- package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
- package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
- package/lib/rules/memo-nested-react-components.d.ts +8 -0
- package/lib/rules/memo-nested-react-components.js +410 -0
- package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
- package/lib/rules/memoize-root-level-hocs.js +242 -0
- package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
- package/lib/rules/no-always-true-false-conditions.js +68 -8
- package/lib/rules/no-array-length-in-deps.d.ts +12 -0
- package/lib/rules/no-array-length-in-deps.js +314 -0
- package/lib/rules/no-async-array-filter.js +5 -2
- package/lib/rules/no-async-foreach.js +210 -12
- package/lib/rules/no-circular-references.js +18 -17
- package/lib/rules/no-class-instance-destructuring.js +55 -12
- package/lib/rules/no-complex-cloud-params.js +22 -5
- package/lib/rules/no-compositing-layer-props.js +6 -4
- package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
- package/lib/rules/no-console-error.d.ts +9 -0
- package/lib/rules/no-console-error.js +525 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
- package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
- package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
- package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
- package/lib/rules/no-entire-object-hook-deps.js +320 -68
- package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
- package/lib/rules/no-excessive-parent-chain.js +273 -0
- package/lib/rules/no-explicit-return-type.d.ts +2 -1
- package/lib/rules/no-explicit-return-type.js +215 -26
- package/lib/rules/no-filter-without-return.js +5 -1
- package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
- package/lib/rules/no-firestore-jest-mock.js +126 -8
- package/lib/rules/no-firestore-object-arrays.js +398 -28
- package/lib/rules/no-handler-suffix.d.ts +12 -0
- package/lib/rules/no-handler-suffix.js +305 -0
- package/lib/rules/no-hungarian.js +14 -14
- package/lib/rules/no-inline-component-prop.d.ts +10 -0
- package/lib/rules/no-inline-component-prop.js +456 -0
- package/lib/rules/no-jsx-in-hooks.js +6 -1
- package/lib/rules/no-jsx-whitespace-literal.js +8 -2
- package/lib/rules/no-margin-properties.d.ts +7 -1
- package/lib/rules/no-margin-properties.js +22 -9
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
- package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
- package/lib/rules/no-misused-switch-case.js +22 -1
- package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
- package/lib/rules/no-mixed-firestore-transactions.js +296 -34
- package/lib/rules/no-mock-firebase-admin.js +5 -2
- package/lib/rules/no-object-values-on-strings.js +10 -2
- package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
- package/lib/rules/no-passthrough-getters.d.ts +4 -0
- package/lib/rules/no-passthrough-getters.js +206 -0
- package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
- package/lib/rules/no-redundant-annotation-assertion.js +402 -0
- package/lib/rules/no-redundant-param-types.js +16 -6
- package/lib/rules/no-redundant-this-params.d.ts +3 -0
- package/lib/rules/no-redundant-this-params.js +459 -0
- package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
- package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
- package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
- package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
- package/lib/rules/no-restricted-properties-fix.js +173 -0
- package/lib/rules/no-separate-loading-state.d.ts +8 -0
- package/lib/rules/no-separate-loading-state.js +133 -0
- package/lib/rules/no-stale-state-across-await.d.ts +1 -0
- package/lib/rules/no-stale-state-across-await.js +185 -0
- package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
- package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
- package/lib/rules/no-type-assertion-returns.js +49 -49
- package/lib/rules/no-undefined-null-passthrough.js +217 -40
- package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
- package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
- package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
- package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
- package/lib/rules/no-unnecessary-destructuring.js +14 -4
- package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unsafe-firestore-spread.js +8 -5
- package/lib/rules/no-unused-props.d.ts +5 -2
- package/lib/rules/no-unused-props.js +451 -112
- package/lib/rules/no-unused-usestate.js +6 -2
- package/lib/rules/no-useless-fragment.js +28 -2
- package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
- package/lib/rules/no-useless-usememo-primitives.js +393 -0
- package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
- package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
- package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
- package/lib/rules/omit-index-html.d.ts +2 -1
- package/lib/rules/omit-index-html.js +62 -7
- package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
- package/lib/rules/optimize-object-boolean-conditions.js +236 -0
- package/lib/rules/parallelize-async-operations.d.ts +8 -0
- package/lib/rules/parallelize-async-operations.js +394 -0
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +228 -35
- package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
- package/lib/rules/prefer-clone-deep.js +3 -3
- package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
- package/lib/rules/prefer-destructuring-no-class.js +244 -59
- package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
- package/lib/rules/prefer-docsetter-setall.js +243 -0
- package/lib/rules/prefer-document-flattening.d.ts +4 -0
- package/lib/rules/prefer-document-flattening.js +239 -0
- package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
- package/lib/rules/prefer-fragment-component.js +4 -4
- package/lib/rules/prefer-fragment-shorthand.js +2 -1
- package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
- package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
- package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
- package/lib/rules/prefer-global-router-state-key.js +305 -31
- package/lib/rules/prefer-memoized-props.d.ts +3 -0
- package/lib/rules/prefer-memoized-props.js +445 -0
- package/lib/rules/prefer-next-dynamic.d.ts +9 -0
- package/lib/rules/prefer-next-dynamic.js +357 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
- package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
- package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
- package/lib/rules/prefer-params-over-parent-id.js +277 -0
- package/lib/rules/prefer-settings-object.js +23 -15
- package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
- package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
- package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
- package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
- package/lib/rules/prevent-children-clobber.d.ts +2 -0
- package/lib/rules/prevent-children-clobber.js +536 -0
- package/lib/rules/react-memoize-literals.d.ts +4 -0
- package/lib/rules/react-memoize-literals.js +495 -0
- package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
- package/lib/rules/react-usememo-should-be-component.js +144 -78
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-hooks-default-params.js +17 -16
- package/lib/rules/require-https-error-cause.d.ts +4 -0
- package/lib/rules/require-https-error-cause.js +136 -0
- package/lib/rules/require-https-error.js +43 -21
- package/lib/rules/require-image-optimized.js +2 -2
- package/lib/rules/require-memo.js +12 -2
- package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
- package/lib/rules/require-memoize-jsx-returners.js +485 -0
- package/lib/rules/require-usememo-object-literals.js +9 -4
- package/lib/rules/semantic-function-prefixes.js +38 -18
- package/lib/rules/sync-onwrite-name-func.js +5 -1
- package/lib/rules/test-file-location-enforcement.d.ts +1 -0
- package/lib/rules/test-file-location-enforcement.js +57 -0
- package/lib/rules/use-custom-link.js +6 -1
- package/lib/rules/use-custom-router.js +6 -1
- package/lib/rules/use-latest-callback.d.ts +3 -0
- package/lib/rules/use-latest-callback.js +271 -0
- package/lib/rules/vertically-group-related-functions.d.ts +16 -0
- package/lib/rules/vertically-group-related-functions.js +480 -0
- package/lib/utils/ASTHelpers.d.ts +13 -1
- package/lib/utils/ASTHelpers.js +34 -0
- package/lib/utils/createRule.js +1 -1
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
- package/lib/utils/ruleTester.d.ts +1 -0
- package/lib/utils/ruleTester.js +4 -1
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +23 -14
- package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
|
@@ -24,17 +24,43 @@ const SUGGESTED_ALTERNATIVES = {
|
|
|
24
24
|
process: ['transform', 'sanitize', 'compute'],
|
|
25
25
|
do: ['execute', 'perform', 'apply'],
|
|
26
26
|
};
|
|
27
|
+
function extractFirstWord(name) {
|
|
28
|
+
let firstWord = name;
|
|
29
|
+
for (let i = 1; i < name.length; i++) {
|
|
30
|
+
const currentChar = name[i];
|
|
31
|
+
const prevChar = name[i - 1];
|
|
32
|
+
// lowercase -> Uppercase (camel/Pascal boundary)
|
|
33
|
+
if (prevChar >= 'a' &&
|
|
34
|
+
prevChar <= 'z' &&
|
|
35
|
+
currentChar >= 'A' &&
|
|
36
|
+
currentChar <= 'Z') {
|
|
37
|
+
firstWord = name.substring(0, i);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
// UPPERCASE acronym -> lowercase word start (e.g., XML|Http)
|
|
41
|
+
if (prevChar >= 'A' &&
|
|
42
|
+
prevChar <= 'Z' &&
|
|
43
|
+
currentChar >= 'a' &&
|
|
44
|
+
currentChar <= 'z') {
|
|
45
|
+
if (i > 1) {
|
|
46
|
+
firstWord = name.substring(0, i - 1);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return firstWord;
|
|
52
|
+
}
|
|
27
53
|
exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
28
54
|
name: 'semantic-function-prefixes',
|
|
29
55
|
meta: {
|
|
30
56
|
type: 'suggestion',
|
|
31
57
|
docs: {
|
|
32
|
-
description: '
|
|
58
|
+
description: 'Require semantic function prefixes instead of generic verbs so callers know whether a function fetches data, transforms input, or mutates state',
|
|
33
59
|
recommended: 'error',
|
|
34
60
|
},
|
|
35
61
|
schema: [],
|
|
36
62
|
messages: {
|
|
37
|
-
avoidGenericPrefix: '
|
|
63
|
+
avoidGenericPrefix: 'Function "{{functionName}}" starts with the generic prefix "{{prefix}}", which hides whether it fetches remote data, transforms input, or mutates state. Use a semantic verb such as {{alternatives}} to describe the operation and set caller expectations.',
|
|
38
64
|
},
|
|
39
65
|
},
|
|
40
66
|
defaultOptions: [],
|
|
@@ -54,20 +80,17 @@ exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
|
54
80
|
if (NEXTJS_DATA_FUNCTIONS.has(methodName))
|
|
55
81
|
return;
|
|
56
82
|
// Extract first word from PascalCase/camelCase
|
|
57
|
-
|
|
58
|
-
for (let i = 1; i < methodName.length; i++) {
|
|
59
|
-
if (methodName[i] >= 'A' && methodName[i] <= 'Z') {
|
|
60
|
-
firstWord = methodName.substring(0, i);
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
83
|
+
const firstWord = extractFirstWord(methodName);
|
|
64
84
|
// Check for disallowed prefixes
|
|
85
|
+
// Only flag if the disallowed word is used as a prefix (not the entire name)
|
|
65
86
|
for (const prefix of DISALLOWED_PREFIXES) {
|
|
66
|
-
if (firstWord.toLowerCase() === prefix.toLowerCase()
|
|
87
|
+
if (firstWord.toLowerCase() === prefix.toLowerCase() &&
|
|
88
|
+
firstWord.length < methodName.length) {
|
|
67
89
|
context.report({
|
|
68
90
|
node: node.key,
|
|
69
91
|
messageId: 'avoidGenericPrefix',
|
|
70
92
|
data: {
|
|
93
|
+
functionName: methodName,
|
|
71
94
|
prefix,
|
|
72
95
|
alternatives: SUGGESTED_ALTERNATIVES[prefix].join(', '),
|
|
73
96
|
},
|
|
@@ -99,20 +122,17 @@ exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
|
99
122
|
if (NEXTJS_DATA_FUNCTIONS.has(functionName))
|
|
100
123
|
return;
|
|
101
124
|
// Extract first word from PascalCase/camelCase
|
|
102
|
-
|
|
103
|
-
for (let i = 1; i < functionName.length; i++) {
|
|
104
|
-
if (functionName[i] >= 'A' && functionName[i] <= 'Z') {
|
|
105
|
-
firstWord = functionName.substring(0, i);
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
125
|
+
const firstWord = extractFirstWord(functionName);
|
|
109
126
|
// Check for disallowed prefixes
|
|
127
|
+
// Only flag if the disallowed word is used as a prefix (not the entire name)
|
|
110
128
|
for (const prefix of DISALLOWED_PREFIXES) {
|
|
111
|
-
if (firstWord.toLowerCase() === prefix.toLowerCase()
|
|
129
|
+
if (firstWord.toLowerCase() === prefix.toLowerCase() &&
|
|
130
|
+
firstWord.length < functionName.length) {
|
|
112
131
|
context.report({
|
|
113
132
|
node: node.id || node,
|
|
114
133
|
messageId: 'avoidGenericPrefix',
|
|
115
134
|
data: {
|
|
135
|
+
functionName,
|
|
116
136
|
prefix,
|
|
117
137
|
alternatives: SUGGESTED_ALTERNATIVES[prefix].join(', '),
|
|
118
138
|
},
|
|
@@ -14,7 +14,7 @@ exports.syncOnwriteNameFunc = (0, createRule_1.createRule)({
|
|
|
14
14
|
fixable: 'code',
|
|
15
15
|
schema: [],
|
|
16
16
|
messages: {
|
|
17
|
-
mismatchedName: 'The name field
|
|
17
|
+
mismatchedName: 'OnWrite handler name "{{nameValue}}" does not match func reference "{{funcName}}". The name field is what gets registered for deploys, logs, and alerts, so a mismatch hides which function is actually running. Rename the "name" value to "{{funcName}}" or point "func" to a function named "{{nameValue}}" so the trigger label and implementation stay in sync.',
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
defaultOptions: [],
|
|
@@ -68,6 +68,10 @@ exports.syncOnwriteNameFunc = (0, createRule_1.createRule)({
|
|
|
68
68
|
context.report({
|
|
69
69
|
node: nameProperty,
|
|
70
70
|
messageId: 'mismatchedName',
|
|
71
|
+
data: {
|
|
72
|
+
nameValue,
|
|
73
|
+
funcName,
|
|
74
|
+
},
|
|
71
75
|
fix(fixer) {
|
|
72
76
|
return fixer.replaceText(value, `'${funcName}'`);
|
|
73
77
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const testFileLocationEnforcement: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"misplacedTestFile", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.testFileLocationEnforcement = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const createRule_1 = require("../utils/createRule");
|
|
10
|
+
const TEST_FILE_PATTERN = /\.test\.tsx?$/i;
|
|
11
|
+
const SUPPORTED_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
12
|
+
exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
13
|
+
name: 'test-file-location-enforcement',
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'suggestion',
|
|
16
|
+
docs: {
|
|
17
|
+
description: 'Enforce colocating *.test.ts or *.test.tsx files with the code they cover.',
|
|
18
|
+
recommended: 'error',
|
|
19
|
+
},
|
|
20
|
+
schema: [],
|
|
21
|
+
messages: {
|
|
22
|
+
misplacedTestFile: 'Test file "{{testFile}}" is not colocated with its subject. Keep tests in the same directory as {{expectedNames}} so refactors move code and coverage together and engineers can find the implementation without searching separate test folders.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [],
|
|
26
|
+
create(context) {
|
|
27
|
+
return {
|
|
28
|
+
Program(node) {
|
|
29
|
+
const filename = context.getFilename();
|
|
30
|
+
if (filename === '<input>' ||
|
|
31
|
+
filename === '<text>' ||
|
|
32
|
+
filename.includes('node_modules') ||
|
|
33
|
+
!TEST_FILE_PATTERN.test(filename)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const directory = path_1.default.dirname(filename);
|
|
37
|
+
const testFileName = path_1.default.basename(filename);
|
|
38
|
+
const baseName = testFileName.replace(TEST_FILE_PATTERN, '');
|
|
39
|
+
const candidates = SUPPORTED_EXTENSIONS.map((extension) => path_1.default.join(directory, `${baseName}${extension}`));
|
|
40
|
+
const hasSibling = candidates.some((candidate) => fs_1.default.existsSync(candidate));
|
|
41
|
+
if (hasSibling) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const relativePath = path_1.default.isAbsolute(filename)
|
|
45
|
+
? path_1.default.relative(process.cwd(), filename) || filename
|
|
46
|
+
: filename;
|
|
47
|
+
const expectedNames = SUPPORTED_EXTENSIONS.map((extension) => `"${baseName}${extension}"`).join(' or ');
|
|
48
|
+
context.report({
|
|
49
|
+
node,
|
|
50
|
+
messageId: 'misplacedTestFile',
|
|
51
|
+
data: { testFile: relativePath, expectedNames },
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=test-file-location-enforcement.js.map
|
|
@@ -13,7 +13,7 @@ exports.useCustomLink = (0, createRule_1.createRule)({
|
|
|
13
13
|
fixable: 'code',
|
|
14
14
|
schema: [],
|
|
15
15
|
messages: {
|
|
16
|
-
useCustomLink: 'Import
|
|
16
|
+
useCustomLink: 'Import "{{localName}}" from src/components/Link instead of next/link. The custom Link wraps Next.js navigation with design system defaults and analytics hooks; importing next/link bypasses those wrappers and leads to inconsistent styling and missing instrumentation. Replace the import source with src/components/Link so routing uses the shared wrapper.',
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
defaultOptions: [],
|
|
@@ -30,6 +30,11 @@ exports.useCustomLink = (0, createRule_1.createRule)({
|
|
|
30
30
|
context.report({
|
|
31
31
|
node,
|
|
32
32
|
messageId: 'useCustomLink',
|
|
33
|
+
data: {
|
|
34
|
+
localName: defaultSpecifier?.local?.name ||
|
|
35
|
+
defaultAsSpecifier?.local?.name ||
|
|
36
|
+
'Link',
|
|
37
|
+
},
|
|
33
38
|
fix(fixer) {
|
|
34
39
|
// Get the local name of the imported Link component
|
|
35
40
|
const localName = defaultSpecifier?.local?.name ||
|
|
@@ -14,7 +14,7 @@ exports.useCustomRouter = (0, createRule_1.createRule)({
|
|
|
14
14
|
fixable: 'code',
|
|
15
15
|
schema: [],
|
|
16
16
|
messages: {
|
|
17
|
-
useCustomRouter: 'Import
|
|
17
|
+
useCustomRouter: 'useRouter import "{{imports}}" comes from next/router, which bypasses the app-specific hook that applies auth checks, analytics, and redirect helpers. Import it from src/hooks/routing/useRouter so routing code stays consistent with the rest of the app.',
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
defaultOptions: [],
|
|
@@ -29,6 +29,11 @@ exports.useCustomRouter = (0, createRule_1.createRule)({
|
|
|
29
29
|
context.report({
|
|
30
30
|
node,
|
|
31
31
|
messageId: 'useCustomRouter',
|
|
32
|
+
data: {
|
|
33
|
+
imports: specifiers
|
|
34
|
+
.map((specifier) => specifier.local.name)
|
|
35
|
+
.join(', '),
|
|
36
|
+
},
|
|
32
37
|
fix(fixer) {
|
|
33
38
|
// If there are other imports from next/router, keep them
|
|
34
39
|
const otherSpecifiers = node.specifiers.filter((specifier) => specifier.type !== utils_1.AST_NODE_TYPES.ImportSpecifier ||
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useLatestCallback = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'use-latest-callback',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce using useLatestCallback from use-latest-callback instead of React useCallback',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
useLatestCallback: 'Replace {{currentHook}} with {{recommendedHook}} from "use-latest-callback" so the callback keeps a stable reference while still reading the latest props/state. useCallback recreates functions whenever dependencies change, which can trigger needless renders and stale closures. Drop the dependency array when switching to {{recommendedHook}}.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
const filename = context.getFilename();
|
|
23
|
+
if (filename.includes('/node_modules/')) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
// Track if the file already has a useLatestCallback import
|
|
27
|
+
let hasUseLatestCallbackImport = false;
|
|
28
|
+
let useLatestCallbackImportName = 'useLatestCallback';
|
|
29
|
+
// Track if any useCallback calls should be replaced
|
|
30
|
+
let hasNonJsxUseCallbacks = false;
|
|
31
|
+
const useCallbackLocalNames = new Set();
|
|
32
|
+
const reactNamespaceNames = new Set();
|
|
33
|
+
const reactDefaultLikeNames = new Set();
|
|
34
|
+
let hasReactMemberUseCallback = false;
|
|
35
|
+
const isJSXLikeReturn = (node) => {
|
|
36
|
+
if (!node)
|
|
37
|
+
return false;
|
|
38
|
+
if (node.type === utils_1.AST_NODE_TYPES.JSXElement ||
|
|
39
|
+
node.type === utils_1.AST_NODE_TYPES.JSXFragment ||
|
|
40
|
+
node.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (node.type === 'ParenthesizedExpression') {
|
|
44
|
+
return isJSXLikeReturn(node.expression);
|
|
45
|
+
}
|
|
46
|
+
if (node.type === utils_1.AST_NODE_TYPES.SequenceExpression) {
|
|
47
|
+
return isJSXLikeReturn(node.expressions[node.expressions.length - 1]);
|
|
48
|
+
}
|
|
49
|
+
if (node.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
50
|
+
return node.elements.some((el) => isJSXLikeReturn(el && el.type === utils_1.AST_NODE_TYPES.SpreadElement
|
|
51
|
+
? el.argument
|
|
52
|
+
: el));
|
|
53
|
+
}
|
|
54
|
+
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
55
|
+
return (isJSXLikeReturn(node.consequent) || isJSXLikeReturn(node.alternate));
|
|
56
|
+
}
|
|
57
|
+
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
|
|
58
|
+
return isJSXLikeReturn(node.left) || isJSXLikeReturn(node.right);
|
|
59
|
+
}
|
|
60
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
61
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
62
|
+
node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
63
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
64
|
+
reactNamespaceNames.has(node.callee.object.name) &&
|
|
65
|
+
node.callee.property.name === 'createElement') {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
// First pass: check imports
|
|
73
|
+
ImportDeclaration(node) {
|
|
74
|
+
if (node.importKind && node.importKind !== 'value')
|
|
75
|
+
return;
|
|
76
|
+
if (node.source.value === 'use-latest-callback') {
|
|
77
|
+
// Check if useLatestCallback is imported
|
|
78
|
+
const specifiers = node.specifiers.filter((specifier) => {
|
|
79
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
80
|
+
return (specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
81
|
+
specifier.imported.name === 'useLatestCallback');
|
|
82
|
+
}
|
|
83
|
+
return (specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
84
|
+
specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
|
|
85
|
+
});
|
|
86
|
+
if (specifiers.length > 0) {
|
|
87
|
+
hasUseLatestCallbackImport = true;
|
|
88
|
+
// Get the local name of the import (in case it's renamed)
|
|
89
|
+
if (specifiers[0].type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
90
|
+
useLatestCallbackImportName = specifiers[0].local.name;
|
|
91
|
+
}
|
|
92
|
+
else if (specifiers[0].type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) {
|
|
93
|
+
useLatestCallbackImportName = specifiers[0].local.name;
|
|
94
|
+
}
|
|
95
|
+
else if (specifiers[0].type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
96
|
+
useLatestCallbackImportName = `${specifiers[0].local.name}.useLatestCallback`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (node.source.value === 'react') {
|
|
101
|
+
// Check if useCallback is imported from React
|
|
102
|
+
const useCallbackSpecifiers = node.specifiers.filter((specifier) => {
|
|
103
|
+
return (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
104
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
105
|
+
specifier.imported.name === 'useCallback');
|
|
106
|
+
});
|
|
107
|
+
node.specifiers.forEach((specifier) => {
|
|
108
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
109
|
+
specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
110
|
+
reactNamespaceNames.add(specifier.local.name);
|
|
111
|
+
reactDefaultLikeNames.add(specifier.local.name);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
if (useCallbackSpecifiers.length > 0) {
|
|
115
|
+
useCallbackSpecifiers.forEach((spec) => {
|
|
116
|
+
useCallbackLocalNames.add(spec.local.name);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
// Check for useCallback usage and identify JSX-returning callbacks
|
|
122
|
+
CallExpression(node) {
|
|
123
|
+
const isUseCallbackIdentifierCall = node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
124
|
+
useCallbackLocalNames.has(node.callee.name);
|
|
125
|
+
const isUseCallbackMemberCall = node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
126
|
+
node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
127
|
+
reactDefaultLikeNames.has(node.callee.object.name) &&
|
|
128
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
129
|
+
node.callee.property.name === 'useCallback';
|
|
130
|
+
if ((isUseCallbackIdentifierCall || isUseCallbackMemberCall) &&
|
|
131
|
+
node.arguments.length >= 1) {
|
|
132
|
+
if (isUseCallbackMemberCall) {
|
|
133
|
+
hasReactMemberUseCallback = true;
|
|
134
|
+
}
|
|
135
|
+
// Check if this is a JSX-returning callback (edge case where we shouldn't replace)
|
|
136
|
+
const callback = node.arguments[0];
|
|
137
|
+
let isJsxReturning = false;
|
|
138
|
+
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
139
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
140
|
+
callback.body) {
|
|
141
|
+
// For arrow functions with implicit return
|
|
142
|
+
if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
143
|
+
if (isJSXLikeReturn(callback.body)) {
|
|
144
|
+
isJsxReturning = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// For functions with block body - check all return statements
|
|
148
|
+
else if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
149
|
+
const returnStatements = callback.body.body.filter((statement) => statement.type === utils_1.AST_NODE_TYPES.ReturnStatement);
|
|
150
|
+
// If there are return statements, check if any return JSX
|
|
151
|
+
if (returnStatements.length > 0) {
|
|
152
|
+
isJsxReturning = returnStatements.some((returnStatement) => returnStatement.argument &&
|
|
153
|
+
isJSXLikeReturn(returnStatement.argument));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!isJsxReturning) {
|
|
158
|
+
hasNonJsxUseCallbacks = true;
|
|
159
|
+
const currentCallbackName = node.callee.type === utils_1.AST_NODE_TYPES.Identifier
|
|
160
|
+
? node.callee.name
|
|
161
|
+
: node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
162
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier
|
|
163
|
+
? node.callee.property.name
|
|
164
|
+
: 'useCallback';
|
|
165
|
+
const replacementName = hasUseLatestCallbackImport
|
|
166
|
+
? useLatestCallbackImportName
|
|
167
|
+
: currentCallbackName === 'useCallback'
|
|
168
|
+
? 'useLatestCallback'
|
|
169
|
+
: currentCallbackName;
|
|
170
|
+
// Report the useCallback call for replacement
|
|
171
|
+
context.report({
|
|
172
|
+
node,
|
|
173
|
+
messageId: 'useLatestCallback',
|
|
174
|
+
data: {
|
|
175
|
+
currentHook: currentCallbackName,
|
|
176
|
+
recommendedHook: replacementName,
|
|
177
|
+
},
|
|
178
|
+
fix(fixer) {
|
|
179
|
+
const sourceCode = context.sourceCode;
|
|
180
|
+
const callbackText = sourceCode.getText(node.arguments[0]);
|
|
181
|
+
const typeParams = node.typeParameters
|
|
182
|
+
? sourceCode.getText(node.typeParameters)
|
|
183
|
+
: '';
|
|
184
|
+
// Replace useCallback with useLatestCallback and remove the dependency array
|
|
185
|
+
return fixer.replaceText(node, `${replacementName}${typeParams}(${callbackText})`);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
// Final pass: check for useCallback import from react (only if there are non-JSX callbacks)
|
|
192
|
+
'Program:exit'() {
|
|
193
|
+
if (!hasNonJsxUseCallbacks) {
|
|
194
|
+
return; // Don't modify imports if all useCallback calls return JSX
|
|
195
|
+
}
|
|
196
|
+
const sourceCode = context.sourceCode;
|
|
197
|
+
const program = sourceCode.ast;
|
|
198
|
+
for (const statement of program.body) {
|
|
199
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
|
|
200
|
+
statement.source.value === 'react') {
|
|
201
|
+
if (statement.importKind && statement.importKind !== 'value') {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const specifiers = statement.specifiers.filter((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
205
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
206
|
+
specifier.imported.name === 'useCallback');
|
|
207
|
+
if (specifiers.length > 0 || hasReactMemberUseCallback) {
|
|
208
|
+
const useCallbackLocalName = specifiers[0]?.local.name ?? 'useCallback';
|
|
209
|
+
const recommendedHook = hasUseLatestCallbackImport
|
|
210
|
+
? useLatestCallbackImportName
|
|
211
|
+
: useCallbackLocalName === 'useCallback'
|
|
212
|
+
? 'useLatestCallback'
|
|
213
|
+
: useCallbackLocalName;
|
|
214
|
+
context.report({
|
|
215
|
+
node: statement,
|
|
216
|
+
messageId: 'useLatestCallback',
|
|
217
|
+
data: {
|
|
218
|
+
currentHook: useCallbackLocalName,
|
|
219
|
+
recommendedHook,
|
|
220
|
+
},
|
|
221
|
+
fix(fixer) {
|
|
222
|
+
const importText = `import ${useCallbackLocalName === 'useCallback'
|
|
223
|
+
? 'useLatestCallback'
|
|
224
|
+
: useCallbackLocalName} from 'use-latest-callback';`;
|
|
225
|
+
if (specifiers.length === 0) {
|
|
226
|
+
if (hasUseLatestCallbackImport)
|
|
227
|
+
return null;
|
|
228
|
+
return fixer.insertTextBefore(statement, `${importText}\n`);
|
|
229
|
+
}
|
|
230
|
+
const defaultOrNamespace = statement.specifiers.find((s) => s.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
231
|
+
s.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
|
|
232
|
+
const remainingNamed = statement.specifiers.filter((s) => s.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
233
|
+
s.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
234
|
+
s.imported.name !== 'useCallback');
|
|
235
|
+
const prefix = statement.importKind && statement.importKind !== 'value'
|
|
236
|
+
? 'import type'
|
|
237
|
+
: 'import';
|
|
238
|
+
if (remainingNamed.length === 0 && !defaultOrNamespace) {
|
|
239
|
+
if (!hasUseLatestCallbackImport) {
|
|
240
|
+
return fixer.replaceText(statement, importText);
|
|
241
|
+
}
|
|
242
|
+
return fixer.remove(statement);
|
|
243
|
+
}
|
|
244
|
+
const parts = [];
|
|
245
|
+
if (defaultOrNamespace) {
|
|
246
|
+
parts.push(sourceCode.getText(defaultOrNamespace));
|
|
247
|
+
}
|
|
248
|
+
if (remainingNamed.length > 0) {
|
|
249
|
+
parts.push(`{ ${remainingNamed
|
|
250
|
+
.map((s) => sourceCode.getText(s))
|
|
251
|
+
.join(', ')} }`);
|
|
252
|
+
}
|
|
253
|
+
const replacement = `${prefix} ${parts.join(', ')} from 'react';`;
|
|
254
|
+
const fixes = [];
|
|
255
|
+
if (!hasUseLatestCallbackImport) {
|
|
256
|
+
fixes.push(fixer.insertTextBefore(statement, `${importText}\n`));
|
|
257
|
+
}
|
|
258
|
+
fixes.push(fixer.replaceText(statement, replacement));
|
|
259
|
+
return fixes;
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
exports.default = exports.useLatestCallback;
|
|
271
|
+
//# sourceMappingURL=use-latest-callback.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type GroupName = 'event-handlers' | 'utilities' | 'other';
|
|
3
|
+
type DependencyDirection = 'callers-first' | 'callees-first';
|
|
4
|
+
type ExportPlacement = 'top' | 'bottom' | 'ignore';
|
|
5
|
+
type Options = [
|
|
6
|
+
Partial<{
|
|
7
|
+
exportPlacement: ExportPlacement;
|
|
8
|
+
dependencyDirection: DependencyDirection;
|
|
9
|
+
groupOrder: GroupName[];
|
|
10
|
+
eventHandlerPattern: string;
|
|
11
|
+
utilityPattern: string;
|
|
12
|
+
}>
|
|
13
|
+
];
|
|
14
|
+
type MessageIds = 'misorderedFunction';
|
|
15
|
+
export declare const verticallyGroupRelatedFunctions: TSESLint.RuleModule<MessageIds, Options>;
|
|
16
|
+
export {};
|