eslint-plugin-power-esrules 0.1.7 → 0.1.9

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.
@@ -11,7 +11,7 @@
11
11
 
12
12
  const path = require("path");
13
13
 
14
- const packetName = "power-linter";
14
+ const packetName = "@hubex/power-linter";
15
15
 
16
16
  /**
17
17
  * Проверяет, является ли класс React компонентом
@@ -20,298 +20,309 @@
20
20
  * Проверяет, содержит ли имя переменной "id" (в любом регистре)
21
21
  */
22
22
  function containsId(name) {
23
- if (!name || typeof name !== 'string') {
24
- return false;
25
- }
26
- return /id/i.test(name);
23
+ if (!name || typeof name !== "string") {
24
+ return false;
25
+ }
26
+ return /id/i.test(name);
27
27
  }
28
28
 
29
29
  /**
30
30
  * Проверяет, правильно ли написано "ID" в имени переменной
31
31
  */
32
32
  function hasCorrectIdFormat(name) {
33
- if (!name || typeof name !== 'string') {
34
- return true; // Если имени нет, пропускаем
35
- }
36
- const lowerName = name.toLowerCase();
37
- const excludedWords = [
38
- 'identifier',
39
- 'valid',
40
- 'invalid',
41
- 'provide',
42
- 'divide',
43
- 'decide',
44
- 'hide',
45
- 'slide',
46
- 'guide',
47
- 'wide',
48
- 'side',
49
- 'messageid',
50
- ];
51
- if (excludedWords.some((word) => lowerName.includes(word)) || lowerName==="id") {
52
- return true;
33
+ if (!name || typeof name !== "string") {
34
+ return true; // Если имени нет, пропускаем
35
+ }
36
+ const lowerName = name.toLowerCase();
37
+ const excludedWords = [
38
+ "identifier",
39
+ "valid",
40
+ "invalid",
41
+ "provide",
42
+ "divide",
43
+ "decide",
44
+ "hide",
45
+ "slide",
46
+ "guide",
47
+ "wide",
48
+ "side",
49
+ "messageid",
50
+ "grid",
51
+ ];
52
+ if (
53
+ excludedWords.some((word) => lowerName.includes(word)) ||
54
+ lowerName === "id"
55
+ ) {
56
+ return true;
57
+ }
58
+ const idMatches = name.matchAll(/([a-zA-Z]*?)([Ii]d|ID)([a-zA-Z]*)/g);
59
+ const matchesArray = Array.from(idMatches);
60
+ for (const match of matchesArray) {
61
+ const idPart = match[2];
62
+ const afterID = match[3];
63
+ if (afterID && afterID.length > 0) {
64
+ continue;
53
65
  }
54
- const idMatches = name.matchAll(/([a-zA-Z]*?)([Ii]d|ID)([a-zA-Z]*)/g);
55
- const matchesArray = Array.from(idMatches);
56
- for (const match of matchesArray) {
57
- const idPart = match[2];
58
- const afterID = match[3];
59
- if (afterID && afterID.length > 0) {
60
- continue;
61
- }
62
- if (idPart !== 'ID') {
63
- return false;
64
- }
66
+ if (idPart !== "ID") {
67
+ return false;
65
68
  }
66
- return true;
69
+ }
70
+ return true;
67
71
  }
68
72
 
69
73
  /**
70
74
  * Получает имя из узла объявления переменной
71
75
  */
72
76
  function getVariableName(node) {
73
- if (!node) {
74
- return null;
75
- }
76
- if (node.type === 'Identifier') {
77
- return node.name;
77
+ if (!node) {
78
+ return null;
79
+ }
80
+ if (node.type === "Identifier") {
81
+ return node.name;
82
+ }
83
+ if (node.type === "VariableDeclarator" && node.id) {
84
+ if (node.id.type === "Identifier") {
85
+ return node.id.name;
78
86
  }
79
- if (node.type === 'VariableDeclarator' && node.id) {
80
- if (node.id.type === 'Identifier') {
81
- return node.id.name;
82
- }
83
- if (node.id.type === 'ObjectPattern') {
84
- return null;
85
- }
87
+ if (node.id.type === "ObjectPattern") {
88
+ return null;
86
89
  }
87
- return null;
90
+ }
91
+ return null;
88
92
  }
89
93
 
90
94
  /**
91
95
  * Генерирует правильное имя с ID в капсе
92
96
  */
93
97
  function fixIdName(name) {
94
- if (!name || typeof name !== 'string') {
95
- return name;
96
- }
97
- return name.replace(/([a-zA-Z]*?)([Ii]d)([^a-zA-Z]*|$)/g, (match, before, idPart, after) => {
98
- if (after && /[a-zA-Z]/.test(after)) {
99
- return match;
100
- }
101
- return `${before}ID${after}`;
102
- });
98
+ if (!name || typeof name !== "string") {
99
+ return name;
100
+ }
101
+ return name.replace(
102
+ /([a-zA-Z]*?)([Ii]d)([^a-zA-Z]*|$)/g,
103
+ (match, before, idPart, after) => {
104
+ if (after && /[a-zA-Z]/.test(after)) {
105
+ return match;
106
+ }
107
+ return `${before}ID${after}`;
108
+ },
109
+ );
103
110
  }
104
111
 
105
112
  /**
106
113
  * Проверяет все идентификаторы в деструктуризации
107
114
  */
108
115
  function checkDestructuredProperties(node, context) {
109
- if (!node || node.type !== 'ObjectPattern') {
110
- return;
111
- }
112
- const { properties } = node;
113
- if (!properties || !Array.isArray(properties)) {
114
- return;
115
- }
116
- for (const prop of properties) {
117
- if (prop.type === 'Property') {
118
- const { key } = prop;
119
- if (key && key.type === 'Identifier') {
120
- const { name } = key;
121
- if (containsId(name) && !hasCorrectIdFormat(name)) {
122
- const suggestedName = fixIdName(name);
123
- context.report({
124
- node: key,
125
- messageId: 'idMustBeUppercase',
126
- data: {
127
- variableName: name,
128
- suggestedName,
129
- },
130
- /*
131
- * OLD auto-fix:
132
- * fix(fixer) {
133
- * return fixer.replaceText(key, suggestedName);
134
- * },
135
- */
136
- suggest: [
137
- {
138
- messageId: 'suggestRenameTo',
139
- data: { suggestedName },
140
- fix(fixer) {
141
- return fixer.replaceText(key, suggestedName);
142
- },
143
- },
144
- ],
145
- });
146
- }
147
- }
116
+ if (!node || node.type !== "ObjectPattern") {
117
+ return;
118
+ }
119
+ const { properties } = node;
120
+ if (!properties || !Array.isArray(properties)) {
121
+ return;
122
+ }
123
+ for (const prop of properties) {
124
+ if (prop.type === "Property") {
125
+ const { key } = prop;
126
+ if (key && key.type === "Identifier") {
127
+ const { name } = key;
128
+ if (containsId(name) && !hasCorrectIdFormat(name)) {
129
+ const suggestedName = fixIdName(name);
130
+ context.report({
131
+ node: key,
132
+ messageId: "idMustBeUppercase",
133
+ data: {
134
+ variableName: name,
135
+ suggestedName,
136
+ },
137
+ /*
138
+ * OLD auto-fix:
139
+ * fix(fixer) {
140
+ * return fixer.replaceText(key, suggestedName);
141
+ * },
142
+ */
143
+ suggest: [
144
+ {
145
+ messageId: "suggestRenameTo",
146
+ data: { suggestedName },
147
+ fix(fixer) {
148
+ return fixer.replaceText(key, suggestedName);
149
+ },
150
+ },
151
+ ],
152
+ });
148
153
  }
154
+ }
149
155
  }
156
+ }
150
157
  }
151
158
 
152
159
  module.exports = {
153
- meta: {
154
- type: 'suggestion',
155
- docs: {
156
- description: 'Проверяет, что идентификаторы используют ID в капсе',
157
- category: 'Stylistic Issues',
158
- recommended: false,
159
- },
160
- // fixable: 'code', // OLD: auto-fixable (eslint --fix)
161
- hasSuggestions: true, // NEW: quick-fix suggestions (not applied by eslint --fix)
162
- schema: [],
163
- messages: {
164
- idMustBeUppercase:
165
- 'Идентификаторы должны использовать "ID" в капсе. ' +
166
- 'Вместо "{{variableName}}" используйте "{{suggestedName}}"',
167
- suggestRenameTo: 'Переименовать в "{{suggestedName}}"',
168
- },
160
+ meta: {
161
+ type: "suggestion",
162
+ docs: {
163
+ description: "Проверяет, что идентификаторы используют ID в капсе",
164
+ category: "Stylistic Issues",
165
+ recommended: false,
169
166
  },
167
+ // fixable: 'code', // OLD: auto-fixable (eslint --fix)
168
+ hasSuggestions: true, // NEW: quick-fix suggestions (not applied by eslint --fix)
169
+ schema: [],
170
+ messages: {
171
+ idMustBeUppercase:
172
+ 'Идентификаторы должны использовать "ID" в капсе. ' +
173
+ 'Вместо "{{variableName}}" используйте "{{suggestedName}}"',
174
+ suggestRenameTo: 'Переименовать в "{{suggestedName}}"',
175
+ },
176
+ },
170
177
 
171
- create(context) {
172
- return {
173
- VariableDeclarator(node) {
174
- const variableName = getVariableName(node);
175
- if (variableName && containsId(variableName) && !hasCorrectIdFormat(variableName)) {
176
- const suggestedName = fixIdName(variableName);
177
- context.report({
178
- node: node.id,
179
- messageId: 'idMustBeUppercase',
180
- data: {
181
- variableName,
182
- suggestedName,
183
- },
184
- /*
185
- * OLD auto-fix:
186
- * fix(fixer) {
187
- * return fixer.replaceText(node.id, suggestedName);
188
- * },
189
- */
190
- suggest: [
191
- {
192
- messageId: 'suggestRenameTo',
193
- data: { suggestedName },
194
- fix(fixer) {
195
- return fixer.replaceText(node.id, suggestedName);
196
- },
197
- },
198
- ],
199
- });
200
- }
201
- if (node.id && node.id.type === 'ObjectPattern') {
202
- checkDestructuredProperties(node.id, context);
203
- }
178
+ create(context) {
179
+ return {
180
+ VariableDeclarator(node) {
181
+ const variableName = getVariableName(node);
182
+ if (
183
+ variableName &&
184
+ containsId(variableName) &&
185
+ !hasCorrectIdFormat(variableName)
186
+ ) {
187
+ const suggestedName = fixIdName(variableName);
188
+ context.report({
189
+ node: node.id,
190
+ messageId: "idMustBeUppercase",
191
+ data: {
192
+ variableName,
193
+ suggestedName,
204
194
  },
195
+ /*
196
+ * OLD auto-fix:
197
+ * fix(fixer) {
198
+ * return fixer.replaceText(node.id, suggestedName);
199
+ * },
200
+ */
201
+ suggest: [
202
+ {
203
+ messageId: "suggestRenameTo",
204
+ data: { suggestedName },
205
+ fix(fixer) {
206
+ return fixer.replaceText(node.id, suggestedName);
207
+ },
208
+ },
209
+ ],
210
+ });
211
+ }
212
+ if (node.id && node.id.type === "ObjectPattern") {
213
+ checkDestructuredProperties(node.id, context);
214
+ }
215
+ },
205
216
 
206
- FunctionDeclaration(node) {
207
- if (!node.params || !Array.isArray(node.params)) {
208
- return;
209
- }
210
- for (const param of node.params) {
211
- if (param.type === 'Identifier') {
212
- const { name } = param;
213
- if (containsId(name) && !hasCorrectIdFormat(name)) {
214
- const suggestedName = fixIdName(name);
215
- context.report({
216
- node: param,
217
- messageId: 'idMustBeUppercase',
218
- data: {
219
- variableName: name,
220
- suggestedName,
221
- },
222
- /*
223
- * OLD auto-fix:
224
- * fix(fixer) {
225
- * return fixer.replaceText(param, suggestedName);
226
- * },
227
- */
228
- suggest: [
229
- {
230
- messageId: 'suggestRenameTo',
231
- data: { suggestedName },
232
- fix(fixer) {
233
- return fixer.replaceText(param, suggestedName);
234
- },
235
- },
236
- ],
237
- });
238
- }
239
- } else if (param.type === 'ObjectPattern') {
240
- checkDestructuredProperties(param, context);
241
- }
242
- }
243
- },
217
+ FunctionDeclaration(node) {
218
+ if (!node.params || !Array.isArray(node.params)) {
219
+ return;
220
+ }
221
+ for (const param of node.params) {
222
+ if (param.type === "Identifier") {
223
+ const { name } = param;
224
+ if (containsId(name) && !hasCorrectIdFormat(name)) {
225
+ const suggestedName = fixIdName(name);
226
+ context.report({
227
+ node: param,
228
+ messageId: "idMustBeUppercase",
229
+ data: {
230
+ variableName: name,
231
+ suggestedName,
232
+ },
233
+ /*
234
+ * OLD auto-fix:
235
+ * fix(fixer) {
236
+ * return fixer.replaceText(param, suggestedName);
237
+ * },
238
+ */
239
+ suggest: [
240
+ {
241
+ messageId: "suggestRenameTo",
242
+ data: { suggestedName },
243
+ fix(fixer) {
244
+ return fixer.replaceText(param, suggestedName);
245
+ },
246
+ },
247
+ ],
248
+ });
249
+ }
250
+ } else if (param.type === "ObjectPattern") {
251
+ checkDestructuredProperties(param, context);
252
+ }
253
+ }
254
+ },
244
255
 
245
- ArrowFunctionExpression(node) {
246
- if (!node.params || !Array.isArray(node.params)) {
247
- return;
248
- }
249
- for (const param of node.params) {
250
- if (param.type === 'Identifier') {
251
- const { name } = param;
252
- if (containsId(name) && !hasCorrectIdFormat(name)) {
253
- const suggestedName = fixIdName(name);
254
- context.report({
255
- node: param,
256
- messageId: 'idMustBeUppercase',
257
- data: {
258
- variableName: name,
259
- suggestedName,
260
- },
261
- /*
262
- * OLD auto-fix:
263
- * fix(fixer) {
264
- * return fixer.replaceText(param, suggestedName);
265
- * },
266
- */
267
- suggest: [
268
- {
269
- messageId: 'suggestRenameTo',
270
- data: { suggestedName },
271
- fix(fixer) {
272
- return fixer.replaceText(param, suggestedName);
273
- },
274
- },
275
- ],
276
- });
277
- }
278
- } else if (param.type === 'ObjectPattern') {
279
- checkDestructuredProperties(param, context);
280
- }
281
- }
282
- },
256
+ ArrowFunctionExpression(node) {
257
+ if (!node.params || !Array.isArray(node.params)) {
258
+ return;
259
+ }
260
+ for (const param of node.params) {
261
+ if (param.type === "Identifier") {
262
+ const { name } = param;
263
+ if (containsId(name) && !hasCorrectIdFormat(name)) {
264
+ const suggestedName = fixIdName(name);
265
+ context.report({
266
+ node: param,
267
+ messageId: "idMustBeUppercase",
268
+ data: {
269
+ variableName: name,
270
+ suggestedName,
271
+ },
272
+ /*
273
+ * OLD auto-fix:
274
+ * fix(fixer) {
275
+ * return fixer.replaceText(param, suggestedName);
276
+ * },
277
+ */
278
+ suggest: [
279
+ {
280
+ messageId: "suggestRenameTo",
281
+ data: { suggestedName },
282
+ fix(fixer) {
283
+ return fixer.replaceText(param, suggestedName);
284
+ },
285
+ },
286
+ ],
287
+ });
288
+ }
289
+ } else if (param.type === "ObjectPattern") {
290
+ checkDestructuredProperties(param, context);
291
+ }
292
+ }
293
+ },
283
294
 
284
- Property(node) {
285
- if (node.key && node.key.type === 'Identifier') {
286
- const { name } = node.key;
287
- if (containsId(name) && !hasCorrectIdFormat(name)) {
288
- const suggestedName = fixIdName(name);
289
- context.report({
290
- node: node.key,
291
- messageId: 'idMustBeUppercase',
292
- data: {
293
- variableName: name,
294
- suggestedName,
295
- },
296
- /*
297
- * OLD auto-fix:
298
- * fix(fixer) {
299
- * return fixer.replaceText(node.key, suggestedName);
300
- * },
301
- */
302
- suggest: [
303
- {
304
- messageId: 'suggestRenameTo',
305
- data: { suggestedName },
306
- fix(fixer) {
307
- return fixer.replaceText(node.key, suggestedName);
308
- },
309
- },
310
- ],
311
- });
312
- }
313
- }
314
- },
315
- };
316
- },
295
+ Property(node) {
296
+ if (node.key && node.key.type === "Identifier") {
297
+ const { name } = node.key;
298
+ if (containsId(name) && !hasCorrectIdFormat(name)) {
299
+ const suggestedName = fixIdName(name);
300
+ context.report({
301
+ node: node.key,
302
+ messageId: "idMustBeUppercase",
303
+ data: {
304
+ variableName: name,
305
+ suggestedName,
306
+ },
307
+ /*
308
+ * OLD auto-fix:
309
+ * fix(fixer) {
310
+ * return fixer.replaceText(node.key, suggestedName);
311
+ * },
312
+ */
313
+ suggest: [
314
+ {
315
+ messageId: "suggestRenameTo",
316
+ data: { suggestedName },
317
+ fix(fixer) {
318
+ return fixer.replaceText(node.key, suggestedName);
319
+ },
320
+ },
321
+ ],
322
+ });
323
+ }
324
+ }
325
+ },
326
+ };
327
+ },
317
328
  };
@@ -44,23 +44,31 @@ function getImportType(node) {
44
44
 
45
45
  // Сторонние библиотеки (не относительные и не начинаются с внутренних путей)
46
46
  const internalPathPatterns = [
47
+ /^actions/,
48
+ /^api/,
49
+ /^base/,
47
50
  /^components/,
48
- /^utils/,
51
+ /^containers/,
52
+ /^hooks/,
53
+ /^layouts/,
54
+ /^locales/,
55
+ /^middlewares/,
49
56
  /^modules/,
50
57
  /^pages/,
58
+ /^reducers/,
59
+ /^sagas/,
60
+ /^selectors/,
61
+ /^theme/,
62
+ /^utils/,
63
+ //
51
64
  /^services/,
52
65
  /^store/,
53
66
  /^types/,
54
67
  /^constants/,
55
- /^hooks/,
56
68
  /^helpers/,
57
- /^selectors/,
58
- /^base/,
59
- /^actions/,
60
- /^containers/,
61
69
  ];
62
70
  const isInternalPath = internalPathPatterns.some((pattern) =>
63
- pattern.test(source)
71
+ pattern.test(source),
64
72
  );
65
73
  if (!isInternalPath) {
66
74
  return "external";
@@ -76,14 +84,14 @@ function getImportType(node) {
76
84
  }
77
85
  // Проверяем, является ли это компонентом
78
86
  const isDefaultImport = node.specifiers.some(
79
- (spec) => spec.type === "ImportDefaultSpecifier"
87
+ (spec) => spec.type === "ImportDefaultSpecifier",
80
88
  );
81
89
  const hasComponentName = node.specifiers.some(
82
90
  (spec) =>
83
91
  spec.type === "ImportSpecifier" &&
84
92
  spec.imported &&
85
93
  /^[A-Z][a-zA-Z]*$/.test(spec.imported.name) && // Имя начинается с большой буквы и не все заглавные
86
- spec.imported.name !== spec.imported.name.toUpperCase() // Не константа (не все заглавные)
94
+ spec.imported.name !== spec.imported.name.toUpperCase(), // Не константа (не все заглавные)
87
95
  );
88
96
  if (isDefaultImport || hasComponentName) {
89
97
  return "component";
@@ -95,7 +103,7 @@ function getImportType(node) {
95
103
  spec.type === "ImportSpecifier" &&
96
104
  spec.imported &&
97
105
  /^[A-Z][a-zA-Z]*$/.test(spec.imported.name) &&
98
- spec.imported.name !== spec.imported.name.toUpperCase() // Не константа
106
+ spec.imported.name !== spec.imported.name.toUpperCase(), // Не константа
99
107
  );
100
108
  if (hasComponentName) {
101
109
  return "component";
@@ -131,6 +139,9 @@ function compareImports(a, b) {
131
139
  // Затем по имени источника (алфавитно)
132
140
  const sourceA = a.source.value;
133
141
  const sourceB = b.source.value;
142
+ // импорты react всегда идут первые
143
+ if (sourceA === "react" && sourceB !== "react") return -1;
144
+ if (sourceB === "react" && sourceA !== "react") return 1;
134
145
  return sourceA.localeCompare(sourceB);
135
146
  }
136
147
 
@@ -166,7 +177,7 @@ module.exports = {
166
177
  return {
167
178
  Program(node) {
168
179
  const imports = node.body.filter(
169
- (stmt) => stmt.type === "ImportDeclaration"
180
+ (stmt) => stmt.type === "ImportDeclaration",
170
181
  );
171
182
 
172
183
  if (imports.length === 0) {
@@ -209,7 +220,7 @@ module.exports = {
209
220
  }
210
221
  return fixer.replaceTextRange(
211
222
  [firstToken.range[0], lastToken.range[1]],
212
- sortedText
223
+ sortedText,
213
224
  );
214
225
  },
215
226
  });
@@ -258,7 +269,7 @@ module.exports = {
258
269
  const fixedText = textBetween.replace(/\n\s*\n+/g, "\n");
259
270
  return fixer.replaceTextRange(
260
271
  [lastTokenOfPrev.range[1], firstToken.range[0]],
261
- fixedText
272
+ fixedText,
262
273
  );
263
274
  },
264
275
  });
@@ -7,7 +7,7 @@
7
7
 
8
8
  const path = require("path");
9
9
 
10
- const packetName = "power-linter";
10
+ const packetName = "@hubex/power-linter";
11
11
 
12
12
  /**
13
13
  * Получает имя JSX элемента
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-power-esrules",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "custom ESLint rules",
5
5
  "main": "index.js",
6
6
  "peerDependencies": {