@webpieces/dev-config 0.2.95 → 0.2.98

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 (181) hide show
  1. package/config/eslint/base.mjs +1 -1
  2. package/executors.json +6 -91
  3. package/package.json +6 -19
  4. package/{executors → src/executors}/help/executor.d.ts +4 -2
  5. package/src/executors/help/executor.js.map +1 -0
  6. package/src/executors/validate-eslint-sync/executor.js.map +1 -0
  7. package/{executors → src/executors}/validate-versions-locked/executor.js +2 -1
  8. package/src/executors/validate-versions-locked/executor.js.map +1 -0
  9. package/src/index.d.ts +1 -1
  10. package/src/index.js +1 -1
  11. package/src/index.js.map +1 -1
  12. package/src/plugin.d.ts +86 -0
  13. package/{plugin.js → src/plugin.js} +31 -15
  14. package/src/plugin.js.map +1 -0
  15. package/src/toError.d.ts +5 -0
  16. package/src/toError.js +37 -0
  17. package/src/toError.js.map +1 -0
  18. package/templates/eslint.webpieces.config.mjs +1 -1
  19. package/architecture/executors/diff-utils.d.ts +0 -24
  20. package/architecture/executors/diff-utils.js +0 -119
  21. package/architecture/executors/diff-utils.js.map +0 -1
  22. package/architecture/executors/diff-utils.ts +0 -127
  23. package/architecture/executors/generate/executor.d.ts +0 -16
  24. package/architecture/executors/generate/executor.js +0 -44
  25. package/architecture/executors/generate/executor.js.map +0 -1
  26. package/architecture/executors/generate/executor.ts +0 -59
  27. package/architecture/executors/generate/schema.json +0 -14
  28. package/architecture/executors/validate-architecture-unchanged/executor.d.ts +0 -17
  29. package/architecture/executors/validate-architecture-unchanged/executor.js +0 -229
  30. package/architecture/executors/validate-architecture-unchanged/executor.js.map +0 -1
  31. package/architecture/executors/validate-architecture-unchanged/executor.ts +0 -251
  32. package/architecture/executors/validate-architecture-unchanged/schema.json +0 -14
  33. package/architecture/executors/validate-code/executor.d.ts +0 -78
  34. package/architecture/executors/validate-code/executor.js +0 -243
  35. package/architecture/executors/validate-code/executor.js.map +0 -1
  36. package/architecture/executors/validate-code/executor.ts +0 -406
  37. package/architecture/executors/validate-code/schema.json +0 -227
  38. package/architecture/executors/validate-dtos/executor.d.ts +0 -42
  39. package/architecture/executors/validate-dtos/executor.js +0 -561
  40. package/architecture/executors/validate-dtos/executor.js.map +0 -1
  41. package/architecture/executors/validate-dtos/executor.ts +0 -689
  42. package/architecture/executors/validate-dtos/schema.json +0 -33
  43. package/architecture/executors/validate-modified-files/executor.d.ts +0 -25
  44. package/architecture/executors/validate-modified-files/executor.js +0 -501
  45. package/architecture/executors/validate-modified-files/executor.js.map +0 -1
  46. package/architecture/executors/validate-modified-files/executor.ts +0 -571
  47. package/architecture/executors/validate-modified-files/schema.json +0 -25
  48. package/architecture/executors/validate-modified-methods/executor.d.ts +0 -31
  49. package/architecture/executors/validate-modified-methods/executor.js +0 -694
  50. package/architecture/executors/validate-modified-methods/executor.js.map +0 -1
  51. package/architecture/executors/validate-modified-methods/executor.ts +0 -797
  52. package/architecture/executors/validate-modified-methods/schema.json +0 -25
  53. package/architecture/executors/validate-new-methods/executor.d.ts +0 -28
  54. package/architecture/executors/validate-new-methods/executor.js +0 -513
  55. package/architecture/executors/validate-new-methods/executor.js.map +0 -1
  56. package/architecture/executors/validate-new-methods/executor.ts +0 -584
  57. package/architecture/executors/validate-new-methods/schema.json +0 -25
  58. package/architecture/executors/validate-no-any-unknown/executor.d.ts +0 -42
  59. package/architecture/executors/validate-no-any-unknown/executor.js +0 -462
  60. package/architecture/executors/validate-no-any-unknown/executor.js.map +0 -1
  61. package/architecture/executors/validate-no-any-unknown/executor.ts +0 -540
  62. package/architecture/executors/validate-no-any-unknown/schema.json +0 -24
  63. package/architecture/executors/validate-no-architecture-cycles/executor.d.ts +0 -16
  64. package/architecture/executors/validate-no-architecture-cycles/executor.js +0 -48
  65. package/architecture/executors/validate-no-architecture-cycles/executor.js.map +0 -1
  66. package/architecture/executors/validate-no-architecture-cycles/executor.ts +0 -60
  67. package/architecture/executors/validate-no-architecture-cycles/schema.json +0 -8
  68. package/architecture/executors/validate-no-destructure/executor.d.ts +0 -52
  69. package/architecture/executors/validate-no-destructure/executor.js +0 -491
  70. package/architecture/executors/validate-no-destructure/executor.js.map +0 -1
  71. package/architecture/executors/validate-no-destructure/executor.ts +0 -578
  72. package/architecture/executors/validate-no-destructure/schema.json +0 -24
  73. package/architecture/executors/validate-no-direct-api-resolver/executor.d.ts +0 -47
  74. package/architecture/executors/validate-no-direct-api-resolver/executor.js +0 -566
  75. package/architecture/executors/validate-no-direct-api-resolver/executor.js.map +0 -1
  76. package/architecture/executors/validate-no-direct-api-resolver/executor.ts +0 -666
  77. package/architecture/executors/validate-no-direct-api-resolver/schema.json +0 -29
  78. package/architecture/executors/validate-no-inline-types/executor.d.ts +0 -91
  79. package/architecture/executors/validate-no-inline-types/executor.js +0 -669
  80. package/architecture/executors/validate-no-inline-types/executor.js.map +0 -1
  81. package/architecture/executors/validate-no-inline-types/executor.ts +0 -775
  82. package/architecture/executors/validate-no-inline-types/schema.json +0 -24
  83. package/architecture/executors/validate-no-skiplevel-deps/executor.d.ts +0 -19
  84. package/architecture/executors/validate-no-skiplevel-deps/executor.js +0 -227
  85. package/architecture/executors/validate-no-skiplevel-deps/executor.js.map +0 -1
  86. package/architecture/executors/validate-no-skiplevel-deps/executor.ts +0 -267
  87. package/architecture/executors/validate-no-skiplevel-deps/schema.json +0 -8
  88. package/architecture/executors/validate-packagejson/executor.d.ts +0 -16
  89. package/architecture/executors/validate-packagejson/executor.js +0 -57
  90. package/architecture/executors/validate-packagejson/executor.js.map +0 -1
  91. package/architecture/executors/validate-packagejson/executor.ts +0 -74
  92. package/architecture/executors/validate-packagejson/schema.json +0 -8
  93. package/architecture/executors/validate-prisma-converters/executor.d.ts +0 -60
  94. package/architecture/executors/validate-prisma-converters/executor.js +0 -634
  95. package/architecture/executors/validate-prisma-converters/executor.js.map +0 -1
  96. package/architecture/executors/validate-prisma-converters/executor.ts +0 -822
  97. package/architecture/executors/validate-prisma-converters/schema.json +0 -38
  98. package/architecture/executors/validate-return-types/executor.d.ts +0 -29
  99. package/architecture/executors/validate-return-types/executor.js +0 -439
  100. package/architecture/executors/validate-return-types/executor.js.map +0 -1
  101. package/architecture/executors/validate-return-types/executor.ts +0 -524
  102. package/architecture/executors/validate-return-types/schema.json +0 -24
  103. package/architecture/executors/visualize/executor.d.ts +0 -17
  104. package/architecture/executors/visualize/executor.js +0 -49
  105. package/architecture/executors/visualize/executor.js.map +0 -1
  106. package/architecture/executors/visualize/executor.ts +0 -63
  107. package/architecture/executors/visualize/schema.json +0 -14
  108. package/architecture/index.d.ts +0 -19
  109. package/architecture/index.js +0 -23
  110. package/architecture/index.js.map +0 -1
  111. package/architecture/index.ts +0 -20
  112. package/architecture/lib/graph-comparator.d.ts +0 -39
  113. package/architecture/lib/graph-comparator.js +0 -100
  114. package/architecture/lib/graph-comparator.js.map +0 -1
  115. package/architecture/lib/graph-comparator.ts +0 -141
  116. package/architecture/lib/graph-generator.d.ts +0 -19
  117. package/architecture/lib/graph-generator.js +0 -84
  118. package/architecture/lib/graph-generator.js.map +0 -1
  119. package/architecture/lib/graph-generator.ts +0 -97
  120. package/architecture/lib/graph-loader.d.ts +0 -31
  121. package/architecture/lib/graph-loader.js +0 -98
  122. package/architecture/lib/graph-loader.js.map +0 -1
  123. package/architecture/lib/graph-loader.ts +0 -116
  124. package/architecture/lib/graph-sorter.d.ts +0 -37
  125. package/architecture/lib/graph-sorter.js +0 -110
  126. package/architecture/lib/graph-sorter.js.map +0 -1
  127. package/architecture/lib/graph-sorter.ts +0 -137
  128. package/architecture/lib/graph-visualizer.d.ts +0 -29
  129. package/architecture/lib/graph-visualizer.js +0 -217
  130. package/architecture/lib/graph-visualizer.js.map +0 -1
  131. package/architecture/lib/graph-visualizer.ts +0 -231
  132. package/architecture/lib/package-validator.d.ts +0 -38
  133. package/architecture/lib/package-validator.js +0 -126
  134. package/architecture/lib/package-validator.js.map +0 -1
  135. package/architecture/lib/package-validator.ts +0 -170
  136. package/eslint-plugin/__tests__/catch-error-pattern.test.ts +0 -374
  137. package/eslint-plugin/__tests__/max-file-lines.test.ts +0 -207
  138. package/eslint-plugin/__tests__/max-method-lines.test.ts +0 -258
  139. package/eslint-plugin/__tests__/no-unmanaged-exceptions.test.ts +0 -359
  140. package/eslint-plugin/index.d.ts +0 -23
  141. package/eslint-plugin/index.js +0 -30
  142. package/eslint-plugin/index.js.map +0 -1
  143. package/eslint-plugin/index.ts +0 -29
  144. package/eslint-plugin/rules/catch-error-pattern.d.ts +0 -11
  145. package/eslint-plugin/rules/catch-error-pattern.js +0 -143
  146. package/eslint-plugin/rules/catch-error-pattern.js.map +0 -1
  147. package/eslint-plugin/rules/catch-error-pattern.ts +0 -246
  148. package/eslint-plugin/rules/enforce-architecture.d.ts +0 -15
  149. package/eslint-plugin/rules/enforce-architecture.js +0 -476
  150. package/eslint-plugin/rules/enforce-architecture.js.map +0 -1
  151. package/eslint-plugin/rules/enforce-architecture.ts +0 -543
  152. package/eslint-plugin/rules/max-file-lines.d.ts +0 -12
  153. package/eslint-plugin/rules/max-file-lines.js +0 -257
  154. package/eslint-plugin/rules/max-file-lines.js.map +0 -1
  155. package/eslint-plugin/rules/max-file-lines.ts +0 -272
  156. package/eslint-plugin/rules/max-method-lines.d.ts +0 -12
  157. package/eslint-plugin/rules/max-method-lines.js +0 -240
  158. package/eslint-plugin/rules/max-method-lines.js.map +0 -1
  159. package/eslint-plugin/rules/max-method-lines.ts +0 -287
  160. package/eslint-plugin/rules/no-unmanaged-exceptions.d.ts +0 -22
  161. package/eslint-plugin/rules/no-unmanaged-exceptions.js +0 -160
  162. package/eslint-plugin/rules/no-unmanaged-exceptions.js.map +0 -1
  163. package/eslint-plugin/rules/no-unmanaged-exceptions.ts +0 -179
  164. package/executors/help/executor.js.map +0 -1
  165. package/executors/help/executor.ts +0 -61
  166. package/executors/validate-eslint-sync/executor.js.map +0 -1
  167. package/executors/validate-eslint-sync/executor.ts +0 -87
  168. package/executors/validate-versions-locked/executor.js.map +0 -1
  169. package/executors/validate-versions-locked/executor.ts +0 -368
  170. package/plugin/README.md +0 -243
  171. package/plugin/index.d.ts +0 -4
  172. package/plugin/index.js +0 -8
  173. package/plugin/index.js.map +0 -1
  174. package/plugin/index.ts +0 -4
  175. /package/{executors → src/executors}/help/executor.js +0 -0
  176. /package/{executors → src/executors}/help/schema.json +0 -0
  177. /package/{executors → src/executors}/validate-eslint-sync/executor.d.ts +0 -0
  178. /package/{executors → src/executors}/validate-eslint-sync/executor.js +0 -0
  179. /package/{executors → src/executors}/validate-eslint-sync/schema.json +0 -0
  180. /package/{executors → src/executors}/validate-versions-locked/executor.d.ts +0 -0
  181. /package/{executors → src/executors}/validate-versions-locked/schema.json +0 -0
