@trackunit/eslint-plugin-trackunit 0.0.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.
Files changed (147) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +117 -0
  3. package/package.json +31 -0
  4. package/src/index.d.ts +8 -0
  5. package/src/index.js +20 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/config/fragments/ignores.d.ts +2 -0
  8. package/src/lib/config/fragments/ignores.js +18 -0
  9. package/src/lib/config/fragments/ignores.js.map +1 -0
  10. package/src/lib/config/fragments/import-rules.d.ts +3 -0
  11. package/src/lib/config/fragments/import-rules.js +58 -0
  12. package/src/lib/config/fragments/import-rules.js.map +1 -0
  13. package/src/lib/config/fragments/jest-overrides.d.ts +2 -0
  14. package/src/lib/config/fragments/jest-overrides.js +30 -0
  15. package/src/lib/config/fragments/jest-overrides.js.map +1 -0
  16. package/src/lib/config/fragments/jsdoc-rules.d.ts +3 -0
  17. package/src/lib/config/fragments/jsdoc-rules.js +71 -0
  18. package/src/lib/config/fragments/jsdoc-rules.js.map +1 -0
  19. package/src/lib/config/fragments/module-boundaries.d.ts +2 -0
  20. package/src/lib/config/fragments/module-boundaries.js +92 -0
  21. package/src/lib/config/fragments/module-boundaries.js.map +1 -0
  22. package/src/lib/config/fragments/react-rules.d.ts +5 -0
  23. package/src/lib/config/fragments/react-rules.js +137 -0
  24. package/src/lib/config/fragments/react-rules.js.map +1 -0
  25. package/src/lib/config/fragments/restricted-imports.d.ts +2 -0
  26. package/src/lib/config/fragments/restricted-imports.js +58 -0
  27. package/src/lib/config/fragments/restricted-imports.js.map +1 -0
  28. package/src/lib/config/fragments/testing-library.d.ts +2 -0
  29. package/src/lib/config/fragments/testing-library.js +7 -0
  30. package/src/lib/config/fragments/testing-library.js.map +1 -0
  31. package/src/lib/config/fragments/typescript-rules.d.ts +2 -0
  32. package/src/lib/config/fragments/typescript-rules.js +97 -0
  33. package/src/lib/config/fragments/typescript-rules.js.map +1 -0
  34. package/src/lib/config/index.d.ts +863 -0
  35. package/src/lib/config/index.js +10 -0
  36. package/src/lib/config/index.js.map +1 -0
  37. package/src/lib/config/plugins.d.ts +90 -0
  38. package/src/lib/config/plugins.js +44 -0
  39. package/src/lib/config/plugins.js.map +1 -0
  40. package/src/lib/config/presets/base.d.ts +265 -0
  41. package/src/lib/config/presets/base.js +145 -0
  42. package/src/lib/config/presets/base.js.map +1 -0
  43. package/src/lib/config/presets/e2e.d.ts +10 -0
  44. package/src/lib/config/presets/e2e.js +19 -0
  45. package/src/lib/config/presets/e2e.js.map +1 -0
  46. package/src/lib/config/presets/public-api.d.ts +147 -0
  47. package/src/lib/config/presets/public-api.js +62 -0
  48. package/src/lib/config/presets/public-api.js.map +1 -0
  49. package/src/lib/config/presets/react.d.ts +598 -0
  50. package/src/lib/config/presets/react.js +97 -0
  51. package/src/lib/config/presets/react.js.map +1 -0
  52. package/src/lib/config/presets/server.d.ts +36 -0
  53. package/src/lib/config/presets/server.js +37 -0
  54. package/src/lib/config/presets/server.js.map +1 -0
  55. package/src/lib/config/utils.d.ts +6 -0
  56. package/src/lib/config/utils.js +28 -0
  57. package/src/lib/config/utils.js.map +1 -0
  58. package/src/lib/config-helpers/create-skip-when.d.ts +35 -0
  59. package/src/lib/config-helpers/create-skip-when.js +54 -0
  60. package/src/lib/config-helpers/create-skip-when.js.map +1 -0
  61. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.d.ts +16 -0
  62. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js +83 -0
  63. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js.map +1 -0
  64. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.d.ts +4 -0
  65. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js +297 -0
  66. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js.map +1 -0
  67. package/src/lib/rules/no-internal-barrel-files/examples.d.ts +80 -0
  68. package/src/lib/rules/no-internal-barrel-files/examples.js +84 -0
  69. package/src/lib/rules/no-internal-barrel-files/examples.js.map +1 -0
  70. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.d.ts +29 -0
  71. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js +178 -0
  72. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js.map +1 -0
  73. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.d.ts +5 -0
  74. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js +67 -0
  75. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js.map +1 -0
  76. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.d.ts +2 -0
  77. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js +34 -0
  78. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js.map +1 -0
  79. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.d.ts +16 -0
  80. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js +55 -0
  81. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js.map +1 -0
  82. package/src/lib/rules/no-typescript-assertion/examples.d.ts +1 -0
  83. package/src/lib/rules/no-typescript-assertion/examples.js +45 -0
  84. package/src/lib/rules/no-typescript-assertion/examples.js.map +1 -0
  85. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.d.ts +20 -0
  86. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js +83 -0
  87. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js.map +1 -0
  88. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.d.ts +73 -0
  89. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js +333 -0
  90. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js.map +1 -0
  91. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.d.ts +56 -0
  92. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js +225 -0
  93. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js.map +1 -0
  94. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.d.ts +49 -0
  95. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js +75 -0
  96. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js.map +1 -0
  97. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.d.ts +32 -0
  98. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js +143 -0
  99. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js.map +1 -0
  100. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.d.ts +27 -0
  101. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js +196 -0
  102. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js.map +1 -0
  103. package/src/lib/rules/prefer-event-specific-callback-naming/utils.d.ts +76 -0
  104. package/src/lib/rules/prefer-event-specific-callback-naming/utils.js +245 -0
  105. package/src/lib/rules/prefer-event-specific-callback-naming/utils.js.map +1 -0
  106. package/src/lib/rules/prefer-field-components/prefer-field-components.d.ts +4 -0
  107. package/src/lib/rules/prefer-field-components/prefer-field-components.js +289 -0
  108. package/src/lib/rules/prefer-field-components/prefer-field-components.js.map +1 -0
  109. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.d.ts +26 -0
  110. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js +402 -0
  111. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js.map +1 -0
  112. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.d.ts +13 -0
  113. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js +271 -0
  114. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js.map +1 -0
  115. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.d.ts +15 -0
  116. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js +245 -0
  117. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js.map +1 -0
  118. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.d.ts +17 -0
  119. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js +133 -0
  120. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js.map +1 -0
  121. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.d.ts +12 -0
  122. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js +128 -0
  123. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js.map +1 -0
  124. package/src/lib/rules-map.d.ts +66 -0
  125. package/src/lib/rules-map.js +34 -0
  126. package/src/lib/rules-map.js.map +1 -0
  127. package/src/lib/utils/ast-utils.d.ts +85 -0
  128. package/src/lib/utils/ast-utils.js +530 -0
  129. package/src/lib/utils/ast-utils.js.map +1 -0
  130. package/src/lib/utils/classname-utils.d.ts +150 -0
  131. package/src/lib/utils/classname-utils.js +492 -0
  132. package/src/lib/utils/classname-utils.js.map +1 -0
  133. package/src/lib/utils/file-utils.d.ts +14 -0
  134. package/src/lib/utils/file-utils.js +106 -0
  135. package/src/lib/utils/file-utils.js.map +1 -0
  136. package/src/lib/utils/import-utils.d.ts +85 -0
  137. package/src/lib/utils/import-utils.js +193 -0
  138. package/src/lib/utils/import-utils.js.map +1 -0
  139. package/src/lib/utils/nx-utils.d.ts +59 -0
  140. package/src/lib/utils/nx-utils.js +103 -0
  141. package/src/lib/utils/nx-utils.js.map +1 -0
  142. package/src/lib/utils/package-utils.d.ts +38 -0
  143. package/src/lib/utils/package-utils.js +74 -0
  144. package/src/lib/utils/package-utils.js.map +1 -0
  145. package/src/lib/utils/typescript-utils.d.ts +29 -0
  146. package/src/lib/utils/typescript-utils.js +213 -0
  147. package/src/lib/utils/typescript-utils.js.map +1 -0
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.designGuidelineButtonIconSizeMatch = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("@typescript-eslint/utils");
6
+ const ts = tslib_1.__importStar(require("typescript"));
7
+ const createRule = utils_1.ESLintUtils.RuleCreator(name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/docs/rules/${name}.md`);
8
+ // Helper to get possible string literal values from a variable's type
9
+ function getPossibleStringLiterals(context, identifier) {
10
+ try {
11
+ const services = utils_1.ESLintUtils.getParserServices(context);
12
+ const checker = services.program.getTypeChecker();
13
+ const tsNode = services.esTreeNodeToTSNodeMap.get(identifier);
14
+ const type = checker.getTypeAtLocation(tsNode);
15
+ const literalValues = [];
16
+ // Type guard for string literal types
17
+ const isStringLiteralType = (t) => {
18
+ if ((t.flags & ts.TypeFlags.StringLiteral) === 0) {
19
+ return false;
20
+ }
21
+ // Check if the type has a value property and it's a string
22
+ // eslint-disable-next-line @trackunit/no-typescript-assertion
23
+ const typeWithValue = t;
24
+ return "value" in typeWithValue && typeof typeWithValue.value === "string";
25
+ };
26
+ if (type.isUnion()) {
27
+ for (const unionType of type.types) {
28
+ if (isStringLiteralType(unionType)) {
29
+ literalValues.push(unionType.value);
30
+ }
31
+ }
32
+ }
33
+ else if (isStringLiteralType(type)) {
34
+ literalValues.push(type.value);
35
+ }
36
+ return literalValues.length > 0 ? literalValues : null;
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ // Helper to check if a conditional expression matches the expected button-icon size pattern
43
+ function isCorrectConditionalExpression(expression, buttonVariableName) {
44
+ if (expression.type !== "ConditionalExpression") {
45
+ return false;
46
+ }
47
+ const { test, consequent, alternate } = expression;
48
+ // Check if test is: variable === "large"
49
+ if (test.type === "BinaryExpression" &&
50
+ test.operator === "===" &&
51
+ test.left.type === "Identifier" &&
52
+ test.left.name === buttonVariableName &&
53
+ test.right.type === "Literal" &&
54
+ test.right.value === "large") {
55
+ // Check if consequent is "medium" and alternate is "small"
56
+ return (consequent.type === "Literal" &&
57
+ consequent.value === "medium" &&
58
+ alternate.type === "Literal" &&
59
+ alternate.value === "small");
60
+ }
61
+ return false;
62
+ }
63
+ exports.designGuidelineButtonIconSizeMatch = createRule({
64
+ name: "design-guideline-button-icon-size-match",
65
+ meta: {
66
+ type: "suggestion",
67
+ docs: {
68
+ description: "Ensure Icon components within Button have the correct size prop based on the Button's size.",
69
+ },
70
+ fixable: "code",
71
+ schema: [],
72
+ messages: {
73
+ incorrectIconSize: "Expected Icon size to be '{{expectedSize}}' for Button size '{{buttonSize}}', found '{{iconSize}}'.",
74
+ },
75
+ },
76
+ defaultOptions: [],
77
+ create(context) {
78
+ return {
79
+ JSXOpeningElement(node) {
80
+ if (node.name.type === "JSXIdentifier" && node.name.name === "Button") {
81
+ const sizeAttr = node.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "size");
82
+ const buttonSizeInfo = (() => {
83
+ if (!sizeAttr?.value)
84
+ return { type: "implicit", value: null };
85
+ if (sizeAttr.value.type === "Literal" && typeof sizeAttr.value.value === "string") {
86
+ return { type: "literal", value: sizeAttr.value.value };
87
+ }
88
+ if (sizeAttr.value.type === "JSXExpressionContainer") {
89
+ const expr = sizeAttr.value.expression;
90
+ if (expr.type === "Identifier") {
91
+ return { type: "variable", value: expr.name, expression: expr };
92
+ }
93
+ return { type: "expression", value: null, expression: expr };
94
+ }
95
+ return { type: "implicit", value: null };
96
+ })();
97
+ node.attributes.forEach(attr => {
98
+ if (attr.type === "JSXAttribute" &&
99
+ attr.name.type === "JSXIdentifier" &&
100
+ (attr.name.name === "prefix" || attr.name.name === "suffix") &&
101
+ attr.value?.type === "JSXExpressionContainer") {
102
+ // Find Icon element - could be direct or inside conditional
103
+ const findIconElement = (expression) => {
104
+ if (expression.type === "JSXElement" &&
105
+ expression.openingElement.name.type === "JSXIdentifier" &&
106
+ expression.openingElement.name.name === "Icon") {
107
+ return expression.openingElement;
108
+ }
109
+ if (expression.type === "ConditionalExpression") {
110
+ // Check both consequent and alternate
111
+ const consequent = findIconElement(expression.consequent);
112
+ if (consequent)
113
+ return consequent;
114
+ const alternate = findIconElement(expression.alternate);
115
+ if (alternate)
116
+ return alternate;
117
+ }
118
+ return null;
119
+ };
120
+ // Skip empty JSX expressions like {}
121
+ if (attr.value.expression.type === "JSXEmptyExpression")
122
+ return;
123
+ const iconElement = findIconElement(attr.value.expression);
124
+ if (!iconElement)
125
+ return;
126
+ const iconSizeAttr = iconElement.attributes.find((iconAttr) => iconAttr.type === "JSXAttribute" &&
127
+ iconAttr.name.type === "JSXIdentifier" &&
128
+ iconAttr.name.name === "size");
129
+ const iconSizeInfo = (() => {
130
+ if (!iconSizeAttr?.value)
131
+ return { type: "implicit", value: null };
132
+ if (iconSizeAttr.value.type === "Literal" && typeof iconSizeAttr.value.value === "string") {
133
+ return { type: "literal", value: iconSizeAttr.value.value };
134
+ }
135
+ if (iconSizeAttr.value.type === "JSXExpressionContainer") {
136
+ const expr = iconSizeAttr.value.expression;
137
+ if (expr.type === "Identifier") {
138
+ return { type: "variable", value: expr.name, expression: expr };
139
+ }
140
+ return { type: "expression", value: null, expression: expr };
141
+ }
142
+ return { type: "implicit", value: null };
143
+ })();
144
+ // Rule: large buttons → medium icons, small/medium buttons → small icons
145
+ const getExpectedIconSize = (buttonSize) => {
146
+ if (buttonSize === "large")
147
+ return "medium";
148
+ return "small"; // both "small" and "medium" buttons get "small" icons
149
+ };
150
+ const shouldReport = (() => {
151
+ // For complex expressions, always warn but don't autofix
152
+ if (buttonSizeInfo.type === "expression") {
153
+ return true;
154
+ }
155
+ // For variables, check if icon already has the correct size
156
+ if (buttonSizeInfo.type === "variable" && buttonSizeInfo.expression?.type === "Identifier") {
157
+ const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);
158
+ if (possibleSizes) {
159
+ // Check if all possible button sizes map to the same icon size
160
+ const iconSizes = possibleSizes.map(getExpectedIconSize);
161
+ const uniqueIconSizes = [...new Set(iconSizes)];
162
+ if (uniqueIconSizes.length === 1) {
163
+ // All map to same icon size - check if icon already has correct literal
164
+ const variableExpectedIconSize = uniqueIconSizes[0];
165
+ const variableIconEffectiveSize = iconSizeInfo.value || "medium";
166
+ if (variableIconEffectiveSize === variableExpectedIconSize) {
167
+ return false; // Icon already has correct literal size
168
+ }
169
+ }
170
+ else {
171
+ // Mixed mapping - check if icon has correct conditional expression
172
+ if (iconSizeInfo.type === "expression" &&
173
+ iconSizeInfo.expression &&
174
+ iconSizeInfo.expression.type !== "JSXEmptyExpression" &&
175
+ buttonSizeInfo.value &&
176
+ isCorrectConditionalExpression(iconSizeInfo.expression, buttonSizeInfo.value)) {
177
+ return false; // Icon already has correct conditional
178
+ }
179
+ }
180
+ }
181
+ // Mixed mapping or can't determine - always warn
182
+ return true;
183
+ }
184
+ // For literals and implicit, check the mapping
185
+ const buttonEffectiveSize = buttonSizeInfo.value || "medium";
186
+ const iconEffectiveSize = iconSizeInfo.value || "medium";
187
+ const expectedIconSize = getExpectedIconSize(buttonEffectiveSize);
188
+ return iconEffectiveSize !== expectedIconSize;
189
+ })();
190
+ if (shouldReport) {
191
+ context.report({
192
+ node: iconElement,
193
+ messageId: "incorrectIconSize",
194
+ data: {
195
+ expectedSize: (() => {
196
+ if (buttonSizeInfo.type === "variable" && buttonSizeInfo.expression?.type === "Identifier") {
197
+ // Analyze the variable's type to determine expected message
198
+ const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);
199
+ if (possibleSizes) {
200
+ const iconSizes = possibleSizes.map(getExpectedIconSize);
201
+ const uniqueIconSizes = [...new Set(iconSizes)];
202
+ if (uniqueIconSizes.length === 1) {
203
+ return uniqueIconSizes[0];
204
+ }
205
+ }
206
+ return `{${buttonSizeInfo.value} === "large" ? "medium" : "small"}`;
207
+ }
208
+ if (buttonSizeInfo.type === "expression") {
209
+ return "small (for small/medium buttons) or medium (for large buttons)";
210
+ }
211
+ const buttonEffectiveSize = buttonSizeInfo.value || "medium";
212
+ return getExpectedIconSize(buttonEffectiveSize);
213
+ })(),
214
+ buttonSize: buttonSizeInfo.type === "implicit"
215
+ ? "medium (default)"
216
+ : buttonSizeInfo.type === "literal"
217
+ ? buttonSizeInfo.value
218
+ : buttonSizeInfo.type === "variable"
219
+ ? (() => {
220
+ if (buttonSizeInfo.expression?.type === "Identifier") {
221
+ const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);
222
+ if (possibleSizes && possibleSizes.length > 0) {
223
+ const sizeList = possibleSizes.map(s => `"${s}"`).join(" | ");
224
+ return `{${buttonSizeInfo.value}} (of type ${sizeList})`;
225
+ }
226
+ }
227
+ return `{${buttonSizeInfo.value}}`;
228
+ })()
229
+ : "expression",
230
+ iconSize: iconSizeInfo.type === "implicit"
231
+ ? "medium (default)"
232
+ : iconSizeInfo.type === "literal"
233
+ ? iconSizeInfo.value
234
+ : iconSizeInfo.type === "variable"
235
+ ? `{${iconSizeInfo.value}}`
236
+ : "expression",
237
+ },
238
+ fix(fixer) {
239
+ // Don't autofix complex expressions, only variables and literals
240
+ if (buttonSizeInfo.type === "expression") {
241
+ return null;
242
+ }
243
+ let targetSizeValue;
244
+ if (buttonSizeInfo.type === "variable" && buttonSizeInfo.expression?.type === "Identifier") {
245
+ // Analyze the variable's type to determine optimal autofix
246
+ const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);
247
+ if (possibleSizes) {
248
+ // Check if all possible button sizes map to the same icon size
249
+ const iconSizes = possibleSizes.map(getExpectedIconSize);
250
+ const uniqueIconSizes = [...new Set(iconSizes)];
251
+ if (uniqueIconSizes.length === 1) {
252
+ // All possible button sizes map to the same icon size
253
+ targetSizeValue = `"${uniqueIconSizes[0]}"`;
254
+ }
255
+ else {
256
+ // Mixed mapping, use conditional
257
+ targetSizeValue = `{${buttonSizeInfo.value} === "large" ? "medium" : "small"}`;
258
+ }
259
+ }
260
+ else {
261
+ // Can't determine type, use conditional
262
+ targetSizeValue = `{${buttonSizeInfo.value} === "large" ? "medium" : "small"}`;
263
+ }
264
+ }
265
+ else {
266
+ // Calculate target icon size based on button size (literal or implicit)
267
+ const buttonEffectiveSize = buttonSizeInfo.value || "medium";
268
+ const targetIconSize = getExpectedIconSize(buttonEffectiveSize);
269
+ targetSizeValue = `"${targetIconSize}"`;
270
+ }
271
+ if (iconSizeAttr) {
272
+ // Replace existing size attribute
273
+ if (!iconSizeAttr.value)
274
+ return null;
275
+ return fixer.replaceText(iconSizeAttr.value, targetSizeValue);
276
+ }
277
+ else {
278
+ // Add size attribute
279
+ const lastAttr = iconElement.attributes[iconElement.attributes.length - 1];
280
+ if (lastAttr) {
281
+ return fixer.insertTextAfter(lastAttr, ` size=${targetSizeValue}`);
282
+ }
283
+ else {
284
+ return fixer.insertTextAfter(iconElement.name, ` size=${targetSizeValue}`);
285
+ }
286
+ }
287
+ },
288
+ });
289
+ }
290
+ }
291
+ });
292
+ }
293
+ },
294
+ };
295
+ },
296
+ });
297
+ //# sourceMappingURL=design-guideline-button-icon-size-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-guideline-button-icon-size-match.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.ts"],"names":[],"mappings":"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","sourcesContent":["import { ESLintUtils, type TSESLint, type TSESTree } from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/docs/rules/${name}.md`\n);\n\n// Helper to get possible string literal values from a variable's type\nfunction getPossibleStringLiterals(\n context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>,\n identifier: TSESTree.Identifier\n): Array<string> | null {\n try {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(identifier);\n const type = checker.getTypeAtLocation(tsNode);\n\n const literalValues: Array<string> = [];\n\n // Type guard for string literal types\n const isStringLiteralType = (t: ts.Type): t is ts.Type & { value: string } => {\n if ((t.flags & ts.TypeFlags.StringLiteral) === 0) {\n return false;\n }\n // Check if the type has a value property and it's a string\n // eslint-disable-next-line @trackunit/no-typescript-assertion\n const typeWithValue = t as { value?: unknown };\n return \"value\" in typeWithValue && typeof typeWithValue.value === \"string\";\n };\n\n if (type.isUnion()) {\n for (const unionType of type.types) {\n if (isStringLiteralType(unionType)) {\n literalValues.push(unionType.value);\n }\n }\n } else if (isStringLiteralType(type)) {\n literalValues.push(type.value);\n }\n\n return literalValues.length > 0 ? literalValues : null;\n } catch {\n return null;\n }\n}\n\n// Helper to check if a conditional expression matches the expected button-icon size pattern\nfunction isCorrectConditionalExpression(expression: TSESTree.Expression, buttonVariableName: string): boolean {\n if (expression.type !== \"ConditionalExpression\") {\n return false;\n }\n\n const { test, consequent, alternate } = expression;\n\n // Check if test is: variable === \"large\"\n if (\n test.type === \"BinaryExpression\" &&\n test.operator === \"===\" &&\n test.left.type === \"Identifier\" &&\n test.left.name === buttonVariableName &&\n test.right.type === \"Literal\" &&\n test.right.value === \"large\"\n ) {\n // Check if consequent is \"medium\" and alternate is \"small\"\n return (\n consequent.type === \"Literal\" &&\n consequent.value === \"medium\" &&\n alternate.type === \"Literal\" &&\n alternate.value === \"small\"\n );\n }\n\n return false;\n}\n\nexport const designGuidelineButtonIconSizeMatch = createRule({\n name: \"design-guideline-button-icon-size-match\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Ensure Icon components within Button have the correct size prop based on the Button's size.\",\n },\n fixable: \"code\",\n schema: [],\n messages: {\n incorrectIconSize:\n \"Expected Icon size to be '{{expectedSize}}' for Button size '{{buttonSize}}', found '{{iconSize}}'.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n JSXOpeningElement(node: TSESTree.JSXOpeningElement) {\n if (node.name.type === \"JSXIdentifier\" && node.name.name === \"Button\") {\n const sizeAttr = node.attributes.find(\n (attr): attr is TSESTree.JSXAttribute =>\n attr.type === \"JSXAttribute\" && attr.name.type === \"JSXIdentifier\" && attr.name.name === \"size\"\n );\n\n const buttonSizeInfo = (() => {\n if (!sizeAttr?.value) return { type: \"implicit\", value: null };\n\n if (sizeAttr.value.type === \"Literal\" && typeof sizeAttr.value.value === \"string\") {\n return { type: \"literal\", value: sizeAttr.value.value };\n }\n\n if (sizeAttr.value.type === \"JSXExpressionContainer\") {\n const expr = sizeAttr.value.expression;\n if (expr.type === \"Identifier\") {\n return { type: \"variable\", value: expr.name, expression: expr };\n }\n return { type: \"expression\", value: null, expression: expr };\n }\n\n return { type: \"implicit\", value: null };\n })();\n\n node.attributes.forEach(attr => {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n (attr.name.name === \"prefix\" || attr.name.name === \"suffix\") &&\n attr.value?.type === \"JSXExpressionContainer\"\n ) {\n // Find Icon element - could be direct or inside conditional\n const findIconElement = (expression: TSESTree.Expression): TSESTree.JSXOpeningElement | null => {\n if (\n expression.type === \"JSXElement\" &&\n expression.openingElement.name.type === \"JSXIdentifier\" &&\n expression.openingElement.name.name === \"Icon\"\n ) {\n return expression.openingElement;\n }\n\n if (expression.type === \"ConditionalExpression\") {\n // Check both consequent and alternate\n const consequent = findIconElement(expression.consequent);\n if (consequent) return consequent;\n\n const alternate = findIconElement(expression.alternate);\n if (alternate) return alternate;\n }\n\n return null;\n };\n\n // Skip empty JSX expressions like {}\n if (attr.value.expression.type === \"JSXEmptyExpression\") return;\n\n const iconElement = findIconElement(attr.value.expression);\n if (!iconElement) return;\n const iconSizeAttr = iconElement.attributes.find(\n (iconAttr): iconAttr is TSESTree.JSXAttribute =>\n iconAttr.type === \"JSXAttribute\" &&\n iconAttr.name.type === \"JSXIdentifier\" &&\n iconAttr.name.name === \"size\"\n );\n\n const iconSizeInfo = (() => {\n if (!iconSizeAttr?.value) return { type: \"implicit\", value: null };\n\n if (iconSizeAttr.value.type === \"Literal\" && typeof iconSizeAttr.value.value === \"string\") {\n return { type: \"literal\", value: iconSizeAttr.value.value };\n }\n\n if (iconSizeAttr.value.type === \"JSXExpressionContainer\") {\n const expr = iconSizeAttr.value.expression;\n if (expr.type === \"Identifier\") {\n return { type: \"variable\", value: expr.name, expression: expr };\n }\n return { type: \"expression\", value: null, expression: expr };\n }\n\n return { type: \"implicit\", value: null };\n })();\n\n // Rule: large buttons → medium icons, small/medium buttons → small icons\n const getExpectedIconSize = (buttonSize: string): string => {\n if (buttonSize === \"large\") return \"medium\";\n return \"small\"; // both \"small\" and \"medium\" buttons get \"small\" icons\n };\n\n const shouldReport = (() => {\n // For complex expressions, always warn but don't autofix\n if (buttonSizeInfo.type === \"expression\") {\n return true;\n }\n\n // For variables, check if icon already has the correct size\n if (buttonSizeInfo.type === \"variable\" && buttonSizeInfo.expression?.type === \"Identifier\") {\n const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);\n\n if (possibleSizes) {\n // Check if all possible button sizes map to the same icon size\n const iconSizes = possibleSizes.map(getExpectedIconSize);\n const uniqueIconSizes = [...new Set(iconSizes)];\n\n if (uniqueIconSizes.length === 1) {\n // All map to same icon size - check if icon already has correct literal\n const variableExpectedIconSize = uniqueIconSizes[0];\n const variableIconEffectiveSize = iconSizeInfo.value || \"medium\";\n\n if (variableIconEffectiveSize === variableExpectedIconSize) {\n return false; // Icon already has correct literal size\n }\n } else {\n // Mixed mapping - check if icon has correct conditional expression\n if (\n iconSizeInfo.type === \"expression\" &&\n iconSizeInfo.expression &&\n iconSizeInfo.expression.type !== \"JSXEmptyExpression\" &&\n buttonSizeInfo.value &&\n isCorrectConditionalExpression(iconSizeInfo.expression, buttonSizeInfo.value)\n ) {\n return false; // Icon already has correct conditional\n }\n }\n }\n\n // Mixed mapping or can't determine - always warn\n return true;\n }\n\n // For literals and implicit, check the mapping\n const buttonEffectiveSize = buttonSizeInfo.value || \"medium\";\n const iconEffectiveSize = iconSizeInfo.value || \"medium\";\n const expectedIconSize = getExpectedIconSize(buttonEffectiveSize);\n\n return iconEffectiveSize !== expectedIconSize;\n })();\n\n if (shouldReport) {\n context.report({\n node: iconElement,\n messageId: \"incorrectIconSize\",\n data: {\n expectedSize: (() => {\n if (buttonSizeInfo.type === \"variable\" && buttonSizeInfo.expression?.type === \"Identifier\") {\n // Analyze the variable's type to determine expected message\n const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);\n\n if (possibleSizes) {\n const iconSizes = possibleSizes.map(getExpectedIconSize);\n const uniqueIconSizes = [...new Set(iconSizes)];\n\n if (uniqueIconSizes.length === 1) {\n return uniqueIconSizes[0];\n }\n }\n\n return `{${buttonSizeInfo.value} === \"large\" ? \"medium\" : \"small\"}`;\n }\n if (buttonSizeInfo.type === \"expression\") {\n return \"small (for small/medium buttons) or medium (for large buttons)\";\n }\n const buttonEffectiveSize = buttonSizeInfo.value || \"medium\";\n return getExpectedIconSize(buttonEffectiveSize);\n })(),\n buttonSize:\n buttonSizeInfo.type === \"implicit\"\n ? \"medium (default)\"\n : buttonSizeInfo.type === \"literal\"\n ? buttonSizeInfo.value\n : buttonSizeInfo.type === \"variable\"\n ? (() => {\n if (buttonSizeInfo.expression?.type === \"Identifier\") {\n const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);\n if (possibleSizes && possibleSizes.length > 0) {\n const sizeList = possibleSizes.map(s => `\"${s}\"`).join(\" | \");\n return `{${buttonSizeInfo.value}} (of type ${sizeList})`;\n }\n }\n return `{${buttonSizeInfo.value}}`;\n })()\n : \"expression\",\n iconSize:\n iconSizeInfo.type === \"implicit\"\n ? \"medium (default)\"\n : iconSizeInfo.type === \"literal\"\n ? iconSizeInfo.value\n : iconSizeInfo.type === \"variable\"\n ? `{${iconSizeInfo.value}}`\n : \"expression\",\n },\n fix(fixer) {\n // Don't autofix complex expressions, only variables and literals\n if (buttonSizeInfo.type === \"expression\") {\n return null;\n }\n\n let targetSizeValue: string;\n\n if (buttonSizeInfo.type === \"variable\" && buttonSizeInfo.expression?.type === \"Identifier\") {\n // Analyze the variable's type to determine optimal autofix\n const possibleSizes = getPossibleStringLiterals(context, buttonSizeInfo.expression);\n\n if (possibleSizes) {\n // Check if all possible button sizes map to the same icon size\n const iconSizes = possibleSizes.map(getExpectedIconSize);\n const uniqueIconSizes = [...new Set(iconSizes)];\n\n if (uniqueIconSizes.length === 1) {\n // All possible button sizes map to the same icon size\n targetSizeValue = `\"${uniqueIconSizes[0]}\"`;\n } else {\n // Mixed mapping, use conditional\n targetSizeValue = `{${buttonSizeInfo.value} === \"large\" ? \"medium\" : \"small\"}`;\n }\n } else {\n // Can't determine type, use conditional\n targetSizeValue = `{${buttonSizeInfo.value} === \"large\" ? \"medium\" : \"small\"}`;\n }\n } else {\n // Calculate target icon size based on button size (literal or implicit)\n const buttonEffectiveSize = buttonSizeInfo.value || \"medium\";\n const targetIconSize = getExpectedIconSize(buttonEffectiveSize);\n targetSizeValue = `\"${targetIconSize}\"`;\n }\n\n if (iconSizeAttr) {\n // Replace existing size attribute\n if (!iconSizeAttr.value) return null;\n return fixer.replaceText(iconSizeAttr.value, targetSizeValue);\n } else {\n // Add size attribute\n const lastAttr = iconElement.attributes[iconElement.attributes.length - 1];\n if (lastAttr) {\n return fixer.insertTextAfter(lastAttr, ` size=${targetSizeValue}`);\n } else {\n return fixer.insertTextAfter(iconElement.name, ` size=${targetSizeValue}`);\n }\n }\n },\n });\n }\n }\n });\n }\n },\n };\n },\n});\n"]}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * This file documents the no-internal-barrel-files rule.
3
+ *
4
+ * Unlike other ESLint rules that check code patterns, this rule checks FILE LOCATION.
5
+ * The rule triggers on any index.ts or index.tsx file that is NOT the root barrel
6
+ * of an NX project (i.e., not at src/index.ts or src/index.tsx).
7
+ *
8
+ * This examples.ts file itself will NOT trigger the rule because it's not named index.ts.
9
+ *
10
+ * ## ✅ VALID - Root barrel file (allowed)
11
+ *
12
+ * ```
13
+ * libs/my-lib/
14
+ * ├── project.json
15
+ * └── src/
16
+ * └── index.ts ← This is the root barrel, allowed
17
+ * ```
18
+ *
19
+ * Content of src/index.ts:
20
+ * ```ts
21
+ * export { Button } from "./components/Button";
22
+ * export { useHook } from "./hooks/useHook";
23
+ * export type { ButtonProps } from "./components/Button";
24
+ * ```
25
+ *
26
+ * ## ❌ INVALID - Internal barrel files (SHOULD be flagged)
27
+ *
28
+ * ```
29
+ * libs/my-lib/
30
+ * ├── project.json
31
+ * └── src/
32
+ * ├── index.ts ← Root barrel (allowed)
33
+ * ├── components/
34
+ * │ ├── index.ts ← VIOLATION: Internal barrel
35
+ * │ └── Button.tsx
36
+ * └── utils/
37
+ * ├── index.ts ← VIOLATION: Internal barrel
38
+ * └── helpers.ts
39
+ * ```
40
+ *
41
+ * ## ❌ INVALID - Deeply nested barrel files (SHOULD be flagged)
42
+ *
43
+ * ```
44
+ * libs/my-lib/
45
+ * ├── project.json
46
+ * └── src/
47
+ * └── features/
48
+ * └── auth/
49
+ * └── index.ts ← VIOLATION: Internal barrel
50
+ * ```
51
+ *
52
+ * ## How to fix violations
53
+ *
54
+ * Option 1: Use the flatten-barrel-exports tool
55
+ * ```bash
56
+ * yarn ts-node devtools/flatten-barrel-exports/flatten-barrel-exports.ts --project=my-lib
57
+ * ```
58
+ *
59
+ * Option 2: Manually move exports to the root barrel
60
+ *
61
+ * Before (with internal barrel):
62
+ * ```ts
63
+ * // src/components/index.ts
64
+ * export { Button } from "./Button";
65
+ * export { Input } from "./Input";
66
+ *
67
+ * // src/index.ts
68
+ * export * from "./components"; // Re-exports the internal barrel
69
+ * ```
70
+ *
71
+ * After (flattened):
72
+ * ```ts
73
+ * // src/index.ts
74
+ * export { Button } from "./components/Button";
75
+ * export { Input } from "./components/Input";
76
+ *
77
+ * // src/components/index.ts - DELETE THIS FILE
78
+ * ```
79
+ */
80
+ export {};
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ // To test this rule: create an index.ts file in a subdirectory and run lint
3
+ // Note: eslint-disable is not needed here because this file is not named index.ts
4
+ /**
5
+ * This file documents the no-internal-barrel-files rule.
6
+ *
7
+ * Unlike other ESLint rules that check code patterns, this rule checks FILE LOCATION.
8
+ * The rule triggers on any index.ts or index.tsx file that is NOT the root barrel
9
+ * of an NX project (i.e., not at src/index.ts or src/index.tsx).
10
+ *
11
+ * This examples.ts file itself will NOT trigger the rule because it's not named index.ts.
12
+ *
13
+ * ## ✅ VALID - Root barrel file (allowed)
14
+ *
15
+ * ```
16
+ * libs/my-lib/
17
+ * ├── project.json
18
+ * └── src/
19
+ * └── index.ts ← This is the root barrel, allowed
20
+ * ```
21
+ *
22
+ * Content of src/index.ts:
23
+ * ```ts
24
+ * export { Button } from "./components/Button";
25
+ * export { useHook } from "./hooks/useHook";
26
+ * export type { ButtonProps } from "./components/Button";
27
+ * ```
28
+ *
29
+ * ## ❌ INVALID - Internal barrel files (SHOULD be flagged)
30
+ *
31
+ * ```
32
+ * libs/my-lib/
33
+ * ├── project.json
34
+ * └── src/
35
+ * ├── index.ts ← Root barrel (allowed)
36
+ * ├── components/
37
+ * │ ├── index.ts ← VIOLATION: Internal barrel
38
+ * │ └── Button.tsx
39
+ * └── utils/
40
+ * ├── index.ts ← VIOLATION: Internal barrel
41
+ * └── helpers.ts
42
+ * ```
43
+ *
44
+ * ## ❌ INVALID - Deeply nested barrel files (SHOULD be flagged)
45
+ *
46
+ * ```
47
+ * libs/my-lib/
48
+ * ├── project.json
49
+ * └── src/
50
+ * └── features/
51
+ * └── auth/
52
+ * └── index.ts ← VIOLATION: Internal barrel
53
+ * ```
54
+ *
55
+ * ## How to fix violations
56
+ *
57
+ * Option 1: Use the flatten-barrel-exports tool
58
+ * ```bash
59
+ * yarn ts-node devtools/flatten-barrel-exports/flatten-barrel-exports.ts --project=my-lib
60
+ * ```
61
+ *
62
+ * Option 2: Manually move exports to the root barrel
63
+ *
64
+ * Before (with internal barrel):
65
+ * ```ts
66
+ * // src/components/index.ts
67
+ * export { Button } from "./Button";
68
+ * export { Input } from "./Input";
69
+ *
70
+ * // src/index.ts
71
+ * export * from "./components"; // Re-exports the internal barrel
72
+ * ```
73
+ *
74
+ * After (flattened):
75
+ * ```ts
76
+ * // src/index.ts
77
+ * export { Button } from "./components/Button";
78
+ * export { Input } from "./components/Input";
79
+ *
80
+ * // src/components/index.ts - DELETE THIS FILE
81
+ * ```
82
+ */
83
+ Object.defineProperty(exports, "__esModule", { value: true });
84
+ //# sourceMappingURL=examples.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examples.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-internal-barrel-files/examples.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,kFAAkF;AAClF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG","sourcesContent":["// To test this rule: create an index.ts file in a subdirectory and run lint\n// Note: eslint-disable is not needed here because this file is not named index.ts\n/**\n * This file documents the no-internal-barrel-files rule.\n *\n * Unlike other ESLint rules that check code patterns, this rule checks FILE LOCATION.\n * The rule triggers on any index.ts or index.tsx file that is NOT the root barrel\n * of an NX project (i.e., not at src/index.ts or src/index.tsx).\n *\n * This examples.ts file itself will NOT trigger the rule because it's not named index.ts.\n *\n * ## ✅ VALID - Root barrel file (allowed)\n *\n * ```\n * libs/my-lib/\n * ├── project.json\n * └── src/\n * └── index.ts ← This is the root barrel, allowed\n * ```\n *\n * Content of src/index.ts:\n * ```ts\n * export { Button } from \"./components/Button\";\n * export { useHook } from \"./hooks/useHook\";\n * export type { ButtonProps } from \"./components/Button\";\n * ```\n *\n * ## ❌ INVALID - Internal barrel files (SHOULD be flagged)\n *\n * ```\n * libs/my-lib/\n * ├── project.json\n * └── src/\n * ├── index.ts ← Root barrel (allowed)\n * ├── components/\n * │ ├── index.ts ← VIOLATION: Internal barrel\n * │ └── Button.tsx\n * └── utils/\n * ├── index.ts ← VIOLATION: Internal barrel\n * └── helpers.ts\n * ```\n *\n * ## ❌ INVALID - Deeply nested barrel files (SHOULD be flagged)\n *\n * ```\n * libs/my-lib/\n * ├── project.json\n * └── src/\n * └── features/\n * └── auth/\n * └── index.ts ← VIOLATION: Internal barrel\n * ```\n *\n * ## How to fix violations\n *\n * Option 1: Use the flatten-barrel-exports tool\n * ```bash\n * yarn ts-node devtools/flatten-barrel-exports/flatten-barrel-exports.ts --project=my-lib\n * ```\n *\n * Option 2: Manually move exports to the root barrel\n *\n * Before (with internal barrel):\n * ```ts\n * // src/components/index.ts\n * export { Button } from \"./Button\";\n * export { Input } from \"./Input\";\n *\n * // src/index.ts\n * export * from \"./components\"; // Re-exports the internal barrel\n * ```\n *\n * After (flattened):\n * ```ts\n * // src/index.ts\n * export { Button } from \"./components/Button\";\n * export { Input } from \"./components/Input\";\n *\n * // src/components/index.ts - DELETE THIS FILE\n * ```\n */\n\nexport {};\n"]}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * ESLint rule to disallow internal barrel files.
3
+ *
4
+ * Internal barrel files (any index.ts or index.tsx that is not at the library root) cause:
5
+ * - Multiple import paths to the same module
6
+ * - Slower builds (bundlers must traverse more barrel files)
7
+ * - Confusion about canonical import paths
8
+ * - Potential bundle size issues
9
+ * - Module identity problems
10
+ *
11
+ * EXCEPTION: Secondary entry points configured in tsconfig.base.json are allowed.
12
+ * These are intentional barrel files that provide separate package imports
13
+ * (e.g., @trackunit/react-core-contexts-host-test pointing to src/test/index.ts).
14
+ *
15
+ * @example
16
+ * // Valid - Root barrel file
17
+ * // libs/my-lib/src/index.ts ✅
18
+ *
19
+ * // Valid - Secondary entry point configured in tsconfig.base.json
20
+ * // libs/my-lib/src/test/index.ts ✅ (if configured as @trackunit/my-lib-test)
21
+ *
22
+ * // Invalid - Internal barrel file
23
+ * // libs/my-lib/src/components/index.ts ❌
24
+ * // libs/my-lib/src/utils/helpers/index.ts ❌
25
+ */
26
+ import { ESLintUtils } from "@typescript-eslint/utils";
27
+ export declare const noInternalBarrelFiles: ESLintUtils.RuleModule<"noInternalBarrel", [], unknown, ESLintUtils.RuleListener> & {
28
+ name: string;
29
+ };