@reasonabletech/eslint-config 0.1.0

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 (61) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +89 -0
  3. package/dist/src/base-configs.d.ts +53 -0
  4. package/dist/src/base-configs.js +105 -0
  5. package/dist/src/custom-rules/architecture-patterns.d.ts +193 -0
  6. package/dist/src/custom-rules/architecture-patterns.js +344 -0
  7. package/dist/src/custom-rules/code-quality.d.ts +201 -0
  8. package/dist/src/custom-rules/code-quality.js +388 -0
  9. package/dist/src/custom-rules/error-handling.d.ts +103 -0
  10. package/dist/src/custom-rules/error-handling.js +349 -0
  11. package/dist/src/custom-rules/index.d.ts +79 -0
  12. package/dist/src/custom-rules/index.js +119 -0
  13. package/dist/src/custom-rules/null-undefined-checks.d.ts +20 -0
  14. package/dist/src/custom-rules/null-undefined-checks.js +153 -0
  15. package/dist/src/custom-rules/platform-conventions.d.ts +115 -0
  16. package/dist/src/custom-rules/platform-conventions.js +249 -0
  17. package/dist/src/custom-rules/test-quality.d.ts +38 -0
  18. package/dist/src/custom-rules/test-quality.js +68 -0
  19. package/dist/src/custom-rules/type-safety.d.ts +71 -0
  20. package/dist/src/custom-rules/type-safety.js +121 -0
  21. package/dist/src/custom-rules/ui-library-imports.d.ts +21 -0
  22. package/dist/src/custom-rules/ui-library-imports.js +31 -0
  23. package/dist/src/custom-rules/utils.d.ts +95 -0
  24. package/dist/src/custom-rules/utils.js +146 -0
  25. package/dist/src/index.d.ts +73 -0
  26. package/dist/src/index.js +80 -0
  27. package/dist/src/next/config.d.ts +26 -0
  28. package/dist/src/next/config.js +76 -0
  29. package/dist/src/next/ignores.d.ts +37 -0
  30. package/dist/src/next/ignores.js +69 -0
  31. package/dist/src/next/plugins.d.ts +44 -0
  32. package/dist/src/next/plugins.js +104 -0
  33. package/dist/src/next/rules.d.ts +39 -0
  34. package/dist/src/next/rules.js +48 -0
  35. package/dist/src/next/settings.d.ts +41 -0
  36. package/dist/src/next/settings.js +45 -0
  37. package/dist/src/next.d.ts +33 -0
  38. package/dist/src/next.js +74 -0
  39. package/dist/src/plugin.d.ts +48 -0
  40. package/dist/src/plugin.js +30 -0
  41. package/dist/src/react/config.d.ts +30 -0
  42. package/dist/src/react/config.js +40 -0
  43. package/dist/src/react/plugins.d.ts +24 -0
  44. package/dist/src/react/plugins.js +46 -0
  45. package/dist/src/react/rules.d.ts +30 -0
  46. package/dist/src/react/rules.js +35 -0
  47. package/dist/src/react.d.ts +27 -0
  48. package/dist/src/react.js +35 -0
  49. package/dist/src/shared/parser-options.d.ts +3 -0
  50. package/dist/src/shared/parser-options.js +19 -0
  51. package/dist/src/shared/plugin-utils.d.ts +8 -0
  52. package/dist/src/shared/plugin-utils.js +23 -0
  53. package/dist/src/shared/react-rules.d.ts +97 -0
  54. package/dist/src/shared/react-rules.js +126 -0
  55. package/dist/src/shared/strict-rules.d.ts +27 -0
  56. package/dist/src/shared/strict-rules.js +54 -0
  57. package/dist/src/shared-ignores.d.ts +15 -0
  58. package/dist/src/shared-ignores.js +68 -0
  59. package/dist/src/shared-rules.d.ts +29 -0
  60. package/dist/src/shared-rules.js +163 -0
  61. package/package.json +122 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Custom ESLint rule: no-null-undefined-checks