@@ -1,359 +0,0 @@
1
- /**
2
- * Tests for no-unmanaged-exceptions ESLint rule
3
- *
4
- * Validates that try-catch blocks are:
5
- * - Auto-allowed in test files (.test.ts, .spec.ts, __tests__/)
6
- * - Allowed with eslint-disable comment
7
- * - Disallowed in production code without approval
8
- */
9
-
10
- import { RuleTester } from 'eslint';
11
- import rule from '../rules/no-unmanaged-exceptions';
12
-
13
- const tsParser = require('@typescript-eslint/parser');
14
-
15
- const ruleTester = new RuleTester({
16
- languageOptions: {
17
- parser: tsParser,
18
- parserOptions: {
19
- ecmaVersion: 2020,
20
- sourceType: 'module',
21
- },
22
- },
23
- });
24
-
25
- ruleTester.run('no-unmanaged-exceptions', rule, {
26
- valid: [
27
- // ============================================
28
- // Test files - auto-allowed
29
- // ============================================
30
- {
31
- code: `
32
- try {
33
- await operation();
34
- } catch (err: unknown) {
35
- const error = toError(err);
36
- expect(error).toBeDefined();
37
- }
38
- `,
39
- filename: 'SaveController.test.ts',
40
- },
41
- {
42
- code: `
43
- try {
44
- await controller.save(request);
45
- fail('Should have thrown');
46
- } catch (err: unknown) {
47
- const error = toError(err);
48
- expect(error.message).toContain('Invalid');
49
- }
50
- `,
51
- filename: 'packages/http/http-server/src/SaveController.test.ts',
52
- },
53
-
54
- // Spec files - auto-allowed
55
- {
56
- code: `
57
- try {
58
- await operation();
59
- } catch (err: unknown) {
60
- const error = toError(err);
61
- }
62
- `,
63
- filename: 'userService.spec.ts',
64
- },
65
- {
66
- code: `
67
- it('should throw error', async () => {
68
- try {
69
- await service.process();
70
- fail();
71
- } catch (err: unknown) {
72
- const error = toError(err);
73
- expect(error).toBeDefined();
74
- }
75
- });
76
- `,
77
- filename: 'packages/services/user/userService.spec.ts',
78
- },
79
-
80
- // __tests__ directory - auto-allowed (need full path)
81
- {
82
- code: `
83
- try {
84
- await operation();
85
- } catch (err: unknown) {
86
- const error = toError(err);
87
- }
88
- `,
89
- filename: '/project/__tests__/integration.ts',
90
- },
91
- {
92
- code: `
93
- try {
94
- await runIntegrationTest();
95
- } catch (err: unknown) {
96
- const error = toError(err);
97
- console.error(error);
98
- }
99
- `,
100
- filename: '/project/packages/http/__tests__/server-integration.ts',
101
- },
102
- {
103
- code: `
104
- try {
105
- const result = performAction();
106
- } catch (err: unknown) {
107
- const error = toError(err);
108
- }
109
- `,
110
- filename: '/project/src/__tests__/helpers/testUtils.ts',
111
- },
112
-
113
- // ============================================
114
- // Code without try-catch (preferred pattern)
115
- // ============================================
116
- {
117
- code: `
118
- async function processOrder(order: Order): Promise<void> {
119
- await validateOrder(order);
120
- await saveToDatabase(order);
121
- }
122
- `,
123
- filename: 'OrderService.ts',
124
- },
125
- {
126
- code: `
127
- export class SaveController {
128
- async save(request: SaveRequest): Promise<SaveResponse> {
129
- const result = await this.service.save(request);
130
- return result;
131
- }
132
- }
133
- `,
134
- filename: 'packages/http/http-server/src/SaveController.ts',
135
- },
136
-
137
- // ============================================
138
- // Nested functions without try-catch
139
- // ============================================
140
- {
141
- code: `
142
- function outer() {
143
- function inner() {
144
- return doSomething();
145
- }
146
- return inner();
147
- }
148
- `,
149
- filename: 'Utils.ts',
150
- },
151
- ],
152
-
153
- invalid: [
154
- // ============================================
155
- // Controllers without approval
156
- // ============================================
157
- {
158
- code: `
159
- try {
160
- await operation();
161
- } catch (err: unknown) {
162
- const error = toError(err);
163
- }
164
- `,
165
- filename: 'SaveController.ts',
166
- errors: [{ messageId: 'noUnmanagedExceptions' }],
167
- },
168
- {
169
- code: `
170
- export class SaveController {
171
- async save(request: SaveRequest): Promise<SaveResponse> {
172
- try {
173
- const result = await this.service.save(request);
174
- return result;
175
- } catch (err: unknown) {
176
- const error = toError(err);
177
- throw error;
178
- }
179
- }
180
- }
181
- `,
182
- filename: 'packages/http/http-server/src/SaveController.ts',
183
- errors: [{ messageId: 'noUnmanagedExceptions' }],
184
- },
185
-
186
- // ============================================
187
- // Services without approval
188
- // ============================================
189
- {
190
- code: `
191
- try {
192
- await this.database.query(sql);
193
- } catch (err: unknown) {
194
- const error = toError(err);
195
- }
196
- `,
197
- filename: 'UserService.ts',
198
- errors: [{ messageId: 'noUnmanagedExceptions' }],
199
- },
200
- {
201
- code: `
202
- export class UserService {
203
- async getUserById(id: string): Promise<User> {
204
- try {
205
- const user = await this.db.findOne({ id });
206
- return user;
207
- } catch (err: unknown) {
208
- const error = toError(err);
209
- console.error('Failed to fetch user:', error);
210
- return null;
211
- }
212
- }
213
- }
214
- `,
215
- filename: 'packages/services/user/UserService.ts',
216
- errors: [{ messageId: 'noUnmanagedExceptions' }],
217
- },
218
-
219
- // ============================================
220
- // Filters without approval
221
- // ============================================
222
- {
223
- code: `
224
- try {
225
- return JSON.parse(body);
226
- } catch (err: unknown) {
227
- const error = toError(err);
228
- }
229
- `,
230
- filename: 'JsonFilter.ts',
231
- errors: [{ messageId: 'noUnmanagedExceptions' }],
232
- },
233
- {
234
- code: `
235
- export class LogFilter implements Filter {
236
- async filter(meta: MethodMeta, next: NextFilter): Promise<Action> {
237
- try {
238
- return await next.execute();
239
- } catch (err: unknown) {
240
- const error = toError(err);
241
- console.error('Filter error:', error);
242
- throw error;
243
- }
244
- }
245
- }
246
- `,
247
- filename: 'packages/http/http-server/src/filters/LogFilter.ts',
248
- errors: [{ messageId: 'noUnmanagedExceptions' }],
249
- },
250
-
251
- // ============================================
252
- // Utility functions without approval
253
- // ============================================
254
- {
255
- code: `
256
- async function fetchData(url: string): Promise<Data> {
257
- try {
258
- const response = await fetch(url);
259
- return await response.json();
260
- } catch (err: unknown) {
261
- const error = toError(err);
262
- throw new Error(\`Fetch failed: \${error.message}\`);
263
- }
264
- }
265
- `,
266
- filename: 'packages/core/core-util/src/httpUtils.ts',
267
- errors: [{ messageId: 'noUnmanagedExceptions' }],
268
- },
269
-
270
- // ============================================
271
- // Multiple try-catch blocks in one file
272
- // ============================================
273
- {
274
- code: `
275
- async function operation1() {
276
- try {
277
- await doSomething();
278
- } catch (err: unknown) {
279
- const error = toError(err);
280
- }
281
- }
282
-
283
- async function operation2() {
284
- try {
285
- await doSomethingElse();
286
- } catch (err: unknown) {
287
- const error = toError(err);
288
- }
289
- }
290
- `,
291
- filename: 'MultipleOperations.ts',
292
- errors: [
293
- { messageId: 'noUnmanagedExceptions' },
294
- { messageId: 'noUnmanagedExceptions' },
295
- ],
296
- },
297
-
298
- // ============================================
299
- // Nested try-catch blocks
300
- // ============================================
301
- {
302
- code: `
303
- try {
304
- try {
305
- await operation();
306
- } catch (err2: unknown) {
307
- const error2 = toError(err2);
308
- }
309
- } catch (err: unknown) {
310
- const error = toError(err);
311
- }
312
- `,
313
- filename: 'NestedOperations.ts',
314
- errors: [
315
- { messageId: 'noUnmanagedExceptions' },
316
- { messageId: 'noUnmanagedExceptions' },
317
- ],
318
- },
319
-
320
- // ============================================
321
- // Try-catch in arrow functions
322
- // ============================================
323
- {
324
- code: `
325
- const handler = async () => {
326
- try {
327
- await doSomething();
328
- } catch (err: unknown) {
329
- const error = toError(err);
330
- }
331
- };
332
- `,
333
- filename: 'Handlers.ts',
334
- errors: [{ messageId: 'noUnmanagedExceptions' }],
335
- },
336
-
337
- // ============================================
338
- // Try-catch in class methods
339
- // ============================================
340
- {
341
- code: `
342
- export class DataProcessor {
343
- process(data: Data): Result {
344
- try {
345
- return this.performProcessing(data);
346
- } catch (err: unknown) {
347
- const error = toError(err);
348
- return null;
349
- }
350
- }
351
- }
352
- `,
353
- filename: 'packages/processors/DataProcessor.ts',
354
- errors: [{ messageId: 'noUnmanagedExceptions' }],
355
- },
356
- ],
357
- });
358
-
359
- console.log('All no-unmanaged-exceptions tests passed!');
@@ -1,23 +0,0 @@
1
- /**
2
- * ESLint plugin for WebPieces
3
- * Provides rules for enforcing WebPieces code patterns
4
- *
5
- * This plugin is automatically included in @webpieces/dev-config
6
- *
7
- * Available rules:
8
- * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)
9
- * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)
10
- * - max-method-lines: Enforce maximum method length (default: 70 lines)
11
- * - max-file-lines: Enforce maximum file length (default: 700 lines)
12
- * - enforce-architecture: Enforce architecture dependency boundaries
13
- */
14
- declare const _default: {
15
- rules: {
16
- 'catch-error-pattern': import("eslint").Rule.RuleModule;
17
- 'no-unmanaged-exceptions': import("eslint").Rule.RuleModule;
18
- 'max-method-lines': import("eslint").Rule.RuleModule;
19
- 'max-file-lines': import("eslint").Rule.RuleModule;
20
- 'enforce-architecture': import("eslint").Rule.RuleModule;
21
- };
22
- };
23
- export = _default;
@@ -1,30 +0,0 @@
1
- "use strict";
2
- /**
3
- * ESLint plugin for WebPieces
4
- * Provides rules for enforcing WebPieces code patterns
5
- *
6
- * This plugin is automatically included in @webpieces/dev-config
7
- *
8
- * Available rules:
9
- * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)
10
- * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)
11
- * - max-method-lines: Enforce maximum method length (default: 70 lines)
12
- * - max-file-lines: Enforce maximum file length (default: 700 lines)
13
- * - enforce-architecture: Enforce architecture dependency boundaries
14
- */
15
- const tslib_1 = require("tslib");
16
- const catch_error_pattern_1 = tslib_1.__importDefault(require("./rules/catch-error-pattern"));
17
- const no_unmanaged_exceptions_1 = tslib_1.__importDefault(require("./rules/no-unmanaged-exceptions"));
18
- const max_method_lines_1 = tslib_1.__importDefault(require("./rules/max-method-lines"));
19
- const max_file_lines_1 = tslib_1.__importDefault(require("./rules/max-file-lines"));
20
- const enforce_architecture_1 = tslib_1.__importDefault(require("./rules/enforce-architecture"));
21
- module.exports = {
22
- rules: {
23
- 'catch-error-pattern': catch_error_pattern_1.default,
24
- 'no-unmanaged-exceptions': no_unmanaged_exceptions_1.default,
25
- 'max-method-lines': max_method_lines_1.default,
26
- 'max-file-lines': max_file_lines_1.default,
27
- 'enforce-architecture': enforce_architecture_1.default,
28
- },
29
- };
30
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/dev-config/eslint-plugin/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAEH,8FAA4D;AAC5D,sGAAoE;AACpE,wFAAsD;AACtD,oFAAkD;AAClD,gGAA+D;AAE/D,iBAAS;IACL,KAAK,EAAE;QACH,qBAAqB,EAAE,6BAAiB;QACxC,yBAAyB,EAAE,iCAAqB;QAChD,kBAAkB,EAAE,0BAAc;QAClC,gBAAgB,EAAE,wBAAY;QAC9B,sBAAsB,EAAE,8BAAmB;KAC9C;CACJ,CAAC","sourcesContent":["/**\n * ESLint plugin for WebPieces\n * Provides rules for enforcing WebPieces code patterns\n *\n * This plugin is automatically included in @webpieces/dev-config\n *\n * Available rules:\n * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)\n * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)\n * - max-method-lines: Enforce maximum method length (default: 70 lines)\n * - max-file-lines: Enforce maximum file length (default: 700 lines)\n * - enforce-architecture: Enforce architecture dependency boundaries\n */\n\nimport catchErrorPattern from './rules/catch-error-pattern';\nimport noUnmanagedExceptions from './rules/no-unmanaged-exceptions';\nimport maxMethodLines from './rules/max-method-lines';\nimport maxFileLines from './rules/max-file-lines';\nimport enforceArchitecture from './rules/enforce-architecture';\n\nexport = {\n rules: {\n 'catch-error-pattern': catchErrorPattern,\n 'no-unmanaged-exceptions': noUnmanagedExceptions,\n 'max-method-lines': maxMethodLines,\n 'max-file-lines': maxFileLines,\n 'enforce-architecture': enforceArchitecture,\n },\n};\n"]}
@@ -1,29 +0,0 @@
1
- /**
2
- * ESLint plugin for WebPieces
3
- * Provides rules for enforcing WebPieces code patterns
4
- *
5
- * This plugin is automatically included in @webpieces/dev-config
6
- *
7
- * Available rules:
8
- * - catch-error-pattern: Enforce toError() usage in catch blocks (HOW to handle)
9
- * - no-unmanaged-exceptions: Discourage try-catch outside tests (WHERE to handle)
10
- * - max-method-lines: Enforce maximum method length (default: 70 lines)
11
- * - max-file-lines: Enforce maximum file length (default: 700 lines)
12
- * - enforce-architecture: Enforce architecture dependency boundaries
13
- */
14
-
15
- import catchErrorPattern from './rules/catch-error-pattern';
16
- import noUnmanagedExceptions from './rules/no-unmanaged-exceptions';
17
- import maxMethodLines from './rules/max-method-lines';
18
- import maxFileLines from './rules/max-file-lines';
19
- import enforceArchitecture from './rules/enforce-architecture';
20
-
21
- export = {
22
- rules: {
23
- 'catch-error-pattern': catchErrorPattern,
24
- 'no-unmanaged-exceptions': noUnmanagedExceptions,
25
- 'max-method-lines': maxMethodLines,
26
- 'max-file-lines': maxFileLines,
27
- 'enforce-architecture': enforceArchitecture,
28
- },
29
- };
@@ -1,11 +0,0 @@
1
- /**
2
- * ESLint rule to enforce standardized catch block error handling patterns
3
- *
4
- * Enforces three approved patterns:
5
- * 1. Standard: catch (err: unknown) { const error = toError(err); }
6
- * 2. Ignored: catch (err: unknown) { //const error = toError(err); }
7
- * 3. Nested: catch (err2: unknown) { const error2 = toError(err2); }
8
- */
9
- import type { Rule } from 'eslint';
10
- declare const rule: Rule.RuleModule;
11
- export = rule;
@@ -1,143 +0,0 @@
1
- "use strict";
2
- /**
3
- * ESLint rule to enforce standardized catch block error handling patterns
4
- *
5
- * Enforces three approved patterns:
6
- * 1. Standard: catch (err: unknown) { const error = toError(err); }
7
- * 2. Ignored: catch (err: unknown) { //const error = toError(err); }
8
- * 3. Nested: catch (err2: unknown) { const error2 = toError(err2); }
9
- */
10
- function validateParamName(context, param, expectedParamName) {
11
- if (param.type === 'Identifier' && param.name !== expectedParamName) {
12
- context.report({
13
- node: param,
14
- messageId: 'wrongParameterName',
15
- data: { actual: param.name },
16
- });
17
- }
18
- }
19
- function validateTypeAnnotation(context, param, expectedParamName) {
20
- if (!param.typeAnnotation ||
21
- !param.typeAnnotation.typeAnnotation ||
22
- param.typeAnnotation.typeAnnotation.type !== 'TSUnknownKeyword') {
23
- context.report({
24
- node: param,
25
- messageId: 'missingTypeAnnotation',
26
- data: { param: param.name || expectedParamName },
27
- });
28
- }
29
- }
30
- function hasIgnoreComment(catchNode, sourceCode, expectedVarName, actualParamName) {
31
- const catchBlockStart = catchNode.body.range[0];
32
- const catchBlockEnd = catchNode.body.range[1];
33
- const catchBlockText = sourceCode.text.substring(catchBlockStart, catchBlockEnd);
34
- const ignorePattern = new RegExp(`//\\s*const\\s+${expectedVarName}\\s*=\\s*toError\\(${actualParamName}\\)`);
35
- return ignorePattern.test(catchBlockText);
36
- }
37
- function reportMissingToError(context,
38
- // webpieces-disable no-any-unknown -- ESLint AST node param requires any type
39
- node, paramName) {
40
- context.report({
41
- node,
42
- messageId: 'missingToError',
43
- data: { param: paramName },
44
- });
45
- }
46
- function validateToErrorCall(context,
47
- // webpieces-disable no-any-unknown -- ESLint AST node param requires any type
48
- firstStatement, expectedParamName, expectedVarName, actualParamName) {
49
- if (firstStatement.type !== 'VariableDeclaration') {
50
- reportMissingToError(context, firstStatement, expectedParamName);
51
- return;
52
- }
53
- const varDecl = firstStatement;
54
- const declaration = varDecl.declarations[0];
55
- if (!declaration) {
56
- reportMissingToError(context, firstStatement, expectedParamName);
57
- return;
58
- }
59
- if (declaration.id.type !== 'Identifier' || declaration.id.name !== expectedVarName) {
60
- context.report({
61
- node: declaration.id,
62
- messageId: 'wrongVariableName',
63
- data: { expected: expectedVarName, actual: declaration.id.name || 'unknown' },
64
- });
65
- return;
66
- }
67
- if (!declaration.init || declaration.init.type !== 'CallExpression') {
68
- reportMissingToError(context, declaration.init || declaration, expectedParamName);
69
- return;
70
- }
71
- const callExpr = declaration.init;
72
- const callee = callExpr.callee;
73
- if (callee.type !== 'Identifier' || callee.name !== 'toError') {
74
- reportMissingToError(context, callee, expectedParamName);
75
- return;
76
- }
77
- const args = callExpr.arguments;
78
- if (args.length !== 1 ||
79
- args[0].type !== 'Identifier' ||
80
- args[0].name !== actualParamName) {
81
- reportMissingToError(context, callExpr, actualParamName);
82
- }
83
- }
84
- const rule = {
85
- meta: {
86
- type: 'problem',
87
- docs: {
88
- description: 'Enforce standardized catch block error handling patterns',
89
- category: 'Best Practices',
90
- recommended: true,
91
- url: 'https://github.com/deanhiller/webpieces-ts/blob/main/claude.patterns.md#error-handling-pattern',
92
- },
93
- messages: {
94
- missingToError: 'Catch block must call toError({{param}}) as first statement or comment it out to explicitly ignore errors',
95
- wrongVariableName: 'Error variable must be named "{{expected}}", got "{{actual}}"',
96
- missingTypeAnnotation: 'Catch parameter must be typed as "unknown": catch ({{param}}: unknown)',
97
- wrongParameterName: 'Catch parameter must be named "err" (or "err2", "err3" for nested catches), got "{{actual}}"',
98
- toErrorNotFirst: 'toError({{param}}) must be the first statement in the catch block',
99
- },
100
- fixable: undefined,
101
- schema: [],
102
- },
103
- create(context) {
104
- const catchStack = [];
105
- return {
106
- CatchClause(node) {
107
- const catchNode = node;
108
- const depth = catchStack.length + 1;
109
- catchStack.push(catchNode);
110
- const suffix = depth === 1 ? '' : String(depth);
111
- const expectedParamName = 'err' + suffix;
112
- const expectedVarName = 'error' + suffix;
113
- const param = catchNode.param;
114
- if (!param) {
115
- context.report({
116
- node: catchNode,
117
- messageId: 'missingTypeAnnotation',
118
- data: { param: expectedParamName },
119
- });
120
- return;
121
- }
122
- const actualParamName = param.type === 'Identifier' ? param.name : expectedParamName;
123
- validateParamName(context, param, expectedParamName);
124
- validateTypeAnnotation(context, param, expectedParamName);
125
- const sourceCode = context.sourceCode || context.getSourceCode();
126
- if (hasIgnoreComment(catchNode, sourceCode, expectedVarName, actualParamName)) {
127
- return;
128
- }
129
- const body = catchNode.body.body;
130
- if (body.length === 0) {
131
- reportMissingToError(context, catchNode.body, expectedParamName);
132
- return;
133
- }
134
- validateToErrorCall(context, body[0], expectedParamName, expectedVarName, actualParamName);
135
- },
136
- 'CatchClause:exit'() {
137
- catchStack.pop();
138
- },
139
- };
140
- },
141
- };
142
- module.exports = rule;
143
- //# sourceMappingURL=catch-error-pattern.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"catch-error-pattern.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/eslint-plugin/rules/catch-error-pattern.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;AAqDH,SAAS,iBAAiB,CACtB,OAAyB,EACzB,KAAqB,EACrB,iBAAyB;IAEzB,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAClE,OAAO,CAAC,MAAM,CAAC;YACX,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,oBAAoB;YAC/B,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE;SAC/B,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB,CAC3B,OAAyB,EACzB,KAAqB,EACrB,iBAAyB;IAEzB,IACI,CAAC,KAAK,CAAC,cAAc;QACrB,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc;QACpC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,KAAK,kBAAkB,EACjE,CAAC;QACC,OAAO,CAAC,MAAM,CAAC;YACX,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,uBAAuB;YAClC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,iBAAiB,EAAE;SACnD,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CACrB,SAA0B,EAC1B,UAA0C,EAC1C,eAAuB,EACvB,eAAuB;IAEvB,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IAEjF,MAAM,aAAa,GAAG,IAAI,MAAM,CAC5B,kBAAkB,eAAe,sBAAsB,eAAe,KAAK,CAC9E,CAAC;IAEF,OAAO,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,oBAAoB,CACzB,OAAyB;AACzB,8EAA8E;AAC9E,IAAS,EACT,SAAiB;IAEjB,OAAO,CAAC,MAAM,CAAC;QACX,IAAI;QACJ,SAAS,EAAE,gBAAgB;QAC3B,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;KAC7B,CAAC,CAAC;AACP,CAAC;AAED,SAAS,mBAAmB,CACxB,OAAyB;AACzB,8EAA8E;AAC9E,cAAmB,EACnB,iBAAyB,EACzB,eAAuB,EACvB,eAAuB;IAEvB,IAAI,cAAc,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;QAChD,oBAAoB,CAAC,OAAO,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;QACjE,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,cAAyC,CAAC;IAC1D,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,oBAAoB,CAAC,OAAO,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;QACjE,OAAO;IACX,CAAC;IAED,IAAI,WAAW,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY,IAAI,WAAW,CAAC,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QAClF,OAAO,CAAC,MAAM,CAAC;YACX,IAAI,EAAE,WAAW,CAAC,EAAE;YACpB,SAAS,EAAE,mBAAmB;YAC9B,IAAI,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,IAAI,IAAI,SAAS,EAAE;SAChF,CAAC,CAAC;QACH,OAAO;IACX,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAClE,oBAAoB,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,IAAI,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAClF,OAAO;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,IAA0B,CAAC;IACxD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC/B,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5D,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACzD,OAAO;IACX,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC;IAChC,IACI,IAAI,CAAC,MAAM,KAAK,CAAC;QACjB,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY;QAC5B,IAAI,CAAC,CAAC,CAAoB,CAAC,IAAI,KAAK,eAAe,EACtD,CAAC;QACC,oBAAoB,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC7D,CAAC;AACL,CAAC;AAED,MAAM,IAAI,GAAoB;IAC1B,IAAI,EAAE;QACF,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACF,WAAW,EAAE,0DAA0D;YACvE,QAAQ,EAAE,gBAAgB;YAC1B,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,gGAAgG;SACxG;QACD,QAAQ,EAAE;YACN,cAAc,EACV,2GAA2G;YAC/G,iBAAiB,EAAE,+DAA+D;YAClF,qBAAqB,EAAE,wEAAwE;YAC/F,kBAAkB,EACd,8FAA8F;YAClG,eAAe,EAAE,mEAAmE;SACvF;QACD,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,EAAE;KACb;IAED,MAAM,CAAC,OAAyB;QAC5B,MAAM,UAAU,GAAsB,EAAE,CAAC;QAEzC,OAAO;YACH,WAAW,CAAC,IAAS;gBACjB,MAAM,SAAS,GAAG,IAAuB,CAAC;gBAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;gBACpC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAE3B,MAAM,MAAM,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChD,MAAM,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAC;gBACzC,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;gBAEzC,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;gBAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;oBACT,OAAO,CAAC,MAAM,CAAC;wBACX,IAAI,EAAE,SAAS;wBACf,SAAS,EAAE,uBAAuB;wBAClC,IAAI,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;qBACrC,CAAC,CAAC;oBACH,OAAO;gBACX,CAAC;gBAED,MAAM,eAAe,GACjB,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC;gBAEjE,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;gBACrD,sBAAsB,CAAC,OAAO,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;gBAE1D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBACjE,IAAI,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC;oBAC5E,OAAO;gBACX,CAAC;gBAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;gBACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACpB,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;oBACjE,OAAO;gBACX,CAAC;gBAED,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;YAC/F,CAAC;YAED,kBAAkB;gBACd,UAAU,CAAC,GAAG,EAAE,CAAC;YACrB,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC;AAEF,iBAAS,IAAI,CAAC","sourcesContent":["/**\n * ESLint rule to enforce standardized catch block error handling patterns\n *\n * Enforces three approved patterns:\n * 1. Standard: catch (err: unknown) { const error = toError(err); }\n * 2. Ignored: catch (err: unknown) { //const error = toError(err); }\n * 3. Nested: catch (err2: unknown) { const error2 = toError(err2); }\n */\n\nimport type { Rule } from 'eslint';\n\n// Using any for ESTree nodes to avoid complex type gymnastics\n// ESLint rules work with dynamic AST nodes anyway\ninterface CatchClauseNode {\n type: 'CatchClause';\n param?: IdentifierNode | null;\n body: BlockStatementNode;\n [key: string]: any;\n}\n\ninterface IdentifierNode {\n type: 'Identifier';\n name: string;\n typeAnnotation?: TypeAnnotationNode;\n [key: string]: any;\n}\n\ninterface TypeAnnotationNode {\n typeAnnotation?: {\n type: string;\n };\n}\n\ninterface BlockStatementNode {\n type: 'BlockStatement';\n body: any[];\n range: [number, number];\n [key: string]: any;\n}\n\ninterface VariableDeclarationNode {\n type: 'VariableDeclaration';\n declarations: VariableDeclaratorNode[];\n [key: string]: any;\n}\n\ninterface VariableDeclaratorNode {\n type: 'VariableDeclarator';\n id: IdentifierNode;\n init?: CallExpressionNode | null;\n [key: string]: any;\n}\n\ninterface CallExpressionNode {\n type: 'CallExpression';\n callee: IdentifierNode;\n arguments: any[];\n [key: string]: any;\n}\n\nfunction validateParamName(\n context: Rule.RuleContext,\n param: IdentifierNode,\n expectedParamName: string,\n): void {\n if (param.type === 'Identifier' && param.name !== expectedParamName) {\n context.report({\n node: param,\n messageId: 'wrongParameterName',\n data: { actual: param.name },\n });\n }\n}\n\nfunction validateTypeAnnotation(\n context: Rule.RuleContext,\n param: IdentifierNode,\n expectedParamName: string,\n): void {\n if (\n !param.typeAnnotation ||\n !param.typeAnnotation.typeAnnotation ||\n param.typeAnnotation.typeAnnotation.type !== 'TSUnknownKeyword'\n ) {\n context.report({\n node: param,\n messageId: 'missingTypeAnnotation',\n data: { param: param.name || expectedParamName },\n });\n }\n}\n\nfunction hasIgnoreComment(\n catchNode: CatchClauseNode,\n sourceCode: Rule.RuleContext['sourceCode'],\n expectedVarName: string,\n actualParamName: string,\n): boolean {\n const catchBlockStart = catchNode.body.range![0];\n const catchBlockEnd = catchNode.body.range![1];\n const catchBlockText = sourceCode.text.substring(catchBlockStart, catchBlockEnd);\n\n const ignorePattern = new RegExp(\n `//\\\\s*const\\\\s+${expectedVarName}\\\\s*=\\\\s*toError\\\\(${actualParamName}\\\\)`,\n );\n\n return ignorePattern.test(catchBlockText);\n}\n\nfunction reportMissingToError(\n context: Rule.RuleContext,\n // webpieces-disable no-any-unknown -- ESLint AST node param requires any type\n node: any,\n paramName: string,\n): void {\n context.report({\n node,\n messageId: 'missingToError',\n data: { param: paramName },\n });\n}\n\nfunction validateToErrorCall(\n context: Rule.RuleContext,\n // webpieces-disable no-any-unknown -- ESLint AST node param requires any type\n firstStatement: any,\n expectedParamName: string,\n expectedVarName: string,\n actualParamName: string,\n): void {\n if (firstStatement.type !== 'VariableDeclaration') {\n reportMissingToError(context, firstStatement, expectedParamName);\n return;\n }\n\n const varDecl = firstStatement as VariableDeclarationNode;\n const declaration = varDecl.declarations[0];\n if (!declaration) {\n reportMissingToError(context, firstStatement, expectedParamName);\n return;\n }\n\n if (declaration.id.type !== 'Identifier' || declaration.id.name !== expectedVarName) {\n context.report({\n node: declaration.id,\n messageId: 'wrongVariableName',\n data: { expected: expectedVarName, actual: declaration.id.name || 'unknown' },\n });\n return;\n }\n\n if (!declaration.init || declaration.init.type !== 'CallExpression') {\n reportMissingToError(context, declaration.init || declaration, expectedParamName);\n return;\n }\n\n const callExpr = declaration.init as CallExpressionNode;\n const callee = callExpr.callee;\n if (callee.type !== 'Identifier' || callee.name !== 'toError') {\n reportMissingToError(context, callee, expectedParamName);\n return;\n }\n\n const args = callExpr.arguments;\n if (\n args.length !== 1 ||\n args[0].type !== 'Identifier' ||\n (args[0] as IdentifierNode).name !== actualParamName\n ) {\n reportMissingToError(context, callExpr, actualParamName);\n }\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Enforce standardized catch block error handling patterns',\n category: 'Best Practices',\n recommended: true,\n url: 'https://github.com/deanhiller/webpieces-ts/blob/main/claude.patterns.md#error-handling-pattern',\n },\n messages: {\n missingToError:\n 'Catch block must call toError({{param}}) as first statement or comment it out to explicitly ignore errors',\n wrongVariableName: 'Error variable must be named \"{{expected}}\", got \"{{actual}}\"',\n missingTypeAnnotation: 'Catch parameter must be typed as \"unknown\": catch ({{param}}: unknown)',\n wrongParameterName:\n 'Catch parameter must be named \"err\" (or \"err2\", \"err3\" for nested catches), got \"{{actual}}\"',\n toErrorNotFirst: 'toError({{param}}) must be the first statement in the catch block',\n },\n fixable: undefined,\n schema: [],\n },\n\n create(context: Rule.RuleContext): Rule.RuleListener {\n const catchStack: CatchClauseNode[] = [];\n\n return {\n CatchClause(node: any): void {\n const catchNode = node as CatchClauseNode;\n const depth = catchStack.length + 1;\n catchStack.push(catchNode);\n\n const suffix = depth === 1 ? '' : String(depth);\n const expectedParamName = 'err' + suffix;\n const expectedVarName = 'error' + suffix;\n\n const param = catchNode.param;\n if (!param) {\n context.report({\n node: catchNode,\n messageId: 'missingTypeAnnotation',\n data: { param: expectedParamName },\n });\n return;\n }\n\n const actualParamName =\n param.type === 'Identifier' ? param.name : expectedParamName;\n\n validateParamName(context, param, expectedParamName);\n validateTypeAnnotation(context, param, expectedParamName);\n\n const sourceCode = context.sourceCode || context.getSourceCode();\n if (hasIgnoreComment(catchNode, sourceCode, expectedVarName, actualParamName)) {\n return;\n }\n\n const body = catchNode.body.body;\n if (body.length === 0) {\n reportMissingToError(context, catchNode.body, expectedParamName);\n return;\n }\n\n validateToErrorCall(context, body[0], expectedParamName, expectedVarName, actualParamName);\n },\n\n 'CatchClause:exit'(): void {\n catchStack.pop();\n },\n };\n },\n};\n\nexport = rule;\n"]}