@webpieces/code-rules 0.0.1 → 0.2.113

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 (63) hide show
  1. package/package.json +4 -3
  2. package/src/cli.d.ts +1 -0
  3. package/src/cli.js +19 -0
  4. package/src/cli.js.map +1 -0
  5. package/src/diff-utils.d.ts +24 -0
  6. package/src/{diff-utils.ts → diff-utils.js} +30 -38
  7. package/src/diff-utils.js.map +1 -0
  8. package/src/from-shared-config.d.ts +28 -0
  9. package/src/from-shared-config.js +119 -0
  10. package/src/from-shared-config.js.map +1 -0
  11. package/src/index.js +33 -0
  12. package/src/index.js.map +1 -0
  13. package/src/validate-catch-error-pattern.d.ts +47 -0
  14. package/src/{validate-catch-error-pattern.ts → validate-catch-error-pattern.js} +74 -195
  15. package/src/validate-catch-error-pattern.js.map +1 -0
  16. package/src/validate-code.d.ts +98 -0
  17. package/src/{validate-code.ts → validate-code.js} +65 -259
  18. package/src/validate-code.js.map +1 -0
  19. package/src/validate-dtos.d.ts +41 -0
  20. package/src/{validate-dtos.ts → validate-dtos.js} +88 -215
  21. package/src/validate-dtos.js.map +1 -0
  22. package/src/validate-modified-files.d.ts +24 -0
  23. package/src/{validate-modified-files.ts → validate-modified-files.js} +46 -115
  24. package/src/validate-modified-files.js.map +1 -0
  25. package/src/validate-modified-methods.d.ts +30 -0
  26. package/src/{validate-modified-methods.ts → validate-modified-methods.js} +94 -196
  27. package/src/validate-modified-methods.js.map +1 -0
  28. package/src/validate-new-methods.d.ts +27 -0
  29. package/src/{validate-new-methods.ts → validate-new-methods.js} +63 -133
  30. package/src/validate-new-methods.js.map +1 -0
  31. package/src/validate-no-any-unknown.d.ts +41 -0
  32. package/src/{validate-no-any-unknown.ts → validate-no-any-unknown.js} +69 -146
  33. package/src/validate-no-any-unknown.js.map +1 -0
  34. package/src/validate-no-destructure.d.ts +51 -0
  35. package/src/{validate-no-destructure.ts → validate-no-destructure.js} +80 -166
  36. package/src/validate-no-destructure.js.map +1 -0
  37. package/src/validate-no-direct-api-resolver.d.ts +46 -0
  38. package/src/{validate-no-direct-api-resolver.ts → validate-no-direct-api-resolver.js} +112 -211
  39. package/src/validate-no-direct-api-resolver.js.map +1 -0
  40. package/src/validate-no-implicit-any.d.ts +36 -0
  41. package/src/{validate-no-implicit-any.ts → validate-no-implicit-any.js} +94 -141
  42. package/src/validate-no-implicit-any.js.map +1 -0
  43. package/src/validate-no-inline-types.d.ts +90 -0
  44. package/src/{validate-no-inline-types.ts → validate-no-inline-types.js} +93 -198
  45. package/src/validate-no-inline-types.js.map +1 -0
  46. package/src/validate-no-unmanaged-exceptions.d.ts +43 -0
  47. package/src/{validate-no-unmanaged-exceptions.ts → validate-no-unmanaged-exceptions.js} +71 -140
  48. package/src/validate-no-unmanaged-exceptions.js.map +1 -0
  49. package/src/validate-prisma-converters.d.ts +59 -0
  50. package/src/{validate-prisma-converters.ts → validate-prisma-converters.js} +120 -307
  51. package/src/validate-prisma-converters.js.map +1 -0
  52. package/src/validate-return-types.d.ts +28 -0
  53. package/src/{validate-return-types.ts → validate-return-types.js} +84 -168
  54. package/src/validate-return-types.js.map +1 -0
  55. package/LICENSE +0 -373
  56. package/jest.config.ts +0 -20
  57. package/project.json +0 -22
  58. package/src/cli.ts +0 -17
  59. package/src/from-shared-config.ts +0 -118
  60. package/tsconfig.json +0 -22
  61. package/tsconfig.lib.json +0 -10
  62. package/tsconfig.spec.json +0 -14
  63. /package/src/{index.ts → index.d.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * Validate Return Types Executor
3
4
  *
@@ -16,39 +17,22 @@
16
17
  *
17
18
  * Escape hatch: Add webpieces-disable require-return-type comment with justification
18
19
  */
19
-
20
- import { execSync } from 'child_process';
21
- import * as fs from 'fs';
22
- import * as path from 'path';
23
- import * as ts from 'typescript';
24
-
25
- export type ReturnTypeMode = 'OFF' | 'NEW_METHODS' | 'NEW_AND_MODIFIED_METHODS' | 'MODIFIED_FILES';
26
-
27
- export interface ValidateReturnTypesOptions {
28
- mode?: ReturnTypeMode;
29
- disableAllowed?: boolean;
30
- ignoreModifiedUntilEpoch?: number;
31
- }
32
-
33
- export interface ExecutorResult {
34
- success: boolean;
35
- }
36
-
37
- interface MethodViolation {
38
- file: string;
39
- methodName: string;
40
- line: number;
41
- }
42
-
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.default = runValidator;
22
+ const tslib_1 = require("tslib");
23
+ const child_process_1 = require("child_process");
24
+ const fs = tslib_1.__importStar(require("fs"));
25
+ const path = tslib_1.__importStar(require("path"));
26
+ const ts = tslib_1.__importStar(require("typescript"));
43
27
  /**
44
28
  * Get changed TypeScript files between base and head (or working tree if head not specified).
45
29
  */
46
30
  // webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths
47
- function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {
31
+ function getChangedTypeScriptFiles(workspaceRoot, base, head) {
48
32
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
49
33
  try {
50
34
  const diffTarget = head ? `${base} ${head}` : base;
51
- const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
35
+ const output = (0, child_process_1.execSync)(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
52
36
  cwd: workspaceRoot,
53
37
  encoding: 'utf-8',
54
38
  });
@@ -56,11 +40,10 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
56
40
  .trim()
57
41
  .split('\n')
58
42
  .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
59
-
60
43
  if (!head) {
61
44
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
62
45
  try {
63
- const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
46
+ const untrackedOutput = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
64
47
  cwd: workspaceRoot,
65
48
  encoding: 'utf-8',
66
49
  });
@@ -70,39 +53,37 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
70
53
  .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
71
54
  const allFiles = new Set([...changedFiles, ...untrackedFiles]);
72
55
  return Array.from(allFiles);
73
- } catch (err: unknown) {
56
+ }
57
+ catch (err) {
74
58
  //const error = toError(err);
75
59
  return changedFiles;
76
60
  }
77
61
  }
78
-
79
62
  return changedFiles;
80
- } catch (err: unknown) {
63
+ }
64
+ catch (err) {
81
65
  //const error = toError(err);
82
66
  return [];
83
67
  }
