@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 Modified Methods Executor
3
4
  *
@@ -18,36 +19,15 @@
18
19
  * Format: // webpieces-disable max-lines-modified 2025/01/15 -- [reason]
19
20
  * The disable expires after 1 month from the date specified.
20
21
  */
21
-
22
- import { execSync } from 'child_process';
23
- import * as fs from 'fs';
24
- import * as path from 'path';
25
- import * as ts from 'typescript';
26
-
27
- export type MethodMaxLimitMode = 'OFF' | 'NEW_METHODS' | 'NEW_AND_MODIFIED_METHODS' | 'MODIFIED_FILES';
28
-
29
- export interface ValidateModifiedMethodsOptions {
30
- limit?: number;
31
- mode?: MethodMaxLimitMode;
32
- disableAllowed?: boolean;
33
- }
34
-
35
- export interface ExecutorResult {
36
- success: boolean;
37
- }
38
-
39
- interface MethodViolation {
40
- file: string;
41
- methodName: string;
42
- line: number;
43
- lines: number;
44
- expiredDisable?: boolean;
45
- expiredDate?: string;
46
- }
47
-
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.default = runValidator;
24
+ const tslib_1 = require("tslib");
25
+ const child_process_1 = require("child_process");
26
+ const fs = tslib_1.__importStar(require("fs"));
27
+ const path = tslib_1.__importStar(require("path"));
28
+ const ts = tslib_1.__importStar(require("typescript"));
48
29
  const TMP_DIR = '.webpieces/instruct-ai';
49
30
  const TMP_MD_FILE = 'webpieces.methodsize.md';
50
-
51
31
  const METHODSIZE_DOC_CONTENT = `# Instructions: Method Too Long
52
32
 
53
33
  ## Requirement
@@ -172,31 +152,27 @@ This ensures that disable comments are reviewed periodically.
172
152
  - The best code explains itself through structure
173
153
  - When in doubt, extract and name it
174
154
  `;
175
-
176
155
  /**
177
156
  * Write the instructions documentation to tmp directory
178
157
  */
179
- function writeTmpInstructions(workspaceRoot: string): string {
158
+ function writeTmpInstructions(workspaceRoot) {
180
159
  const tmpDir = path.join(workspaceRoot, TMP_DIR);
181
160
  const mdPath = path.join(tmpDir, TMP_MD_FILE);
182
-
183
161
  fs.mkdirSync(tmpDir, { recursive: true });
184
162
  fs.writeFileSync(mdPath, METHODSIZE_DOC_CONTENT);
185
-
186
163
  return mdPath;
187
164
  }
188
-
189
165
  /**
190
166
  * Get changed TypeScript files between base and head (or working tree if head not specified).
191
167
  * Uses `git diff base [head]` to match what `nx affected` does.
192
168
  * When head is NOT specified, also includes untracked files (matching nx affected behavior).
193
169
  */
194
- function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {
170
+ function getChangedTypeScriptFiles(workspaceRoot, base, head) {
195
171
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
196
172
  try {
197
173
  // If head is specified, diff base to head; otherwise diff base to working tree
198
174
  const diffTarget = head ? `${base} ${head}` : base;
199
- const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
175
+ const output = (0, child_process_1.execSync)(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
200
176
  cwd: workspaceRoot,
201
177
  encoding: 'utf-8',
202
178
  });
@@ -204,13 +180,12 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
204
180
  .trim()
205
181
  .split('\n')
206
182
  .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
207
-
208
183
  // When comparing to working tree (no head specified), also include untracked files
209
184
  // This matches what nx affected does: "All modified files not yet committed or tracked will also be added"
210
185
  if (!head) {
211
186
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
212
187
  try {
213
- const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
188
+ const untrackedOutput = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
214
189
  cwd: workspaceRoot,
215
190
  encoding: 'utf-8',
216
191
  });
@@ -221,45 +196,43 @@ function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: s
221
196
  // Merge and dedupe
222
197
  const allFiles = new Set([...changedFiles, ...untrackedFiles]);
223
198
  return Array.from(allFiles);
224
- } catch (err: unknown) {
199
+ }
200
+ catch (err) {
225
201
  //const error = toError(err);
226
202
  // If ls-files fails, just return the changed files
227
203
  return changedFiles;
228
204
  }
229
205
  }
230
-
231
206
  return changedFiles;
232
- } catch (err: unknown) {
207
+ }
208
+ catch (err) {
233
209
  //const error = toError(err);
234
210
  return [];
235
211
  }
