@blumintinc/eslint-plugin-blumint 1.20.170 → 1.20.172
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/lib/index.js +1 -1
- package/lib/rules/enforce-assert-safe-object-key.js +78 -3
- package/lib/rules/enforce-dynamic-firebase-imports.js +587 -52
- package/lib/rules/enforce-early-destructuring.js +181 -51
- package/lib/rules/enforce-empty-object-check.d.ts +1 -0
- package/lib/rules/enforce-empty-object-check.js +686 -1
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +138 -12
- package/lib/rules/enforce-firestore-doc-ref-generic.js +101 -21
- package/lib/rules/enforce-firestore-set-merge.js +220 -20
- package/lib/rules/enforce-m3-sentence-case.js +39 -5
- package/lib/rules/enforce-memoize-async.js +12 -0
- package/lib/rules/enforce-memoize-getters.js +20 -0
- package/lib/rules/enforce-stable-hash-spread-props.js +296 -13
- package/lib/rules/ensure-pointer-events-none.d.ts +11 -1
- package/lib/rules/ensure-pointer-events-none.js +231 -14
- package/lib/rules/fast-deep-equal-over-microdiff.js +30 -2
- package/lib/rules/flatten-push-calls.js +97 -38
- package/lib/rules/jsdoc-above-field.js +47 -19
- package/lib/rules/memo-compare-deeply-complex-props.js +238 -0
- package/lib/rules/no-explicit-return-type.js +35 -0
- package/lib/rules/no-jsx-whitespace-literal.js +47 -0
- package/lib/rules/no-useless-usememo-primitives.js +204 -20
- package/lib/rules/parallelize-async-operations.d.ts +1 -0
- package/lib/rules/parallelize-async-operations.js +216 -15
- package/lib/rules/prefer-clone-deep.js +133 -2
- package/lib/rules/prefer-next-dynamic.js +45 -14
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +239 -27
- package/lib/rules/prefer-type-over-interface.d.ts +10 -2
- package/lib/rules/prefer-type-over-interface.js +238 -13
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +6 -1
- package/lib/rules/prefer-use-deep-compare-memo.js +184 -7
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +167 -50
- package/lib/rules/require-props-composition.js +257 -0
- package/lib/rules/use-latest-callback.js +11 -2
- package/lib/utils/arrowAnnotationGap.js +227 -10
- package/lib/utils/importRemoval.d.ts +33 -4
- package/lib/utils/importRemoval.js +141 -9
- package/lib/utils/inlineExpressionParens.d.ts +30 -0
- package/lib/utils/inlineExpressionParens.js +465 -0
- package/lib/utils/reindentRelocated.d.ts +7 -0
- package/lib/utils/reindentRelocated.js +108 -0
- package/lib/utils/replacementSegments.d.ts +5 -0
- package/lib/utils/replacementSegments.js +2 -1
- package/lib/utils/resourceHandleType.d.ts +21 -0
- package/lib/utils/resourceHandleType.js +217 -0
- package/package.json +1 -1
- package/release-manifest.json +243 -0
package/lib/index.js
CHANGED
|
@@ -640,6 +640,47 @@ function foldKeyDomains(domains) {
|
|
|
640
640
|
}
|
|
641
641
|
return values;
|
|
642
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* The span of the access a written key belongs to: `obj[key]` including the
|
|
645
|
+
* object and both brackets, `[key]` for a computed property (whose value is an
|
|
646
|
+
* expression of its own), and the whole comparison for `key in obj`. Null where
|
|
647
|
+
* the key sits in none of those, which leaves the wrap to span the key alone.
|
|
648
|
+
*
|
|
649
|
+
* This is the unit a fix that rewrites a key has to claim. The key's own range
|
|
650
|
+
* stops short of the `]` that closes the access, and a fixer that reformats the
|
|
651
|
+
* access spreads its edits across the whole of it — so a span ending between
|
|
652
|
+
* the key and its bracket splits that set in half. Claiming the access instead
|
|
653
|
+
* leaves a competing rewrite of it either wholly discarded (and re-made against
|
|
654
|
+
* the fixed text on a later pass) or wholly applied, both of which parse.
|
|
655
|
+
*/
|
|
656
|
+
function accessSpan(sourceCode, written) {
|
|
657
|
+
const { parent } = written;
|
|
658
|
+
if (!parent) {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
if (parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
662
|
+
parent.computed &&
|
|
663
|
+
parent.property === written) {
|
|
664
|
+
return [parent.range[0], parent.range[1]];
|
|
665
|
+
}
|
|
666
|
+
if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
667
|
+
parent.computed &&
|
|
668
|
+
parent.key === written) {
|
|
669
|
+
// A computed property's range runs past its key to the end of its value,
|
|
670
|
+
// which the wrap has no business claiming; the bracket that closes the key
|
|
671
|
+
// is where this access ends.
|
|
672
|
+
const closing = sourceCode.getTokenAfter(written);
|
|
673
|
+
return closing?.value === ']'
|
|
674
|
+
? [parent.range[0], closing.range[1]]
|
|
675
|
+
: [written.range[0], written.range[1]];
|
|
676
|
+
}
|
|
677
|
+
if (parent.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
678
|
+
parent.operator === 'in' &&
|
|
679
|
+
parent.left === written) {
|
|
680
|
+
return [parent.range[0], parent.range[1]];
|
|
681
|
+
}
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
643
684
|
/**
|
|
644
685
|
* An enum is a compiler-checked finite set. Members with literal initializers
|
|
645
686
|
* enumerate their runtime key strings (which is what lets the forbidden-name
|
|
@@ -836,17 +877,51 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
836
877
|
const importStatement = `import { assertSafe } from '${computeImportSpecifier()}';\n`;
|
|
837
878
|
return (0, importInsertion_1.insertAtImportAnchor)(context.sourceCode, fixer, (0, importInsertion_1.importInsertionAnchor)(context.sourceCode), importStatement);
|
|
838
879
|
};
|
|
880
|
+
/**
|
|
881
|
+
* The wrap of the key, emitted over the whole access the key belongs to
|
|
882
|
+
* rather than over the key alone.
|
|
883
|
+
*
|
|
884
|
+
* ESLint merges the fixes of one report into a single edit spanning
|
|
885
|
+
* [first start, last end], splicing the original text back in between. The
|
|
886
|
+
* import anchor sits at the top of the file, so bundling it with the wrap
|
|
887
|
+
* turns a three-character edit into one that claims everything from the
|
|
888
|
+
* file's first statement to the end of the key — which is a point in the
|
|
889
|
+
* middle of the access, before the `]` that closes it. That span sorts
|
|
890
|
+
* ahead of every competing fix and wins the race against all of them, but
|
|
891
|
+
* only up to its end. A fixer whose edits are coherent only as a set (a
|
|
892
|
+
* formatter re-wrapping one access across several lines is the common
|
|
893
|
+
* case) then has the edits inside the span discarded and the edits past
|
|
894
|
+
* its end applied, and the two halves do not fit together: the emitted
|
|
895
|
+
* file does not parse.
|
|
896
|
+
*
|
|
897
|
+
* Ending the span where the access ends puts every edit of such a
|
|
898
|
+
* competing rewrite on one side of the boundary — either wholly inside the
|
|
899
|
+
* span (discarded whole, and re-made against the fixed text on the next
|
|
900
|
+
* pass) or wholly outside it. Both parse. The re-emitted head and tail are
|
|
901
|
+
* copied verbatim from the source, so the text this fix produces is
|
|
902
|
+
* character-for-character what replacing the key alone produced.
|
|
903
|
+
*/
|
|
904
|
+
const wrapKey = (fixer, node, argText) => {
|
|
905
|
+
const replacement = `assertSafe(${argText})`;
|
|
906
|
+
const span = accessSpan(context.sourceCode, node);
|
|
907
|
+
if (!span) {
|
|
908
|
+
return fixer.replaceText(node, replacement);
|
|
909
|
+
}
|
|
910
|
+
const [start, end] = span;
|
|
911
|
+
const { text } = context.sourceCode;
|
|
912
|
+
return fixer.replaceTextRange([start, end], `${text.slice(start, node.range[0])}${replacement}${text.slice(node.range[1], end)}`);
|
|
913
|
+
};
|
|
839
914
|
/**
|
|
840
915
|
* Helper function to create fixes for a node
|
|
841
916
|
*/
|
|
842
917
|
const createFixes = (fixer, node, argText) => {
|
|
843
918
|
const fixes = [];
|
|
844
|
-
|
|
919
|
+
const carriesImport = !importClaimed && !importsAssertSafe(context.sourceCode.ast);
|
|
920
|
+
if (carriesImport) {
|
|
845
921
|
fixes.push(addAssertSafeImport(fixer));
|
|
846
922
|
importClaimed = true;
|
|
847
923
|
}
|
|
848
|
-
|
|
849
|
-
fixes.push(fixer.replaceText(node, `assertSafe(${argText})`));
|
|
924
|
+
fixes.push(wrapKey(fixer, node, argText));
|
|
850
925
|
return fixes;
|
|
851
926
|
};
|
|
852
927
|
// The report is emitted even when suppressed: ESLint discards it, and
|