84
68
  }
85
-
86
69
  /**
87
70
  * Get the diff content for a specific file.
88
71
  */
89
- function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {
72
+ function getFileDiff(workspaceRoot, file, base, head) {
90
73
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
91
74
  try {
92
75
  const diffTarget = head ? `${base} ${head}` : base;
93
- const diff = execSync(`git diff ${diffTarget} -- "${file}"`, {
76
+ const diff = (0, child_process_1.execSync)(`git diff ${diffTarget} -- "${file}"`, {
94
77
  cwd: workspaceRoot,
95
78
  encoding: 'utf-8',
96
79
  });
97
-
98
80
  if (!diff && !head) {
99
81
  const fullPath = path.join(workspaceRoot, file);
100
82
  if (fs.existsSync(fullPath)) {
101
- const isUntracked = execSync(`git ls-files --others --exclude-standard "${file}"`, {
83
+ const isUntracked = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard "${file}"`, {
102
84
  cwd: workspaceRoot,
103
85
  encoding: 'utf-8',
104
86
  }).trim();
105
-
106
87
  if (isUntracked) {
107
88
  const content = fs.readFileSync(fullPath, 'utf-8');
108
89
  const lines = content.split('\n');
@@ -110,28 +91,25 @@ function getFileDiff(workspaceRoot: string, file: string, base: string, head?: s
110
91
  }
111
92
  }
112
93
  }
113
-
114
94
  return diff;
115
- } catch (err: unknown) {
95
+ }
96
+ catch (err) {
116
97
  //const error = toError(err);
117
98
  return '';
118
99
  }
119
100
  }
120
-
121
101
  /**
122
102
  * Parse diff to find newly added method signatures.
123
103
  */
124
- function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {
125
- const newMethods = new Set<string>();
104
+ function findNewMethodSignaturesInDiff(diffContent) {
105
+ const newMethods = new Set();
126
106
  const lines = diffContent.split('\n');
127
-
128
107
  const patterns = [
129
108
  /^\+\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
130
109
  /^\+\s*(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/,
131
110
  /^\+\s*(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?function/,
132
111
  /^\+\s*(?:(?:public|private|protected)\s+)?(?:static\s+)?(?:async\s+)?(\w+)\s*\(/,
133
112
  ];
134
-
135
113
  for (const line of lines) {
136
114
  if (line.startsWith('+') && !line.startsWith('+++')) {
137
115
  for (const pattern of patterns) {
@@ -146,14 +124,12 @@ function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {
146
124
  }
147
125
  }
148
126
  }
149
-
150
127
  return newMethods;
151
128
  }
152
-
153
129
  /**
154
130
  * Check if a line contains a webpieces-disable comment for return type.
155
131
  */
156
- function hasDisableComment(lines: string[], lineNumber: number): boolean {
132
+ function hasDisableComment(lines, lineNumber) {
157
133
  const startCheck = Math.max(0, lineNumber - 5);
158
134
  for (let i = lineNumber - 2; i >= startCheck; i--) {
159
135
  const line = lines[i]?.trim() ?? '';
@@ -166,43 +142,30 @@ function hasDisableComment(lines: string[], lineNumber: number): boolean {
166
142
  }
167
143
  return false;
168
144
  }
169
-
170
145
  /**
171
146
  * Check if a method has an explicit return type annotation.
172
147
  */
173
- function hasExplicitReturnType(node: ts.MethodDeclaration | ts.FunctionDeclaration | ts.ArrowFunction): boolean {
148
+ function hasExplicitReturnType(node) {
174
149
  return node.type !== undefined;
175
150
  }
176
-
177
- interface MethodInfo {
178
- name: string;
179
- line: number;
180
- endLine: number;
181
- hasReturnType: boolean;
182
- hasDisableComment: boolean;
183
- }
184
-
185
151
  /**
186
152
  * Parse a TypeScript file and find methods with their return type status.
187
153
  */
188
154
  // webpieces-disable max-lines-new-methods -- AST traversal requires inline visitor function
189
- function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[] {
155
+ function findMethodsInFile(filePath, workspaceRoot) {
190
156
  const fullPath = path.join(workspaceRoot, filePath);
191
- if (!fs.existsSync(fullPath)) return [];
192
-
157
+ if (!fs.existsSync(fullPath))
158
+ return [];
193
159
  const content = fs.readFileSync(fullPath, 'utf-8');
194
160
  const fileLines = content.split('\n');
195
161
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
196
-
197
- const methods: MethodInfo[] = [];
198
-
162
+ const methods = [];
199
163
  // webpieces-disable max-lines-new-methods -- AST visitor pattern requires handling multiple node types
200
- function visit(node: ts.Node): void {
201
- let methodName: string | undefined;
202
- let startLine: number | undefined;
203
- let endLine: number | undefined;
164
+ function visit(node) {
165
+ let methodName;
166
+ let startLine;
167
+ let endLine;
204
168
  let hasReturnType = false;
205
-
206
169
  if (ts.isMethodDeclaration(node) && node.name) {
207
170
  methodName = node.name.getText(sourceFile);
208
171
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
@@ -210,14 +173,16 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
210
173
  startLine = start.line + 1;
211
174
  endLine = end.line + 1;
212
175
  hasReturnType = hasExplicitReturnType(node);
213
- } else if (ts.isFunctionDeclaration(node) && node.name) {
176
+ }
177
+ else if (ts.isFunctionDeclaration(node) && node.name) {
214
178
  methodName = node.name.getText(sourceFile);
215
179
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
216
180
  const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
217
181
  startLine = start.line + 1;
218
182
  endLine = end.line + 1;
219
183
  hasReturnType = hasExplicitReturnType(node);
220
- } else if (ts.isArrowFunction(node)) {
184
+ }
185
+ else if (ts.isArrowFunction(node)) {
221
186
  if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
222
187
  methodName = node.parent.name.getText(sourceFile);
223
188
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
@@ -227,7 +192,6 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
227
192
  hasReturnType = hasExplicitReturnType(node);
228
193
  }
229
194
  }
230
-
231
195
  if (methodName && startLine !== undefined && endLine !== undefined) {
232
196
  methods.push({
233
197
  name: methodName,
@@ -237,46 +201,41 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
237
201
  hasDisableComment: hasDisableComment(fileLines, startLine),
238
202
  });
239
203
  }
240
-
241
204
  ts.forEachChild(node, visit);
242
205
  }
243
-
244
206
  visit(sourceFile);
245
207
  return methods;
246
208
  }
247
-
248
209
  /**
249
210
  * Parse diff to extract changed line numbers (both additions and modifications).
250
211
  */
251
- function getChangedLineNumbers(diffContent: string): Set<number> {
252
- const changedLines = new Set<number>();
212
+ function getChangedLineNumbers(diffContent) {
213
+ const changedLines = new Set();
253
214
  const lines = diffContent.split('\n');
254
215
  let currentLine = 0;
255
-
256
216
  for (const line of lines) {
257
217
  const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
258
218
  if (hunkMatch) {
259
219
  currentLine = parseInt(hunkMatch[1], 10);
260
220
  continue;
261
221
  }
262
-
263
222
  if (line.startsWith('+') && !line.startsWith('+++')) {
264
223
  changedLines.add(currentLine);
265
224
  currentLine++;
266
- } else if (line.startsWith('-') && !line.startsWith('---')) {
225
+ }
226
+ else if (line.startsWith('-') && !line.startsWith('---')) {
267
227
  // Deletions don't increment line number
268
- } else {
228
+ }
229
+ else {
269
230
  currentLine++;
270
231
  }
271
232
  }
272
-
273
233
  return changedLines;
274
234
  }
275
-
276
235
  /**
277
236
  * Check if a method has any changed lines within its range.
278
237
  */
279
- function methodHasChanges(method: MethodInfo, changedLines: Set<number>): boolean {
238
+ function methodHasChanges(method, changedLines) {
280
239
  for (let line = method.line; line <= method.endLine; line++) {
281
240
  if (changedLines.has(line)) {
282
241
  return true;
@@ -284,33 +243,25 @@ function methodHasChanges(method: MethodInfo, changedLines: Set<number>): boolea
284
243
  }
285
244
  return false;
286
245
  }
287
-
288
246
  /**
289
247
  * Find NEW methods without explicit return types (NEW_METHODS mode).
290
248
  */
291
249
  // webpieces-disable max-lines-new-methods -- File iteration with diff parsing and method matching
292
- function findViolationsForNewMethods(
293
- workspaceRoot: string,
294
- changedFiles: string[],
295
- base: string,
296
- head: string | undefined,
297
- disableAllowed: boolean
298
- ): MethodViolation[] {
299
- const violations: MethodViolation[] = [];
300
-
250
+ function findViolationsForNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed) {
251
+ const violations = [];
301
252
  for (const file of changedFiles) {
302
253
  const diff = getFileDiff(workspaceRoot, file, base, head);
303
254
  const newMethodNames = findNewMethodSignaturesInDiff(diff);
304
-
305
- if (newMethodNames.size === 0) continue;
306
-
255
+ if (newMethodNames.size === 0)
256
+ continue;
307
257
  const methods = findMethodsInFile(file, workspaceRoot);
308
-
309
258
  for (const method of methods) {
310
- if (!newMethodNames.has(method.name)) continue;
311
- if (method.hasReturnType) continue;
312
- if (disableAllowed && method.hasDisableComment) continue;
313
-
259
+ if (!newMethodNames.has(method.name))
260
+ continue;
261
+ if (method.hasReturnType)
262
+ continue;
263
+ if (disableAllowed && method.hasDisableComment)
264
+ continue;
314
265
  violations.push({
315
266
  file,
316
267
  methodName: method.name,
@@ -318,39 +269,28 @@ function findViolationsForNewMethods(
318
269
  });
319
270
  }
320
271
  }
321
-
322
272
  return violations;
323
273
  }
324
-
325
274
  /**
326
275
  * Find NEW methods AND methods with changes (NEW_AND_MODIFIED_METHODS mode).
327
276
  */
328
277
  // webpieces-disable max-lines-new-methods -- Combines new method detection with change detection
329
- function findViolationsForModifiedAndNewMethods(
330
- workspaceRoot: string,
331
- changedFiles: string[],
332
- base: string,
333
- head: string | undefined,
334
- disableAllowed: boolean
335
- ): MethodViolation[] {
336
- const violations: MethodViolation[] = [];
337
-
278
+ function findViolationsForModifiedAndNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed) {
279
+ const violations = [];
338
280
  for (const file of changedFiles) {
339
281
  const diff = getFileDiff(workspaceRoot, file, base, head);
340
282
  const newMethodNames = findNewMethodSignaturesInDiff(diff);
341
283
  const changedLines = getChangedLineNumbers(diff);
342
-
343
284
  const methods = findMethodsInFile(file, workspaceRoot);
344
-
345
285
  for (const method of methods) {
346
- if (method.hasReturnType) continue;
347
- if (disableAllowed && method.hasDisableComment) continue;
348
-
286
+ if (method.hasReturnType)
287
+ continue;
288
+ if (disableAllowed && method.hasDisableComment)
289
+ continue;
349
290
  const isNewMethod = newMethodNames.has(method.name);
350
291
  const isModifiedMethod = methodHasChanges(method, changedLines);
351
-
352
- if (!isNewMethod && !isModifiedMethod) continue;
353
-
292
+ if (!isNewMethod && !isModifiedMethod)
293
+ continue;
354
294
  violations.push({
355
295
  file,
356
296
  methodName: method.name,
@@ -358,23 +298,20 @@ function findViolationsForModifiedAndNewMethods(
358
298
  });
359
299
  }
360
300
  }
361
-
362
301
  return violations;
363
302
  }
364
-
365
303
  /**
366
304
  * Find all methods without explicit return types in modified files (MODIFIED_FILES mode).
367
305
  */
368
- function findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: string[], disableAllowed: boolean): MethodViolation[] {
369
- const violations: MethodViolation[] = [];
370
-
306
+ function findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed) {
307
+ const violations = [];
371
308
  for (const file of changedFiles) {
372
309
  const methods = findMethodsInFile(file, workspaceRoot);
373
-
374
310
  for (const method of methods) {
375
- if (method.hasReturnType) continue;
376
- if (disableAllowed && method.hasDisableComment) continue;
377
-
311
+ if (method.hasReturnType)
312
+ continue;
313
+ if (disableAllowed && method.hasDisableComment)
314
+ continue;
378
315
  violations.push({
379
316
  file,
380
317
  methodName: method.name,
@@ -382,50 +319,47 @@ function findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: str
382
319
  });
383
320
  }
384
321
  }
385
-
386
322
  return violations;
387
323
  }
388
-
389
324
  /**
390
325
  * Auto-detect the base branch by finding the merge-base with origin/main.
391
326
  */
392
- function detectBase(workspaceRoot: string): string | null {
327
+ function detectBase(workspaceRoot) {
393
328
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
394
329
  try {
395
- const mergeBase = execSync('git merge-base HEAD origin/main', {
330
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD origin/main', {
396
331
  cwd: workspaceRoot,
397
332
  encoding: 'utf-8',
398
333
  stdio: ['pipe', 'pipe', 'pipe'],
399
334
  }).trim();
400
-
401
335
  if (mergeBase) {
402
336
  return mergeBase;
403
337
  }
404
- } catch (err: unknown) {
338
+ }
339
+ catch (err) {
405
340
  //const error = toError(err);
406
341
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
407
342
  try {
408
- const mergeBase = execSync('git merge-base HEAD main', {
343
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD main', {
409
344
  cwd: workspaceRoot,
410
345
  encoding: 'utf-8',
411
346
  stdio: ['pipe', 'pipe', 'pipe'],
412
347
  }).trim();
413
-
414
348
  if (mergeBase) {
415
349
  return mergeBase;
416
350
  }
417
- } catch (err2: unknown) {
351
+ }
352
+ catch (err2) {
418
353
  //const error2 = toError(err2);
419
354
  // Ignore
420
355
  }
421
356
  }
422
357
  return null;
423
358
  }
424
-
425
359
  /**
426
360
  * Report violations to console.
427
361
  */
428
- function reportViolations(violations: MethodViolation[], mode: ReturnTypeMode): void {
362
+ function reportViolations(violations, mode) {
429
363
  console.error('');
430
364
  console.error('❌ Methods missing explicit return types!');
431
365
  console.error('');
@@ -435,13 +369,11 @@ function reportViolations(violations: MethodViolation[], mode: ReturnTypeMode):
435
369
  console.error(' GOOD: method(): MyClass { return new MyClass(); }');
436
370
  console.error(' GOOD: async method(): Promise<MyType> { ... }');
437
371
  console.error('');
438
-
439
372
  for (const v of violations) {
440
373
  console.error(` ❌ ${v.file}:${v.line}`);
441
374
  console.error(` Method: ${v.methodName} - missing return type annotation`);
442
375
  }
443
376
  console.error('');
444
-
445
377
  console.error(' To fix: Add explicit return type after the parameter list');
446
378
  console.error('');
447
379
  console.error(' Escape hatch (use sparingly):');
@@ -450,12 +382,11 @@ function reportViolations(violations: MethodViolation[], mode: ReturnTypeMode):
450
382
  console.error(` Current mode: ${mode}`);
451
383
  console.error('');
452
384
  }
453
-
454
385
  /**
455
386
  * Resolve mode considering ignoreModifiedUntilEpoch override.
456
387
  * When active, downgrades to OFF. When expired, logs a warning.
457
388
  */
458
- function resolveMode(normalMode: ReturnTypeMode, epoch: number | undefined): ReturnTypeMode {
389
+ function resolveMode(normalMode, epoch) {
459
390
  if (epoch === undefined || normalMode === 'OFF') {
460
391
  return normalMode;
461
392
  }
@@ -468,65 +399,50 @@ function resolveMode(normalMode: ReturnTypeMode, epoch: number | undefined): Ret
468
399
  }
469
400
  return normalMode;
470
401
  }
471
-
472
- export default async function runValidator(
473
- options: ValidateReturnTypesOptions,
474
- workspaceRoot: string
475
- ): Promise<ExecutorResult> {
476
- const mode: ReturnTypeMode = resolveMode(options.mode ?? 'NEW_METHODS', options.ignoreModifiedUntilEpoch);
402
+ async function runValidator(options, workspaceRoot) {
403
+ const mode = resolveMode(options.mode ?? 'NEW_METHODS', options.ignoreModifiedUntilEpoch);
477
404
  const disableAllowed = options.disableAllowed ?? true;
478
-
479
405
  if (mode === 'OFF') {
480
406
  console.log('\n⏭️ Skipping return type validation (mode: OFF)');
481
407
  console.log('');
482
408
  return { success: true };
483
409
  }
484
-
485
410
  console.log('\n📏 Validating Return Types\n');
486
411
  console.log(` Mode: ${mode}`);
487
-
488
412
  let base = process.env['NX_BASE'];
489
413
  const head = process.env['NX_HEAD'];
490
-
491
414
  if (!base) {
492
415
  base = detectBase(workspaceRoot) ?? undefined;
493
-
494
416
  if (!base) {
495
417
  console.log('\n⏭️ Skipping return type validation (could not detect base branch)');
496
418
  console.log('');
497
419
  return { success: true };
498
420
  }
499
421
  }
500
-
501
422
  console.log(` Base: ${base}`);
502
423
  console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
503
424
  console.log('');
504
-
505
425
  const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
506
-
507
426
  if (changedFiles.length === 0) {
508
427
  console.log('✅ No TypeScript files changed');
509
428
  return { success: true };
510
429
  }
511
-
512
430
  console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
513
-
514
- let violations: MethodViolation[] = [];
515
-
431
+ let violations = [];
516
432
  if (mode === 'NEW_METHODS') {
517
433
  violations = findViolationsForNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed);
518
- } else if (mode === 'NEW_AND_MODIFIED_METHODS') {
434
+ }
435
+ else if (mode === 'NEW_AND_MODIFIED_METHODS') {
519
436
  violations = findViolationsForModifiedAndNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed);
520
- } else if (mode === 'MODIFIED_FILES') {
437
+ }
438
+ else if (mode === 'MODIFIED_FILES') {
521
439
  violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);
522
440
  }
523
-
524
441
  if (violations.length === 0) {
525
442
  console.log('✅ All methods have explicit return types');
526
443
  return { success: true };
527
444
  }
528
-
529
445
  reportViolations(violations, mode);
530
-
531
446
  return { success: false };
532
447
  }
448
+ //# sourceMappingURL=validate-return-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-return-types.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-return-types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;AAscH,+BA4DC;;AAhgBD,iDAAyC;AACzC,+CAAyB;AACzB,mDAA6B;AAC7B,uDAAiC;AAoBjC;;GAEG;AACH,oHAAoH;AACpH,SAAS,yBAAyB,CAAC,aAAqB,EAAE,IAAY,EAAE,IAAa;IACjF,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,wBAAwB,UAAU,oBAAoB,EAAE;YAC5E,GAAG,EAAE,aAAa;YAClB,QAAQ,EAAE,OAAO;SACpB,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,MAAM;aACtB,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAE5E,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,8DAA8D;YAC9D,IAAI,CAAC;gBACD,MAAM,eAAe,GAAG,IAAA,wBAAQ,EAAC,yDAAyD,EAAE;oBACxF,GAAG,EAAE,aAAa;oBAClB,QAAQ,EAAE,OAAO;iBACpB,CAAC,CAAC;gBACH,MAAM,cAAc,GAAG,eAAe;qBACjC,IAAI,EAAE;qBACN,KAAK,CAAC,IAAI,CAAC;qBACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;gBAC/D,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,6BAA6B;gBAC7B,OAAO,YAAY,CAAC;YACxB,CAAC;QACL,CAAC;QAED,OAAO,YAAY,CAAC;IACxB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,6BAA6B;QAC7B,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,aAAqB,EAAE,IAAY,EAAE,IAAY,EAAE,IAAa;IACjF,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,IAAA,wBAAQ,EAAC,YAAY,UAAU,QAAQ,IAAI,GAAG,EAAE;YACzD,GAAG,EAAE,aAAa;YAClB,QAAQ,EAAE,OAAO;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YAChD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,IAAA,wBAAQ,EAAC,6CAA6C,IAAI,GAAG,EAAE;oBAC/E,GAAG,EAAE,aAAa;oBAClB,QAAQ,EAAE,OAAO;iBACpB,CAAC,CAAC,IAAI,EAAE,CAAC;gBAEV,IAAI,WAAW,EAAE,CAAC;oBACd,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAClC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtD,CAAC;YACL,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,6BAA6B;QAC7B,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,6BAA6B,CAAC,WAAmB;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEtC,MAAM,QAAQ,GAAG;QACb,wDAAwD;QACxD,iEAAiE;QACjE,uEAAuE;QACvE,iFAAiF;KACpF,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAClC,IAAI,KAAK,EAAE,CAAC;oBACR,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC5B,IAAI,UAAU,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC/F,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM;gBACV,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,KAAe,EAAE,UAAkB;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,UAAU,GAAG,CAAC,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAClF,MAAM;QACV,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC7E,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAsE;IACjG,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AACnC,CAAC;AAUD;;GAEG;AACH,4FAA4F;AAC5F,SAAS,iBAAiB,CAAC,QAAgB,EAAE,aAAqB;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAExF,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,uGAAuG;IACvG,SAAS,KAAK,CAAC,IAAa;QACxB,IAAI,UAA8B,CAAC;QACnC,IAAI,SAA6B,CAAC;QAClC,IAAI,OAA2B,CAAC;QAChC,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5C,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACxE,MAAM,GAAG,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACpE,SAAS,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YAC3B,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YACvB,aAAa,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrD,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACxE,MAAM,GAAG,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACpE,SAAS,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YAC3B,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YACvB,aAAa,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7E,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBAClD,MAAM,KAAK,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxE,MAAM,GAAG,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBACpE,SAAS,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;gBAC3B,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;gBACvB,aAAa,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAChD,CAAC;QACL,CAAC;QAED,IAAI,UAAU,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,SAAS;gBACf,OAAO;gBACP,aAAa;gBACb,iBAAiB,EAAE,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC;aAC7D,CAAC,CAAC;QACP,CAAC;QAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,CAAC;IAClB,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,WAAmB;IAC9C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACtE,IAAI,SAAS,EAAE,CAAC;YACZ,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzC,SAAS;QACb,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC9B,WAAW,EAAE,CAAC;QAClB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,wCAAwC;QAC5C,CAAC;aAAM,CAAC;YACJ,WAAW,EAAE,CAAC;QAClB,CAAC;IACL,CAAC;IAED,OAAO,YAAY,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,MAAkB,EAAE,YAAyB;IACnE,KAAK,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC1D,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,kGAAkG;AAClG,SAAS,2BAA2B,CAChC,aAAqB,EACrB,YAAsB,EACtB,IAAY,EACZ,IAAwB,EACxB,cAAuB;IAEvB,MAAM,UAAU,GAAsB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAExC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEvD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC/C,IAAI,MAAM,CAAC,aAAa;gBAAE,SAAS;YACnC,IAAI,cAAc,IAAI,MAAM,CAAC,iBAAiB;gBAAE,SAAS;YAEzD,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aACpB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,iGAAiG;AACjG,SAAS,sCAAsC,CAC3C,aAAqB,EACrB,YAAsB,EACtB,IAAY,EACZ,IAAwB,EACxB,cAAuB;IAEvB,MAAM,UAAU,GAAsB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEvD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,MAAM,CAAC,aAAa;gBAAE,SAAS;YACnC,IAAI,cAAc,IAAI,MAAM,CAAC,iBAAiB;gBAAE,SAAS;YAEzD,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YAEhE,IAAI,CAAC,WAAW,IAAI,CAAC,gBAAgB;gBAAE,SAAS;YAEhD,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aACpB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,8BAA8B,CAAC,aAAqB,EAAE,YAAsB,EAAE,cAAuB;IAC1G,MAAM,UAAU,GAAsB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEvD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,MAAM,CAAC,aAAa;gBAAE,SAAS;YACnC,IAAI,cAAc,IAAI,MAAM,CAAC,iBAAiB;gBAAE,SAAS;YAEzD,UAAU,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aACpB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,aAAqB;IACrC,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,SAAS,GAAG,IAAA,wBAAQ,EAAC,iCAAiC,EAAE;YAC1D,GAAG,EAAE,aAAa;YAClB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,IAAI,SAAS,EAAE,CAAC;YACZ,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,6BAA6B;QAC7B,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,IAAA,wBAAQ,EAAC,0BAA0B,EAAE;gBACnD,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAClC,CAAC,CAAC,IAAI,EAAE,CAAC;YAEV,IAAI,SAAS,EAAE,CAAC;gBACZ,OAAO,SAAS,CAAC;YACrB,CAAC;QACL,CAAC;QAAC,OAAO,IAAa,EAAE,CAAC;YACrB,+BAA+B;YAC/B,SAAS;QACb,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,UAA6B,EAAE,IAAoB;IACzE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC1D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACpE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC7D,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACtE,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElB,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,UAAU,mCAAmC,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElB,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAC9E,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAC9E,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,UAA0B,EAAE,KAAyB;IACtE,IAAI,KAAK,KAAK,SAAS,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QAC9C,OAAO,UAAU,CAAC;IACtB,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACrC,IAAI,UAAU,GAAG,KAAK,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,4FAA4F,WAAW,GAAG,CAAC,CAAC;QACxH,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAEc,KAAK,UAAU,YAAY,CACtC,OAAmC,EACnC,aAAqB;IAErB,MAAM,IAAI,GAAmB,WAAW,CAAC,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC1G,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC;IAEtD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAEhC,IAAI,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEpC,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,IAAI,GAAG,UAAU,CAAC,aAAa,CAAC,IAAI,SAAS,CAAC;QAE9C,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;YACpF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,6CAA6C,EAAE,CAAC,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,YAAY,GAAG,yBAAyB,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAE1E,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,YAAY,CAAC,MAAM,qBAAqB,CAAC,CAAC;IAErE,IAAI,UAAU,GAAsB,EAAE,CAAC;IAEvC,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QACzB,UAAU,GAAG,2BAA2B,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACtG,CAAC;SAAM,IAAI,IAAI,KAAK,0BAA0B,EAAE,CAAC;QAC7C,UAAU,GAAG,sCAAsC,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACjH,CAAC;SAAM,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACnC,UAAU,GAAG,8BAA8B,CAAC,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAEnC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC","sourcesContent":["/**\n * Validate Return Types Executor\n *\n * Validates that methods have explicit return type annotations for better code readability.\n * Instead of relying on TypeScript's type inference, explicit return types make code clearer:\n *\n * BAD: method() { return new MyClass(); }\n * GOOD: method(): MyClass { return new MyClass(); }\n * GOOD: async method(): Promise<MyType> { ... }\n *\n * Modes:\n * - OFF: Skip validation entirely\n * - NEW_METHODS: Only validate new methods (detected via git diff)\n * - NEW_AND_MODIFIED_METHODS: Validate new methods + methods with changes in their line range\n * - MODIFIED_FILES: Validate all methods in modified files\n *\n * Escape hatch: Add webpieces-disable require-return-type comment with justification\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\n\nexport type ReturnTypeMode = 'OFF' | 'NEW_METHODS' | 'NEW_AND_MODIFIED_METHODS' | 'MODIFIED_FILES';\n\nexport interface ValidateReturnTypesOptions {\n mode?: ReturnTypeMode;\n disableAllowed?: boolean;\n ignoreModifiedUntilEpoch?: number;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface MethodViolation {\n file: string;\n methodName: string;\n line: number;\n}\n\n/**\n * Get changed TypeScript files between base and head (or working tree if head not specified).\n */\n// webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const changedFiles = output\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n\n if (!head) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const untrackedFiles = untrackedOutput\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n const allFiles = new Set([...changedFiles, ...untrackedFiles]);\n return Array.from(allFiles);\n } catch (err: unknown) {\n //const error = toError(err);\n return changedFiles;\n }\n }\n\n return changedFiles;\n } catch (err: unknown) {\n //const error = toError(err);\n return [];\n }\n}\n\n/**\n * Get the diff content for a specific file.\n */\nfunction getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const diff = execSync(`git diff ${diffTarget} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n\n if (!diff && !head) {\n const fullPath = path.join(workspaceRoot, file);\n if (fs.existsSync(fullPath)) {\n const isUntracked = execSync(`git ls-files --others --exclude-standard \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n }).trim();\n\n if (isUntracked) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n const lines = content.split('\\n');\n return lines.map((line) => `+${line}`).join('\\n');\n }\n }\n }\n\n return diff;\n } catch (err: unknown) {\n //const error = toError(err);\n return '';\n }\n}\n\n/**\n * Parse diff to find newly added method signatures.\n */\nfunction findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n const patterns = [\n /^\\+\\s*(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?function/,\n /^\\+\\s*(?:(?:public|private|protected)\\s+)?(?:static\\s+)?(?:async\\s+)?(\\w+)\\s*\\(/,\n ];\n\n for (const line of lines) {\n if (line.startsWith('+') && !line.startsWith('+++')) {\n for (const pattern of patterns) {\n const match = line.match(pattern);\n if (match) {\n const methodName = match[1];\n if (methodName && !['if', 'for', 'while', 'switch', 'catch', 'constructor'].includes(methodName)) {\n newMethods.add(methodName);\n }\n break;\n }\n }\n }\n }\n\n return newMethods;\n}\n\n/**\n * Check if a line contains a webpieces-disable comment for return type.\n */\nfunction hasDisableComment(lines: string[], lineNumber: number): boolean {\n const startCheck = Math.max(0, lineNumber - 5);\n for (let i = lineNumber - 2; i >= startCheck; i--) {\n const line = lines[i]?.trim() ?? '';\n if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {\n break;\n }\n if (line.includes('webpieces-disable') && line.includes('require-return-type')) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a method has an explicit return type annotation.\n */\nfunction hasExplicitReturnType(node: ts.MethodDeclaration | ts.FunctionDeclaration | ts.ArrowFunction): boolean {\n return node.type !== undefined;\n}\n\ninterface MethodInfo {\n name: string;\n line: number;\n endLine: number;\n hasReturnType: boolean;\n hasDisableComment: boolean;\n}\n\n/**\n * Parse a TypeScript file and find methods with their return type status.\n */\n// webpieces-disable max-lines-new-methods -- AST traversal requires inline visitor function\nfunction findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[] {\n const fullPath = path.join(workspaceRoot, filePath);\n if (!fs.existsSync(fullPath)) return [];\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const fileLines = content.split('\\n');\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n const methods: MethodInfo[] = [];\n\n // webpieces-disable max-lines-new-methods -- AST visitor pattern requires handling multiple node types\n function visit(node: ts.Node): void {\n let methodName: string | undefined;\n let startLine: number | undefined;\n let endLine: number | undefined;\n let hasReturnType = false;\n\n if (ts.isMethodDeclaration(node) && node.name) {\n methodName = node.name.getText(sourceFile);\n const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n startLine = start.line + 1;\n endLine = end.line + 1;\n hasReturnType = hasExplicitReturnType(node);\n } else if (ts.isFunctionDeclaration(node) && node.name) {\n methodName = node.name.getText(sourceFile);\n const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n startLine = start.line + 1;\n endLine = end.line + 1;\n hasReturnType = hasExplicitReturnType(node);\n } else if (ts.isArrowFunction(node)) {\n if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {\n methodName = node.parent.name.getText(sourceFile);\n const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n startLine = start.line + 1;\n endLine = end.line + 1;\n hasReturnType = hasExplicitReturnType(node);\n }\n }\n\n if (methodName && startLine !== undefined && endLine !== undefined) {\n methods.push({\n name: methodName,\n line: startLine,\n endLine,\n hasReturnType,\n hasDisableComment: hasDisableComment(fileLines, startLine),\n });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return methods;\n}\n\n/**\n * Parse diff to extract changed line numbers (both additions and modifications).\n */\nfunction getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n let currentLine = 0;\n\n for (const line of lines) {\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Deletions don't increment line number\n } else {\n currentLine++;\n }\n }\n\n return changedLines;\n}\n\n/**\n * Check if a method has any changed lines within its range.\n */\nfunction methodHasChanges(method: MethodInfo, changedLines: Set<number>): boolean {\n for (let line = method.line; line <= method.endLine; line++) {\n if (changedLines.has(line)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Find NEW methods without explicit return types (NEW_METHODS mode).\n */\n// webpieces-disable max-lines-new-methods -- File iteration with diff parsing and method matching\nfunction findViolationsForNewMethods(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean\n): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n\n if (newMethodNames.size === 0) continue;\n\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n if (!newMethodNames.has(method.name)) continue;\n if (method.hasReturnType) continue;\n if (disableAllowed && method.hasDisableComment) continue;\n\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Find NEW methods AND methods with changes (NEW_AND_MODIFIED_METHODS mode).\n */\n// webpieces-disable max-lines-new-methods -- Combines new method detection with change detection\nfunction findViolationsForModifiedAndNewMethods(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n head: string | undefined,\n disableAllowed: boolean\n): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base, head);\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n const changedLines = getChangedLineNumbers(diff);\n\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n if (method.hasReturnType) continue;\n if (disableAllowed && method.hasDisableComment) continue;\n\n const isNewMethod = newMethodNames.has(method.name);\n const isModifiedMethod = methodHasChanges(method, changedLines);\n\n if (!isNewMethod && !isModifiedMethod) continue;\n\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Find all methods without explicit return types in modified files (MODIFIED_FILES mode).\n */\nfunction findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: string[], disableAllowed: boolean): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n if (method.hasReturnType) continue;\n if (disableAllowed && method.hasDisableComment) continue;\n\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Auto-detect the base branch by finding the merge-base with origin/main.\n */\nfunction detectBase(workspaceRoot: string): string | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD origin/main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n } catch (err: unknown) {\n //const error = toError(err);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const mergeBase = execSync('git merge-base HEAD main', {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (mergeBase) {\n return mergeBase;\n }\n } catch (err2: unknown) {\n //const error2 = toError(err2);\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Report violations to console.\n */\nfunction reportViolations(violations: MethodViolation[], mode: ReturnTypeMode): void {\n console.error('');\n console.error('❌ Methods missing explicit return types!');\n console.error('');\n console.error('📚 Explicit return types improve code readability:');\n console.error('');\n console.error(' BAD: method() { return new MyClass(); }');\n console.error(' GOOD: method(): MyClass { return new MyClass(); }');\n console.error(' GOOD: async method(): Promise<MyType> { ... }');\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} - missing return type annotation`);\n }\n console.error('');\n\n console.error(' To fix: Add explicit return type after the parameter list');\n console.error('');\n console.error(' Escape hatch (use sparingly):');\n console.error(' // webpieces-disable require-return-type -- [your reason]');\n console.error('');\n console.error(` Current mode: ${mode}`);\n console.error('');\n}\n\n/**\n * Resolve mode considering ignoreModifiedUntilEpoch override.\n * When active, downgrades to OFF. When expired, logs a warning.\n */\nfunction resolveMode(normalMode: ReturnTypeMode, epoch: number | undefined): ReturnTypeMode {\n if (epoch === undefined || normalMode === 'OFF') {\n return normalMode;\n }\n const nowSeconds = Date.now() / 1000;\n if (nowSeconds < epoch) {\n const expiresDate = new Date(epoch * 1000).toISOString().split('T')[0];\n console.log(`\\n⏭️ Skipping require-return-type validation (ignoreModifiedUntilEpoch active, expires: ${expiresDate})`);\n console.log('');\n return 'OFF';\n }\n return normalMode;\n}\n\nexport default async function runValidator(\n options: ValidateReturnTypesOptions,\n workspaceRoot: string\n): Promise<ExecutorResult> {\n const mode: ReturnTypeMode = resolveMode(options.mode ?? 'NEW_METHODS', options.ignoreModifiedUntilEpoch);\n const disableAllowed = options.disableAllowed ?? true;\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping return type validation (mode: OFF)');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Return Types\\n');\n console.log(` Mode: ${mode}`);\n\n let base = process.env['NX_BASE'];\n const head = process.env['NX_HEAD'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping return type validation (could not detect base branch)');\n console.log('');\n return { success: true };\n }\n }\n\n console.log(` Base: ${base}`);\n console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);\n console.log('');\n\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);\n\n if (changedFiles.length === 0) {\n console.log('✅ No TypeScript files changed');\n return { success: true };\n }\n\n console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);\n\n let violations: MethodViolation[] = [];\n\n if (mode === 'NEW_METHODS') {\n violations = findViolationsForNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed);\n } else if (mode === 'NEW_AND_MODIFIED_METHODS') {\n violations = findViolationsForModifiedAndNewMethods(workspaceRoot, changedFiles, base, head, disableAllowed);\n } else if (mode === 'MODIFIED_FILES') {\n violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);\n }\n\n if (violations.length === 0) {\n console.log('✅ All methods have explicit return types');\n return { success: true };\n }\n\n reportViolations(violations, mode);\n\n return { success: false };\n}\n"]}