zeiger-eslint-plugin 0.1.0 → 0.1.2

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # zeiger-eslint-plugin
2
2
 
3
- ESLint plugin for [Zeiger](https://github.com/DNepovim/zeiger) - ensures proper usage of Zeiger hooks with dependency arrays.
3
+ ESLint plugin for [Zeiger](https://www.npmjs.com/package/zeiger) - ensures proper usage of Zeiger hooks with dependency arrays.
4
+
5
+ Zeiger is a library for creating memoized collection item selectors for [Zustand](https://github.com/pmndrs/zustand) that eliminates unnecessary re-renders. This ESLint plugin helps you use Zeiger correctly by catching common mistakes with dependency arrays.
4
6
 
5
7
  ## Installation
6
8
 
@@ -86,6 +88,34 @@ return (
86
88
  );
87
89
  ```
88
90
 
91
+ ## Autofix
92
+
93
+ The plugin supports automatic fixing of unused dependencies. When you run ESLint with the `--fix` flag, it will automatically remove unused properties from dependency arrays:
94
+
95
+ ```bash
96
+ eslint --fix .
97
+ ```
98
+
99
+ **Example:**
100
+
101
+ ```tsx
102
+ // Before
103
+ const user = useUserPointer('1', ['firstName', 'surname', 'email']);
104
+ return (
105
+ <div>
106
+ {user?.firstName} - {user?.email}
107
+ </div>
108
+ );
109
+
110
+ // After autofix
111
+ const user = useUserPointer('1', ['firstName', 'email']);
112
+ return (
113
+ <div>
114
+ {user?.firstName} - {user?.email}
115
+ </div>
116
+ );
117
+ ```
118
+
89
119
  ## Why This Plugin?
90
120
 
91
121
  Zeiger hooks use dependency arrays to specify which properties to subscribe to. This plugin helps you:
@@ -93,6 +123,7 @@ Zeiger hooks use dependency arrays to specify which properties to subscribe to.
93
123
  - **Avoid unnecessary subscriptions** - Catch unused properties in dependency arrays
94
124
  - **Prevent empty arrays** - Ensure you're always subscribing to at least one property
95
125
  - **Maintain code quality** - Keep your Zeiger hooks clean and efficient
126
+ - **Automatic fixes** - Remove unused dependencies with ESLint's autofix feature
96
127
 
97
128
  ## License
98
129
 
package/dist/index.cjs CHANGED
@@ -29,6 +29,7 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  const rule = {
30
30
  meta: {
31
31
  type: 'problem',
32
+ fixable: 'code',
32
33
  docs: {
33
34
  description: 'Ensure zeiger hook dependency arrays are not empty and all dependencies are used'
34
35
  },
@@ -47,23 +48,30 @@ const rule = {
47
48
  if ('Identifier' !== callee.type) return false;
48
49
  const hookName = callee.name;
49
50
  if (!hookName.startsWith('use')) return false;
51
+ const args = callNode.arguments;
52
+ if (0 === args.length) return false;
53
+ const lastArg = args[args.length - 1];
54
+ if ('ArrayExpression' !== lastArg.type) return false;
50
55
  const scope = sourceCode.getScope(node);
51
56
  const variable = scope.variables.find((v)=>v.name === hookName);
52
57
  if (!variable) return false;
53
- for (const def of variable.defs)if ('Variable' === def.type && def.node.init) {
54
- const init = def.node.init;
55
- if ('CallExpression' === init.type) {
56
- const initCall = init;
57
- const initCallee = initCall.callee;
58
- if ('Identifier' === initCallee.type) {
59
- const initName = initCallee.name;
60
- if ('createCollectionPointer' === initName || 'createCollectionItemPointer' === initName) return true;
61
- }
62
- if ('MemberExpression' === initCallee.type) {
63
- const member = initCallee;
64
- if ('Identifier' === member.property.type && ('createCollectionPointer' === member.property.name || 'createCollectionItemPointer' === member.property.name)) return true;
58
+ for (const def of variable.defs){
59
+ if ('Variable' === def.type && def.node.init) {
60
+ const init = def.node.init;
61
+ if ('CallExpression' === init.type) {
62
+ const initCall = init;
63
+ const initCallee = initCall.callee;
64
+ if ('Identifier' === initCallee.type) {
65
+ const initName = initCallee.name;
66
+ if ('createCollectionPointer' === initName || 'createCollectionItemPointer' === initName) return true;
67
+ }
68
+ if ('MemberExpression' === initCallee.type) {
69
+ const member = initCallee;
70
+ if ('Identifier' === member.property.type && ('createCollectionPointer' === member.property.name || 'createCollectionItemPointer' === member.property.name)) return true;
71
+ }
65
72
  }
66
73
  }
74
+ if ('ImportBinding' === def.type) return true;
67
75
  }
68
76
  return false;
69
77
  }
@@ -145,19 +153,34 @@ const rule = {
145
153
  const depsArg = callNode.arguments.length > 0 ? callNode.arguments[callNode.arguments.length - 1] : null;
146
154
  if (depsArg && 'ArrayExpression' === depsArg.type) {
147
155
  const arrayNode = depsArg;
156
+ const unusedDeps = [];
148
157
  for(let i = 0; i < deps.length; i++){
149
158
  const dep = deps[i];
150
159
  if (!usedProperties.has(dep)) {
151
160
  const depElement = arrayNode.elements[i];
152
- if (depElement) context.report({
153
- node: depElement,
154
- messageId: 'unusedDep',
155
- data: {
156
- property: dep
157
- }
161
+ if (depElement) unusedDeps.push({
162
+ element: depElement,
163
+ index: i,
164
+ property: dep
158
165
  });
159
166
  }
160
167
  }
168
+ if (unusedDeps.length > 0) {
169
+ const usedDeps = deps.filter((dep)=>usedProperties.has(dep));
170
+ for (const { element, property } of unusedDeps)context.report({
171
+ node: element,
172
+ messageId: 'unusedDep',
173
+ data: {
174
+ property
175
+ },
176
+ fix (fixer) {
177
+ if (0 === usedDeps.length) return null;
178
+ const arrayText = usedDeps.map((dep)=>`'${dep}'`).join(', ');
179
+ const newArrayText = `[${arrayText}]`;
180
+ return fixer.replaceText(depsArg, newArrayText);
181
+ }
182
+ });
183
+ }
161
184
  }
162
185
  }
163
186
  };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const rule = {
2
2
  meta: {
3
3
  type: 'problem',
4
+ fixable: 'code',
4
5
  docs: {
5
6
  description: 'Ensure zeiger hook dependency arrays are not empty and all dependencies are used'
6
7
  },
@@ -19,23 +20,30 @@ const rule = {
19
20
  if ('Identifier' !== callee.type) return false;
20
21
  const hookName = callee.name;
21
22
  if (!hookName.startsWith('use')) return false;
23
+ const args = callNode.arguments;
24
+ if (0 === args.length) return false;
25
+ const lastArg = args[args.length - 1];
26
+ if ('ArrayExpression' !== lastArg.type) return false;
22
27
  const scope = sourceCode.getScope(node);
23
28
  const variable = scope.variables.find((v)=>v.name === hookName);
24
29
  if (!variable) return false;
25
- for (const def of variable.defs)if ('Variable' === def.type && def.node.init) {
26
- const init = def.node.init;
27
- if ('CallExpression' === init.type) {
28
- const initCall = init;
29
- const initCallee = initCall.callee;
30
- if ('Identifier' === initCallee.type) {
31
- const initName = initCallee.name;
32
- if ('createCollectionPointer' === initName || 'createCollectionItemPointer' === initName) return true;
33
- }
34
- if ('MemberExpression' === initCallee.type) {
35
- const member = initCallee;
36
- if ('Identifier' === member.property.type && ('createCollectionPointer' === member.property.name || 'createCollectionItemPointer' === member.property.name)) return true;
30
+ for (const def of variable.defs){
31
+ if ('Variable' === def.type && def.node.init) {
32
+ const init = def.node.init;
33
+ if ('CallExpression' === init.type) {
34
+ const initCall = init;
35
+ const initCallee = initCall.callee;
36
+ if ('Identifier' === initCallee.type) {
37
+ const initName = initCallee.name;
38
+ if ('createCollectionPointer' === initName || 'createCollectionItemPointer' === initName) return true;
39
+ }
40
+ if ('MemberExpression' === initCallee.type) {
41
+ const member = initCallee;
42
+ if ('Identifier' === member.property.type && ('createCollectionPointer' === member.property.name || 'createCollectionItemPointer' === member.property.name)) return true;
43
+ }
37
44
  }
38
45
  }
46
+ if ('ImportBinding' === def.type) return true;
39
47
  }
40
48
  return false;
41
49
  }
@@ -117,19 +125,34 @@ const rule = {
117
125
  const depsArg = callNode.arguments.length > 0 ? callNode.arguments[callNode.arguments.length - 1] : null;
118
126
  if (depsArg && 'ArrayExpression' === depsArg.type) {
119
127
  const arrayNode = depsArg;
128
+ const unusedDeps = [];
120
129
  for(let i = 0; i < deps.length; i++){
121
130
  const dep = deps[i];
122
131
  if (!usedProperties.has(dep)) {
123
132
  const depElement = arrayNode.elements[i];
124
- if (depElement) context.report({
125
- node: depElement,
126
- messageId: 'unusedDep',
127
- data: {
128
- property: dep
129
- }
133
+ if (depElement) unusedDeps.push({
134
+ element: depElement,
135
+ index: i,
136
+ property: dep
130
137
  });
131
138
  }
132
139
  }
140
+ if (unusedDeps.length > 0) {
141
+ const usedDeps = deps.filter((dep)=>usedProperties.has(dep));
142
+ for (const { element, property } of unusedDeps)context.report({
143
+ node: element,
144
+ messageId: 'unusedDep',
145
+ data: {
146
+ property
147
+ },
148
+ fix (fixer) {
149
+ if (0 === usedDeps.length) return null;
150
+ const arrayText = usedDeps.map((dep)=>`'${dep}'`).join(', ');
151
+ const newArrayText = `[${arrayText}]`;
152
+ return fixer.replaceText(depsArg, newArrayText);
153
+ }
154
+ });
155
+ }
133
156
  }
134
157
  }
135
158
  };
@@ -1 +1 @@
1
- {"version":3,"file":"zeiger-deps.d.ts","sourceRoot":"","sources":["../../src/rules/zeiger-deps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA2VhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"zeiger-deps.d.ts","sourceRoot":"","sources":["../../src/rules/zeiger-deps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAyYhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
package/package.json CHANGED
@@ -4,8 +4,9 @@
4
4
  "homepage": "https://github.com/DNepovim/zeiger",
5
5
  "repository": "https://github.com/DNepovim/zeiger",
6
6
  "author": "Dominik Bláha <iam@dominikblaha.cz>",
7
- "version": "0.1.0",
7
+ "version": "0.1.2",
8
8
  "type": "module",
9
+ "license": "MIT",
9
10
  "exports": {
10
11
  ".": {
11
12
  "types": "./dist/index.d.ts",