eslint-plugin-th-rules 3.6.0 → 3.6.1

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/dist/plugin.d.ts CHANGED
@@ -11,10 +11,9 @@ export declare const rules: {
11
11
  'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
12
12
  name: string;
13
13
  };
14
- 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative" | "directAccessRequired", [{
14
+ 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative", [{
15
15
  maximumDestructuredVariables?: number;
16
16
  maximumLineLength?: number;
17
- directAccessIdentifiers?: string[];
18
17
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
19
18
  name: string;
20
19
  };
@@ -64,10 +63,9 @@ declare const plugin: {
64
63
  'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
65
64
  name: string;
66
65
  };
67
- 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative" | "directAccessRequired", [{
66
+ 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative", [{
68
67
  maximumDestructuredVariables?: number;
69
68
  maximumLineLength?: number;
70
- directAccessIdentifiers?: string[];
71
69
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
72
70
  name: string;
73
71
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAajB,CAAC;AAEF,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAY,CAAC;AACzB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAajB,CAAC;AAEF,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAY,CAAC;AACzB,eAAe,MAAM,CAAC"}
@@ -3,10 +3,9 @@ type Options = [
3
3
  {
4
4
  maximumDestructuredVariables?: number;
5
5
  maximumLineLength?: number;
6
- directAccessIdentifiers?: string[];
7
6
  }
8
7
  ];
9
- type MessageIds = 'tooDeep' | 'tooMany' | 'tooLong' | 'tooManyCumulative' | 'directAccessRequired';
8
+ type MessageIds = 'tooDeep' | 'tooMany' | 'tooLong' | 'tooManyCumulative';
10
9
  declare const noDestructuring: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
11
10
  name: string;
12
11
  };
@@ -1 +1 @@
1
- {"version":3,"file":"no-destructuring.d.ts","sourceRoot":"","sources":["../../src/rules/no-destructuring.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAItF,KAAK,OAAO,GAAG;IACd;QACC,4BAA4B,CAAC,EAAE,MAAM,CAAC;QACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;KACnC;CACD,CAAC;AAEF,KAAK,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,sBAAsB,CAAC;AAEnG,QAAA,MAAM,eAAe;;CA0NnB,CAAC;AAEH,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"no-destructuring.d.ts","sourceRoot":"","sources":["../../src/rules/no-destructuring.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAItF,KAAK,OAAO,GAAG;IACd;QACC,4BAA4B,CAAC,EAAE,MAAM,CAAC;QACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC3B;CACD,CAAC;AAEF,KAAK,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,CAAC;AAE1E,QAAA,MAAM,eAAe;;CA+NnB,CAAC;AAEH,eAAe,eAAe,CAAC"}
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable new-cap */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
3
  import _ from 'lodash';
3
4
  import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
4
- const MAX_INDENT_SPACES = 3;
5
+ const MAX_TAB_COUNT = 3;
5
6
  const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-destructuring.md')({
6
7
  name: 'no-destructuring',
7
8
  meta: {
@@ -15,51 +16,48 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
15
16
  properties: {
16
17
  maximumDestructuredVariables: { type: 'integer', minimum: 0 },
17
18
  maximumLineLength: { type: 'integer', minimum: 0 },
18
- directAccessIdentifiers: {
19
- type: 'array',
20
- items: { type: 'string', minLength: 1 },
21
- },
22
19
  },
23
20
  additionalProperties: false,
24
21
  },
25
22
  ],
26
23
  messages: {
27
- tooDeep: 'Destructuring at an indentation above {{max}} is not allowed; found {{actual}}.',
24
+ tooDeep: 'Destructuring at a nesting level above {{max}} is not allowed; found {{actual}} levels of nesting.',
28
25
  tooMany: 'Destructuring of more than {{max}} variables is not allowed.',
29
26
  tooLong: 'Destructuring spanning a line exceeding {{max}} characters is not allowed.',
30
- tooManyCumulative: 'Too many destructured variables from "{{source}}" in the same scope. Max is {{max}}, total is {{total}}.',
31
- directAccessRequired: 'Do not destructure from "{{identifier}}". Use direct member access, for example {{identifier}}.onChangeText.',
27
+ tooManyCumulative: 'Destructuring of more than {{max}} variables from "{{source}}" in the same scope is not allowed; found {{total}}.',
32
28
  },
33
29
  },