236
212
  }
237
-
238
213
  /**
239
214
  * Get the diff content for a specific file between base and head (or working tree if head not specified).
240
215
  * Uses `git diff base [head]` to match what `nx affected` does.
241
216
  * For untracked files, returns the entire file content as additions.
242
217
  */
243
- function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {
218
+ function getFileDiff(workspaceRoot, file, base, head) {
244
219
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
245
220
  try {
246
221
  // If head is specified, diff base to head; otherwise diff base to working tree
247
222
  const diffTarget = head ? `${base} ${head}` : base;
248
- const diff = execSync(`git diff ${diffTarget} -- "${file}"`, {
223
+ const diff = (0, child_process_1.execSync)(`git diff ${diffTarget} -- "${file}"`, {
249
224
  cwd: workspaceRoot,
250
225
  encoding: 'utf-8',
251
226
  });
252
-
253
227
  // If diff is empty and we're comparing to working tree, check if it's an untracked file
254
228
  if (!diff && !head) {
255
229
  const fullPath = path.join(workspaceRoot, file);
256
230
  if (fs.existsSync(fullPath)) {
257
231
  // Check if file is untracked
258
- const isUntracked = execSync(`git ls-files --others --exclude-standard "${file}"`, {
232
+ const isUntracked = (0, child_process_1.execSync)(`git ls-files --others --exclude-standard "${file}"`, {
259
233
  cwd: workspaceRoot,
260
234
  encoding: 'utf-8',
261
235
  }).trim();
262
-
263
236
  if (isUntracked) {
264
237
  // For untracked files, treat entire content as additions
265
238
  const content = fs.readFileSync(fullPath, 'utf-8');
@@ -269,23 +242,21 @@ function getFileDiff(workspaceRoot: string, file: string, base: string, head?: s
269
242
  }
270
243
  }
271
244
  }
272
-
273
245
  return diff;
274
- } catch (err: unknown) {
246
+ }
247
+ catch (err) {
275
248
  //const error = toError(err);
276
249
  return '';
277
250
  }
278
251
  }
279
-
280
252
  /**
281
253
  * Parse diff to find NEW method signatures.
282
254
  * Must handle: export function, async function, const/let arrow functions, class methods
283
255
  */
284
256
  // webpieces-disable max-lines-new-methods -- Regex patterns require inline documentation
285
- function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {
286
- const newMethods = new Set<string>();
257
+ function findNewMethodSignaturesInDiff(diffContent) {
258
+ const newMethods = new Set();
287
259
  const lines = diffContent.split('\n');
288
-
289
260
  // Patterns to match method definitions (same as validate-new-methods)
290
261
  const patterns = [
291
262
  // [export] [async] function methodName( - most explicit, check first
@@ -297,7 +268,6 @@ function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {
297
268
  // class method: [public/private/protected] [static] [async] methodName( - but NOT constructor, if, for, while, etc.
298
269
  /^\+\s*(?:(?:public|private|protected)\s+)?(?:static\s+)?(?:async\s+)?(\w+)\s*\(/,
299
270
  ];
300
-
301
271
  for (const line of lines) {
302
272
  if (line.startsWith('+') && !line.startsWith('+++')) {
303
273
  for (const pattern of patterns) {
@@ -313,19 +283,15 @@ function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {
313
283
  }
314
284
  }
315
285
  }
316
-
317
286
  return newMethods;
318
287
  }
319
-
320
288
  /**
321
289
  * Parse diff to find line numbers that have changes in the new file
322
290
  */
323
- function getChangedLineNumbers(diffContent: string): Set<number> {
324
- const changedLines = new Set<number>();
291
+ function getChangedLineNumbers(diffContent) {
292
+ const changedLines = new Set();
325
293
  const lines = diffContent.split('\n');
326
-
327
294
  let currentNewLine = 0;
328
-
329
295
  for (const line of lines) {
330
296
  // Parse hunk header: @@ -oldStart,oldCount +newStart,newCount @@
331
297
  const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
@@ -333,73 +299,60 @@ function getChangedLineNumbers(diffContent: string): Set<number> {
333
299
  currentNewLine = parseInt(hunkMatch[1], 10);
334
300
  continue;
335
301
  }
336
-
337
- if (currentNewLine === 0) continue;
338
-
302
+ if (currentNewLine === 0)
303
+ continue;
339
304
  if (line.startsWith('+') && !line.startsWith('+++')) {
340
305
  // Added line
341
306
  changedLines.add(currentNewLine);
342
307
  currentNewLine++;
343
- } else if (line.startsWith('-') && !line.startsWith('---')) {
308
+ }
309
+ else if (line.startsWith('-') && !line.startsWith('---')) {
344
310
  // Removed line - doesn't increment new line counter
345
- } else if (!line.startsWith('\\')) {
311
+ }
312
+ else if (!line.startsWith('\\')) {
346
313
  // Context line (unchanged)
347
314
  currentNewLine++;
348
315
  }
349
316
  }
350
-
351
317
  return changedLines;
352
318
  }
353
-
354
319
  /**
355
320
  * Parse a date string in yyyy/mm/dd format and return a Date object.
356
321
  * Returns null if the format is invalid.
357
322
  */
358
- function parseDisableDate(dateStr: string): Date | null {
323
+ function parseDisableDate(dateStr) {
359
324
  // Match yyyy/mm/dd format
360
325
  const match = dateStr.match(/^(\d{4})\/(\d{2})\/(\d{2})$/);
361
- if (!match) return null;
362
-
326
+ if (!match)
327
+ return null;
363
328
  const year = parseInt(match[1], 10);
364
329
  const month = parseInt(match[2], 10) - 1; // JS months are 0-indexed
365
330
  const day = parseInt(match[3], 10);
366
-
367
331
  const date = new Date(year, month, day);
368
-
369
332
  // Validate the date is valid (e.g., not Feb 30)
370
333
  if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
371
334
  return null;
372
335
  }
373
-
374
336
  return date;
375
337
  }
376
-
377
338
  /**
378
339
  * Check if a date is within the last month (not expired).
379
340
  */
380
- function isDateWithinMonth(date: Date): boolean {
341
+ function isDateWithinMonth(date) {
381
342
  const now = new Date();
382
343
  const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
383
344
  return date >= oneMonthAgo;
384
345
  }
385
-
386
346
  /**
387
347
  * Get today's date in yyyy/mm/dd format for error messages
388
348
  */
389
- function getTodayDateString(): string {
349
+ function getTodayDateString() {
390
350
  const now = new Date();
391
351
  const year = now.getFullYear();
392
352
  const month = String(now.getMonth() + 1).padStart(2, '0');
393
353
  const day = String(now.getDate()).padStart(2, '0');
394
354
  return `${year}/${month}/${day}`;
395
355
  }
396
-
397
- interface DisableInfo {
398
- type: 'full' | 'new-only' | 'none';
399
- isExpired: boolean;
400
- date?: string;
401
- }
402
-
403
356
  /**
404
357
  * Check what kind of webpieces-disable comment is present for a method.
405
358
  * Returns: DisableInfo with type, expiration status, and date
@@ -408,7 +361,7 @@ interface DisableInfo {
408
361
  * - 'none': no escape hatch
409
362
  */
410
363
  // webpieces-disable max-lines-new-methods -- Complex validation logic with multiple escape hatch types
411
- function getDisableInfo(lines: string[], lineNumber: number): DisableInfo {
364
+ function getDisableInfo(lines, lineNumber) {
412
365
  const startCheck = Math.max(0, lineNumber - 5);
413
366
  for (let i = lineNumber - 2; i >= startCheck; i--) {
414
367
  const line = lines[i]?.trim() ?? '';
@@ -419,60 +372,48 @@ function getDisableInfo(lines: string[], lineNumber: number): DisableInfo {
419
372
  if (line.includes('max-lines-modified')) {
420
373
  // Check for date in format: max-lines-modified yyyy/mm/dd
421
374
  const dateMatch = line.match(/max-lines-modified\s+(\d{4}\/\d{2}\/\d{2}|XXXX\/XX\/XX)/);
422
-
423
375
  if (!dateMatch) {
424
376
  // No date found - treat as expired (invalid)
425
377
  return { type: 'full', isExpired: true, date: undefined };
426
378
  }
427
-
428
379
  const dateStr = dateMatch[1];
429
-
430
380
  // Secret permanent disable
431
381
  if (dateStr === 'XXXX/XX/XX') {
432
382
  return { type: 'full', isExpired: false, date: dateStr };
433
383
  }
434
-
435
384
  const date = parseDisableDate(dateStr);
436
385
  if (!date) {
437
386
  // Invalid date format - treat as expired
438
387
  return { type: 'full', isExpired: true, date: dateStr };
439
388
  }
440
-
441
389
  if (!isDateWithinMonth(date)) {
442
390
  // Date is expired (older than 1 month)
443
391
  return { type: 'full', isExpired: true, date: dateStr };
444
392
  }
445
-
446
393
  // Valid and not expired
447
394
  return { type: 'full', isExpired: false, date: dateStr };
448
395
  }
449
396
  if (line.includes('max-lines-new-methods')) {
450
397
  // Check for date in format: max-lines-new-methods yyyy/mm/dd
451
398
  const dateMatch = line.match(/max-lines-new-methods\s+(\d{4}\/\d{2}\/\d{2}|XXXX\/XX\/XX)/);
452
-
453
399
  if (!dateMatch) {
454
400
  // No date found - treat as expired (invalid)
455
401
  return { type: 'new-only', isExpired: true, date: undefined };
456
402
  }
457
-
458
403
  const dateStr = dateMatch[1];
459
-
460
404
  // Secret permanent disable
461
405
  if (dateStr === 'XXXX/XX/XX') {
462
406
  return { type: 'new-only', isExpired: false, date: dateStr };
463
407
  }
464
-
465
408
  const date = parseDisableDate(dateStr);
466
409
  if (!date) {
467
410
  // Invalid date format - treat as expired
468
411
  return { type: 'new-only', isExpired: true, date: dateStr };
469
412
  }
470
-
471
413
  if (!isDateWithinMonth(date)) {
472
414
  // Date is expired (older than 1 month)
473
415
  return { type: 'new-only', isExpired: true, date: dateStr };
474
416
  }
475
-
476
417
  // Valid and not expired
477
418
  return { type: 'new-only', isExpired: false, date: dateStr };
478
419
  }
@@ -480,48 +421,38 @@ function getDisableInfo(lines: string[], lineNumber: number): DisableInfo {
480
421
  }
481
422
  return { type: 'none', isExpired: false };
482
423
  }
483
-
484
- interface MethodInfo {
485
- name: string;
486
- line: number;
487
- endLine: number;
488
- lines: number;
489
- disableInfo: DisableInfo;
490
- }
491
-
492
424
  /**
493
425
  * Parse a TypeScript file and find methods with their line counts
494
426
  */
495
427
  // webpieces-disable max-lines-new-methods -- AST traversal requires inline visitor function
496
- function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[] {
428
+ function findMethodsInFile(filePath, workspaceRoot) {
497
429
  const fullPath = path.join(workspaceRoot, filePath);
498
- if (!fs.existsSync(fullPath)) return [];
499
-
430
+ if (!fs.existsSync(fullPath))
431
+ return [];
500
432
  const content = fs.readFileSync(fullPath, 'utf-8');
501
433
  const fileLines = content.split('\n');
502
434
  const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
503
-
504
- const methods: MethodInfo[] = [];
505
-
435
+ const methods = [];
506
436
  // webpieces-disable max-lines-new-methods -- AST visitor pattern requires handling multiple node types
507
- function visit(node: ts.Node): void {
508
- let methodName: string | undefined;
509
- let startLine: number | undefined;
510
- let endLine: number | undefined;
511
-
437
+ function visit(node) {
438
+ let methodName;
439
+ let startLine;
440
+ let endLine;
512
441
  if (ts.isMethodDeclaration(node) && node.name) {
513
442
  methodName = node.name.getText(sourceFile);
514
443
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
515
444
  const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
516
445
  startLine = start.line + 1;
517
446
  endLine = end.line + 1;
518
- } else if (ts.isFunctionDeclaration(node) && node.name) {
447
+ }
448
+ else if (ts.isFunctionDeclaration(node) && node.name) {
519
449
  methodName = node.name.getText(sourceFile);
520
450
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
521
451
  const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
522
452
  startLine = start.line + 1;
523
453
  endLine = end.line + 1;
524
- } else if (ts.isArrowFunction(node)) {
454
+ }
455
+ else if (ts.isArrowFunction(node)) {
525
456
  if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
526
457
  methodName = node.parent.name.getText(sourceFile);
527
458
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
@@ -530,7 +461,6 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
530
461
  endLine = end.line + 1;
531
462
  }
532
463
  }
533
-
534
464
  if (methodName && startLine !== undefined && endLine !== undefined) {
535
465
  methods.push({
536
466
  name: methodName,
@@ -540,57 +470,51 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
540
470
  disableInfo: getDisableInfo(fileLines, startLine),
541
471
  });
542
472
  }
543
-
544
473
  ts.forEachChild(node, visit);
545
474
  }
546
-
547
475
  visit(sourceFile);
548
476
  return methods;
549
477
  }
550
-
551
478
  /**
552
479
  * Check if a method has any changes within its line range
553
480
  */
554
- function methodHasChanges(method: MethodInfo, changedLineNumbers: Set<number>): boolean {
481
+ function methodHasChanges(method, changedLineNumbers) {
555
482
  for (let line = method.line; line <= method.endLine; line++) {
556
- if (changedLineNumbers.has(line)) return true;
483
+ if (changedLineNumbers.has(line))
484
+ return true;
557
485
  }
558
486
  return false;
559
487
  }
560
-
561
488
  /**
562
489
  * Check a NEW method and return violation if applicable
563
490
  */
564
- function checkNewMethodViolation(file: string, method: MethodInfo, disableAllowed: boolean): MethodViolation | null {
491
+ function checkNewMethodViolation(file, method, disableAllowed) {
565
492
  const disableType = method.disableInfo.type;
566
493
  const isExpired = method.disableInfo.isExpired;
567
494
  const disableDate = method.disableInfo.date;
568
-
569
495
  if (!disableAllowed) {
570
496
  // When disableAllowed is false, skip NEW methods without escape (let validate-new-methods handle)
571
- if (disableType === 'none') return null;
497
+ if (disableType === 'none')
498
+ return null;
572
499
  return { file, methodName: method.name, line: method.line, lines: method.lines };
573
500
  }
574
-
575
501
  if (disableType === 'full' && isExpired) {
576
502
  return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
577
503
  }
578
- if (disableType !== 'new-only') return null;
579
-
504
+ if (disableType !== 'new-only')
505
+ return null;
580
506
  if (isExpired) {
581
507
  return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
582
508
  }
583
509
  return { file, methodName: method.name, line: method.line, lines: method.lines };
584
510
  }
585
-
586
511
  /**
587
512
  * Check a MODIFIED method and return violation if applicable
588
513
  */
589
- function checkModifiedMethodViolation(file: string, method: MethodInfo, disableAllowed: boolean): MethodViolation | null {
514
+ function checkModifiedMethodViolation(file, method, disableAllowed) {
590
515
  const disableType = method.disableInfo.type;
591
516
  const isExpired = method.disableInfo.isExpired;
592
517
  const disableDate = method.disableInfo.date;
593
-
594
518
  if (!disableAllowed) {
595
519
  return { file, methodName: method.name, line: method.line, lines: method.lines };
596
520
  }
@@ -603,95 +527,84 @@ function checkModifiedMethodViolation(file: string, method: MethodInfo, disableA
603
527
  }
604
528
  return { file, methodName: method.name, line: method.line, lines: method.lines };
605
529
  }
606
-
607
530
  /**
608
531
  * Find methods that exceed the limit.
609
532
  * Checks NEW methods with escape hatches and MODIFIED methods.
610
533
  */
611
- function findViolations(
612
- workspaceRoot: string,
613
- changedFiles: string[],
614
- base: string,
615
- limit: number,
616
- disableAllowed: boolean,
617
- head?: string
618
- ): MethodViolation[] {
619
- const violations: MethodViolation[] = [];
620
-
534
+ function findViolations(workspaceRoot, changedFiles, base, limit, disableAllowed, head) {
535
+ const violations = [];
621
536
  for (const file of changedFiles) {
622
537
  const diff = getFileDiff(workspaceRoot, file, base, head);
623
- if (!diff) continue;
624
-
538
+ if (!diff)
539
+ continue;
625
540
  const newMethodNames = findNewMethodSignaturesInDiff(diff);
626
541
  const changedLineNumbers = getChangedLineNumbers(diff);
627
- if (changedLineNumbers.size === 0) continue;
628
-
542
+ if (changedLineNumbers.size === 0)
543
+ continue;
629
544
  const methods = findMethodsInFile(file, workspaceRoot);
630
-
631
545
  for (const method of methods) {
632
546
  const disableType = method.disableInfo.type;
633
547
  const isExpired = method.disableInfo.isExpired;
634
-
635
548
  // Skip methods with valid, non-expired full escape - unless disableAllowed is false
636
- if (disableAllowed && disableType === 'full' && !isExpired) continue;
637
- if (method.lines <= limit) continue;
638
-
549
+ if (disableAllowed && disableType === 'full' && !isExpired)
550
+ continue;
551
+ if (method.lines <= limit)
552
+ continue;
639
553
  const isNewMethod = newMethodNames.has(method.name);
640
-
641
554
  if (isNewMethod) {
642
555
  const violation = checkNewMethodViolation(file, method, disableAllowed);
643
- if (violation) violations.push(violation);
644
- } else if (methodHasChanges(method, changedLineNumbers)) {
556
+ if (violation)
557
+ violations.push(violation);
558
+ }
559
+ else if (methodHasChanges(method, changedLineNumbers)) {
645
560
  const violation = checkModifiedMethodViolation(file, method, disableAllowed);
646
- if (violation) violations.push(violation);
561
+ if (violation)
562
+ violations.push(violation);
647
563
  }
648
564
  }
649
565
  }
650
-
651
566
  return violations;
652
567
  }
653
-
654
568
  /**
655
569
  * Auto-detect the base branch by finding the merge-base with origin/main.
656
570
  */
657
- function detectBase(workspaceRoot: string): string | null {
571
+ function detectBase(workspaceRoot) {
658
572
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
659
573
  try {
660
- const mergeBase = execSync('git merge-base HEAD origin/main', {
574
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD origin/main', {
661
575
  cwd: workspaceRoot,
662
576
  encoding: 'utf-8',
663
577
  stdio: ['pipe', 'pipe', 'pipe'],
664
578
  }).trim();
665
-
666
579
  if (mergeBase) {
667
580
  return mergeBase;
668
581
  }
669
- } catch (err: unknown) {
582
+ }
583
+ catch (err) {
670
584
  //const error = toError(err);
671
585
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
672
586
  try {
673
- const mergeBase = execSync('git merge-base HEAD main', {
587
+ const mergeBase = (0, child_process_1.execSync)('git merge-base HEAD main', {
674
588
  cwd: workspaceRoot,
675
589
  encoding: 'utf-8',
676
590
  stdio: ['pipe', 'pipe', 'pipe'],
677
591
  }).trim();
678
-
679
592
  if (mergeBase) {
680
593
  return mergeBase;
681
594
  }
682
- } catch (err2: unknown) {
595
+ }
596
+ catch (err2) {
683
597
  //const error2 = toError(err2);
684
598
  // Ignore
685
599
  }
686
600
  }
687
601
  return null;
688
602
  }
689
-
690
603
  /**
691
604
  * Report violations to console
692
605
  */
693
606
  // webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections
694
- function reportViolations(violations: MethodViolation[], limit: number, disableAllowed: boolean): void {
607
+ function reportViolations(violations, limit, disableAllowed) {
695
608
  console.error('');
696
609
  console.error('\u274c Modified methods exceed ' + limit + ' lines!');
697
610
  console.error('');
@@ -701,24 +614,21 @@ function reportViolations(violations: MethodViolation[], limit: number, disableA
701
614
  console.error(' (this is for vibe coding and AI to fix as it touches things).');
702
615
  console.error(' You can refactor to stay under the limit 50% of the time. If not feasible, use the escape hatch.');
703
616
  console.error('');
704
- console.error(
705
- '\u26a0\ufe0f *** READ .webpieces/instruct-ai/webpieces.methodsize.md for detailed guidance on how to fix this easily *** \u26a0\ufe0f'
706
- );
617
+ console.error('\u26a0\ufe0f *** READ .webpieces/instruct-ai/webpieces.methodsize.md for detailed guidance on how to fix this easily *** \u26a0\ufe0f');
707
618
  console.error('');
708
-
709
619
  for (const v of violations) {
710
620
  if (v.expiredDisable) {
711
621
  console.error(` \u274c ${v.file}:${v.line}`);
712
622
  console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${limit})`);
713
623
  console.error(` \u23f0 EXPIRED DISABLE: Your disable comment dated ${v.expiredDate ?? 'unknown'} has expired (>1 month old).`);
714
624
  console.error(` You must either FIX the method or UPDATE the date to get another month.`);
715
- } else {
625
+ }
626
+ else {
716
627
  console.error(` \u274c ${v.file}:${v.line}`);
717
628
  console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${limit})`);
718
629
  }
719
630
  }
720
631
  console.error('');
721
-
722
632
  // Only show escape hatch instructions when disableAllowed is true
723
633
  if (disableAllowed) {
724
634
  console.error(' You can disable this error, but you will be forced to fix again in 1 month');
@@ -727,7 +637,8 @@ function reportViolations(violations: MethodViolation[], limit: number, disableA
727
637
  console.error(' Use escape with DATE (expires in 1 month):');
728
638
  console.error(` // webpieces-disable max-lines-modified ${getTodayDateString()} -- [your reason]`);
729
639
  console.error('');
730
- } else {
640
+ }
641
+ else {
731
642
  console.error(' \u26a0\ufe0f disableAllowed is false - disable comments are NOT allowed.');
732
643
  console.error(' This rule must be met and cannot be disabled since nx.json disableAllowed is set to false.');
733
644
  console.error(' You MUST refactor to reduce method size.');
@@ -740,73 +651,60 @@ function reportViolations(violations: MethodViolation[], limit: number, disableA
740
651
  console.error('');
741
652
  }
742
653
  }
743
-
744
- export default async function runValidator(
745
- options: ValidateModifiedMethodsOptions,
746
- workspaceRoot: string
747
- ): Promise<ExecutorResult> {
654
+ async function runValidator(options, workspaceRoot) {
748
655
  const limit = options.limit ?? 80;
749
- const mode: MethodMaxLimitMode = options.mode ?? 'NEW_AND_MODIFIED_METHODS';
656
+ const mode = options.mode ?? 'NEW_AND_MODIFIED_METHODS';
750
657
  const disableAllowed = options.disableAllowed ?? true;
751
-
752
658
  // Skip validation entirely if mode is OFF
753
659
  if (mode === 'OFF') {
754
660
  console.log('\n\u23ed\ufe0f Skipping modified method validation (mode: OFF)');
755
661
  console.log('');
756
662
  return { success: true };
757
663
  }
758
-
759
664
  // If NX_HEAD is set (via nx affected --head=X), use it; otherwise compare to working tree
760
665
  let base = process.env['NX_BASE'];
761
666
  const head = process.env['NX_HEAD'];
762
-
763
667
  if (!base) {
764
668
  base = detectBase(workspaceRoot) ?? undefined;
765
-
766
669
  if (!base) {
767
670
  console.log('\n\u23ed\ufe0f Skipping modified method validation (could not detect base branch)');
768
671
  console.log(' To run explicitly: nx affected --target=validate-modified-methods --base=origin/main');
769
672
  console.log('');
770
673
  return { success: true };
771
674
  }
772
-
773
675
  console.log('\n\ud83d\udccf Validating Modified Method Sizes (auto-detected base)\n');
774
- } else {
676
+ }
677
+ else {
775
678
  console.log('\n\ud83d\udccf Validating Modified Method Sizes\n');
776
679
  }
777
-
778
680
  console.log(` Base: ${base}`);
779
681
  console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
780
682
  console.log(` Mode: ${mode}`);
781
683
  console.log(` Limit for modified methods: ${limit}`);
782
684
  console.log(` Disable allowed: ${disableAllowed}${!disableAllowed ? ' (no escape hatch)' : ''}`);
783
685
  console.log('');
784
-
785
686
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
786
687
  try {
787
688
  const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
788
-
789
689
  if (changedFiles.length === 0) {
790
690
  console.log('\u2705 No TypeScript files changed');
791
691
  return { success: true };
792
692
  }
793
-
794
693
  console.log(`\ud83d\udcc2 Checking ${changedFiles.length} changed file(s)...`);
795
-
796
694
  const violations = findViolations(workspaceRoot, changedFiles, base, limit, disableAllowed, head);
797
-
798
695
  if (violations.length === 0) {
799
696
  console.log('\u2705 All modified methods are under ' + limit + ' lines');
800
697
  return { success: true };
801
698
  }
802
-
803
699
  writeTmpInstructions(workspaceRoot);
804
700
  reportViolations(violations, limit, disableAllowed);
805
701
  return { success: false };
806
- } catch (err: unknown) {
702
+ }
703
+ catch (err) {
807
704
  //const error = toError(err);
808
705
  const error = err instanceof Error ? err : new Error(String(err));
809
706
  console.error('\u274c Modified method validation failed:', error.message);
810
707
  return { success: false };
811
708
  }
812
709
  }
710
+ //# sourceMappingURL=validate-modified-methods.js.map