@trackunit/eslint-plugin-trackunit 0.6.50 → 0.6.52

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.6.52 (2026-07-01)
2
+
3
+ ### 🧱 Updated Dependencies
4
+
5
+ - Updated shared-utils to 1.15.52
6
+
7
+ ## 0.6.51 (2026-07-01)
8
+
9
+ ### 🧱 Updated Dependencies
10
+
11
+ - Updated shared-utils to 1.15.51
12
+
1
13
  ## 0.6.50 (2026-06-30)
2
14
 
3
15
  ### 🧱 Updated Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/eslint-plugin-trackunit",
3
- "version": "0.6.50",
3
+ "version": "0.6.52",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "repository": "https://github.com/Trackunit/manager",
6
6
  "engines": {
@@ -8,7 +8,7 @@ exports.typescriptRules = {
8
8
  "@typescript-eslint/no-unnecessary-condition": "error",
9
9
  "no-unused-vars": "off",
10
10
  "@typescript-eslint/no-unused-vars": [
11
- "warn",
11
+ "error",
12
12
  {
13
13
  args: "all",
14
14
  argsIgnorePattern: "^_",
@@ -4,19 +4,6 @@
4
4
  * used by both string-based and type-based detection strategies.
5
5
  */
6
6
  import { TSESLint, TSESTree } from "@typescript-eslint/utils";
7
- /**
8
- * Result of extracting callback information from an expression.
9
- * Does not include messageId - each strategy adds its own.
10
- */
11
- export type CallbackExtractionResult = {
12
- callbackName: string;
13
- suggestedName: string;
14
- node: TSESTree.Node;
15
- /** The identifier node for the callback - used for fix ranges */
16
- identifierNode: TSESTree.Identifier;
17
- /** Whether the callback is wrapped in an arrow function */
18
- isWrapped: boolean;
19
- } | null;
20
7
  /**
21
8
  * Information about a locally defined variable, including its definition
22
9
  * and all references within the same file.
@@ -40,8 +27,7 @@ export type LocalDefinitionInfo = {
40
27
  export declare const findLocalDefinitionInfo: (identifier: TSESTree.Identifier, context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>) => LocalDefinitionInfo | null;
41
28
  /**
42
29
  * Result of extracting a callback identifier from an expression.
43
- * Unlike CallbackExtractionResult, this doesn't include suggested names
44
- * since those depend on the specific event handler being used.
30
+ * Does not include suggested names since those depend on the specific event handler being used.
45
31
  */
46
32
  export type CallbackIdentifierResult = {
47
33
  callbackName: string;
@@ -105,87 +105,6 @@ const findLocalDefinitionInfo = (identifier, context) => {
105
105
  };
106
106
  };
107
107
  exports.findLocalDefinitionInfo = findLocalDefinitionInfo;
108
- /**
109
- * Checks if a callback name is problematic when used with click handlers.
110
- * A name is problematic if it starts with "on" but not "onClick".
111
- *
112
- * @example
113
- * isProblematicName("onClose") // true - should be onClickClose
114
- * isProblematicName("onClickClose") // false - already correct
115
- * isProblematicName("handleClose") // false - different convention
116
- * isProblematicName("close") // false - doesn't start with "on"
117
- */
118
- const isProblematicName = (name) => {
119
- return name.startsWith("on") && !name.startsWith("onClick");
120
- };
121
- /**
122
- * Converts a problematic callback name to the suggested onClick* format.
123
- *
124
- * @example
125
- * getSuggestedName("onClose") // "onClickClose"
126
- * getSuggestedName("onCancel") // "onClickCancel"
127
- */
128
- const getSuggestedName = (name) => {
129
- // Remove "on" prefix and add "onClick" prefix
130
- return `onClick${name.slice(2)}`;
131
- };
132
- /**
133
- * Extracts callback info from a direct identifier pattern: onClick={onClose}
134
- */
135
- const extractFromDirectIdentifier = (expression) => {
136
- if (expression.type !== utils_1.AST_NODE_TYPES.Identifier) {
137
- return null;
138
- }
139
- const callbackName = expression.name;
140
- if (!isProblematicName(callbackName)) {
141
- return null;
142
- }
143
- return {
144
- callbackName,
145
- suggestedName: getSuggestedName(callbackName),
146
- node: expression,
147
- identifierNode: expression,
148
- isWrapped: false,
149
- };
150
- };
151
- /**
152
- * Extracts callback info from a single-call arrow function: onClick={() => onClose()}
153
- */
154
- const extractFromSingleCallArrow = (expression) => {
155
- if (expression.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
156
- return null;
157
- }
158
- const { body } = expression;
159
- // Only check if body is a single call expression (not a block statement)
160
- if (body.type !== utils_1.AST_NODE_TYPES.CallExpression) {
161
- return null;
162
- }
163
- // Only check if the callee is a simple identifier
164
- if (body.callee.type !== utils_1.AST_NODE_TYPES.Identifier) {
165
- return null;
166
- }
167
- const callbackName = body.callee.name;
168
- if (!isProblematicName(callbackName)) {
169
- return null;
170
- }
171
- return {
172
- callbackName,
173
- suggestedName: getSuggestedName(callbackName),
174
- node: body.callee,
175
- identifierNode: body.callee,
176
- isWrapped: true,
177
- };
178
- };
179
- /**
180
- * Extracts callback information from an expression.
181
- * Handles both direct identifiers (onClick={onClose}) and
182
- * single-call arrow functions (onClick={() => onClose()}).
183
- *
184
- * @returns Callback extraction result without messageId, or null if no problematic callback found
185
- */
186
- const extractCallbackInfo = (expression) => {
187
- return extractFromDirectIdentifier(expression) ?? extractFromSingleCallArrow(expression);
188
- };
189
108
  /**
190
109
  * Extracts the callback identifier from an expression without checking naming patterns.
191
110
  * Used by strict prop events check which has its own naming criteria.
@@ -12,7 +12,6 @@ const utils_1 = require("@typescript-eslint/utils");
12
12
  * - `isParameterExplicitlyDestructured()` - Check if parameter is explicitly destructured
13
13
  * - `isParameterInitialized()` - Check if parameter has a default value
14
14
  * - `isParameterUsedInFunction()` - Check if parameter is used in function body
15
- * - `getParameterRestUsage()` - Get info about parameter accessed via rest params
16
15
  * - `hasExistingBodyInitializations()` - Check for initialization patterns in function body
17
16
  *
18
17
  * ## Identifier Origin Analysis (Usage → Origin)
@@ -194,44 +193,6 @@ const isParameterUsedInFunction = (context, functionNode, paramName) => {
194
193
  return false;
195
194
  };
196
195
  exports.isParameterUsedInFunction = isParameterUsedInFunction;
197
- /**
198
- * Check if a parameter is accessed via rest parameters (e.g., rest.paramName)
199
- * and return information about the rest parameter usage
200
- */
201
- const getParameterRestUsage = (context, functionNode, paramName) => {
202
- // Look for rest elements in both direct params and inside object patterns
203
- const restParameters = [];
204
- functionNode.params.forEach(param => {
205
- if (param.type === utils_1.AST_NODE_TYPES.RestElement && param.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
206
- // Direct rest parameter: (...rest: Params)
207
- restParameters.push({ name: param.argument.name, param });
208
- }
209
- else if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
210
- // Look for rest elements inside object patterns: ({ param1, ...rest }: Params)
211
- param.properties.forEach(property => {
212
- if (property.type === utils_1.AST_NODE_TYPES.RestElement && property.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
213
- restParameters.push({ name: property.argument.name, param });
214
- }
215
- });
216
- }
217
- });
218
- if (restParameters.length === 0) {
219
- return { isUsedViaRest: false };
220
- }
221
- // Check if the function body contains rest.paramName patterns
222
- const functionText = context.sourceCode.getText(functionNode);
223
- for (const restParam of restParameters) {
224
- const pattern = new RegExp(`\\b${restParam.name}\\.${paramName}\\b`);
225
- if (pattern.test(functionText)) {
226
- return {
227
- isUsedViaRest: true,
228
- restParamName: restParam.name,
229
- restParam: restParam.param,
230
- };
231
- }
232
- }
233
- return { isUsedViaRest: false };
234
- };
235
196
  const jsxTagNameIsIntrinsic = (name) => name.type === utils_1.AST_NODE_TYPES.JSXIdentifier && /^[a-z]/.test(name.name);
236
197
  const nodeContainsJSX = (node) => {
237
198
  if (node.type === utils_1.AST_NODE_TYPES.JSXElement) {
@@ -337,49 +337,6 @@ const extractFromJsxAttribute = (node) => {
337
337
  return [];
338
338
  };
339
339
  // =============================================================================
340
- // Variable Declaration Extraction
341
- // =============================================================================
342
- /**
343
- * Extract classname locations from a variable declaration with a classname-like name.
344
- *
345
- * Example: const buttonClass = "flex items-center";
346
- */
347
- const extractFromVariableDeclarator = (node) => {
348
- if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
349
- return [];
350
- }
351
- const varName = node.id.name;
352
- if (!(0, exports.isClassnameVariable)(varName)) {
353
- return [];
354
- }
355
- if (!node.init) {
356
- return [];
357
- }
358
- const context = {
359
- type: "variable",
360
- variableName: varName,
361
- };
362
- // Handle string literal
363
- const extracted = extractStringValue(node.init);
364
- if (extracted) {
365
- return [
366
- {
367
- value: extracted.value,
368
- reportNode: node.init,
369
- fixNode: extracted.node,
370
- context,
371
- },
372
- ];
373
- }
374
- // Handle array of strings
375
- if (node.init.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
376
- return extractFromArray(node.init, context);
377
- }
378
- // Note: Function calls in variable initializers are handled separately
379
- // by the CallExpression handler
380
- return [];
381
- };
382
- // =============================================================================
383
340
  // Unified Entry Points
384
341
  // =============================================================================
385
342
  /**
@@ -426,27 +383,6 @@ const findClassnameStringsInAttribute = (node) => {
426
383
  return extractFromJsxAttribute(node);
427
384
  };
428
385
  exports.findClassnameStringsInAttribute = findClassnameStringsInAttribute;
429
- /**
430
- * Find all classname string locations in a VariableDeclarator node.
431
- *
432
- * Use this in your ESLint rule's VariableDeclarator handler to process
433
- * variables with classname-like names.
434
- *
435
- * @example
436
- * ```typescript
437
- * VariableDeclarator(node) {
438
- * const locations = findClassnameStringsInVariable(node);
439
- * for (const location of locations) {
440
- * if (hasBannedPattern(location.value)) {
441
- * context.report({ node: location.reportNode, ... });
442
- * }
443
- * }
444
- * }
445
- * ```
446
- */
447
- const findClassnameStringsInVariable = (node) => {
448
- return extractFromVariableDeclarator(node);
449
- };
450
386
  // =============================================================================
451
387
  // Utility Helpers for Fixing
452
388
  // =============================================================================
@@ -473,20 +409,4 @@ const formatAsArray = (classes) => {
473
409
  return JSON.stringify(classes);
474
410
  };
475
411
  exports.formatAsArray = formatAsArray;
476
- /**
477
- * Get the quote character used in a string literal.
478
- */
479
- const getQuoteChar = (node) => {
480
- if (typeof node.value === "string" && node.raw) {
481
- return node.raw.charAt(0);
482
- }
483
- return '"';
484
- };
485
- /**
486
- * Create a quoted string with the same quote style as the original.
487
- */
488
- const quoteString = (value, originalNode) => {
489
- const quote = getQuoteChar(originalNode);
490
- return `${quote}${value}${quote}`;
491
- };
492
412
  //# sourceMappingURL=classname-utils.js.map
@@ -12,11 +12,8 @@ const utils_1 = require("@typescript-eslint/utils");
12
12
  * ## Main Functions
13
13
  * - `addImportSpecifiers()` - Add new imports or merge with existing ones
14
14
  * - `replaceNamespaceWithDestructured()` - Replace namespace imports (e.g., `import React from "react"`)
15
- * - `addSingleImportSpecifier()` - Convenience for adding a single import
16
- *
17
15
  * ## Helper Functions
18
16
  * - `findImportDeclaration()` - Find existing import for a package
19
- * - `hasImportSpecifier()` - Check if specific import already exists
20
17
  * - `getImportSpecifiers()` - Extract specifiers from an import
21
18
  */
22
19
  /**
@@ -39,21 +36,6 @@ const findImportDeclaration = (sourceCode, packageName) => {
39
36
  return found;
40
37
  };
41
38
  exports.findImportDeclaration = findImportDeclaration;
42
- /**
43
- * Check if a specific specifier is already imported from a package.
44
- *
45
- * @example
46
- * // File has: import { useState, useEffect } from "react";
47
- * hasImportSpecifier(sourceCode, "react", "useState") // → true
48
- * hasImportSpecifier(sourceCode, "react", "useRef") // → false
49
- */
50
- const hasImportSpecifier = (sourceCode, packageName, specifierName) => {
51
- return sourceCode.ast.body.some(node => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
52
- node.source.value === packageName &&
53
- node.specifiers.some(specifier => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
54
- specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
55
- specifier.imported.name === specifierName));
56
- };
57
39
  /**
58
40
  * Get existing import specifiers from an import declaration.
59
41
  *
@@ -119,24 +101,6 @@ const addImportSpecifiers = (options) => {
119
101
  return fixes;
120
102
  };
121
103
  exports.addImportSpecifiers = addImportSpecifiers;
122
- /**
123
- * Add a single import specifier - convenience wrapper around addImportSpecifiers.
124
- * Automatically finds and merges with existing imports from the same package.
125
- *
126
- * @example
127
- * // Before: import { useState } from "react";
128
- * // After: import { useEffect, useState } from "react";
129
- * addSingleImportSpecifier({ sourceCode, fixer, packageName: "react", specifier: "useEffect" })
130
- */
131
- const addSingleImportSpecifier = (options) => {
132
- const { sourceCode, packageName, specifier } = options;
133
- const existingImport = (0, exports.findImportDeclaration)(sourceCode, packageName);
134
- return (0, exports.addImportSpecifiers)({
135
- ...options,
136
- specifiers: [specifier],
137
- existingImport,
138
- });
139
- };
140
104
  /**
141
105
  * Replace a namespace/default import with destructured imports.
142
106
  * Automatically handles merging, semicolons, sorting, and formatting.