34
30
  defaultOptions: [
35
31
  {
36
32
  maximumDestructuredVariables: 2,
37
33
  maximumLineLength: 100,
38
- directAccessIdentifiers: ['properties'],
39
34
  },
40
35
  ],
41
36
  create(context, [options]) {
42
37
  const maxVariables = options.maximumDestructuredVariables ?? 2;
43
38
  const maxLineLength = options.maximumLineLength ?? 100;
44
- const directAccessIdentifiers = new Set(options.directAccessIdentifiers ?? ['properties']);
45
- const sourceTotalsByScope = new WeakMap();
39
+ /**
40
+ * Tracks total destructured properties per initializer expression text per scope node.
41
+ * WeakMap is used so scopes can be GC'ed and we do not leak memory across files.
42
+ */
43
+ const totalsByScope = new WeakMap();
46
44
  function getLineText(lineNumber) {
47
45
  return context.sourceCode.lines[lineNumber - 1] ?? '';
48
46
  }
49
- function getIndentSpacesForLine(lineNumber) {
50
- const lineText = getLineText(lineNumber);
51
- return lineText.search(/\S|$/);
52
- }
53
47
  function getMaxSpannedLineLength(startLine, endLine) {
54
48
  let max = 0;
55
49
  for (let i = startLine; i <= endLine; i++) {
56
- const text = getLineText(i);
57
- if (text.length > max) {
58
- max = text.length;
50
+ const line = getLineText(i);
51
+ if (line.length > max) {
52
+ max = line.length;
59
53
  }
60
54
  }
61
55
  return max;
62
56
  }
57
+ function getIndentCountForLine(lineNumber) {
58
+ const lineText = getLineText(lineNumber);
59
+ return lineText.search(/\S|$/);
60
+ }
63
61
  function getScopeNode(node) {
64
62
  const ancestors = context.sourceCode.getAncestors(node);
65
63
  for (let i = ancestors.length - 1; i >= 0; i--) {
@@ -74,78 +72,52 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
74
72
  }
75
73
  return context.sourceCode.ast;
76
74
  }
77
- function getOrCreateScopeMap(scope) {
78
- const existing = sourceTotalsByScope.get(scope);
75
+ function getScopeMap(scopeNode) {
76
+ const existing = totalsByScope.get(scopeNode);
79
77
  if (!_.isNil(existing)) {
80
78
  return existing;
81
79
  }
82
80
  const created = new Map();
83
- sourceTotalsByScope.set(scope, created);
81
+ totalsByScope.set(scopeNode, created);
84
82
  return created;
85
83
  }
86
- function isDirectAccessForbiddenInitializer(init) {
87
- if (_.isNil(init)) {
88
- return false;
89
- }
90
- return init.type === AST_NODE_TYPES.Identifier && directAccessIdentifiers.has(init.name);
91
- }
92
- function reportIfNeeded(patternNode, reportNode, initExpression) {
84
+ function reportIfNeeded(patternNode, reportNode = patternNode) {
93
85
  if (patternNode?.type !== AST_NODE_TYPES.ObjectPattern || _.isNil(patternNode.loc)) {
94
86
  return;
95
87
  }
96
- if (isDirectAccessForbiddenInitializer(initExpression)) {
97
- context.report({
98
- node: reportNode,
99
- messageId: 'directAccessRequired',
100
- data: { identifier: initExpression.name },
101
- });
102
- return;
103
- }
104
88
  const startLine = patternNode.loc.start.line;
105
89
  const endLine = patternNode.loc.end.line;
106
- const indentSpaces = getIndentSpacesForLine(startLine);
90
+ const indentCount = getIndentCountForLine(startLine);
107
91
  const propertyCount = patternNode.properties?.length ?? 0;
108
92
  const maxSpannedLineLength = getMaxSpannedLineLength(startLine, endLine);
109
- if (indentSpaces > MAX_INDENT_SPACES) {
93
+ if (indentCount > MAX_TAB_COUNT) {
110
94
  context.report({
111
95
  node: reportNode,
112
96
  messageId: 'tooDeep',
113
- data: { max: MAX_INDENT_SPACES, actual: indentSpaces },
97
+ data: {
98
+ max: MAX_TAB_COUNT,
99
+ actual: indentCount,
100
+ },
114
101
  });
115
102
  }
116
103
  if (propertyCount > maxVariables) {
117
104
  context.report({
118
105
  node: reportNode,
119
106
  messageId: 'tooMany',
120
- data: { max: maxVariables },
107
+ data: {
108
+ max: maxVariables,
109
+ },
121
110
  });
122
111
  }
123
112
  if (maxSpannedLineLength > maxLineLength) {
124
113
  context.report({
125
114
  node: reportNode,
126
115
  messageId: 'tooLong',
127
- data: { max: maxLineLength },
116
+ data: {
117
+ max: maxLineLength,
118
+ },
128
119
  });
129
120
  }
130
- if (!_.isNil(initExpression)) {
131
- const scopeNode = getScopeNode(reportNode);
132
- const scopeMap = getOrCreateScopeMap(scopeNode);
133
- const sourceText = context.sourceCode.getText(initExpression);
134
- const previousTotal = scopeMap.get(sourceText) ?? 0;
135
- const newTotal = previousTotal + propertyCount;
136
- scopeMap.set(sourceText, newTotal);
137
- if (newTotal > maxVariables) {
138
- context.report({
139
- node: reportNode,
140
- messageId: 'tooManyCumulative',
141
- data: {
142
- source: sourceText,
143
- max: maxVariables,
144
- total: newTotal,
145
- },
146
- });
147
- }
148
- }
149
121
  }
150
122
  function checkParameters(parameters) {
151
123
  for (const p of parameters || []) {
@@ -153,15 +125,45 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
153
125
  continue;
154
126
  }
155
127
  if (p.type === AST_NODE_TYPES.AssignmentPattern) {
156
- reportIfNeeded(p.left, p, undefined);
128
+ reportIfNeeded(p.left, p);
157
129
  continue;
158
130
  }
159
- reportIfNeeded(p, p, undefined);
131
+ reportIfNeeded(p, p);
132
+ }
133
+ }
134
+ function checkCumulativeVariableDeclarator(node) {
135
+ if (node.id.type !== AST_NODE_TYPES.ObjectPattern) {
136
+ return;
137
+ }
138
+ if (_.isNil(node.init)) {
139
+ return;
140
+ }
141
+ const propertyCount = node.id.properties?.length ?? 0;
142
+ if (propertyCount > maxVariables) {
143
+ return;
144
+ }
145
+ const scopeNode = getScopeNode(node);
146
+ const scopeMap = getScopeMap(scopeNode);
147
+ const sourceText = context.sourceCode.getText(node.init);
148
+ const previousTotal = scopeMap.get(sourceText) ?? 0;
149
+ const newTotal = previousTotal + propertyCount;
150
+ scopeMap.set(sourceText, newTotal);
151
+ if (previousTotal > 0 && newTotal > maxVariables) {
152
+ context.report({
153
+ node,
154
+ messageId: 'tooManyCumulative',
155
+ data: {
156
+ source: sourceText,
157
+ max: maxVariables,
158
+ total: newTotal,
159
+ },
160
+ });
160
161
  }
161
162
  }
162
163
  return {
163
164
  VariableDeclarator(node) {
164
- reportIfNeeded(node.id, node, node.init);
165
+ reportIfNeeded(node.id, node);
166
+ checkCumulativeVariableDeclarator(node);
165
167
  },
166
168
  FunctionDeclaration(node) {
167
169
  checkParameters(node.params);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",