@blumintinc/eslint-plugin-blumint 1.20.172 → 1.20.173
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 +333 -10
- package/lib/rules/enforce-empty-object-check.js +266 -48
- package/lib/rules/enforce-firestore-rules-get-access.js +159 -7
- package/lib/rules/enforce-memoize-async.js +30 -0
- package/lib/rules/enforce-memoize-getters.js +30 -0
- package/lib/rules/enforce-microdiff.js +103 -2
- package/lib/rules/enforce-mui-rounded-icons.js +121 -3
- package/lib/rules/enforce-querykey-ts.js +256 -5
- package/lib/rules/global-const-style.js +145 -20
- package/lib/rules/jsdoc-above-field.d.ts +2 -1
- package/lib/rules/jsdoc-above-field.js +180 -5
- package/lib/rules/memo-compare-deeply-complex-props.js +124 -23
- package/lib/rules/no-array-length-in-deps.js +102 -9
- package/lib/rules/no-entire-object-hook-deps.js +24 -2
- package/lib/rules/no-explicit-return-type.js +367 -8
- package/lib/rules/no-firestore-jest-mock.js +74 -1
- package/lib/rules/no-redundant-annotation-assertion.js +164 -1
- package/lib/rules/no-redundant-param-types.js +374 -1
- package/lib/rules/no-unnecessary-destructuring.js +26 -2
- package/lib/rules/no-useless-fragment.d.ts +2 -1
- package/lib/rules/no-useless-fragment.js +81 -1
- package/lib/rules/no-usememo-for-pass-by-value.js +45 -3
- package/lib/rules/prefer-clone-deep.js +286 -28
- package/lib/rules/prefer-map-over-conditional-dispatch.d.ts +35 -0
- package/lib/rules/prefer-map-over-conditional-dispatch.js +293 -26
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +291 -81
- package/lib/rules/prefer-use-deep-compare-memo.js +164 -63
- package/lib/rules/require-hooks-default-params.d.ts +2 -1
- package/lib/rules/require-hooks-default-params.js +258 -2
- package/lib/rules/require-image-optimized.js +243 -19
- package/lib/rules/require-memo.js +113 -18
- package/lib/rules/require-memoize-jsx-returners.js +30 -0
- package/lib/rules/use-latest-callback.js +164 -3
- package/lib/utils/harvestRuleTesterCases.js +2 -2
- package/lib/utils/tempFixtureDir.d.ts +36 -0
- package/lib/utils/tempFixtureDir.js +95 -0
- package/package.json +1 -1
- package/release-manifest.json +240 -0
package/lib/index.js
CHANGED
|
@@ -652,8 +652,14 @@ function foldKeyDomains(domains) {
|
|
|
652
652
|
* the key and its bracket splits that set in half. Claiming the access instead
|
|
653
653
|
* leaves a competing rewrite of it either wholly discarded (and re-made against
|
|
654
654
|
* the fixed text on a later pass) or wholly applied, both of which parse.
|
|
655
|
+
*
|
|
656
|
+
* `keyEnd` is where the written key ends once the parentheses around it are
|
|
657
|
+
* counted in, which is what the bracket closing a computed property is looked
|
|
658
|
+
* for after: in `{ [(id)]: value }` the token following the key itself is the
|
|
659
|
+
* `)`, and reading that as "no bracket here" would shrink the span back onto the
|
|
660
|
+
* key and leave the property's own brackets outside it.
|
|
655
661
|
*/
|
|
656
|
-
function accessSpan(sourceCode, written) {
|
|
662
|
+
function accessSpan(sourceCode, written, keyEnd = written) {
|
|
657
663
|
const { parent } = written;
|
|
658
664
|
if (!parent) {
|
|
659
665
|
return null;
|
|
@@ -669,7 +675,7 @@ function accessSpan(sourceCode, written) {
|
|
|
669
675
|
// A computed property's range runs past its key to the end of its value,
|
|
670
676
|
// which the wrap has no business claiming; the bracket that closes the key
|
|
671
677
|
// is where this access ends.
|
|
672
|
-
const closing = sourceCode.getTokenAfter(
|
|
678
|
+
const closing = sourceCode.getTokenAfter(keyEnd);
|
|
673
679
|
return closing?.value === ']'
|
|
674
680
|
? [parent.range[0], closing.range[1]]
|
|
675
681
|
: [written.range[0], written.range[1]];
|
|
@@ -681,6 +687,119 @@ function accessSpan(sourceCode, written) {
|
|
|
681
687
|
}
|
|
682
688
|
return null;
|
|
683
689
|
}
|
|
690
|
+
/**
|
|
691
|
+
* The grouping parentheses written around a key, innermost first.
|
|
692
|
+
*
|
|
693
|
+
* A pair is claimed only when both halves face the key at once, which is what
|
|
694
|
+
* keeps a parenthesis belonging to some enclosing construct out of the run: the
|
|
695
|
+
* `(` of `if (key in obj)` and of `read(key in obj)` is answered by the `in`
|
|
696
|
+
* keyword rather than by a `)`, so the walk stops before it.
|
|
697
|
+
*
|
|
698
|
+
* Anything other than whitespace between a parenthesis and what it encloses ends
|
|
699
|
+
* the walk too. That text is a comment, and dropping the parenthesis would move
|
|
700
|
+
* the comment out of the group the author wrote it inside — where a line comment
|
|
701
|
+
* decides what the lines after it may hold.
|
|
702
|
+
*/
|
|
703
|
+
function groupingParensAround(sourceCode, node) {
|
|
704
|
+
const pairs = [];
|
|
705
|
+
const { text } = sourceCode;
|
|
706
|
+
let innerStart = node.range[0];
|
|
707
|
+
let innerEnd = node.range[1];
|
|
708
|
+
let open = sourceCode.getTokenBefore(node);
|
|
709
|
+
let close = sourceCode.getTokenAfter(node);
|
|
710
|
+
while (open?.value === '(' && close?.value === ')') {
|
|
711
|
+
if (text.slice(open.range[1], innerStart).trim() !== '' ||
|
|
712
|
+
text.slice(innerEnd, close.range[0]).trim() !== '') {
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
pairs.push({ open, close });
|
|
716
|
+
innerStart = open.range[0];
|
|
717
|
+
innerEnd = close.range[1];
|
|
718
|
+
open = sourceCode.getTokenBefore(open);
|
|
719
|
+
close = sourceCode.getTokenAfter(close);
|
|
720
|
+
}
|
|
721
|
+
return pairs;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Whether the parentheses the source wrote around a key can be dropped.
|
|
725
|
+
*
|
|
726
|
+
* The emission is a call, which is the tightest-binding expression there is, and
|
|
727
|
+
* every position that yields an access span takes one bare: between the brackets
|
|
728
|
+
* of a lookup or of a computed property, and as the left operand of `in`. So the
|
|
729
|
+
* pair is redundant the moment the key is wrapped, and prettier deletes it
|
|
730
|
+
* again — which is the whole defect (#2108).
|
|
731
|
+
*
|
|
732
|
+
* The one thing the parenthesis is doing is separating the key from the token
|
|
733
|
+
* before it. Dropping it where that token ends in an identifier character would
|
|
734
|
+
* fuse the two into one word, so the pair stays there.
|
|
735
|
+
*/
|
|
736
|
+
function dropsGroupingParens(text, span, outermost) {
|
|
737
|
+
const prefix = text.slice(span[0], outermost.open.range[0]);
|
|
738
|
+
const preceding = prefix === '' ? text.slice(Math.max(0, span[0] - 1), span[0]) : prefix;
|
|
739
|
+
return !/[\p{ID_Continue}$]$/u.test(preceding);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Prettier's default print width, which is what this repo and agora both format
|
|
743
|
+
* with. Wrapping a key widens the line it sits on, and a line the formatter
|
|
744
|
+
* would break is a line the formatter DOES break: agora runs prettier and
|
|
745
|
+
* `eslint --fix` over the same tree, so an emission past this width churns the
|
|
746
|
+
* file on every pass (#2108).
|
|
747
|
+
*/
|
|
748
|
+
const PRINT_WIDTH = 80;
|
|
749
|
+
/** Prettier's default `tabWidth`, the step it indents a broken body by. */
|
|
750
|
+
const INDENT_STEP = ' ';
|
|
751
|
+
/**
|
|
752
|
+
* A `;` closing the rewritten line, optionally followed by same-line
|
|
753
|
+
* comments. Prettier leaves such comments where they are — after the `;` on
|
|
754
|
+
* the closing line — when it opens a lookup, so the emission may carry them
|
|
755
|
+
* (measured against prettier 2.8.8). A comment after a `,` is re-hosted
|
|
756
|
+
* BEFORE the comma instead, a relocation the emitter declines to model.
|
|
757
|
+
*/
|
|
758
|
+
const SEMICOLON_WITH_TRAILING_COMMENTS = /^;(?:\s*\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:\s*\/\/.*)?$/;
|
|
759
|
+
/**
|
|
760
|
+
* Bodies prettier keeps on the arrow's own line instead of breaking after `=>`.
|
|
761
|
+
* Each of these opens a block of its own that absorbs the overflow, so a break
|
|
762
|
+
* inserted ahead of one is a layout prettier does not print.
|
|
763
|
+
*/
|
|
764
|
+
const HUGGED_ARROW_BODY_TYPES = new Set([
|
|
765
|
+
utils_1.AST_NODE_TYPES.ArrayExpression,
|
|
766
|
+
utils_1.AST_NODE_TYPES.ObjectExpression,
|
|
767
|
+
utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
|
|
768
|
+
utils_1.AST_NODE_TYPES.TemplateLiteral,
|
|
769
|
+
utils_1.AST_NODE_TYPES.TaggedTemplateExpression,
|
|
770
|
+
utils_1.AST_NODE_TYPES.SequenceExpression,
|
|
771
|
+
utils_1.AST_NODE_TYPES.JSXElement,
|
|
772
|
+
utils_1.AST_NODE_TYPES.JSXFragment,
|
|
773
|
+
]);
|
|
774
|
+
/**
|
|
775
|
+
* Positions where a concise arrow body is laid out by the construct around it
|
|
776
|
+
* rather than by the arrow's own group: an argument list hugs its last argument
|
|
777
|
+
* and breaks the list first, and an arrow chain is printed as one unit. The
|
|
778
|
+
* accepted positions are the ones measured against prettier 2.8.8 — a
|
|
779
|
+
* declarator's initializer, a property or class-property value, an assignment's
|
|
780
|
+
* right-hand side, and a returned or thrown value.
|
|
781
|
+
*/
|
|
782
|
+
const BREAKABLE_ARROW_OWNER_TYPES = new Set([
|
|
783
|
+
utils_1.AST_NODE_TYPES.VariableDeclarator,
|
|
784
|
+
utils_1.AST_NODE_TYPES.Property,
|
|
785
|
+
utils_1.AST_NODE_TYPES.PropertyDefinition,
|
|
786
|
+
utils_1.AST_NODE_TYPES.AssignmentExpression,
|
|
787
|
+
utils_1.AST_NODE_TYPES.ReturnStatement,
|
|
788
|
+
]);
|
|
789
|
+
/**
|
|
790
|
+
* The arrow function whose concise body holds the key, or null when the key
|
|
791
|
+
* sits somewhere the arrow's `=>` does not govern — in its parameters, in a
|
|
792
|
+
* block body, or in no arrow at all.
|
|
793
|
+
*/
|
|
794
|
+
function enclosingConciseArrow(node) {
|
|
795
|
+
let child = node;
|
|
796
|
+
let parent = node.parent;
|
|
797
|
+
while (parent && parent.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
798
|
+
child = parent;
|
|
799
|
+
parent = parent.parent;
|
|
800
|
+
}
|
|
801
|
+
return parent && parent.body === child ? parent : null;
|
|
802
|
+
}
|
|
684
803
|
/**
|
|
685
804
|
* An enum is a compiler-checked finite set. Members with literal initializers
|
|
686
805
|
* enumerate their runtime key strings (which is what lets the forbidden-name
|
|
@@ -899,17 +1018,213 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
899
1018
|
* span (discarded whole, and re-made against the fixed text on the next
|
|
900
1019
|
* pass) or wholly outside it. Both parse. The re-emitted head and tail are
|
|
901
1020
|
* copied verbatim from the source, so the text this fix produces is
|
|
902
|
-
* character-for-character what replacing the key alone produced
|
|
1021
|
+
* character-for-character what replacing the key alone produced — bar the
|
|
1022
|
+
* grouping parentheses `dropsGroupingParens` settles.
|
|
903
1023
|
*/
|
|
904
|
-
const
|
|
905
|
-
const
|
|
906
|
-
const
|
|
1024
|
+
const planWrap = (node, argText) => {
|
|
1025
|
+
const { sourceCode } = context;
|
|
1026
|
+
const replacement = `${ASSERT_SAFE_NAME}(${argText})`;
|
|
1027
|
+
const parens = groupingParensAround(sourceCode, node);
|
|
1028
|
+
const outermost = parens[parens.length - 1];
|
|
1029
|
+
const span = accessSpan(sourceCode, node, outermost?.close ?? node);
|
|
907
1030
|
if (!span) {
|
|
908
|
-
return
|
|
1031
|
+
return { range: [node.range[0], node.range[1]], text: replacement };
|
|
909
1032
|
}
|
|
910
1033
|
const [start, end] = span;
|
|
911
|
-
const { text } =
|
|
912
|
-
|
|
1034
|
+
const { text } = sourceCode;
|
|
1035
|
+
const edits = [
|
|
1036
|
+
{ range: [node.range[0], node.range[1]], text: replacement },
|
|
1037
|
+
];
|
|
1038
|
+
if (outermost && dropsGroupingParens(text, span, outermost)) {
|
|
1039
|
+
for (const pair of parens) {
|
|
1040
|
+
edits.push({ range: [pair.open.range[0], pair.open.range[1]], text: '' }, { range: [pair.close.range[0], pair.close.range[1]], text: '' });
|
|
1041
|
+
}
|
|
1042
|
+
edits.sort((left, right) => left.range[0] - right.range[0]);
|
|
1043
|
+
}
|
|
1044
|
+
let emitted = '';
|
|
1045
|
+
let cursor = start;
|
|
1046
|
+
for (const edit of edits) {
|
|
1047
|
+
emitted += `${text.slice(cursor, edit.range[0])}${edit.text}`;
|
|
1048
|
+
cursor = edit.range[1];
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
range: [start, end],
|
|
1052
|
+
text: `${emitted}${text.slice(cursor, end)}`,
|
|
1053
|
+
};
|
|
1054
|
+
};
|
|
1055
|
+
/**
|
|
1056
|
+
* The line break prettier prints after `=>` once the wrap has widened the
|
|
1057
|
+
* arrow body past the print width, or null where this emitter cannot say
|
|
1058
|
+
* what prettier would print.
|
|
1059
|
+
*
|
|
1060
|
+
* Prettier lays a concise arrow body out as `group(indent([line, body]))`,
|
|
1061
|
+
* so the break after `=>` is the FIRST one it reaches for: a body that fits
|
|
1062
|
+
* on the indented line is laid out exactly this way, and one that does not
|
|
1063
|
+
* is broken further inside — which `planMemberBreak` prints for the one
|
|
1064
|
+
* shape it has measured, and which this emitter otherwise declines rather
|
|
1065
|
+
* than guesses at. Every other decline below is deliberate for the same
|
|
1066
|
+
* reason — the one-line emission it falls back to is what shipped before
|
|
1067
|
+
* the width was measured at all, so a decline costs formatting and never
|
|
1068
|
+
* meaning.
|
|
1069
|
+
*/
|
|
1070
|
+
const planArrowBreak = (node, wrap) => {
|
|
1071
|
+
const { sourceCode } = context;
|
|
1072
|
+
const { line } = node.loc.start;
|
|
1073
|
+
const original = sourceCode.lines[line - 1] ?? '';
|
|
1074
|
+
const delta = wrap.text.length - (wrap.range[1] - wrap.range[0]);
|
|
1075
|
+
if (original.length + delta <= PRINT_WIDTH) {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
// A span that already crosses lines carries the author's own breaks, so
|
|
1079
|
+
// the single line measured above is not the whole of what moves.
|
|
1080
|
+
if (wrap.text.includes('\n') ||
|
|
1081
|
+
sourceCode.getLocFromIndex(wrap.range[0]).line !== line ||
|
|
1082
|
+
sourceCode.getLocFromIndex(wrap.range[1]).line !== line) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
const arrow = enclosingConciseArrow(node);
|
|
1086
|
+
const body = arrow?.body;
|
|
1087
|
+
if (!arrow ||
|
|
1088
|
+
!body ||
|
|
1089
|
+
body.type === utils_1.AST_NODE_TYPES.BlockStatement ||
|
|
1090
|
+
HUGGED_ARROW_BODY_TYPES.has(body.type) ||
|
|
1091
|
+
!arrow.parent ||
|
|
1092
|
+
!BREAKABLE_ARROW_OWNER_TYPES.has(arrow.parent.type)) {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
const arrowToken = sourceCode.getTokenBefore(body);
|
|
1096
|
+
if (arrowToken?.value !== '=>' ||
|
|
1097
|
+
arrowToken.loc.end.line !== line ||
|
|
1098
|
+
body.loc.start.line !== line ||
|
|
1099
|
+
body.loc.end.line !== line ||
|
|
1100
|
+
sourceCode.text.slice(arrowToken.range[1], body.range[0]).trim() !== '') {
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
// Prettier indents the body one step in from the line the arrow's own
|
|
1104
|
+
// group opens on, which is the line its parameter list starts.
|
|
1105
|
+
const anchor = sourceCode.lines[arrow.loc.start.line - 1] ?? '';
|
|
1106
|
+
const bodyIndent = `${/^[\t ]*/.exec(anchor)?.[0] ?? ''}${INDENT_STEP}`;
|
|
1107
|
+
const head = original.slice(0, arrowToken.loc.end.column).trimEnd();
|
|
1108
|
+
const tail = original.slice(body.loc.start.column);
|
|
1109
|
+
if (head.length > PRINT_WIDTH ||
|
|
1110
|
+
bodyIndent.length + tail.length + delta > PRINT_WIDTH) {
|
|
1111
|
+
return null;
|
|
1112
|
+
}
|
|
1113
|
+
return {
|
|
1114
|
+
range: [arrowToken.range[1], body.range[0]],
|
|
1115
|
+
text: `\n${bodyIndent}`,
|
|
1116
|
+
};
|
|
1117
|
+
};
|
|
1118
|
+
/**
|
|
1119
|
+
* The wrap re-emitted in the shape prettier prints a computed lookup in
|
|
1120
|
+
* once the wrap has widened the line past the print width and the break
|
|
1121
|
+
* after `=>` is already taken, or null where this emitter cannot say what
|
|
1122
|
+
* prettier would print.
|
|
1123
|
+
*
|
|
1124
|
+
* Prettier lays a computed lookup out as `object[` + indent(softline +
|
|
1125
|
+
* key) + softline + `]`, so a body that no longer fits on its own line is
|
|
1126
|
+
* opened at the bracket with the key one step in; the call is then
|
|
1127
|
+
* measured on that line and, where it does not fit there either, opened
|
|
1128
|
+
* at its own parenthesis with the argument one step further in and a
|
|
1129
|
+
* trailing comma. Both steps are measured against prettier 2.8.8 at
|
|
1130
|
+
* agora's options, whose `trailingComma: "all"` is what puts the comma
|
|
1131
|
+
* after the argument (#2134).
|
|
1132
|
+
*
|
|
1133
|
+
* The emission is limited to the shape measured: a single-line lookup
|
|
1134
|
+
* that starts its line as the whole body of an arrow whose `=>` closes
|
|
1135
|
+
* an earlier line, followed by the `;` or `,` that ends the statement or
|
|
1136
|
+
* the element — a `;` optionally carrying same-line comments. Anything
|
|
1137
|
+
* else — a key the author
|
|
1138
|
+
* parenthesized or commented, a lookup broken across lines, a key so long
|
|
1139
|
+
* that even its own line overflows — falls back to the one-line emission,
|
|
1140
|
+
* which is what shipped before the width was measured at all.
|
|
1141
|
+
*/
|
|
1142
|
+
const planMemberBreak = (node, wrap, argText) => {
|
|
1143
|
+
const { sourceCode } = context;
|
|
1144
|
+
const { text } = sourceCode;
|
|
1145
|
+
const { line } = node.loc.start;
|
|
1146
|
+
const original = sourceCode.lines[line - 1] ?? '';
|
|
1147
|
+
const delta = wrap.text.length - (wrap.range[1] - wrap.range[0]);
|
|
1148
|
+
if (original.length + delta <= PRINT_WIDTH) {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
const member = node.parent;
|
|
1152
|
+
if (!member ||
|
|
1153
|
+
member.type !== utils_1.AST_NODE_TYPES.MemberExpression ||
|
|
1154
|
+
!member.computed ||
|
|
1155
|
+
member.property !== node ||
|
|
1156
|
+
wrap.range[0] !== member.range[0] ||
|
|
1157
|
+
wrap.range[1] !== member.range[1] ||
|
|
1158
|
+
wrap.text.includes('\n') ||
|
|
1159
|
+
member.loc.start.line !== line ||
|
|
1160
|
+
member.loc.end.line !== line ||
|
|
1161
|
+
groupingParensAround(sourceCode, node).length > 0 ||
|
|
1162
|
+
sourceCode.getCommentsInside(member).length > 0) {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
const arrow = enclosingConciseArrow(node);
|
|
1166
|
+
const body = arrow?.body;
|
|
1167
|
+
// An optional chain wraps the lookup in a ChainExpression of the same
|
|
1168
|
+
// extent, so the body is the lookup either way.
|
|
1169
|
+
const bodyIsLookup = body === member ||
|
|
1170
|
+
(body?.type === utils_1.AST_NODE_TYPES.ChainExpression &&
|
|
1171
|
+
body.expression === member);
|
|
1172
|
+
const arrowToken = sourceCode.getTokenBefore(member);
|
|
1173
|
+
if (!arrow ||
|
|
1174
|
+
!bodyIsLookup ||
|
|
1175
|
+
arrowToken?.value !== '=>' ||
|
|
1176
|
+
arrowToken.loc.end.line >= line) {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
// A comment on a line of its own between `=>` and the body stays on
|
|
1180
|
+
// that line and leaves the body's layout untouched, so it is carried;
|
|
1181
|
+
// one that shares the arrow's line is text prettier relocates into the
|
|
1182
|
+
// parameter list, which this emitter does not model.
|
|
1183
|
+
const gapComments = sourceCode.getTokensBetween(arrowToken, member, {
|
|
1184
|
+
includeComments: true,
|
|
1185
|
+
});
|
|
1186
|
+
if (gapComments.some((comment) => comment.loc.start.line <= arrowToken.loc.end.line)) {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
const openBracket = sourceCode.getTokenBefore(node);
|
|
1190
|
+
const closeBracket = sourceCode.getLastToken(member);
|
|
1191
|
+
if (openBracket?.value !== '[' ||
|
|
1192
|
+
closeBracket?.value !== ']' ||
|
|
1193
|
+
text.slice(openBracket.range[1], node.range[0]) !== '' ||
|
|
1194
|
+
text.slice(node.range[1], closeBracket.range[0]) !== '') {
|
|
1195
|
+
return null;
|
|
1196
|
+
}
|
|
1197
|
+
const indent = original.slice(0, member.loc.start.column);
|
|
1198
|
+
const tail = original.slice(member.loc.end.column);
|
|
1199
|
+
const isBreakableTail = tail === ';' ||
|
|
1200
|
+
tail === ',' ||
|
|
1201
|
+
SEMICOLON_WITH_TRAILING_COMMENTS.test(tail);
|
|
1202
|
+
if (indent.trim() !== '' || !isBreakableTail) {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
// Everything through the bracket — `m[`, `s.map[`, `m?.[`, `m[a][` —
|
|
1206
|
+
// stays on the body's line; prettier opens only the outermost lookup.
|
|
1207
|
+
const head = text.slice(member.range[0], openBracket.range[1]);
|
|
1208
|
+
if (indent.length + head.length > PRINT_WIDTH) {
|
|
1209
|
+
return null;
|
|
1210
|
+
}
|
|
1211
|
+
const keyIndent = `${indent}${INDENT_STEP}`;
|
|
1212
|
+
const argIndent = `${keyIndent}${INDENT_STEP}`;
|
|
1213
|
+
const call = `${ASSERT_SAFE_NAME}(${argText})`;
|
|
1214
|
+
let key;
|
|
1215
|
+
if (keyIndent.length + call.length <= PRINT_WIDTH) {
|
|
1216
|
+
key = `${keyIndent}${call}`;
|
|
1217
|
+
}
|
|
1218
|
+
else if (argIndent.length + argText.length + 1 <= PRINT_WIDTH) {
|
|
1219
|
+
key = `${keyIndent}${ASSERT_SAFE_NAME}(\n${argIndent}${argText},\n${keyIndent})`;
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
range: [member.range[0], member.range[1]],
|
|
1226
|
+
text: `${head}\n${key}\n${indent}]`,
|
|
1227
|
+
};
|
|
913
1228
|
};
|
|
914
1229
|
/**
|
|
915
1230
|
* Helper function to create fixes for a node
|
|
@@ -921,7 +1236,15 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
|
|
|
921
1236
|
fixes.push(addAssertSafeImport(fixer));
|
|
922
1237
|
importClaimed = true;
|
|
923
1238
|
}
|
|
924
|
-
|
|
1239
|
+
const wrap = planWrap(node, argText);
|
|
1240
|
+
const arrowBreak = planArrowBreak(node, wrap);
|
|
1241
|
+
if (arrowBreak) {
|
|
1242
|
+
fixes.push(fixer.replaceTextRange(arrowBreak.range, arrowBreak.text));
|
|
1243
|
+
}
|
|
1244
|
+
const access = arrowBreak
|
|
1245
|
+
? wrap
|
|
1246
|
+
: planMemberBreak(node, wrap, argText) ?? wrap;
|
|
1247
|
+
fixes.push(fixer.replaceTextRange(access.range, access.text));
|
|
925
1248
|
return fixes;
|
|
926
1249
|
};
|
|
927
1250
|
// The report is emitted even when suppressed: ESLint discards it, and
|