@blumintinc/eslint-plugin-blumint 1.8.2 → 1.9.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/lib/index.js CHANGED
@@ -96,10 +96,11 @@ const enforce_css_media_queries_1 = require("./rules/enforce-css-media-queries")
96
96
  const omit_index_html_1 = require("./rules/omit-index-html");
97
97
  const enforce_id_capitalization_1 = require("./rules/enforce-id-capitalization");
98
98
  const no_unused_usestate_1 = require("./rules/no-unused-usestate");
99
+ const no_uuidv4_base62_as_key_1 = require("./rules/no-uuidv4-base62-as-key");
99
100
  module.exports = {
100
101
  meta: {
101
102
  name: '@blumintinc/eslint-plugin-blumint',
102
- version: '1.8.2',
103
+ version: '1.9.1',
103
104
  },
104
105
  parseOptions: {
105
106
  ecmaVersion: 2020,
@@ -134,6 +135,7 @@ module.exports = {
134
135
  '@blumintinc/blumint/no-misused-switch-case': 'error',
135
136
  '@blumintinc/blumint/no-unpinned-dependencies': 'error',
136
137
  '@blumintinc/blumint/no-unused-props': 'error',
138
+ '@blumintinc/blumint/no-uuidv4-base62-as-key': 'error',
137
139
  //'@blumintinc/blumint/no-useless-fragment': 'error',
138
140
  //'@blumintinc/blumint/prefer-fragment-shorthand': 'error',
139
141
  '@blumintinc/blumint/prefer-type-over-interface': 'error',
@@ -235,6 +237,7 @@ module.exports = {
235
237
  'no-unpinned-dependencies': no_unpinned_dependencies_1.noUnpinnedDependencies,
236
238
  'no-unused-props': no_unused_props_1.noUnusedProps,
237
239
  'no-useless-fragment': no_useless_fragment_1.noUselessFragment,
240
+ 'no-uuidv4-base62-as-key': no_uuidv4_base62_as_key_1.noUuidv4Base62AsKey,
238
241
  'prefer-fragment-shorthand': prefer_fragment_shorthand_1.preferFragmentShorthand,
239
242
  'prefer-type-over-interface': prefer_type_over_interface_1.preferTypeOverInterface,
240
243
  'require-memo': require_memo_1.requireMemo,
@@ -1,5 +1 @@
1
- type Options = {
2
- allowClassInstances?: boolean;
3
- };
4
- export declare const noHungarian: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noHungarian", [Options], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
5
- export {};
1
+ export declare const noHungarian: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noHungarian", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -2,220 +2,376 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noHungarian = void 0;
4
4
  const createRule_1 = require("../utils/createRule");
5
+ const utils_1 = require("@typescript-eslint/utils");
5
6
  // Common built-in types that might be used in Hungarian notation
6
7
  const COMMON_TYPES = [
7
- 'String', 'Number', 'Boolean', 'Array', 'Object',
8
- 'Function', 'Date', 'RegExp', 'Promise', 'Map',
9
- 'Set', 'Symbol', 'BigInt', 'Error'
8
+ 'String',
9
+ 'Number',
10
+ 'Boolean',
11
+ 'Array',
12
+ 'Object',
13
+ 'Function',
14
+ 'Date',
15
+ 'RegExp',
16
+ 'Promise',
17
+ 'Symbol',
18
+ 'BigInt',
10
19
  ];
11
- // Common Hungarian notation prefixes
12
- const HUNGARIAN_PREFIXES = [
13
- 'str', 'num', 'int', 'bool', 'arr', 'obj', 'fn', 'func'
20
+ // Combined type markers (former Hungarian prefixes and type suffixes)
21
+ const TYPE_MARKERS = [
22
+ 'str',
23
+ 'num',
24
+ 'int',
25
+ 'bool',
26
+ 'arr',
27
+ 'obj',
28
+ 'fn',
29
+ 'func',
30
+ 'array',
31
+ ...COMMON_TYPES,
32
+ 'Class',
33
+ 'Interface',
34
+ 'Type',
35
+ 'Enum',
14
36
  ];
37
+ // Common built-in JavaScript prototype methods
38
+ const BUILT_IN_METHODS = new Set([
39
+ // String methods
40
+ 'charAt',
41
+ 'charCodeAt',
42
+ 'codePointAt',
43
+ 'concat',
44
+ 'endsWith',
45
+ 'includes',
46
+ 'indexOf',
47
+ 'lastIndexOf',
48
+ 'localeCompare',
49
+ 'match',
50
+ 'matchAll',
51
+ 'normalize',
52
+ 'padEnd',
53
+ 'padStart',
54
+ 'repeat',
55
+ 'replace',
56
+ 'replaceAll',
57
+ 'search',
58
+ 'slice',
59
+ 'split',
60
+ 'startsWith',
61
+ 'substring',
62
+ 'toLocaleLowerCase',
63
+ 'toLocaleUpperCase',
64
+ 'toLowerCase',
65
+ 'toString',
66
+ 'toUpperCase',
67
+ 'trim',
68
+ 'trimEnd',
69
+ 'trimStart',
70
+ 'valueOf',
71
+ // Array methods
72
+ 'forEach',
73
+ 'map',
74
+ 'filter',
75
+ 'reduce',
76
+ 'reduceRight',
77
+ 'some',
78
+ 'every',
79
+ 'find',
80
+ 'findIndex',
81
+ 'findLast',
82
+ 'findLastIndex',
83
+ 'keys',
84
+ 'values',
85
+ 'entries',
86
+ 'push',
87
+ 'pop',
88
+ 'shift',
89
+ 'unshift',
90
+ 'slice',
91
+ 'splice',
92
+ 'sort',
93
+ 'reverse',
94
+ 'flatMap',
95
+ 'flat',
96
+ 'concat',
97
+ 'join',
98
+ 'includes',
99
+ 'indexOf',
100
+ 'lastIndexOf',
101
+ 'fill',
102
+ 'copyWithin',
103
+ // Object methods
104
+ 'hasOwnProperty',
105
+ 'isPrototypeOf',
106
+ 'propertyIsEnumerable',
107
+ 'toLocaleString',
108
+ 'toString',
109
+ 'valueOf',
110
+ 'assign',
111
+ 'create',
112
+ 'defineProperty',
113
+ 'defineProperties',
114
+ 'entries',
115
+ 'freeze',
116
+ 'fromEntries',
117
+ 'getOwnPropertyDescriptor',
118
+ 'getOwnPropertyDescriptors',
119
+ 'getOwnPropertyNames',
120
+ 'getOwnPropertySymbols',
121
+ 'getPrototypeOf',
122
+ 'is',
123
+ 'isExtensible',
124
+ 'isFrozen',
125
+ 'isSealed',
126
+ 'keys',
127
+ 'preventExtensions',
128
+ 'seal',
129
+ 'setPrototypeOf',
130
+ 'values',
131
+ // Date methods
132
+ 'getDate',
133
+ 'getDay',
134
+ 'getFullYear',
135
+ 'getHours',
136
+ 'getMilliseconds',
137
+ 'getMinutes',
138
+ 'getMonth',
139
+ 'getSeconds',
140
+ 'getTime',
141
+ 'getTimezoneOffset',
142
+ 'getUTCDate',
143
+ 'getUTCDay',
144
+ 'getUTCFullYear',
145
+ 'getUTCHours',
146
+ 'getUTCMilliseconds',
147
+ 'getUTCMinutes',
148
+ 'getUTCMonth',
149
+ 'getUTCSeconds',
150
+ 'setDate',
151
+ 'setFullYear',
152
+ 'setHours',
153
+ 'setMilliseconds',
154
+ 'setMinutes',
155
+ 'setMonth',
156
+ 'setSeconds',
157
+ 'setTime',
158
+ 'setUTCDate',
159
+ 'setUTCFullYear',
160
+ 'setUTCHours',
161
+ 'setUTCMilliseconds',
162
+ 'setUTCMinutes',
163
+ 'setUTCMonth',
164
+ 'setUTCSeconds',
165
+ 'toDateString',
166
+ 'toISOString',
167
+ 'toJSON',
168
+ 'toLocaleDateString',
169
+ 'toLocaleString',
170
+ 'toLocaleTimeString',
171
+ 'toString',
172
+ 'toTimeString',
173
+ 'toUTCString',
174
+ 'valueOf',
175
+ // Promise methods
176
+ 'then',
177
+ 'catch',
178
+ 'finally',
179
+ ]);
15
180
  exports.noHungarian = (0, createRule_1.createRule)({
16
181
  name: 'no-hungarian',
17
182
  meta: {
18
183
  type: 'suggestion',
19
184
  docs: {
20
- description: 'Disallow Hungarian notation in variable names',
185
+ description: 'Disallow Hungarian notation in locally declared variables, types, and classes',
21
186
  recommended: 'error',
22
187
  },
23
- fixable: 'code',
24
- schema: [
25
- {
26
- type: 'object',
27
- properties: {
28
- allowClassInstances: {
29
- type: 'boolean',
30
- },
31
- },
32
- additionalProperties: false,
33
- },
34
- ],
188
+ fixable: undefined,
189
+ schema: [],
35
190
  messages: {
36
191
  noHungarian: 'Avoid Hungarian notation in variable name "{{name}}"',
37
192
  },
38
193
  },
39
- defaultOptions: [
40
- {
41
- allowClassInstances: true,
42
- },
43
- ],
44
- create(context, [options]) {
45
- const allowClassInstances = options.allowClassInstances !== false;
46
- // Helper function to extract the class/type name from a node
47
- function getTypeName(node) {
48
- if (!node)
49
- return null;
50
- // Handle new expressions (e.g., new Controller())
51
- if (node.type === 'NewExpression' && node.callee.type === 'Identifier') {
52
- return node.callee.name;
194
+ defaultOptions: [],
195
+ create(context) {
196
+ // Track identifiers that have already been checked to prevent double reporting
197
+ const checkedIdentifiers = new Set();
198
+ // Check if a variable name contains a type marker with proper word boundaries
199
+ function hasTypeMarker(variableName) {
200
+ const normalizedVarName = variableName.toLowerCase();
201
+ // Handle SCREAMING_SNAKE_CASE separately
202
+ if (variableName === variableName.toUpperCase() &&
203
+ variableName.includes('_')) {
204
+ return TYPE_MARKERS.some((marker) => {
205
+ const markerUpper = marker.toUpperCase();
206
+ // Check if it's a prefix (PREFIX_REST)
207
+ if (variableName.startsWith(markerUpper + '_') &&
208
+ variableName.length > markerUpper.length + 1) {
209
+ return true;
210
+ }
211
+ // Check if it's a suffix (REST_SUFFIX)
212
+ if (variableName.endsWith('_' + markerUpper) &&
213
+ variableName.length > markerUpper.length + 1) {
214
+ return true;
215
+ }
216
+ // Check if it's in the middle (PART_MARKER_PART)
217
+ const parts = variableName.split('_');
218
+ return parts.some((part) => part === markerUpper);
219
+ });
53
220
  }
54
- // Handle type annotations if available
55
- if (node.type === 'TSTypeAnnotation') {
56
- const typeNode = node.typeAnnotation;
57
- if (typeNode.type === 'TSStringKeyword')
58
- return 'String';
59
- if (typeNode.type === 'TSNumberKeyword')
60
- return 'Number';
61
- if (typeNode.type === 'TSBooleanKeyword')
62
- return 'Boolean';
63
- if (typeNode.type === 'TSArrayType')
64
- return 'Array';
65
- if (typeNode.type === 'TSObjectKeyword')
66
- return 'Object';
67
- if (typeNode.type === 'TSFunctionType')
68
- return 'Function';
69
- // Handle reference types like interfaces or classes
70
- if (typeNode.type === 'TSTypeReference' && typeNode.typeName.type === 'Identifier') {
71
- return typeNode.typeName.name;
221
+ // For camelCase, PascalCase, etc.
222
+ return TYPE_MARKERS.some((marker) => {
223
+ const normalizedMarker = marker.toLowerCase();
224
+ // If the variable name is exactly the marker, ignore it
225
+ if (normalizedVarName === normalizedMarker) {
226
+ return false;
72
227
  }
73
- }
74
- return null;
75
- }
76
- // Check if a variable name has a type name as suffix (Hungarian notation)
77
- function hasTypeSuffix(variableName, typeName) {
78
- // Normalize both strings to lowercase for case-insensitive comparison
79
- const normalizedVarName = variableName.toLowerCase();
80
- const normalizedTypeName = typeName.toLowerCase();
81
- // Check if the variable name ends with the type name (suffix)
82
- return normalizedVarName.endsWith(normalizedTypeName) &&
83
- normalizedVarName !== normalizedTypeName;
84
- }
85
- // Check if a variable name has a Hungarian prefix
86
- function hasHungarianPrefix(variableName) {
87
- const normalizedVarName = variableName.toLowerCase();
88
- return HUNGARIAN_PREFIXES.some(prefix => {
89
- // Check if the variable starts with the prefix
90
- return normalizedVarName.startsWith(prefix.toLowerCase()) &&
91
- // Make sure it's not just the prefix itself
92
- normalizedVarName.length > prefix.length;
228
+ // Check if it's a prefix with proper boundary (e.g., strName, numCount)
229
+ if (normalizedVarName.startsWith(normalizedMarker) &&
230
+ normalizedVarName.length > normalizedMarker.length &&
231
+ /[A-Z0-9]/.test(variableName[normalizedMarker.length])) {
232
+ return true;
233
+ }
234
+ // Check if it's a suffix with proper boundary (e.g., userString, itemArray)
235
+ if (normalizedVarName.endsWith(normalizedMarker) &&
236
+ normalizedVarName.length > normalizedMarker.length &&
237
+ /[A-Z0-9]/.test(variableName[variableName.length - normalizedMarker.length - 1])) {
238
+ return true;
239
+ }
240
+ const markerIndex = normalizedVarName.indexOf(normalizedMarker);
241
+ if (markerIndex === -1) {
242
+ return false;
243
+ }
244
+ const markerPrefix = variableName.at(markerIndex);
245
+ const preMarkerPrefix = variableName.at(markerIndex - 1);
246
+ const suffix = variableName.at(markerIndex + normalizedMarker.length);
247
+ return ((!markerPrefix ||
248
+ preMarkerPrefix === '_' ||
249
+ /[A-Z]/.test(markerPrefix)) &&
250
+ (!suffix || suffix === '_' || /[A-Z]/.test(suffix)));
93
251
  });
94
252
  }
95
- // Check if a variable name uses Hungarian notation
96
- function isHungarianNotation(variableName, declaredTypeName) {
97
- // Check for Hungarian prefixes (e.g., strName, boolIsActive)
98
- if (hasHungarianPrefix(variableName)) {
99
- return true;
100
- }
101
- // Check for type suffixes (e.g., nameString, countNumber)
102
- // If we have a declared type, check if it's used as a suffix
103
- if (declaredTypeName && hasTypeSuffix(variableName, declaredTypeName)) {
104
- return true;
253
+ // Check if the identifier is a built-in method or imported from an external module
254
+ function isExternalOrBuiltIn(node) {
255
+ // Check if the identifier is a property in a member expression
256
+ // (e.g., the 'startsWith' in 'pathname.startsWith')
257
+ if (node.parent &&
258
+ node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
259
+ node.parent.property === node) {
260
+ // Check if it's a known built-in method
261
+ if (BUILT_IN_METHODS.has(node.name)) {
262
+ return true;
263
+ }
105
264
  }
106
- // Check against common types if no declared type is found
107
- return COMMON_TYPES.some(typeName => hasTypeSuffix(variableName, typeName));
108
- }
109
- // Helper function to check if a variable name contains a class name
110
- function variableContainsClassName(varName, className) {
111
- return varName.toLowerCase().includes(className.toLowerCase());
112
- }
113
- // Helper function to check if a node is a class property or method
114
- function isClassProperty(node) {
115
- let current = node.parent;
116
- while (current) {
117
- if (current.type === 'ClassBody' || current.type === 'ClassDeclaration' || current.type === 'ClassExpression') {
265
+ // Check if it's an imported identifier
266
+ const scope = context.getScope();
267
+ const variable = scope.variables.find((v) => v.name === node.name);
268
+ if (variable && variable.defs.length > 0) {
269
+ // Check if it's an import binding
270
+ const def = variable.defs[0];
271
+ if (def.type === 'ImportBinding') {
118
272
  return true;
119
273
  }
120
- current = current.parent;
121
274
  }
122
275
  return false;
123
276
  }
124
- // Helper function to check if a variable name is a valid exception
125
- function isValidException(variableName) {
126
- // List of valid variable names that might be incorrectly flagged
127
- const validExceptions = [
128
- 'stringifyData', 'numberFormatter', 'booleanLogic', 'arrayMethods',
129
- 'objectAssign', 'booleanToggle', 'arrayHelpers', 'objectMapper',
130
- 'stringBuilder', 'numberParser', 'booleanEvaluator', 'arrayCollection',
131
- 'objectPool', 'myStringUtils', 'numberConverter', 'strongPassword',
132
- 'wrongAnswer', 'longList', 'foreignKey'
133
- ];
134
- return validExceptions.includes(variableName);
135
- }
136
- // Helper function to check if a variable name is in the test cases
137
- function isTestCase(variableName) {
138
- // List of variable names from the test cases that should be flagged
139
- const testCases = [
140
- 'usernameString', 'isReadyBoolean', 'countNumber', 'itemsArray',
141
- 'userDataObject', 'resultString', 'indexNumber', 'nameString',
142
- 'ageNumber', 'outerString', 'innerString', 'nestedString',
143
- 'userObjectArray', 'arrayOfItems', 'strName', 'intAge', 'boolIsActive'
144
- ];
145
- return testCases.includes(variableName);
277
+ // Check identifier for type markers (Hungarian notation)
278
+ function checkIdentifier(node) {
279
+ const name = node.name;
280
+ // Create a unique ID for this node to avoid checking it twice
281
+ // Use the name along with source location for uniqueness
282
+ const nodeId = `${name}:${node.loc.start.line}:${node.loc.start.column}`;
283
+ // Skip if we've already checked this identifier
284
+ if (checkedIdentifiers.has(nodeId)) {
285
+ return;
286
+ }
287
+ // Mark this identifier as checked
288
+ checkedIdentifiers.add(nodeId);
289
+ // Skip if the identifier is a built-in method or imported from an external module
290
+ if (isExternalOrBuiltIn(node))
291
+ return;
292
+ // Check for type markers
293
+ if (hasTypeMarker(name)) {
294
+ context.report({
295
+ node,
296
+ messageId: 'noHungarian',
297
+ data: { name },
298
+ });
299
+ }
146
300
  }
147
301
  return {
302
+ // Check variable declarations
148
303
  VariableDeclarator(node) {
149
- // Skip destructuring patterns
150
- if (node.id.type !== 'Identifier') {
151
- return;
152
- }
153
- const variableName = node.id.name;
154
- // Skip variables that are exactly the same as a type name
155
- if (COMMON_TYPES.includes(variableName)) {
156
- return;
304
+ if (node.id.type === utils_1.AST_NODE_TYPES.Identifier) {
305
+ checkIdentifier(node.id);
157
306
  }
158
- // Skip known valid exceptions
159
- if (isValidException(variableName)) {
160
- return;
307
+ },
308
+ // Check function declarations
309
+ FunctionDeclaration(node) {
310
+ if (node.id) {
311
+ checkIdentifier(node.id);
161
312
  }
162
- // Special handling for test cases
163
- if (isTestCase(variableName)) {
164
- context.report({
165
- node,
166
- messageId: 'noHungarian',
167
- data: {
168
- name: variableName,
169
- },
170
- });
171
- return;
313
+ // Check function parameters
314
+ for (const param of node.params) {
315
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
316
+ checkIdentifier(param);
317
+ }
318
+ else if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
319
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
320
+ checkIdentifier(param.left);
321
+ }
172
322
  }
173
- // Get type information from initialization or type annotation
174
- let typeName = null;
175
- // Check for type annotation
176
- if (node.id.typeAnnotation) {
177
- typeName = getTypeName(node.id.typeAnnotation);
323
+ },
324
+ // Check function expressions and arrow functions
325
+ 'FunctionExpression, ArrowFunctionExpression'(node) {
326
+ // Check function parameters
327
+ for (const param of node.params) {
328
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
329
+ checkIdentifier(param);
330
+ }
331
+ else if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
332
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
333
+ checkIdentifier(param.left);
334
+ }
178
335
  }
179
- // Check initialization if no type annotation or to get more specific type
180
- if (!typeName && node.init) {
181
- typeName = getTypeName(node.init);
336
+ },
337
+ // Check class declarations
338
+ ClassDeclaration(node) {
339
+ if (node.id) {
340
+ checkIdentifier(node.id);
182
341
  }
183
- // Handle class instances
184
- if (node.init && node.init.type === 'NewExpression') {
185
- const className = getTypeName(node.init);
186
- // If we're allowing class instances and the variable name contains the class name
187
- if (allowClassInstances && className && variableContainsClassName(variableName, className)) {
188
- return;
342
+ // Check class methods and properties
343
+ for (const member of node.body.body) {
344
+ if (member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
345
+ member.key.type === utils_1.AST_NODE_TYPES.Identifier) {
346
+ // Check method name
347
+ checkIdentifier(member.key);
348
+ // Check method parameters
349
+ if (member.value.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
350
+ for (const param of member.value.params) {
351
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
352
+ checkIdentifier(param);
353
+ }
354
+ else if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
355
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
356
+ checkIdentifier(param.left);
357
+ }
358
+ }
359
+ }
189
360
  }
190
- // Special handling for the case with allowClassInstances: false
191
- if (!allowClassInstances && className && variableContainsClassName(variableName, className)) {
192
- context.report({
193
- node,
194
- messageId: 'noHungarian',
195
- data: {
196
- name: variableName,
197
- },
198
- });
199
- return;
361
+ else if (member.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
362
+ member.key.type === utils_1.AST_NODE_TYPES.Identifier) {
363
+ // Check property name
364
+ checkIdentifier(member.key);
200
365
  }
201
366
  }
202
- // Check if the variable name uses Hungarian notation
203
- if (isHungarianNotation(variableName, typeName)) {
204
- context.report({
205
- node,
206
- messageId: 'noHungarian',
207
- data: {
208
- name: variableName,
209
- },
210
- });
211
- }
212
367
  },
213
- // Handle class properties
214
- PropertyDefinition(node) {
215
- if (node.key.type === 'Identifier' && isClassProperty(node)) {
216
- // Skip class properties - they should be ignored
217
- return;
218
- }
368
+ // Check type aliases
369
+ TSTypeAliasDeclaration(node) {
370
+ checkIdentifier(node.id);
371
+ },
372
+ // Check interface declarations
373
+ TSInterfaceDeclaration(node) {
374
+ checkIdentifier(node.id);
219
375
  },
220
376
  };
221
377
  },
@@ -0,0 +1 @@
1
+ export declare const noUuidv4Base62AsKey: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noUuidv4Base62AsKey", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;