@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,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noInternalBarrelFiles = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * ESLint rule to disallow internal barrel files.
7
+ *
8
+ * Internal barrel files (any index.ts or index.tsx that is not at the library root) cause:
9
+ * - Multiple import paths to the same module
10
+ * - Slower builds (bundlers must traverse more barrel files)
11
+ * - Confusion about canonical import paths
12
+ * - Potential bundle size issues
13
+ * - Module identity problems
14
+ *
15
+ * EXCEPTION: Secondary entry points configured in tsconfig.base.json are allowed.
16
+ * These are intentional barrel files that provide separate package imports
17
+ * (e.g., @trackunit/react-core-contexts-host-test pointing to src/test/index.ts).
18
+ *
19
+ * @example
20
+ * // Valid - Root barrel file
21
+ * // libs/my-lib/src/index.ts ✅
22
+ *
23
+ * // Valid - Secondary entry point configured in tsconfig.base.json
24
+ * // libs/my-lib/src/test/index.ts ✅ (if configured as @trackunit/my-lib-test)
25
+ *
26
+ * // Invalid - Internal barrel file
27
+ * // libs/my-lib/src/components/index.ts ❌
28
+ * // libs/my-lib/src/utils/helpers/index.ts ❌
29
+ */
30
+ const utils_1 = require("@typescript-eslint/utils");
31
+ const fs = tslib_1.__importStar(require("fs"));
32
+ const path = tslib_1.__importStar(require("path"));
33
+ const zod_1 = require("zod");
34
+ const file_utils_1 = require("../../utils/file-utils");
35
+ const projectJsonSchema = zod_1.z.object({
36
+ sourceRoot: zod_1.z.string().optional(),
37
+ });
38
+ const tsconfigSchema = zod_1.z.object({
39
+ compilerOptions: zod_1.z
40
+ .object({
41
+ paths: zod_1.z.record(zod_1.z.array(zod_1.z.string())).optional(),
42
+ })
43
+ .optional(),
44
+ });
45
+ /**
46
+ * Cache for tsconfig.base.json secondary entry points.
47
+ * Key: workspace root path
48
+ * Value: Set of absolute paths to allowed secondary entry points
49
+ */
50
+ const secondaryEntryPointsCache = new Map();
51
+ /**
52
+ * Find the workspace root by looking for nx.json
53
+ */
54
+ const findWorkspaceRoot = (startPath) => {
55
+ const nxJsonPath = (0, file_utils_1.findNearestFile)(startPath, "nx.json");
56
+ return nxJsonPath ? path.dirname(nxJsonPath) : null;
57
+ };
58
+ /**
59
+ * Get secondary entry points from tsconfig.base.json.
60
+ * These are path mappings that point to index.ts files inside src/ subdirectories.
61
+ * Results are cached per workspace root.
62
+ */
63
+ const getSecondaryEntryPoints = (workspaceRoot) => {
64
+ const cached = secondaryEntryPointsCache.get(workspaceRoot);
65
+ if (cached) {
66
+ return cached;
67
+ }
68
+ const entryPoints = new Set();
69
+ const tsconfigPath = path.join(workspaceRoot, "tsconfig.base.json");
70
+ try {
71
+ const tsconfigContent = fs.readFileSync(tsconfigPath, "utf-8");
72
+ const parsed = tsconfigSchema.safeParse(JSON.parse(tsconfigContent));
73
+ if (parsed.success && parsed.data.compilerOptions?.paths) {
74
+ const paths = parsed.data.compilerOptions.paths;
75
+ for (const pathMappings of Object.values(paths)) {
76
+ for (const mapping of pathMappings) {
77
+ // Only consider mappings that point to index.ts files
78
+ if (mapping.endsWith("/index.ts") || mapping.endsWith("/index.tsx")) {
79
+ const absolutePath = path.normalize(path.join(workspaceRoot, mapping));
80
+ entryPoints.add(absolutePath);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ catch {
87
+ // If we can't read tsconfig.base.json, return empty set
88
+ }
89
+ secondaryEntryPointsCache.set(workspaceRoot, entryPoints);
90
+ return entryPoints;
91
+ };
92
+ const createRule = utils_1.ESLintUtils.RuleCreator(name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`);
93
+ /**
94
+ * Check if a file is a barrel file (index.ts or index.tsx).
95
+ */
96
+ const isBarrelFile = (filePath) => {
97
+ const basename = path.basename(filePath);
98
+ return basename === "index.ts" || basename === "index.tsx";
99
+ };
100
+ /**
101
+ * Normalize file path for comparison (handles potential trailing slashes, etc.)
102
+ */
103
+ const normalizePath = (filePath) => {
104
+ return path.normalize(filePath);
105
+ };
106
+ exports.noInternalBarrelFiles = createRule({
107
+ name: "no-internal-barrel-files",
108
+ meta: {
109
+ type: "suggestion",
110
+ docs: {
111
+ description: "Disallow internal barrel files (index.ts) that are not at the library root or configured as secondary entry points",
112
+ },
113
+ schema: [],
114
+ messages: {
115
+ noInternalBarrel: "Internal barrel files are not allowed. Move exports to the root barrel file (src/index.ts) instead. Use the flatten-barrel-exports tool: yarn ts-node devtools/flatten-barrel-exports/flatten-barrel-exports.ts",
116
+ },
117
+ },
118
+ defaultOptions: [],
119
+ create(context) {
120
+ return {
121
+ Program(node) {
122
+ const currentFile = context.filename;
123
+ // Only analyze barrel files (index.ts/tsx)
124
+ if (!isBarrelFile(currentFile)) {
125
+ return;
126
+ }
127
+ // Find the project root by looking for project.json
128
+ const projectJsonPath = (0, file_utils_1.findNearestFile)(currentFile, "project.json");
129
+ if (projectJsonPath === null) {
130
+ // Not in an NX project, skip
131
+ return;
132
+ }
133
+ const projectRoot = path.dirname(projectJsonPath);
134
+ const workspaceRoot = findWorkspaceRoot(currentFile);
135
+ // Read sourceRoot from project.json, defaulting to "src" if not specified
136
+ // sourceRoot in project.json is relative to workspace root (e.g., "libs/my-lib/src")
137
+ let sourceRootAbsolute = path.join(projectRoot, "src");
138
+ try {
139
+ const projectJsonContent = fs.readFileSync(projectJsonPath, "utf-8");
140
+ const parsed = projectJsonSchema.safeParse(JSON.parse(projectJsonContent));
141
+ if (parsed.success && parsed.data.sourceRoot && workspaceRoot) {
142
+ // sourceRoot is relative to workspace root, so resolve it
143
+ sourceRootAbsolute = path.join(workspaceRoot, parsed.data.sourceRoot);
144
+ }
145
+ }
146
+ catch {
147
+ // If we can't read project.json, fall back to projectRoot/src
148
+ }
149
+ // The root barrel is at {sourceRoot}/index.ts or {sourceRoot}/index.tsx
150
+ const rootBarrelTs = normalizePath(path.join(sourceRootAbsolute, "index.ts"));
151
+ const rootBarrelTsx = normalizePath(path.join(sourceRootAbsolute, "index.tsx"));
152
+ const normalizedCurrentFile = normalizePath(currentFile);
153
+ // If this IS the root barrel, it's allowed
154
+ if (normalizedCurrentFile === rootBarrelTs || normalizedCurrentFile === rootBarrelTsx) {
155
+ return;
156
+ }
157
+ // Check if this is a secondary entry point configured in tsconfig.base.json
158
+ if (workspaceRoot) {
159
+ const secondaryEntryPoints = getSecondaryEntryPoints(workspaceRoot);
160
+ if (secondaryEntryPoints.has(normalizedCurrentFile)) {
161
+ // This is a configured secondary entry point, allow it
162
+ return;
163
+ }
164
+ }
165
+ // This is an internal barrel file - flag it
166
+ // Note: No autofix or suggestion is provided because:
167
+ // 1. The exports need to be moved to the root barrel
168
+ // 2. Imports throughout the codebase may need updating
169
+ // 3. The flatten-barrel-exports tool handles this properly
170
+ context.report({
171
+ node,
172
+ messageId: "noInternalBarrel",
173
+ });
174
+ },
175
+ };
176
+ },
177
+ });
178
+ //# sourceMappingURL=no-internal-barrel-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-internal-barrel-files.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ESLint rule to disallow internal barrel files.\n *\n * Internal barrel files (any index.ts or index.tsx that is not at the library root) cause:\n * - Multiple import paths to the same module\n * - Slower builds (bundlers must traverse more barrel files)\n * - Confusion about canonical import paths\n * - Potential bundle size issues\n * - Module identity problems\n *\n * EXCEPTION: Secondary entry points configured in tsconfig.base.json are allowed.\n * These are intentional barrel files that provide separate package imports\n * (e.g., @trackunit/react-core-contexts-host-test pointing to src/test/index.ts).\n *\n * @example\n * // Valid - Root barrel file\n * // libs/my-lib/src/index.ts ✅\n *\n * // Valid - Secondary entry point configured in tsconfig.base.json\n * // libs/my-lib/src/test/index.ts ✅ (if configured as @trackunit/my-lib-test)\n *\n * // Invalid - Internal barrel file\n * // libs/my-lib/src/components/index.ts ❌\n * // libs/my-lib/src/utils/helpers/index.ts ❌\n */\nimport { ESLintUtils } from \"@typescript-eslint/utils\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { z } from \"zod\";\nimport { findNearestFile } from \"../../utils/file-utils\";\n\nconst projectJsonSchema = z.object({\n sourceRoot: z.string().optional(),\n});\n\nconst tsconfigSchema = z.object({\n compilerOptions: z\n .object({\n paths: z.record(z.array(z.string())).optional(),\n })\n .optional(),\n});\n\n/**\n * Cache for tsconfig.base.json secondary entry points.\n * Key: workspace root path\n * Value: Set of absolute paths to allowed secondary entry points\n */\nconst secondaryEntryPointsCache = new Map<string, Set<string>>();\n\n/**\n * Find the workspace root by looking for nx.json\n */\nconst findWorkspaceRoot = (startPath: string): string | null => {\n const nxJsonPath = findNearestFile(startPath, \"nx.json\");\n return nxJsonPath ? path.dirname(nxJsonPath) : null;\n};\n\n/**\n * Get secondary entry points from tsconfig.base.json.\n * These are path mappings that point to index.ts files inside src/ subdirectories.\n * Results are cached per workspace root.\n */\nconst getSecondaryEntryPoints = (workspaceRoot: string): Set<string> => {\n const cached = secondaryEntryPointsCache.get(workspaceRoot);\n if (cached) {\n return cached;\n }\n\n const entryPoints = new Set<string>();\n const tsconfigPath = path.join(workspaceRoot, \"tsconfig.base.json\");\n\n try {\n const tsconfigContent = fs.readFileSync(tsconfigPath, \"utf-8\");\n const parsed = tsconfigSchema.safeParse(JSON.parse(tsconfigContent));\n\n if (parsed.success && parsed.data.compilerOptions?.paths) {\n const paths = parsed.data.compilerOptions.paths;\n\n for (const pathMappings of Object.values(paths)) {\n for (const mapping of pathMappings) {\n // Only consider mappings that point to index.ts files\n if (mapping.endsWith(\"/index.ts\") || mapping.endsWith(\"/index.tsx\")) {\n const absolutePath = path.normalize(path.join(workspaceRoot, mapping));\n entryPoints.add(absolutePath);\n }\n }\n }\n }\n } catch {\n // If we can't read tsconfig.base.json, return empty set\n }\n\n secondaryEntryPointsCache.set(workspaceRoot, entryPoints);\n return entryPoints;\n};\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype MessageIds = \"noInternalBarrel\";\ntype Options = [];\n\n/**\n * Check if a file is a barrel file (index.ts or index.tsx).\n */\nconst isBarrelFile = (filePath: string): boolean => {\n const basename = path.basename(filePath);\n return basename === \"index.ts\" || basename === \"index.tsx\";\n};\n\n/**\n * Normalize file path for comparison (handles potential trailing slashes, etc.)\n */\nconst normalizePath = (filePath: string): string => {\n return path.normalize(filePath);\n};\n\nexport const noInternalBarrelFiles = createRule<Options, MessageIds>({\n name: \"no-internal-barrel-files\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Disallow internal barrel files (index.ts) that are not at the library root or configured as secondary entry points\",\n },\n schema: [],\n messages: {\n noInternalBarrel:\n \"Internal barrel files are not allowed. Move exports to the root barrel file (src/index.ts) instead. Use the flatten-barrel-exports tool: yarn ts-node devtools/flatten-barrel-exports/flatten-barrel-exports.ts\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n Program(node) {\n const currentFile = context.filename;\n\n // Only analyze barrel files (index.ts/tsx)\n if (!isBarrelFile(currentFile)) {\n return;\n }\n\n // Find the project root by looking for project.json\n const projectJsonPath = findNearestFile(currentFile, \"project.json\");\n if (projectJsonPath === null) {\n // Not in an NX project, skip\n return;\n }\n\n const projectRoot = path.dirname(projectJsonPath);\n const workspaceRoot = findWorkspaceRoot(currentFile);\n\n // Read sourceRoot from project.json, defaulting to \"src\" if not specified\n // sourceRoot in project.json is relative to workspace root (e.g., \"libs/my-lib/src\")\n let sourceRootAbsolute = path.join(projectRoot, \"src\");\n try {\n const projectJsonContent = fs.readFileSync(projectJsonPath, \"utf-8\");\n const parsed = projectJsonSchema.safeParse(JSON.parse(projectJsonContent));\n if (parsed.success && parsed.data.sourceRoot && workspaceRoot) {\n // sourceRoot is relative to workspace root, so resolve it\n sourceRootAbsolute = path.join(workspaceRoot, parsed.data.sourceRoot);\n }\n } catch {\n // If we can't read project.json, fall back to projectRoot/src\n }\n\n // The root barrel is at {sourceRoot}/index.ts or {sourceRoot}/index.tsx\n const rootBarrelTs = normalizePath(path.join(sourceRootAbsolute, \"index.ts\"));\n const rootBarrelTsx = normalizePath(path.join(sourceRootAbsolute, \"index.tsx\"));\n const normalizedCurrentFile = normalizePath(currentFile);\n\n // If this IS the root barrel, it's allowed\n if (normalizedCurrentFile === rootBarrelTs || normalizedCurrentFile === rootBarrelTsx) {\n return;\n }\n\n // Check if this is a secondary entry point configured in tsconfig.base.json\n if (workspaceRoot) {\n const secondaryEntryPoints = getSecondaryEntryPoints(workspaceRoot);\n if (secondaryEntryPoints.has(normalizedCurrentFile)) {\n // This is a configured secondary entry point, allow it\n return;\n }\n }\n\n // This is an internal barrel file - flag it\n // Note: No autofix or suggestion is provided because:\n // 1. The exports need to be moved to the root barrel\n // 2. Imports throughout the codebase may need updating\n // 3. The flatten-barrel-exports tool handles this properly\n context.report({\n node,\n messageId: \"noInternalBarrel\",\n });\n },\n };\n },\n});\n"]}
@@ -0,0 +1,5 @@
1
+ import { Rule } from "eslint";
2
+ export declare const noInternalGraphqlWhenTaggedWithGqlPublic: {
3
+ meta: Rule.RuleMetaData;
4
+ create: (context: Rule.RuleContext) => Rule.RuleListener;
5
+ };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noInternalGraphqlWhenTaggedWithGqlPublic = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const meta = {
8
+ type: "problem",
9
+ docs: {
10
+ description: "Disallow '-internal.graphql' files in projects tagged with 'gql:public'",
11
+ },
12
+ messages: {
13
+ internalGraphqlFileFound: "'-internal.graphql' file found in a 'gql:public' tagged project.",
14
+ },
15
+ schema: [],
16
+ };
17
+ const create = (context) => {
18
+ const getLineAndColumnFromPosition = (content, position) => {
19
+ const lines = content.substring(0, position).split("\n");
20
+ const line = lines.length;
21
+ const column = lines[lines.length - 1]?.length ?? 0;
22
+ return { line, column };
23
+ };
24
+ const checkDirectoryForInternalGraphQLFiles = (directory, projectJson) => {
25
+ const files = fs.readdirSync(directory);
26
+ for (const file of files) {
27
+ const fullPath = path.join(directory, file);
28
+ // Handle broken symlinks or inaccessible paths gracefully
29
+ let stat;
30
+ try {
31
+ stat = fs.statSync(fullPath);
32
+ }
33
+ catch {
34
+ // Skip paths that can't be accessed (broken symlinks, permission issues, etc.)
35
+ continue;
36
+ }
37
+ if (stat.isDirectory()) {
38
+ checkDirectoryForInternalGraphQLFiles(fullPath, projectJson);
39
+ }
40
+ else if (file.endsWith("-internal.graphql")) {
41
+ context.report({
42
+ message: `'*-internal.graphql' file found in a 'gql:public' tagged project. - ${fullPath}`,
43
+ loc: getLineAndColumnFromPosition(projectJson, projectJson.indexOf("gql:public")),
44
+ });
45
+ return;
46
+ }
47
+ }
48
+ };
49
+ return {
50
+ Program: _node => {
51
+ const sourceFilePath = context.getFilename();
52
+ if (sourceFilePath.endsWith("/project.json")) {
53
+ const projectJsonString = fs.readFileSync(sourceFilePath, "utf8");
54
+ const projectJson = JSON.parse(projectJsonString);
55
+ if (Array.isArray(projectJson.tags) && projectJson.tags.includes("gql:public")) {
56
+ const projectDir = path.dirname(sourceFilePath);
57
+ checkDirectoryForInternalGraphQLFiles(projectDir, projectJsonString);
58
+ }
59
+ }
60
+ },
61
+ };
62
+ };
63
+ exports.noInternalGraphqlWhenTaggedWithGqlPublic = {
64
+ meta,
65
+ create,
66
+ };
67
+ //# sourceMappingURL=no-internal-graphql-when-tagged-with-gql-public.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-internal-graphql-when-tagged-with-gql-public.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.ts"],"names":[],"mappings":";;;;AACA,+CAAyB;AACzB,mDAA6B;AAE7B,MAAM,IAAI,GAAsB;IAC9B,IAAI,EAAE,SAAS;IACf,IAAI,EAAE;QACJ,WAAW,EAAE,yEAAyE;KACvF;IACD,QAAQ,EAAE;QACR,wBAAwB,EAAE,kEAAkE;KAC7F;IACD,MAAM,EAAE,EAAE;CACX,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,OAAyB,EAAqB,EAAE;IAC9D,MAAM,4BAA4B,GAAG,CAAC,OAAe,EAAE,QAAgB,EAAE,EAAE;QACzE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;QACpD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC,CAAC;IAEF,MAAM,qCAAqC,GAAG,CAAC,SAAiB,EAAE,WAAmB,EAAE,EAAE;QACvF,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAE5C,0DAA0D;YAC1D,IAAI,IAAc,CAAC;YACnB,IAAI,CAAC;gBACH,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,+EAA+E;gBAC/E,SAAS;YACX,CAAC;YAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,qCAAqC,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBAC9C,OAAO,CAAC,MAAM,CAAC;oBACb,OAAO,EAAE,uEAAuE,QAAQ,EAAE;oBAC1F,GAAG,EAAE,4BAA4B,CAAC,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;iBAClF,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,EAAE;YACf,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAC7C,IAAI,cAAc,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;gBAC7C,MAAM,iBAAiB,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;gBAClE,MAAM,WAAW,GAA6B,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBAC5E,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;oBAC/E,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;oBAChD,qCAAqC,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEW,QAAA,wCAAwC,GAAG;IACtD,IAAI;IACJ,MAAM;CACP,CAAC","sourcesContent":["import { Rule } from \"eslint\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nconst meta: Rule.RuleMetaData = {\n type: \"problem\",\n docs: {\n description: \"Disallow '-internal.graphql' files in projects tagged with 'gql:public'\",\n },\n messages: {\n internalGraphqlFileFound: \"'-internal.graphql' file found in a 'gql:public' tagged project.\",\n },\n schema: [],\n};\n\nconst create = (context: Rule.RuleContext): Rule.RuleListener => {\n const getLineAndColumnFromPosition = (content: string, position: number) => {\n const lines = content.substring(0, position).split(\"\\n\");\n const line = lines.length;\n const column = lines[lines.length - 1]?.length ?? 0;\n return { line, column };\n };\n\n const checkDirectoryForInternalGraphQLFiles = (directory: string, projectJson: string) => {\n const files = fs.readdirSync(directory);\n for (const file of files) {\n const fullPath = path.join(directory, file);\n\n // Handle broken symlinks or inaccessible paths gracefully\n let stat: fs.Stats;\n try {\n stat = fs.statSync(fullPath);\n } catch {\n // Skip paths that can't be accessed (broken symlinks, permission issues, etc.)\n continue;\n }\n\n if (stat.isDirectory()) {\n checkDirectoryForInternalGraphQLFiles(fullPath, projectJson);\n } else if (file.endsWith(\"-internal.graphql\")) {\n context.report({\n message: `'*-internal.graphql' file found in a 'gql:public' tagged project. - ${fullPath}`,\n loc: getLineAndColumnFromPosition(projectJson, projectJson.indexOf(\"gql:public\")),\n });\n return;\n }\n }\n };\n return {\n Program: _node => {\n const sourceFilePath = context.getFilename();\n if (sourceFilePath.endsWith(\"/project.json\")) {\n const projectJsonString = fs.readFileSync(sourceFilePath, \"utf8\");\n const projectJson: { tags?: Array<string> } = JSON.parse(projectJsonString);\n if (Array.isArray(projectJson.tags) && projectJson.tags.includes(\"gql:public\")) {\n const projectDir = path.dirname(sourceFilePath);\n checkDirectoryForInternalGraphQLFiles(projectDir, projectJsonString);\n }\n }\n },\n };\n};\n\nexport const noInternalGraphqlWhenTaggedWithGqlPublic = {\n meta,\n create,\n};\n"]}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "eslint";
2
+ export declare const noJestMockTrackunitReactCoreHooks: Rule.RuleModule;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noJestMockTrackunitReactCoreHooks = void 0;
4
+ exports.noJestMockTrackunitReactCoreHooks = {
5
+ meta: {
6
+ type: "problem",
7
+ docs: {
8
+ description: "Disallow direct jest.mock() of @trackunit/react-core-hooks",
9
+ },
10
+ messages: {
11
+ noDirectMock: 'Don\'t use jest.mock("@trackunit/react-core-hooks"). Use mock builder from trackunitProviders(), trackunitProvidersInternal() or trackunitProvidersHost() instead.',
12
+ },
13
+ schema: [],
14
+ },
15
+ create(context) {
16
+ return {
17
+ CallExpression(node) {
18
+ if (node.callee.type === "MemberExpression" &&
19
+ node.callee.object.type === "Identifier" &&
20
+ node.callee.object.name === "jest" &&
21
+ node.callee.property.type === "Identifier" &&
22
+ node.callee.property.name === "mock" &&
23
+ node.arguments[0]?.type === "Literal" &&
24
+ node.arguments[0].value === "@trackunit/react-core-hooks") {
25
+ context.report({
26
+ node,
27
+ messageId: "noDirectMock",
28
+ });
29
+ }
30
+ },
31
+ };
32
+ },
33
+ };
34
+ //# sourceMappingURL=no-jest-mock-trackunit-react-core-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-jest-mock-trackunit-react-core-hooks.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.ts"],"names":[],"mappings":";;;AAEa,QAAA,iCAAiC,GAAoB;IAChE,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,4DAA4D;SAC1E;QACD,QAAQ,EAAE;YACR,YAAY,EACV,oKAAoK;SACvK;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,IACE,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBACvC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM;oBAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,MAAM;oBACpC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,SAAS;oBACrC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,6BAA6B,EACzD,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,cAAc;qBAC1B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import type { Rule } from \"eslint\";\n\nexport const noJestMockTrackunitReactCoreHooks: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow direct jest.mock() of @trackunit/react-core-hooks\",\n },\n messages: {\n noDirectMock:\n 'Don\\'t use jest.mock(\"@trackunit/react-core-hooks\"). Use mock builder from trackunitProviders(), trackunitProvidersInternal() or trackunitProvidersHost() instead.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n node.callee.object.type === \"Identifier\" &&\n node.callee.object.name === \"jest\" &&\n node.callee.property.type === \"Identifier\" &&\n node.callee.property.name === \"mock\" &&\n node.arguments[0]?.type === \"Literal\" &&\n node.arguments[0].value === \"@trackunit/react-core-hooks\"\n ) {\n context.report({\n node,\n messageId: \"noDirectMock\",\n });\n }\n },\n };\n },\n};\n"]}
@@ -0,0 +1,16 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+ /**
3
+ * ESLint rule to disallow template strings in className prop.
4
+ *
5
+ * @example
6
+ * // Bad
7
+ * <div className={`${dynamicClass} staticClass`}>Content</div>
8
+ *
9
+ * // Good
10
+ * <div className={twMerge(dynamicClassName, "staticClassName")}>Content</div>
11
+ * // or
12
+ * <div className={yourCvaMergeFunction({ yourVariantProp, className: "extraClassName" })}>Content</div>
13
+ */
14
+ export declare const noTemplateStringsInClassName: ESLintUtils.RuleModule<"templateStringInClassName", [], unknown, ESLintUtils.RuleListener> & {
15
+ name: string;
16
+ };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noTemplateStringsInClassName = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const classname_utils_1 = require("../../utils/classname-utils");
6
+ const createRule = utils_1.ESLintUtils.RuleCreator(name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`);
7
+ /**
8
+ * ESLint rule to disallow template strings in className prop.
9
+ *
10
+ * @example
11
+ * // Bad
12
+ * <div className={`${dynamicClass} staticClass`}>Content</div>
13
+ *
14
+ * // Good
15
+ * <div className={twMerge(dynamicClassName, "staticClassName")}>Content</div>
16
+ * // or
17
+ * <div className={yourCvaMergeFunction({ yourVariantProp, className: "extraClassName" })}>Content</div>
18
+ */
19
+ exports.noTemplateStringsInClassName = createRule({
20
+ name: "no-template-strings-in-classname-prop",
21
+ meta: {
22
+ type: "suggestion",
23
+ docs: {
24
+ description: "Disallow the use of template strings in className prop to encourage better class management.",
25
+ },
26
+ messages: {
27
+ templateStringInClassName: "Do not use template strings in className prop. If you wish to merge classes from different sources, use twMerge() for simple inline merging or make a cvaMerge() function if it has variants.",
28
+ },
29
+ schema: [],
30
+ },
31
+ defaultOptions: [],
32
+ create(context) {
33
+ return {
34
+ /**
35
+ * Handle JSXAttribute with className prop that uses template strings.
36
+ *
37
+ * @example
38
+ * // Bad
39
+ * <div className={`${dynamicClass} staticClass`}>Content</div>
40
+ */
41
+ JSXAttribute(node) {
42
+ // Check if this is a classname attribute (className or class)
43
+ if (node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier || !(0, classname_utils_1.isClassnameAttribute)(node.name.name)) {
44
+ return;
45
+ }
46
+ // Check if the value is a template literal in a JSX expression container
47
+ if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
48
+ node.value.expression.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
49
+ context.report({ node, messageId: "templateStringInClassName" });
50
+ }
51
+ },
52
+ };
53
+ },
54
+ });
55
+ //# sourceMappingURL=no-template-strings-in-classname-prop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-template-strings-in-classname-prop.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.ts"],"names":[],"mappings":";;;AAAA,oDAAiF;AACjF,iEAAmE;AAEnE,MAAM,UAAU,GAAG,mBAAW,CAAC,WAAW,CACxC,IAAI,CAAC,EAAE,CAAC,6FAA6F,IAAI,KAAK,CAC/G,CAAC;AAKF;;;;;;;;;;;GAWG;AACU,QAAA,4BAA4B,GAAG,UAAU,CAAsB;IAC1E,IAAI,EAAE,uCAAuC;IAC7C,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EAAE,8FAA8F;SAC5G;QACD,QAAQ,EAAE;YACR,yBAAyB,EACvB,+LAA+L;SAClM;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL;;;;;;eAMG;YACH,YAAY,CAAC,IAA2B;gBACtC,8DAA8D;gBAC9D,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,sBAAc,CAAC,aAAa,IAAI,CAAC,IAAA,sCAAoB,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7F,OAAO;gBACT,CAAC;gBAED,yEAAyE;gBACzE,IACE,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,sBAAc,CAAC,sBAAsB;oBAC1D,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,sBAAc,CAAC,eAAe,EAC7D,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport { isClassnameAttribute } from \"../../utils/classname-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}.ts`\n);\n\ntype MessageIds = \"templateStringInClassName\";\ntype Options = [];\n\n/**\n * ESLint rule to disallow template strings in className prop.\n *\n * @example\n * // Bad\n * <div className={`${dynamicClass} staticClass`}>Content</div>\n *\n * // Good\n * <div className={twMerge(dynamicClassName, \"staticClassName\")}>Content</div>\n * // or\n * <div className={yourCvaMergeFunction({ yourVariantProp, className: \"extraClassName\" })}>Content</div>\n */\nexport const noTemplateStringsInClassName = createRule<Options, MessageIds>({\n name: \"no-template-strings-in-classname-prop\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Disallow the use of template strings in className prop to encourage better class management.\",\n },\n messages: {\n templateStringInClassName:\n \"Do not use template strings in className prop. If you wish to merge classes from different sources, use twMerge() for simple inline merging or make a cvaMerge() function if it has variants.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n return {\n /**\n * Handle JSXAttribute with className prop that uses template strings.\n *\n * @example\n * // Bad\n * <div className={`${dynamicClass} staticClass`}>Content</div>\n */\n JSXAttribute(node: TSESTree.JSXAttribute) {\n // Check if this is a classname attribute (className or class)\n if (node.name.type !== AST_NODE_TYPES.JSXIdentifier || !isClassnameAttribute(node.name.name)) {\n return;\n }\n\n // Check if the value is a template literal in a JSX expression container\n if (\n node.value?.type === AST_NODE_TYPES.JSXExpressionContainer &&\n node.value.expression.type === AST_NODE_TYPES.TemplateLiteral\n ) {\n context.report({ node, messageId: \"templateStringInClassName\" });\n }\n },\n };\n },\n});\n"]}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ // To test this rule: comment out the eslint-disable line below and run: yarn nx run eslint-plugin-trackunit:lint
3
+ /* eslint-disable @trackunit/no-typescript-assertion */
4
+ // This file demonstrates the no-typescript-assertion rule
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ // ✅ VALID - Using as const (allowed exception)
7
+ const _config = { name: "test", value: 123 };
8
+ const _colors = ["red", "green", "blue"];
9
+ // ✅ VALID - Using as unknown as intermediate (allowed exception, but still discouraged)
10
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
11
+ const _data = someValue;
12
+ // ✅ VALID - Proper type guard
13
+ function isString(val) {
14
+ return typeof val === "string";
15
+ }
16
+ const _validValue = isString(something) ? something : "default";
17
+ // ✅ VALID - Explicit null/undefined handling
18
+ const _validOptional = maybeValue ?? "default";
19
+ // ❌ INVALID - Angle bracket assertion (SHOULD be flagged)
20
+ const _angleExample = someValue;
21
+ // ❌ INVALID - 'as' keyword assertion (SHOULD be flagged)
22
+ const _asExample = someValue;
23
+ const _asNumber = userInput;
24
+ const _asObject = response;
25
+ // ❌ INVALID - Non-null assertion (SHOULD be flagged)
26
+ // These are intentionally invalid examples - the rule should catch these
27
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28
+ const _nonNullExample = maybeNull;
29
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion
30
+ const _chainedNonNull = obj.prop.value;
31
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion
32
+ const _arrayNonNull = arr[0];
33
+ // ❌ INVALID - Multiple assertion types in one file
34
+ const _mixed1 = value;
35
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
36
+ const _mixed2 = anotherValue;
37
+ const _mixed3 = yetAnotherValue;
38
+ // ❌ INVALID - Common real-world cases that should be caught
39
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
40
+ const _apiResponse = fetch("/api/data");
41
+ const _jsonData = JSON.parse(str);
42
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
43
+ const _element = document.getElementById("root");
44
+ const _typedArray = Array.from(collection);
45
+ //# sourceMappingURL=examples.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examples.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-typescript-assertion/examples.ts"],"names":[],"mappings":";AAAA,iHAAiH;AACjH,uDAAuD;AACvD,0DAA0D;;AAuB1D,+CAA+C;AAC/C,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAW,CAAC;AACtD,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAElD,wFAAwF;AACxF,4EAA4E;AAC5E,MAAM,KAAK,GAAG,SAAoB,CAAC;AAEnC,8BAA8B;AAC9B,SAAS,QAAQ,CAAC,GAAY;IAC5B,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC;AACjC,CAAC;AACD,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAEhE,6CAA6C;AAC7C,MAAM,cAAc,GAAG,UAAU,IAAI,SAAS,CAAC;AAE/C,0DAA0D;AAC1D,MAAM,aAAa,GAAW,SAAS,CAAC;AAExC,yDAAyD;AACzD,MAAM,UAAU,GAAG,SAAmB,CAAC;AACvC,MAAM,SAAS,GAAG,SAAmB,CAAC;AACtC,MAAM,SAAS,GAAG,QAAwC,CAAC;AAE3D,qDAAqD;AACrD,yEAAyE;AACzE,oEAAoE;AACpE,MAAM,eAAe,GAAG,SAAU,CAAC;AACnC,sHAAsH;AACtH,MAAM,eAAe,GAAG,GAAI,CAAC,IAAK,CAAC,KAAM,CAAC;AAC1C,sHAAsH;AACtH,MAAM,aAAa,GAAG,GAAI,CAAC,CAAC,CAAC,CAAC;AAE9B,mDAAmD;AACnD,MAAM,OAAO,GAAG,KAAiB,CAAC;AAClC,oEAAoE;AACpE,MAAM,OAAO,GAAG,YAAa,CAAC;AAC9B,MAAM,OAAO,GAAc,eAAe,CAAC;AAE3C,4DAA4D;AAC5D,4EAA4E;AAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAsB,CAAC;AAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACjD,oEAAoE;AACpE,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAE,CAAC;AAClD,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAqB,CAAC","sourcesContent":["// To test this rule: comment out the eslint-disable line below and run: yarn nx run eslint-plugin-trackunit:lint\n/* eslint-disable @trackunit/no-typescript-assertion */\n// This file demonstrates the no-typescript-assertion rule\n\n// Declare variables used in examples\ndeclare const someValue: unknown;\ndeclare const something: unknown;\ndeclare const maybeValue: unknown;\ndeclare const userInput: unknown;\ndeclare const response: unknown;\ndeclare const maybeNull: unknown;\ndeclare const obj: { prop?: { value?: unknown } };\ndeclare const arr: Array<unknown>;\ndeclare const value: unknown;\ndeclare const anotherValue: unknown;\ndeclare const yetAnotherValue: unknown;\ndeclare const str: string;\ndeclare const collection: Iterable<unknown>;\n\ntype SomeType = Record<string, unknown>;\ntype MyInterface = { id: number; name: string };\ntype _ApiData = { data: unknown };\ntype ThirdType = Record<string, unknown>;\ntype TypedItem = { id: string };\n\n// ✅ VALID - Using as const (allowed exception)\nconst _config = { name: \"test\", value: 123 } as const;\nconst _colors = [\"red\", \"green\", \"blue\"] as const;\n\n// ✅ VALID - Using as unknown as intermediate (allowed exception, but still discouraged)\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\nconst _data = someValue as unknown;\n\n// ✅ VALID - Proper type guard\nfunction isString(val: unknown): val is string {\n return typeof val === \"string\";\n}\nconst _validValue = isString(something) ? something : \"default\";\n\n// ✅ VALID - Explicit null/undefined handling\nconst _validOptional = maybeValue ?? \"default\";\n\n// ❌ INVALID - Angle bracket assertion (SHOULD be flagged)\nconst _angleExample = <string>someValue;\n\n// ❌ INVALID - 'as' keyword assertion (SHOULD be flagged)\nconst _asExample = someValue as string;\nconst _asNumber = userInput as number;\nconst _asObject = response as { id: number; name: string };\n\n// ❌ INVALID - Non-null assertion (SHOULD be flagged)\n// These are intentionally invalid examples - the rule should catch these\n// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\nconst _nonNullExample = maybeNull!;\n// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion\nconst _chainedNonNull = obj!.prop!.value!;\n// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion\nconst _arrayNonNull = arr![0];\n\n// ❌ INVALID - Multiple assertion types in one file\nconst _mixed1 = value as SomeType;\n// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\nconst _mixed2 = anotherValue!;\nconst _mixed3 = <ThirdType>yetAnotherValue;\n\n// ❌ INVALID - Common real-world cases that should be caught\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\nconst _apiResponse = fetch(\"/api/data\") as Promise<Response>;\nconst _jsonData = JSON.parse(str) as MyInterface;\n// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\nconst _element = document.getElementById(\"root\")!;\nconst _typedArray = Array.from(collection) as Array<TypedItem>;\n\nexport {};\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ESLint rule to disallow type assertions in TypeScript.
3
+ * Initial implementation based on eslint-plugin-no-type-assertion
4
+ * https://github.com/Dremora/eslint-plugin-no-type-assertion/blob/master/lib/rules/no-type-assertion.js
5
+ *
6
+ * @example
7
+ * // Bad - type assertions bypass type checking
8
+ * const foo = <string> bar;
9
+ * const baz = bar as string;
10
+ * const qux = bar!;
11
+ *
12
+ * // Good - proper typing or const assertions
13
+ * const foo = bar;
14
+ * const options = ["a", "b"] as const;
15
+ */
16
+ import { Rule } from "eslint";
17
+ export declare const noTypescriptAssertion: {
18
+ meta: Rule.RuleMetaData;
19
+ create: (context: Rule.RuleContext) => Rule.RuleListener;
20
+ };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noTypescriptAssertion = void 0;
4
+ const meta = {
5
+ type: "suggestion",
6
+ docs: {
7
+ description: "Prevent the use of type assertions to encourage better type safety.",
8
+ },
9
+ messages: {
10
+ angleBracketAssertion: "Avoid using angle bracket for type casting. It bypasses type checking.",
11
+ asAssertion: "Avoid using the `as` keyword for type casting. It bypasses type checking.",
12
+ nonNullAssertion: "Avoid using the non-null assertion operator (!). It can lead to runtime errors.",
13
+ },
14
+ schema: [],
15
+ };
16
+ /**
17
+ * Create ESLint rule listener.
18
+ *
19
+ * @param {Rule.RuleContext} context - The ESLint rule context.
20
+ * @returns {Rule.RuleListener} - The listener object for this rule.
21
+ */
22
+ const create = (context) => {
23
+ return {
24
+ /**
25
+ * Handle TypeScript type assertions using angle brackets.
26
+ *
27
+ * @example
28
+ * // Bad
29
+ * const foo = <string> bar;
30
+ * @param {any} node - The AST node.
31
+ */
32
+ TSTypeAssertion(node) {
33
+ const { typeAnnotation } = node;
34
+ if (typeAnnotation.type === "TSUnknownKeyword") {
35
+ return;
36
+ }
37
+ if (typeAnnotation.type === "TSTypeReference") {
38
+ const { typeName } = typeAnnotation;
39
+ if (typeName.type === "Identifier" && typeName.name === "const") {
40
+ return;
41
+ }
42
+ }
43
+ context.report({ node, messageId: "angleBracketAssertion" });
44
+ },
45
+ /**
46
+ * Handle TypeScript 'as' expressions for type assertions.
47
+ *
48
+ * @example
49
+ * // Bad
50
+ * const baz = bar as string;
51
+ * @param {any} node - The AST node.
52
+ */
53
+ TSAsExpression(node) {
54
+ const { typeAnnotation } = node;
55
+ if (typeAnnotation.type === "TSUnknownKeyword") {
56
+ return;
57
+ }
58
+ if (typeAnnotation.type === "TSTypeReference") {
59
+ const { typeName } = typeAnnotation;
60
+ if (typeName.type === "Identifier" && typeName.name === "const") {
61
+ return;
62
+ }
63
+ }
64
+ context.report({ node, messageId: "asAssertion" });
65
+ },
66
+ /**
67
+ * Handle TypeScript non-null expressions using the postfix expression '!'.
68
+ *
69
+ * @example
70
+ * // Bad
71
+ * const qux = bar!;
72
+ * @param {any} node - The AST node.
73
+ */
74
+ TSNonNullExpression(node) {
75
+ context.report({ node, messageId: "nonNullAssertion" });
76
+ },
77
+ };
78
+ };
79
+ exports.noTypescriptAssertion = {
80
+ meta,
81
+ create,
82
+ };
83
+ //# sourceMappingURL=no-typescript-assertion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-typescript-assertion.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/no-typescript-assertion/no-typescript-assertion.ts"],"names":[],"mappings":";;;AAkBA,MAAM,IAAI,GAAsB;IAC9B,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE;QACJ,WAAW,EAAE,qEAAqE;KACnF;IACD,QAAQ,EAAE;QACR,qBAAqB,EAAE,wEAAwE;QAC/F,WAAW,EAAE,2EAA2E;QACxF,gBAAgB,EAAE,iFAAiF;KACpG;IACD,MAAM,EAAE,EAAE;CACX,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,OAAyB,EAAqB,EAAE;IAC9D,OAAO;QACL;;;;;;;WAOG;QACH,eAAe,CAAC,IAAS;YACvB,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;YAEhC,IAAI,cAAc,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC/C,OAAO;YACT,CAAC;YAED,IAAI,cAAc,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC9C,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC;gBACpC,IAAI,QAAQ,CAAC,IAAI,KAAK,YAAY,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAChE,OAAO;gBACT,CAAC;YACH,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD;;;;;;;WAOG;QACH,cAAc,CAAC,IAAS;YACtB,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;YAEhC,IAAI,cAAc,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC/C,OAAO;YACT,CAAC;YAED,IAAI,cAAc,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC9C,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC;gBACpC,IAAI,QAAQ,CAAC,IAAI,KAAK,YAAY,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAChE,OAAO;gBACT,CAAC;YACH,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;QACrD,CAAC;QACD;;;;;;;WAOG;QACH,mBAAmB,CAAC,IAAS;YAC3B,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEW,QAAA,qBAAqB,GAAG;IACnC,IAAI;IACJ,MAAM;CACP,CAAC","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * ESLint rule to disallow type assertions in TypeScript.\n * Initial implementation based on eslint-plugin-no-type-assertion\n * https://github.com/Dremora/eslint-plugin-no-type-assertion/blob/master/lib/rules/no-type-assertion.js\n *\n * @example\n * // Bad - type assertions bypass type checking\n * const foo = <string> bar;\n * const baz = bar as string;\n * const qux = bar!;\n *\n * // Good - proper typing or const assertions\n * const foo = bar;\n * const options = [\"a\", \"b\"] as const;\n */\nimport { Rule } from \"eslint\";\n\nconst meta: Rule.RuleMetaData = {\n type: \"suggestion\",\n docs: {\n description: \"Prevent the use of type assertions to encourage better type safety.\",\n },\n messages: {\n angleBracketAssertion: \"Avoid using angle bracket for type casting. It bypasses type checking.\",\n asAssertion: \"Avoid using the `as` keyword for type casting. It bypasses type checking.\",\n nonNullAssertion: \"Avoid using the non-null assertion operator (!). It can lead to runtime errors.\",\n },\n schema: [],\n};\n\n/**\n * Create ESLint rule listener.\n *\n * @param {Rule.RuleContext} context - The ESLint rule context.\n * @returns {Rule.RuleListener} - The listener object for this rule.\n */\nconst create = (context: Rule.RuleContext): Rule.RuleListener => {\n return {\n /**\n * Handle TypeScript type assertions using angle brackets.\n *\n * @example\n * // Bad\n * const foo = <string> bar;\n * @param {any} node - The AST node.\n */\n TSTypeAssertion(node: any) {\n const { typeAnnotation } = node;\n\n if (typeAnnotation.type === \"TSUnknownKeyword\") {\n return;\n }\n\n if (typeAnnotation.type === \"TSTypeReference\") {\n const { typeName } = typeAnnotation;\n if (typeName.type === \"Identifier\" && typeName.name === \"const\") {\n return;\n }\n }\n\n context.report({ node, messageId: \"angleBracketAssertion\" });\n },\n /**\n * Handle TypeScript 'as' expressions for type assertions.\n *\n * @example\n * // Bad\n * const baz = bar as string;\n * @param {any} node - The AST node.\n */\n TSAsExpression(node: any) {\n const { typeAnnotation } = node;\n\n if (typeAnnotation.type === \"TSUnknownKeyword\") {\n return;\n }\n\n if (typeAnnotation.type === \"TSTypeReference\") {\n const { typeName } = typeAnnotation;\n if (typeName.type === \"Identifier\" && typeName.name === \"const\") {\n return;\n }\n }\n\n context.report({ node, messageId: \"asAssertion\" });\n },\n /**\n * Handle TypeScript non-null expressions using the postfix expression '!'.\n *\n * @example\n * // Bad\n * const qux = bar!;\n * @param {any} node - The AST node.\n */\n TSNonNullExpression(node: any) {\n context.report({ node, messageId: \"nonNullAssertion\" });\n },\n };\n};\n\nexport const noTypescriptAssertion = {\n meta,\n create,\n};\n"]}