3
+ *
4
+ * Flags code that checks for both null and undefined on the same value,
5
+ * which indicates a type mismatch. null and undefined are different types
6
+ * in TypeScript, and you should only check for what's in your type union.
7
+ */
8
+ import { ESLintUtils, AST_NODE_TYPES, } from "@typescript-eslint/utils";
9
+ /**
10
+ * Type guard for BinaryExpression
11
+ * @param node The node to check
12
+ * @returns True if the node is a BinaryExpression
13
+ */
14
+ function isBinaryExpression(node) {
15
+ return node.type === AST_NODE_TYPES.BinaryExpression;
16
+ }
17
+ /**
18
+ * Type guard for Identifier
19
+ * @param node The node to check
20
+ * @returns True if the node is an Identifier
21
+ */
22
+ function isIdentifier(node) {
23
+ return node.type === AST_NODE_TYPES.Identifier;
24
+ }
25
+ /**
26
+ * Type guard for Literal
27
+ * @param node The node to check
28
+ * @returns True if the node is a Literal
29
+ */
30
+ function isLiteral(node) {
31
+ return node.type === AST_NODE_TYPES.Literal;
32
+ }
33
+ /**
34
+ * Checks if a node is a null comparison (=== null, !== null, == null, != null)
35
+ * @param node The node to check
36
+ * @returns The variable being compared to null, or null if not a null comparison
37
+ */
38
+ function getNullCheckVariable(node) {
39
+ if (!isBinaryExpression(node)) {
40
+ return null;
41
+ }
42
+ const { operator, left, right } = node;
43
+ const isComparisonOp = operator === "===" ||
44
+ operator === "!==" ||
45
+ operator === "==" ||
46
+ operator === "!=";
47
+ if (!isComparisonOp) {
48
+ return null;
49
+ }
50
+ // Check if right side is null literal
51
+ if (isLiteral(right) && right.value === null && isIdentifier(left)) {
52
+ return left.name;
53
+ }
54
+ // Check if left side is null literal
55
+ if (isLiteral(left) && left.value === null && isIdentifier(right)) {
56
+ return right.name;
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * Checks if a node is an undefined comparison (=== undefined, !== undefined, == undefined, != undefined)
62
+ * @param node The node to check
63
+ * @returns The variable being compared to undefined, or null if not an undefined comparison
64
+ */
65
+ function getUndefinedCheckVariable(node) {
66
+ if (!isBinaryExpression(node)) {
67
+ return null;
68
+ }
69
+ const { operator, left, right } = node;
70
+ const isComparisonOp = operator === "===" ||
71
+ operator === "!==" ||
72
+ operator === "==" ||
73
+ operator === "!=";
74
+ if (!isComparisonOp) {
75
+ return null;
76
+ }
77
+ // Check if right side is undefined identifier
78
+ if (isIdentifier(right) && right.name === "undefined" && isIdentifier(left)) {
79
+ return left.name;
80
+ }
81
+ // Check if left side is undefined identifier
82
+ if (isIdentifier(left) && left.name === "undefined" && isIdentifier(right)) {
83
+ return right.name;
84
+ }
85
+ return null;
86
+ }
87
+ /**
88
+ * Creates the no-null-undefined-checks rule
89
+ */
90
+ export const noNullUndefinedChecksRule = ESLintUtils.RuleCreator(() => "docs/standards/typescript-design-patterns.md")({
91
+ name: "no-null-undefined-checks",
92
+ meta: {
93
+ type: "problem",
94
+ docs: {
95
+ description: "Flags checking for both null and undefined on the same value, which indicates a type mismatch. null and undefined are different types in TypeScript.",
96
+ },
97
+ messages: {
98
+ checksBoth: "Checking for both null and undefined suggests a type mismatch. Simplify the type: use `T | null` for nullable values, or `T | undefined` for optional values. If both are genuinely in the type, use `x == null` which covers both in a single check.",
99
+ },
100
+ schema: [],
101
+ },
102
+ defaultOptions: [],
103
+ create(context) {
104
+ return {
105
+ LogicalExpression(node) {
106
+ // Only check OR conditions (||)
107
+ if (node.operator !== "||") {
108
+ return;
109
+ }
110
+ // Get the variables being checked on each side
111
+ const leftNullVar = getNullCheckVariable(node.left);
112
+ const leftUndefinedVar = getUndefinedCheckVariable(node.left);
113
+ const rightNullVar = getNullCheckVariable(node.right);
114
+ const rightUndefinedVar = getUndefinedCheckVariable(node.right);
115
+ // Look for a variable that appears in both null and undefined checks
116
+ const nullVars = new Set();
117
+ const undefinedVars = new Set();
118
+ if (leftNullVar !== null) {
119
+ nullVars.add(leftNullVar);
120
+ }
121
+ if (rightNullVar !== null) {
122
+ nullVars.add(rightNullVar);
123
+ }
124
+ if (leftUndefinedVar !== null) {
125
+ undefinedVars.add(leftUndefinedVar);
126
+ }
127
+ if (rightUndefinedVar !== null) {
128
+ undefinedVars.add(rightUndefinedVar);
129
+ }
130
+ // Check if any variable is checked against both null and undefined
131
+ for (const variable of nullVars) {
132
+ if (undefinedVars.has(variable)) {
133
+ context.report({
134
+ node,
135
+ messageId: "checksBoth",
136
+ });
137
+ break;
138
+ }
139
+ }
140
+ },
141
+ };
142
+ },
143
+ });
144
+ /**
145
+ * Creates the rule configuration for the null/undefined checks rule
146
+ * @returns ESLint rule configuration
147
+ */
148
+ export function createNullUndefinedChecksRules() {
149
+ return {
150
+ "@reasonabletech/no-null-undefined-checks": "error",
151
+ };
152
+ }
153
+ //# sourceMappingURL=null-undefined-checks.js.map
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Platform-specific convention rules
3
+ *
4
+ * These rules enforce conventions specific to the platform's
5
+ * standard library and architectural patterns, ensuring consistent use
6
+ * of platform utilities across the codebase.
7
+ */
8
+ import { ESLintUtils } from "@typescript-eslint/utils";
9
+ import type { Linter } from "eslint";
10
+ import { type UILibraryImportRuleOptions } from "./ui-library-imports.js";
11
+ /**
12
+ * Configuration options for platform convention rules
13
+ */
14
+ export interface PlatformConventionRuleOptions {
15
+ /** Base URL for documentation references */
16
+ docBaseUrl?: string;
17
+ /** Whether to enforce Result helper usage (default: true) */
18
+ enforceResultHelpers?: boolean;
19
+ /**
20
+ * Legacy alias for discouraging UI library barrel imports.
21
+ * Prefer `discourageUILibraryBarrelImports`.
22
+ */
23
+ discourageUIBarrelImports?: boolean;
24
+ /**
25
+ * UI import boundary options for the shared UI library.
26
+ */
27
+ uiImportBoundaries?: UILibraryImportRuleOptions;
28
+ }
29
+ /**
30
+ * Custom ESLint rule that enforces using ok() and err() helpers from `@reasonabletech/utils`
31
+ *
32
+ * This rule prevents manual Result object construction, ensuring developers use
33
+ * the platform's standard utility functions for consistency and type safety.
34
+ *
35
+ * **Core Principle**: Always use ok() and err() helpers for Result types instead
36
+ * of manually constructing objects with { success: true/false, ... }
37
+ *
38
+ * ❌ FORBIDDEN (Manual Result construction):
39
+ * ```typescript
40
+ * // Wrong: Manual success result
41
+ * return { success: true, data: user };
42
+ *
43
+ * // Wrong: Manual error result
44
+ * return { success: false, error: "not_found" };
45
+ *
46
+ * // Wrong: Inline object literals
47
+ * const result = { success: true, data: value };
48
+ * ```
49
+ *
50
+ * ✅ CORRECT (Using platform helpers):
51
+ * ```typescript
52
+ * // Right: Use ok() for success
53
+ * import { ok } from "@reasonabletech/utils";
54
+ * return ok(user);
55
+ *
56
+ * // Right: Use err() for errors
57
+ * import { err } from "@reasonabletech/utils";
58
+ * return err("not_found");
59
+ *
60
+ * // Right: Import and use consistently
61
+ * import { ok, err, type Result } from "@reasonabletech/utils";
62
+ *
63
+ * async function getUser(id: string): Promise<Result<User, "not_found">> {
64
+ * const user = await db.user.findUnique({ where: { id } });
65
+ * if (!user) return err("not_found");
66
+ * return ok(user);
67
+ * }
68
+ * ```
69
+ *
70
+ * **Why this matters**:
71
+ * - Ensures consistent Result construction across the platform
72
+ * - Leverages type inference from helper functions
73
+ * - Makes refactoring easier (change implementation in one place)
74
+ * - Provides better editor autocomplete and type checking
75
+ * - Prevents typos in manual object construction ({ sucess: true })
76
+ */
77
+ export declare const useResultHelpersRule: ESLintUtils.RuleModule<"useOkHelper" | "useErrHelper", [{
78
+ docBaseUrl: string;
79
+ }], unknown, ESLintUtils.RuleListener> & {
80
+ name: string;
81
+ };
82
+ /**
83
+ * Creates rules that enforce Result helper usage
84
+ *
85
+ * Uses the `@reasonabletech/use-result-helpers` custom rule which detects manual
86
+ * Result construction including non-literal success values, spread elements,
87
+ * and computed property keys.
88
+ * @param options Configuration options for platform convention rules
89
+ * @returns ESLint rules that enforce Result helper usage
90
+ */
91
+ export declare function createResultHelperRules(options?: PlatformConventionRuleOptions): Linter.RulesRecord;
92
+ /**
93
+ * Creates UI library import-boundary rules.
94
+ *
95
+ * This is a thin adapter that keeps the platform-conventions API stable while
96
+ * delegating UI-library specifics to the dedicated `ui-library-imports` module.
97
+ * @param options Configuration options for platform convention rules
98
+ * @returns ESLint rules that discourage UI library barrel imports
99
+ */
100
+ export declare function createUIBarrelImportRules(options?: PlatformConventionRuleOptions): Linter.RulesRecord;
101
+ /**
102
+ * Creates a complete set of platform convention rules
103
+ *
104
+ * This is the main function that combines all platform-specific convention
105
+ * rules into a single configuration object.
106
+ * @param options Configuration options for platform convention rules
107
+ * @returns Complete set of platform convention ESLint rules
108
+ */
109
+ export declare function createPlatformConventionRules(options?: PlatformConventionRuleOptions): Linter.RulesRecord;
110
+ /**
111
+ * Preset for platform convention rules
112
+ * @returns ESLint rules configured for platform conventions
113
+ */
114
+ export declare function createPlatformConventionPresetRules(): Linter.RulesRecord;
115
+ //# sourceMappingURL=platform-conventions.d.ts.map
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Platform-specific convention rules
3
+ *
4
+ * These rules enforce conventions specific to the platform's
5
+ * standard library and architectural patterns, ensuring consistent use
6
+ * of platform utilities across the codebase.
7
+ */
8
+ import { AST_NODE_TYPES, ESLintUtils, } from "@typescript-eslint/utils";
9
+ import { mergeRuleConfigurations } from "./utils.js";
10
+ import { createUILibraryImportRules, } from "./ui-library-imports.js";
11
+ /**
12
+ * Default configuration for platform convention rules
13
+ */
14
+ const DEFAULT_OPTIONS = {
15
+ docBaseUrl: "",
16
+ enforceResultHelpers: true,
17
+ discourageUIBarrelImports: true,
18
+ uiImportBoundaries: {
19
+ discourageUILibraryBarrelImports: true,
20
+ },
21
+ };
22
+ /**
23
+ * Custom ESLint rule that enforces using ok() and err() helpers from `@reasonabletech/utils`
24
+ *
25
+ * This rule prevents manual Result object construction, ensuring developers use
26
+ * the platform's standard utility functions for consistency and type safety.
27
+ *
28
+ * **Core Principle**: Always use ok() and err() helpers for Result types instead
29
+ * of manually constructing objects with { success: true/false, ... }
30
+ *
31
+ * ❌ FORBIDDEN (Manual Result construction):
32
+ * ```typescript
33
+ * // Wrong: Manual success result
34
+ * return { success: true, data: user };
35
+ *
36
+ * // Wrong: Manual error result
37
+ * return { success: false, error: "not_found" };
38
+ *
39
+ * // Wrong: Inline object literals
40
+ * const result = { success: true, data: value };
41
+ * ```
42
+ *
43
+ * ✅ CORRECT (Using platform helpers):
44
+ * ```typescript
45
+ * // Right: Use ok() for success
46
+ * import { ok } from "@reasonabletech/utils";
47
+ * return ok(user);
48
+ *
49
+ * // Right: Use err() for errors
50
+ * import { err } from "@reasonabletech/utils";
51
+ * return err("not_found");
52
+ *
53
+ * // Right: Import and use consistently
54
+ * import { ok, err, type Result } from "@reasonabletech/utils";
55
+ *
56
+ * async function getUser(id: string): Promise<Result<User, "not_found">> {
57
+ * const user = await db.user.findUnique({ where: { id } });
58
+ * if (!user) return err("not_found");
59
+ * return ok(user);
60
+ * }
61
+ * ```
62
+ *
63
+ * **Why this matters**:
64
+ * - Ensures consistent Result construction across the platform
65
+ * - Leverages type inference from helper functions
66
+ * - Makes refactoring easier (change implementation in one place)
67
+ * - Provides better editor autocomplete and type checking
68
+ * - Prevents typos in manual object construction ({ sucess: true })
69
+ */
70
+ export const useResultHelpersRule = ESLintUtils.RuleCreator(() => "")({
71
+ name: "use-result-helpers",
72
+ meta: {
73
+ type: "problem",
74
+ docs: {
75
+ description: "Enforces using ok() and err() helpers from @reasonabletech/utils instead of manual Result construction",
76
+ },
77
+ messages: {
78
+ useOkHelper: "❌ FORBIDDEN: Use ok() from @reasonabletech/utils instead of manually constructing { success: true, data: ... }",
79
+ useErrHelper: "❌ FORBIDDEN: Use err() from @reasonabletech/utils instead of manually constructing { success: false, error: ... }",
80
+ },
81
+ schema: [
82
+ {
83
+ type: "object",
84
+ properties: {
85
+ docBaseUrl: {
86
+ type: "string",
87
+ },
88
+ },
89
+ additionalProperties: false,
90
+ },
91
+ ],
92
+ },
93
+ defaultOptions: [
94
+ {
95
+ docBaseUrl: "",
96
+ },
97
+ ],
98
+ create(context) {
99
+ /**
100
+ * Checks if an object expression looks like a manual Result construction
101
+ * @param node - AST node representing an object expression
102
+ * @returns True if the object looks like a Result type
103
+ */
104
+ function isResultLikeObject(node) {
105
+ let hasSuccess = false;
106
+ let successValue = null;
107
+ let hasData = false;
108
+ let hasError = false;
109
+ for (const prop of node.properties) {
110
+ if (prop.type === AST_NODE_TYPES.Property &&
111
+ prop.key.type === AST_NODE_TYPES.Identifier) {
112
+ const keyName = prop.key.name;
113
+ if (keyName === "success") {
114
+ hasSuccess = true;
115
+ // Try to determine if success is true or false
116
+ if (prop.value.type === AST_NODE_TYPES.Literal &&
117
+ typeof prop.value.value === "boolean") {
118
+ successValue = prop.value.value;
119
+ }
120
+ }
121
+ else if (keyName === "data") {
122
+ hasData = true;
123
+ }
124
+ else if (keyName === "error") {
125
+ hasError = true;
126
+ }
127
+ }
128
+ }
129
+ // Result type has 'success' and either 'data' or 'error'
130
+ const isResult = hasSuccess && (hasData || hasError);
131
+ return { isResult, isSuccess: successValue };
132
+ }
133
+ return {
134
+ ObjectExpression(node) {
135
+ const { isResult, isSuccess } = isResultLikeObject(node);
136
+ if (!isResult) {
137
+ return;
138
+ }
139
+ // Check if this is a success result (use ok helper)
140
+ if (isSuccess === true) {
141
+ context.report({
142
+ node,
143
+ messageId: "useOkHelper",
144
+ });
145
+ }
146
+ // Check if this is an error result (use err helper)
147
+ if (isSuccess === false) {
148
+ context.report({
149
+ node,
150
+ messageId: "useErrHelper",
151
+ });
152
+ }
153
+ // If we can't determine the success value (e.g., variable), still report
154
+ // because manual construction should be avoided
155
+ if (isSuccess === null) {
156
+ // Check which property exists to give better message
157
+ const hasData = node.properties.some((prop) => prop.type === AST_NODE_TYPES.Property &&
158
+ prop.key.type === AST_NODE_TYPES.Identifier &&
159
+ prop.key.name === "data");
160
+ if (hasData) {
161
+ context.report({
162
+ node,
163
+ messageId: "useOkHelper",
164
+ });
165
+ }
166
+ else {
167
+ context.report({
168
+ node,
169
+ messageId: "useErrHelper",
170
+ });
171
+ }
172
+ }
173
+ },
174
+ };
175
+ },
176
+ });
177
+ /**
178
+ * Creates rules that enforce Result helper usage
179
+ *
180
+ * Uses the `@reasonabletech/use-result-helpers` custom rule which detects manual
181
+ * Result construction including non-literal success values, spread elements,
182
+ * and computed property keys.
183
+ * @param options Configuration options for platform convention rules
184
+ * @returns ESLint rules that enforce Result helper usage
185
+ */
186
+ export function createResultHelperRules(options = {}) {
187
+ const config = { ...DEFAULT_OPTIONS, ...options };
188
+ if (!config.enforceResultHelpers) {
189
+ return {};
190
+ }
191
+ return {
192
+ "@reasonabletech/use-result-helpers": [
193
+ "error",
194
+ {
195
+ docBaseUrl: config.docBaseUrl,
196
+ },
197
+ ],
198
+ };
199
+ }
200
+ /**
201
+ * Creates UI library import-boundary rules.
202
+ *
203
+ * This is a thin adapter that keeps the platform-conventions API stable while
204
+ * delegating UI-library specifics to the dedicated `ui-library-imports` module.
205
+ * @param options Configuration options for platform convention rules
206
+ * @returns ESLint rules that discourage UI library barrel imports
207
+ */
208
+ export function createUIBarrelImportRules(options = {}) {
209
+ const config = {
210
+ ...DEFAULT_OPTIONS,
211
+ ...options,
212
+ uiImportBoundaries: {
213
+ ...DEFAULT_OPTIONS.uiImportBoundaries,
214
+ ...options.uiImportBoundaries,
215
+ },
216
+ };
217
+ return createUILibraryImportRules({
218
+ discourageUILibraryBarrelImports: (config.uiImportBoundaries.discourageUILibraryBarrelImports ?? true) &&
219
+ config.discourageUIBarrelImports,
220
+ });
221
+ }
222
+ /**
223
+ * Creates a complete set of platform convention rules
224
+ *
225
+ * This is the main function that combines all platform-specific convention
226
+ * rules into a single configuration object.
227
+ * @param options Configuration options for platform convention rules
228
+ * @returns Complete set of platform convention ESLint rules
229
+ */
230
+ export function createPlatformConventionRules(options = {}) {
231
+ const resultHelperRules = createResultHelperRules(options);
232
+ const uiBarrelImportRules = createUIBarrelImportRules(options);
233
+ return mergeRuleConfigurations(resultHelperRules, uiBarrelImportRules);
234
+ }
235
+ /**
236
+ * Preset for platform convention rules
237
+ * @returns ESLint rules configured for platform conventions
238
+ */
239
+ export function createPlatformConventionPresetRules() {
240
+ return createPlatformConventionRules({
241
+ docBaseUrl: "",
242
+ enforceResultHelpers: true,
243
+ discourageUIBarrelImports: true,
244
+ uiImportBoundaries: {
245
+ discourageUILibraryBarrelImports: true,
246
+ },
247
+ });
248
+ }
249
+ //# sourceMappingURL=platform-conventions.js.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Test quality rule definitions for ReasonableTech projects
3
+ *
4
+ * These rules prevent low-value test patterns that inflate coverage
5
+ * metrics without verifying real behavior. They are specifically designed
6
+ * to catch AI-generated test anti-patterns.
7
+ */
8
+ import type { Linter } from "eslint";
9
+ /**
10
+ * Creates ESLint rules that ban `expect(typeof <expr>).<matcher>(...)` patterns.
11
+ *
12
+ * These assertions only verify that JavaScript knows the type of an expression
13
+ * at the time the test runs — information that TypeScript already guarantees at
14
+ * compile time. They are a classic AI-generated coverage-padding anti-pattern.
15
+ *
16
+ * ❌ FORBIDDEN:
17
+ * ```typescript
18
+ * expect(typeof tabula.close).toBe("function");
19
+ * expect(typeof result).toBe("object");
20
+ * expect(typeof user.name).toEqual("string");
21
+ * ```
22
+ *
23
+ * ✅ CORRECT: test observable behaviour
24
+ * ```typescript
25
+ * // Call the function and assert what it does
26
+ * await tabula.close();
27
+ * expect(tabula.isClosed()).toBe(true);
28
+ *
29
+ * // Assert the shape of a result
30
+ * expect(result).toMatchObject({ id: expect.any(String) });
31
+ *
32
+ * // Assert the value, not the type
33
+ * expect(user.name).toBe("Alice");
34
+ * ```
35
+ * @returns ESLint rules that prevent typeof-in-expect patterns
36
+ */
37
+ export declare function createNoTypeofInExpectRules(): Linter.RulesRecord;
38
+ //# sourceMappingURL=test-quality.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Test quality rule definitions for ReasonableTech projects
3
+ *
4
+ * These rules prevent low-value test patterns that inflate coverage
5
+ * metrics without verifying real behavior. They are specifically designed
6
+ * to catch AI-generated test anti-patterns.
7
+ */
8
+ /**
9
+ * AST selector matching `expect(typeof <expr>).<matcher>(...)`.
10
+ *
11
+ * The pattern:
12
+ * - Outer CallExpression → the matcher call, e.g. `.toBe("function")`
13
+ * - callee is a MemberExpression whose object is a CallExpression to `expect`
14
+ * - The argument passed to `expect` is a UnaryExpression with operator `"typeof"`
15
+ *
16
+ * This catches all variants regardless of the matcher used:
17
+ * `expect(typeof x).toBe("function")`
18
+ * `expect(typeof x).toEqual("object")`
19
+ * `expect(typeof x).toStrictEqual("string")`
20
+ */
21
+ const TYPEOF_IN_EXPECT_SELECTOR = 'CallExpression[callee.type="MemberExpression"]' +
22
+ '[callee.object.type="CallExpression"]' +
23
+ '[callee.object.callee.name="expect"]' +
24
+ '[callee.object.arguments.0.type="UnaryExpression"]' +
25
+ '[callee.object.arguments.0.operator="typeof"]';
26
+ const NO_TYPEOF_IN_EXPECT_MESSAGE = "❌ FORBIDDEN: expect(typeof x).toBe(...) adds zero test value and hides real" +
27
+ " gaps in functionality. Test observable behaviour instead — call the function," +
28
+ " check its return value, assert side-effects.";
29
+ /**
30
+ * Creates ESLint rules that ban `expect(typeof <expr>).<matcher>(...)` patterns.
31
+ *
32
+ * These assertions only verify that JavaScript knows the type of an expression
33
+ * at the time the test runs — information that TypeScript already guarantees at
34
+ * compile time. They are a classic AI-generated coverage-padding anti-pattern.
35
+ *
36
+ * ❌ FORBIDDEN:
37
+ * ```typescript
38
+ * expect(typeof tabula.close).toBe("function");
39
+ * expect(typeof result).toBe("object");
40
+ * expect(typeof user.name).toEqual("string");
41
+ * ```
42
+ *
43
+ * ✅ CORRECT: test observable behaviour
44
+ * ```typescript
45
+ * // Call the function and assert what it does
46
+ * await tabula.close();
47
+ * expect(tabula.isClosed()).toBe(true);
48
+ *
49
+ * // Assert the shape of a result
50
+ * expect(result).toMatchObject({ id: expect.any(String) });
51
+ *
52
+ * // Assert the value, not the type
53
+ * expect(user.name).toBe("Alice");
54
+ * ```
55
+ * @returns ESLint rules that prevent typeof-in-expect patterns
56
+ */
57
+ export function createNoTypeofInExpectRules() {
58
+ return {
59
+ "no-restricted-syntax": [
60
+ "error",
61
+ {
62
+ selector: TYPEOF_IN_EXPECT_SELECTOR,
63
+ message: NO_TYPEOF_IN_EXPECT_MESSAGE,
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ //# sourceMappingURL=test-quality.js.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Type safety rule definitions for ReasonableTech projects
3
+ *
4
+ * These rules prevent type system bypasses and enforce safe type handling
5
+ * patterns, particularly around Result types and type assertions.
6
+ */
7
+ import { ESLintUtils } from "@typescript-eslint/utils";
8
+ import type { Linter } from "eslint";
9
+ /**
10
+ * Configuration options for type safety rules
11
+ */
12
+ export interface TypeSafetyRuleOptions {
13
+ /** Base URL for documentation references */
14
+ docBaseUrl?: string;
15
+ /** Whether to allow type assertions in test files (default: false) */
16
+ allowInTests?: boolean;
17
+ }
18
+ /**
19
+ * Custom ESLint rule that prevents `as any` and `<any>` type casts
20
+ *
21
+ * This rule detects all forms of casting to `any`, including:
22
+ * - `expr as any`
23
+ * - `<any>expr`
24
+ * - Double casts through any: `(expr as any) as T`
25
+ *
26
+ * Each variant produces a distinct, descriptive error message rather than the
27
+ * generic "no-restricted-syntax" label that the previous AST-selector approach
28
+ * showed in editors.
29
+ */
30
+ export declare const noAsAnyRule: ESLintUtils.RuleModule<"asAny" | "angleAny" | "doubleCast", [], unknown, ESLintUtils.RuleListener> & {
31
+ name: string;
32
+ };
33
+ /**
34
+ * Creates rules that prevent `as any` type casts
35
+ *
36
+ * These rules block the use of `as any` type assertions, which bypass
37
+ * TypeScript's type checking and create type safety holes.
38
+ *
39
+ * ❌ FORBIDDEN:
40
+ * ```typescript
41
+ * const data = response as any; // Bypasses type checking
42
+ * const x = (response as any) as User; // Double cast through any
43
+ * ```
44
+ *
45
+ * ✅ CORRECT:
46
+ * ```typescript
47
+ * const data = parseResponse(response); // Proper typed parsing
48
+ * const user = data.result.user; // Type-safe access
49
+ * ```
50
+ * @param _options Configuration options for type safety rules (reserved for future use)
51
+ * @returns ESLint rules that prevent `as any` casts
52
+ */
53
+ export declare function createNoAnyRules(_options?: TypeSafetyRuleOptions): Linter.RulesRecord;
54
+ /**
55
+ * Creates a complete set of type safety rules
56
+ *
57
+ * This is the main function that combines all type safety rules
58
+ * into a single configuration object.
59
+ * @param options Configuration options for type safety rules
60
+ * @returns Complete set of type safety ESLint rules
61
+ */
62
+ export declare function createTypeSafetyRules(options?: TypeSafetyRuleOptions): Linter.RulesRecord;
63
+ /**
64
+ * Preset for Platform-specific type safety rules
65
+ *
66
+ * This preset provides all type safety rules configured specifically
67
+ * for platform project conventions and documentation structure.
68
+ * @returns ESLint rules configured for platform projects
69
+ */
70
+ export declare function createPlatformTypeSafetyRules(): Linter.RulesRecord;
71
+ //# sourceMappingURL=type-safety.d.ts.map