@webpieces/dev-config 0.2.63 → 0.2.64
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.
- package/architecture/executors/validate-modified-files/executor.d.ts +2 -1
- package/architecture/executors/validate-modified-files/executor.js +17 -13
- package/architecture/executors/validate-modified-files/executor.js.map +1 -1
- package/architecture/executors/validate-modified-files/executor.ts +21 -14
- package/architecture/executors/validate-modified-files/schema.json +5 -4
- package/architecture/executors/validate-modified-methods/executor.d.ts +2 -0
- package/architecture/executors/validate-modified-methods/executor.js +80 -95
- package/architecture/executors/validate-modified-methods/executor.js.map +1 -1
- package/architecture/executors/validate-modified-methods/executor.ts +88 -96
- package/architecture/executors/validate-modified-methods/schema.json +6 -0
- package/package.json +1 -1
- package/plugin.js +9 -9
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
* The disable expires after 1 month from the date specified.
|
|
14
14
|
*/
|
|
15
15
|
import type { ExecutorContext } from '@nx/devkit';
|
|
16
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
16
17
|
export interface ValidateModifiedFilesOptions {
|
|
17
18
|
max?: number;
|
|
18
|
-
|
|
19
|
+
mode?: ValidationMode;
|
|
19
20
|
}
|
|
20
21
|
export interface ExecutorResult {
|
|
21
22
|
success: boolean;
|
|
@@ -287,7 +287,7 @@ function checkDisableComment(content) {
|
|
|
287
287
|
* Count lines in a file and check for violations
|
|
288
288
|
*/
|
|
289
289
|
// webpieces-disable max-lines-new-methods -- File iteration with disable checking logic
|
|
290
|
-
function findViolations(workspaceRoot, changedFiles, maxLines,
|
|
290
|
+
function findViolations(workspaceRoot, changedFiles, maxLines, mode) {
|
|
291
291
|
const violations = [];
|
|
292
292
|
for (const file of changedFiles) {
|
|
293
293
|
const fullPath = path.join(workspaceRoot, file);
|
|
@@ -298,8 +298,8 @@ function findViolations(workspaceRoot, changedFiles, maxLines, forceLimit) {
|
|
|
298
298
|
// Skip files under the limit
|
|
299
299
|
if (lineCount <= maxLines)
|
|
300
300
|
continue;
|
|
301
|
-
// When
|
|
302
|
-
if (
|
|
301
|
+
// When mode is STRICT, ignore all disable comments
|
|
302
|
+
if (mode === 'STRICT') {
|
|
303
303
|
violations.push({ file, lines: lineCount });
|
|
304
304
|
continue;
|
|
305
305
|
}
|
|
@@ -374,7 +374,7 @@ function getTodayDateString() {
|
|
|
374
374
|
* Report violations to console
|
|
375
375
|
*/
|
|
376
376
|
// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections
|
|
377
|
-
function reportViolations(violations, maxLines,
|
|
377
|
+
function reportViolations(violations, maxLines, mode) {
|
|
378
378
|
console.error('');
|
|
379
379
|
console.error('❌ YOU MUST FIX THIS AND NOT be more than ' + maxLines + ' lines of code per file');
|
|
380
380
|
console.error(' as it slows down IDEs AND is VERY VERY EASY to refactor.');
|
|
@@ -398,8 +398,8 @@ function reportViolations(violations, maxLines, forceLimit) {
|
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
400
|
console.error('');
|
|
401
|
-
// Only show escape hatch instructions when
|
|
402
|
-
if (
|
|
401
|
+
// Only show escape hatch instructions when mode is not STRICT
|
|
402
|
+
if (mode !== 'STRICT') {
|
|
403
403
|
console.error(' You can disable this error, but you will be forced to fix again in 1 month');
|
|
404
404
|
console.error(' since 99% of files can be less than ' + maxLines + ' lines of code.');
|
|
405
405
|
console.error('');
|
|
@@ -408,7 +408,7 @@ function reportViolations(violations, maxLines, forceLimit) {
|
|
|
408
408
|
console.error('');
|
|
409
409
|
}
|
|
410
410
|
else {
|
|
411
|
-
console.error(' ⚠️
|
|
411
|
+
console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');
|
|
412
412
|
console.error(' You MUST refactor to reduce file size.');
|
|
413
413
|
console.error('');
|
|
414
414
|
}
|
|
@@ -416,7 +416,13 @@ function reportViolations(violations, maxLines, forceLimit) {
|
|
|
416
416
|
async function runExecutor(options, context) {
|
|
417
417
|
const workspaceRoot = context.root;
|
|
418
418
|
const maxLines = options.max ?? 900;
|
|
419
|
-
const
|
|
419
|
+
const mode = options.mode ?? 'NORMAL';
|
|
420
|
+
// Skip validation entirely if mode is OFF
|
|
421
|
+
if (mode === 'OFF') {
|
|
422
|
+
console.log('\n⏭️ Skipping modified files validation (validationMode: OFF)');
|
|
423
|
+
console.log('');
|
|
424
|
+
return { success: true };
|
|
425
|
+
}
|
|
420
426
|
let base = process.env['NX_BASE'];
|
|
421
427
|
if (!base) {
|
|
422
428
|
base = detectBase(workspaceRoot) ?? undefined;
|
|
@@ -434,9 +440,7 @@ async function runExecutor(options, context) {
|
|
|
434
440
|
console.log(` Base: ${base}`);
|
|
435
441
|
console.log(' Comparing to: working tree (includes uncommitted changes)');
|
|
436
442
|
console.log(` Max lines for modified files: ${maxLines}`);
|
|
437
|
-
|
|
438
|
-
console.log(' Force limit: ENABLED (disable comments will be ignored)');
|
|
439
|
-
}
|
|
443
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);
|
|
440
444
|
console.log('');
|
|
441
445
|
try {
|
|
442
446
|
const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);
|
|
@@ -445,13 +449,13 @@ async function runExecutor(options, context) {
|
|
|
445
449
|
return { success: true };
|
|
446
450
|
}
|
|
447
451
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
448
|
-
const violations = findViolations(workspaceRoot, changedFiles, maxLines,
|
|
452
|
+
const violations = findViolations(workspaceRoot, changedFiles, maxLines, mode);
|
|
449
453
|
if (violations.length === 0) {
|
|
450
454
|
console.log('✅ All modified files are under ' + maxLines + ' lines');
|
|
451
455
|
return { success: true };
|
|
452
456
|
}
|
|
453
457
|
writeTmpInstructions(workspaceRoot);
|
|
454
|
-
reportViolations(violations, maxLines,
|
|
458
|
+
reportViolations(violations, maxLines, mode);
|
|
455
459
|
return { success: false };
|
|
456
460
|
}
|
|
457
461
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-modified-files/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Modified Files Executor\n *\n * Validates that modified files don't exceed a maximum line count (default 900).\n * This encourages keeping files small and focused - when you touch a file,\n * you must bring it under the limit.\n *\n * Usage:\n * nx affected --target=validate-modified-files --base=origin/main\n *\n * Escape hatch: Add webpieces-disable max-lines-modified-files comment with date and justification\n * Format: // webpieces-disable max-lines-modified-files 2025/01/15 -- [reason]\n * The disable expires after 1 month from the date specified.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateModifiedFilesOptions {\n max?: number;\n forceLimit?: boolean;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface FileViolation {\n file: string;\n lines: number;\n expiredDisable?: boolean;\n expiredDate?: string;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.filesize.md';\n\nconst FILESIZE_DOC_CONTENT = `# AI Agent Instructions: File Too Long\n\n**READ THIS FILE to fix files that are too long**\n\n## Core Principle\n\nWith **stateless systems + dependency injection, refactor is trivial**.\nPick a method or a few and move to new class XXXXX, then inject XXXXX\ninto all users of those methods via the constructor.\nDelete those methods from original class.\n\n**99% of files can be less than the configured max lines of code.**\n\nFiles should contain a SINGLE COHESIVE UNIT.\n- One class per file (Java convention)\n- If class is too large, extract child responsibilities\n- Use dependency injection to compose functionality\n\n## Command: Reduce File Size\n\n### Step 1: Check for Multiple Classes\nIf the file contains multiple classes, **SEPARATE each class into its own file**.\n\n\\`\\`\\`typescript\n// BAD: UserController.ts (multiple classes)\nexport class UserController { /* ... */ }\nexport class UserValidator { /* ... */ }\nexport class UserNotifier { /* ... */ }\n\n// GOOD: Three separate files\n// UserController.ts\nexport class UserController { /* ... */ }\n\n// UserValidator.ts\nexport class UserValidator { /* ... */ }\n\n// UserNotifier.ts\nexport class UserNotifier { /* ... */ }\n\\`\\`\\`\n\n### Step 2: Extract Child Responsibilities (if single class is too large)\n\n#### Pattern: Create New Service Class with Dependency Injection\n\n\\`\\`\\`typescript\n// BAD: UserController.ts (800 lines, single class)\n@provideSingleton()\n@Controller()\nexport class UserController {\n // 200 lines: CRUD operations\n // 300 lines: validation logic\n // 200 lines: notification logic\n // 100 lines: analytics logic\n}\n\n// GOOD: Extract validation service\n// 1. Create UserValidationService.ts\n@provideSingleton()\nexport class UserValidationService {\n validateUserData(data: UserData): ValidationResult {\n // 300 lines of validation logic moved here\n }\n\n validateEmail(email: string): boolean { /* ... */ }\n validatePassword(password: string): boolean { /* ... */ }\n}\n\n// 2. Inject into UserController.ts\n@provideSingleton()\n@Controller()\nexport class UserController {\n constructor(\n @inject(TYPES.UserValidationService)\n private validator: UserValidationService\n ) {}\n\n async createUser(data: UserData): Promise<User> {\n const validation = this.validator.validateUserData(data);\n if (!validation.isValid) {\n throw new ValidationError(validation.errors);\n }\n // ... rest of logic\n }\n}\n\\`\\`\\`\n\n## AI Agent Action Steps\n\n1. **ANALYZE** the file structure:\n - Count classes (if >1, separate immediately)\n - Identify logical responsibilities within single class\n\n2. **IDENTIFY** \"child code\" to extract:\n - Validation logic -> ValidationService\n - Notification logic -> NotificationService\n - Data transformation -> TransformerService\n - External API calls -> ApiService\n - Business rules -> RulesEngine\n\n3. **CREATE** new service file(s):\n - Start with temporary name: \\`XXXX.ts\\` or \\`ChildService.ts\\`\n - Add \\`@provideSingleton()\\` decorator\n - Move child methods to new class\n\n4. **UPDATE** dependency injection:\n - Add to \\`TYPES\\` constants (if using symbol-based DI)\n - Inject new service into original class constructor\n - Replace direct method calls with \\`this.serviceName.method()\\`\n\n5. **RENAME** extracted file:\n - Read the extracted code to understand its purpose\n - Rename \\`XXXX.ts\\` to logical name (e.g., \\`UserValidationService.ts\\`)\n\n6. **VERIFY** file sizes:\n - Original file should now be under the limit\n - Each extracted file should be under the limit\n - If still too large, extract more services\n\n## Examples of Child Responsibilities to Extract\n\n| If File Contains | Extract To | Pattern |\n|-----------------|------------|---------|\n| Validation logic (200+ lines) | \\`XValidator.ts\\` or \\`XValidationService.ts\\` | Singleton service |\n| Notification logic (150+ lines) | \\`XNotifier.ts\\` or \\`XNotificationService.ts\\` | Singleton service |\n| Data transformation (200+ lines) | \\`XTransformer.ts\\` | Singleton service |\n| External API calls (200+ lines) | \\`XApiClient.ts\\` | Singleton service |\n| Complex business rules (300+ lines) | \\`XRulesEngine.ts\\` | Singleton service |\n| Database queries (200+ lines) | \\`XRepository.ts\\` | Singleton service |\n\n## WebPieces Dependency Injection Pattern\n\n\\`\\`\\`typescript\n// 1. Define service with @provideSingleton\nimport { provideSingleton } from '@webpieces/http-routing';\n\n@provideSingleton()\nexport class MyService {\n doSomething(): void { /* ... */ }\n}\n\n// 2. Inject into consumer\nimport { inject } from 'inversify';\nimport { TYPES } from './types';\n\n@provideSingleton()\n@Controller()\nexport class MyController {\n constructor(\n @inject(TYPES.MyService) private service: MyService\n ) {}\n}\n\\`\\`\\`\n\n## Escape Hatch\n\nIf refactoring is genuinely not feasible (generated files, complex algorithms, etc.),\nadd a disable comment at the TOP of the file (within first 5 lines) with a DATE:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-modified-files 2025/01/15 -- Complex generated file, refactoring would break generation\n\\`\\`\\`\n\n**IMPORTANT**: The date format is yyyy/mm/dd. The disable will EXPIRE after 1 month from this date.\nAfter expiration, you must either fix the file or update the date to get another month.\nThis ensures that disable comments are reviewed periodically.\n\nRemember: Find the \"child code\" and pull it down into a new class. Once moved, the code's purpose becomes clear, making it easy to rename to a logical name.\n`;\n\n/**\n * Write the instructions documentation to tmp directory\n */\nfunction writeTmpInstructions(workspaceRoot: string): string {\n const tmpDir = path.join(workspaceRoot, TMP_DIR);\n const mdPath = path.join(tmpDir, TMP_MD_FILE);\n\n fs.mkdirSync(tmpDir, { recursive: true });\n fs.writeFileSync(mdPath, FILESIZE_DOC_CONTENT);\n\n return mdPath;\n}\n\n/**\n * Get changed TypeScript files between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string): string[] {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n const output = execSync(`git diff --name-only ${base} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n return output\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n } catch {\n return [];\n }\n}\n\n/**\n * Parse a date string in yyyy/mm/dd format and return a Date object.\n * Returns null if the format is invalid.\n */\nfunction parseDisableDate(dateStr: string): Date | null {\n // Match yyyy/mm/dd format\n const match = dateStr.match(/^(\\d{4})\\/(\\d{2})\\/(\\d{2})$/);\n if (!match) return null;\n\n const year = parseInt(match[1], 10);\n const month = parseInt(match[2], 10) - 1; // JS months are 0-indexed\n const day = parseInt(match[3], 10);\n\n const date = new Date(year, month, day);\n\n // Validate the date is valid (e.g., not Feb 30)\n if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Check if a date is within the last month (not expired).\n */\nfunction isDateWithinMonth(date: Date): boolean {\n const now = new Date();\n const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());\n return date >= oneMonthAgo;\n}\n\ninterface DisableStatus {\n hasDisable: boolean;\n isValid: boolean;\n isExpired: boolean;\n date?: string;\n}\n\n/**\n * Check if a file has a valid, non-expired disable comment at the top (within first 5 lines).\n * Returns status object with details about the disable comment.\n */\n// webpieces-disable max-lines-new-methods -- Date validation logic requires checking multiple conditions\nfunction checkDisableComment(content: string): DisableStatus {\n const lines = content.split('\\n').slice(0, 5);\n\n for (const line of lines) {\n if (line.includes('webpieces-disable') && line.includes('max-lines-modified-files')) {\n // Found disable comment, now check for date\n // Format: // webpieces-disable max-lines-modified-files yyyy/mm/dd -- reason\n const dateMatch = line.match(/max-lines-modified-files\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - invalid disable comment\n return { hasDisable: true, isValid: false, isExpired: false };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { hasDisable: true, isValid: true, isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format\n return { hasDisable: true, isValid: false, isExpired: false, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { hasDisable: true, isValid: true, isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { hasDisable: true, isValid: true, isExpired: false, date: dateStr };\n }\n }\n\n return { hasDisable: false, isValid: false, isExpired: false };\n}\n\n/**\n * Count lines in a file and check for violations\n */\n// webpieces-disable max-lines-new-methods -- File iteration with disable checking logic\nfunction findViolations(workspaceRoot: string, changedFiles: string[], maxLines: number, forceLimit: boolean): FileViolation[] {\n const violations: FileViolation[] = [];\n\n for (const file of changedFiles) {\n const fullPath = path.join(workspaceRoot, file);\n\n if (!fs.existsSync(fullPath)) continue;\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const lineCount = content.split('\\n').length;\n\n // Skip files under the limit\n if (lineCount <= maxLines) continue;\n\n // When forceLimit is true, ignore all disable comments\n if (forceLimit) {\n violations.push({ file, lines: lineCount });\n continue;\n }\n\n // Check for disable comment\n const disableStatus = checkDisableComment(content);\n\n if (disableStatus.hasDisable) {\n if (disableStatus.isValid && !disableStatus.isExpired) {\n // Valid, non-expired disable - skip this file\n continue;\n }\n\n if (disableStatus.isExpired) {\n // Expired disable - report as violation with expired info\n violations.push({\n file,\n lines: lineCount,\n expiredDisable: true,\n expiredDate: disableStatus.date,\n });\n continue;\n }\n\n // Invalid disable (missing/bad date) - fall through to report as violation\n }\n\n violations.push({\n file,\n lines: lineCount,\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 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 {\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 {\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Get today's date in yyyy/mm/dd format for error messages\n */\nfunction getTodayDateString(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n return `${year}/${month}/${day}`;\n}\n\n/**\n * Report violations to console\n */\n// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections\nfunction reportViolations(violations: FileViolation[], maxLines: number, forceLimit: boolean): void {\n console.error('');\n console.error('❌ YOU MUST FIX THIS AND NOT be more than ' + maxLines + ' lines of code per file');\n console.error(' as it slows down IDEs AND is VERY VERY EASY to refactor.');\n console.error('');\n console.error('📚 With stateless systems + dependency injection, refactor is trivial:');\n console.error(' Pick a method or a few and move to new class XXXXX, then inject XXXXX');\n console.error(' into all users of those methods via the constructor.');\n console.error(' Delete those methods from original class.');\n console.error(' 99% of files can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error('⚠️ *** READ tmp/webpieces/webpieces.filesize.md for detailed guidance on how to fix this easily *** ⚠️');\n console.error('');\n\n for (const v of violations) {\n if (v.expiredDisable) {\n console.error(` ❌ ${v.file} (${v.lines} lines, max: ${maxLines})`);\n console.error(` ⏰ EXPIRED DISABLE: Your disable comment dated ${v.expiredDate} has expired (>1 month old).`);\n console.error(` You must either FIX the file or UPDATE the date to get another month.`);\n } else {\n console.error(` ❌ ${v.file} (${v.lines} lines, max: ${maxLines})`);\n }\n }\n console.error('');\n\n // Only show escape hatch instructions when forceLimit is not enabled\n if (!forceLimit) {\n console.error(' You can disable this error, but you will be forced to fix again in 1 month');\n console.error(' since 99% of files can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error(' Use escape with DATE (expires in 1 month):');\n console.error(` // webpieces-disable max-lines-modified-files ${getTodayDateString()} -- [your reason]`);\n console.error('');\n } else {\n console.error(' ⚠️ forceModifiedFilesLimit is enabled - disable comments are NOT allowed.');\n console.error(' You MUST refactor to reduce file size.');\n console.error('');\n }\n}\n\nexport default async function runExecutor(\n options: ValidateModifiedFilesOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 900;\n const forceLimit = options.forceLimit ?? false;\n\n let base = process.env['NX_BASE'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping modified files validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-modified-files --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Modified File Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating Modified File Sizes\\n');\n }\n\n console.log(` Base: ${base}`);\n console.log(' Comparing to: working tree (includes uncommitted changes)');\n console.log(` Max lines for modified files: ${maxLines}`);\n if (forceLimit) {\n console.log(' Force limit: ENABLED (disable comments will be ignored)');\n }\n console.log('');\n\n try {\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);\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 const violations = findViolations(workspaceRoot, changedFiles, maxLines, forceLimit);\n\n if (violations.length === 0) {\n console.log('✅ All modified files are under ' + maxLines + ' lines');\n return { success: true };\n }\n\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines, forceLimit);\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Modified files validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-modified-files/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Modified Files Executor\n *\n * Validates that modified files don't exceed a maximum line count (default 900).\n * This encourages keeping files small and focused - when you touch a file,\n * you must bring it under the limit.\n *\n * Usage:\n * nx affected --target=validate-modified-files --base=origin/main\n *\n * Escape hatch: Add webpieces-disable max-lines-modified-files comment with date and justification\n * Format: // webpieces-disable max-lines-modified-files 2025/01/15 -- [reason]\n * The disable expires after 1 month from the date specified.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';\n\nexport interface ValidateModifiedFilesOptions {\n max?: number;\n mode?: ValidationMode;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface FileViolation {\n file: string;\n lines: number;\n expiredDisable?: boolean;\n expiredDate?: string;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.filesize.md';\n\nconst FILESIZE_DOC_CONTENT = `# AI Agent Instructions: File Too Long\n\n**READ THIS FILE to fix files that are too long**\n\n## Core Principle\n\nWith **stateless systems + dependency injection, refactor is trivial**.\nPick a method or a few and move to new class XXXXX, then inject XXXXX\ninto all users of those methods via the constructor.\nDelete those methods from original class.\n\n**99% of files can be less than the configured max lines of code.**\n\nFiles should contain a SINGLE COHESIVE UNIT.\n- One class per file (Java convention)\n- If class is too large, extract child responsibilities\n- Use dependency injection to compose functionality\n\n## Command: Reduce File Size\n\n### Step 1: Check for Multiple Classes\nIf the file contains multiple classes, **SEPARATE each class into its own file**.\n\n\\`\\`\\`typescript\n// BAD: UserController.ts (multiple classes)\nexport class UserController { /* ... */ }\nexport class UserValidator { /* ... */ }\nexport class UserNotifier { /* ... */ }\n\n// GOOD: Three separate files\n// UserController.ts\nexport class UserController { /* ... */ }\n\n// UserValidator.ts\nexport class UserValidator { /* ... */ }\n\n// UserNotifier.ts\nexport class UserNotifier { /* ... */ }\n\\`\\`\\`\n\n### Step 2: Extract Child Responsibilities (if single class is too large)\n\n#### Pattern: Create New Service Class with Dependency Injection\n\n\\`\\`\\`typescript\n// BAD: UserController.ts (800 lines, single class)\n@provideSingleton()\n@Controller()\nexport class UserController {\n // 200 lines: CRUD operations\n // 300 lines: validation logic\n // 200 lines: notification logic\n // 100 lines: analytics logic\n}\n\n// GOOD: Extract validation service\n// 1. Create UserValidationService.ts\n@provideSingleton()\nexport class UserValidationService {\n validateUserData(data: UserData): ValidationResult {\n // 300 lines of validation logic moved here\n }\n\n validateEmail(email: string): boolean { /* ... */ }\n validatePassword(password: string): boolean { /* ... */ }\n}\n\n// 2. Inject into UserController.ts\n@provideSingleton()\n@Controller()\nexport class UserController {\n constructor(\n @inject(TYPES.UserValidationService)\n private validator: UserValidationService\n ) {}\n\n async createUser(data: UserData): Promise<User> {\n const validation = this.validator.validateUserData(data);\n if (!validation.isValid) {\n throw new ValidationError(validation.errors);\n }\n // ... rest of logic\n }\n}\n\\`\\`\\`\n\n## AI Agent Action Steps\n\n1. **ANALYZE** the file structure:\n - Count classes (if >1, separate immediately)\n - Identify logical responsibilities within single class\n\n2. **IDENTIFY** \"child code\" to extract:\n - Validation logic -> ValidationService\n - Notification logic -> NotificationService\n - Data transformation -> TransformerService\n - External API calls -> ApiService\n - Business rules -> RulesEngine\n\n3. **CREATE** new service file(s):\n - Start with temporary name: \\`XXXX.ts\\` or \\`ChildService.ts\\`\n - Add \\`@provideSingleton()\\` decorator\n - Move child methods to new class\n\n4. **UPDATE** dependency injection:\n - Add to \\`TYPES\\` constants (if using symbol-based DI)\n - Inject new service into original class constructor\n - Replace direct method calls with \\`this.serviceName.method()\\`\n\n5. **RENAME** extracted file:\n - Read the extracted code to understand its purpose\n - Rename \\`XXXX.ts\\` to logical name (e.g., \\`UserValidationService.ts\\`)\n\n6. **VERIFY** file sizes:\n - Original file should now be under the limit\n - Each extracted file should be under the limit\n - If still too large, extract more services\n\n## Examples of Child Responsibilities to Extract\n\n| If File Contains | Extract To | Pattern |\n|-----------------|------------|---------|\n| Validation logic (200+ lines) | \\`XValidator.ts\\` or \\`XValidationService.ts\\` | Singleton service |\n| Notification logic (150+ lines) | \\`XNotifier.ts\\` or \\`XNotificationService.ts\\` | Singleton service |\n| Data transformation (200+ lines) | \\`XTransformer.ts\\` | Singleton service |\n| External API calls (200+ lines) | \\`XApiClient.ts\\` | Singleton service |\n| Complex business rules (300+ lines) | \\`XRulesEngine.ts\\` | Singleton service |\n| Database queries (200+ lines) | \\`XRepository.ts\\` | Singleton service |\n\n## WebPieces Dependency Injection Pattern\n\n\\`\\`\\`typescript\n// 1. Define service with @provideSingleton\nimport { provideSingleton } from '@webpieces/http-routing';\n\n@provideSingleton()\nexport class MyService {\n doSomething(): void { /* ... */ }\n}\n\n// 2. Inject into consumer\nimport { inject } from 'inversify';\nimport { TYPES } from './types';\n\n@provideSingleton()\n@Controller()\nexport class MyController {\n constructor(\n @inject(TYPES.MyService) private service: MyService\n ) {}\n}\n\\`\\`\\`\n\n## Escape Hatch\n\nIf refactoring is genuinely not feasible (generated files, complex algorithms, etc.),\nadd a disable comment at the TOP of the file (within first 5 lines) with a DATE:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-modified-files 2025/01/15 -- Complex generated file, refactoring would break generation\n\\`\\`\\`\n\n**IMPORTANT**: The date format is yyyy/mm/dd. The disable will EXPIRE after 1 month from this date.\nAfter expiration, you must either fix the file or update the date to get another month.\nThis ensures that disable comments are reviewed periodically.\n\nRemember: Find the \"child code\" and pull it down into a new class. Once moved, the code's purpose becomes clear, making it easy to rename to a logical name.\n`;\n\n/**\n * Write the instructions documentation to tmp directory\n */\nfunction writeTmpInstructions(workspaceRoot: string): string {\n const tmpDir = path.join(workspaceRoot, TMP_DIR);\n const mdPath = path.join(tmpDir, TMP_MD_FILE);\n\n fs.mkdirSync(tmpDir, { recursive: true });\n fs.writeFileSync(mdPath, FILESIZE_DOC_CONTENT);\n\n return mdPath;\n}\n\n/**\n * Get changed TypeScript files between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string): string[] {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n const output = execSync(`git diff --name-only ${base} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n return output\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n } catch {\n return [];\n }\n}\n\n/**\n * Parse a date string in yyyy/mm/dd format and return a Date object.\n * Returns null if the format is invalid.\n */\nfunction parseDisableDate(dateStr: string): Date | null {\n // Match yyyy/mm/dd format\n const match = dateStr.match(/^(\\d{4})\\/(\\d{2})\\/(\\d{2})$/);\n if (!match) return null;\n\n const year = parseInt(match[1], 10);\n const month = parseInt(match[2], 10) - 1; // JS months are 0-indexed\n const day = parseInt(match[3], 10);\n\n const date = new Date(year, month, day);\n\n // Validate the date is valid (e.g., not Feb 30)\n if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Check if a date is within the last month (not expired).\n */\nfunction isDateWithinMonth(date: Date): boolean {\n const now = new Date();\n const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());\n return date >= oneMonthAgo;\n}\n\ninterface DisableStatus {\n hasDisable: boolean;\n isValid: boolean;\n isExpired: boolean;\n date?: string;\n}\n\n/**\n * Check if a file has a valid, non-expired disable comment at the top (within first 5 lines).\n * Returns status object with details about the disable comment.\n */\n// webpieces-disable max-lines-new-methods -- Date validation logic requires checking multiple conditions\nfunction checkDisableComment(content: string): DisableStatus {\n const lines = content.split('\\n').slice(0, 5);\n\n for (const line of lines) {\n if (line.includes('webpieces-disable') && line.includes('max-lines-modified-files')) {\n // Found disable comment, now check for date\n // Format: // webpieces-disable max-lines-modified-files yyyy/mm/dd -- reason\n const dateMatch = line.match(/max-lines-modified-files\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - invalid disable comment\n return { hasDisable: true, isValid: false, isExpired: false };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { hasDisable: true, isValid: true, isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format\n return { hasDisable: true, isValid: false, isExpired: false, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { hasDisable: true, isValid: true, isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { hasDisable: true, isValid: true, isExpired: false, date: dateStr };\n }\n }\n\n return { hasDisable: false, isValid: false, isExpired: false };\n}\n\n/**\n * Count lines in a file and check for violations\n */\n// webpieces-disable max-lines-new-methods -- File iteration with disable checking logic\nfunction findViolations(workspaceRoot: string, changedFiles: string[], maxLines: number, mode: ValidationMode): FileViolation[] {\n const violations: FileViolation[] = [];\n\n for (const file of changedFiles) {\n const fullPath = path.join(workspaceRoot, file);\n\n if (!fs.existsSync(fullPath)) continue;\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n const lineCount = content.split('\\n').length;\n\n // Skip files under the limit\n if (lineCount <= maxLines) continue;\n\n // When mode is STRICT, ignore all disable comments\n if (mode === 'STRICT') {\n violations.push({ file, lines: lineCount });\n continue;\n }\n\n // Check for disable comment\n const disableStatus = checkDisableComment(content);\n\n if (disableStatus.hasDisable) {\n if (disableStatus.isValid && !disableStatus.isExpired) {\n // Valid, non-expired disable - skip this file\n continue;\n }\n\n if (disableStatus.isExpired) {\n // Expired disable - report as violation with expired info\n violations.push({\n file,\n lines: lineCount,\n expiredDisable: true,\n expiredDate: disableStatus.date,\n });\n continue;\n }\n\n // Invalid disable (missing/bad date) - fall through to report as violation\n }\n\n violations.push({\n file,\n lines: lineCount,\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 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 {\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 {\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Get today's date in yyyy/mm/dd format for error messages\n */\nfunction getTodayDateString(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n return `${year}/${month}/${day}`;\n}\n\n/**\n * Report violations to console\n */\n// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections\nfunction reportViolations(violations: FileViolation[], maxLines: number, mode: ValidationMode): void {\n console.error('');\n console.error('❌ YOU MUST FIX THIS AND NOT be more than ' + maxLines + ' lines of code per file');\n console.error(' as it slows down IDEs AND is VERY VERY EASY to refactor.');\n console.error('');\n console.error('📚 With stateless systems + dependency injection, refactor is trivial:');\n console.error(' Pick a method or a few and move to new class XXXXX, then inject XXXXX');\n console.error(' into all users of those methods via the constructor.');\n console.error(' Delete those methods from original class.');\n console.error(' 99% of files can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error('⚠️ *** READ tmp/webpieces/webpieces.filesize.md for detailed guidance on how to fix this easily *** ⚠️');\n console.error('');\n\n for (const v of violations) {\n if (v.expiredDisable) {\n console.error(` ❌ ${v.file} (${v.lines} lines, max: ${maxLines})`);\n console.error(` ⏰ EXPIRED DISABLE: Your disable comment dated ${v.expiredDate} has expired (>1 month old).`);\n console.error(` You must either FIX the file or UPDATE the date to get another month.`);\n } else {\n console.error(` ❌ ${v.file} (${v.lines} lines, max: ${maxLines})`);\n }\n }\n console.error('');\n\n // Only show escape hatch instructions when mode is not STRICT\n if (mode !== 'STRICT') {\n console.error(' You can disable this error, but you will be forced to fix again in 1 month');\n console.error(' since 99% of files can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error(' Use escape with DATE (expires in 1 month):');\n console.error(` // webpieces-disable max-lines-modified-files ${getTodayDateString()} -- [your reason]`);\n console.error('');\n } else {\n console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');\n console.error(' You MUST refactor to reduce file size.');\n console.error('');\n }\n}\n\nexport default async function runExecutor(\n options: ValidateModifiedFilesOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 900;\n const mode: ValidationMode = options.mode ?? 'NORMAL';\n\n // Skip validation entirely if mode is OFF\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping modified files validation (validationMode: OFF)');\n console.log('');\n return { success: true };\n }\n\n let base = process.env['NX_BASE'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping modified files validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-modified-files --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Modified File Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating Modified File Sizes\\n');\n }\n\n console.log(` Base: ${base}`);\n console.log(' Comparing to: working tree (includes uncommitted changes)');\n console.log(` Max lines for modified files: ${maxLines}`);\n console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);\n console.log('');\n\n try {\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);\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 const violations = findViolations(workspaceRoot, changedFiles, maxLines, mode);\n\n if (violations.length === 0) {\n console.log('✅ All modified files are under ' + maxLines + ' lines');\n return { success: true };\n }\n\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines, mode);\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Modified files validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -18,9 +18,11 @@ import { execSync } from 'child_process';
|
|
|
18
18
|
import * as fs from 'fs';
|
|
19
19
|
import * as path from 'path';
|
|
20
20
|
|
|
21
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
22
|
+
|
|
21
23
|
export interface ValidateModifiedFilesOptions {
|
|
22
24
|
max?: number;
|
|
23
|
-
|
|
25
|
+
mode?: ValidationMode;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export interface ExecutorResult {
|
|
@@ -328,7 +330,7 @@ function checkDisableComment(content: string): DisableStatus {
|
|
|
328
330
|
* Count lines in a file and check for violations
|
|
329
331
|
*/
|
|
330
332
|
// webpieces-disable max-lines-new-methods -- File iteration with disable checking logic
|
|
331
|
-
function findViolations(workspaceRoot: string, changedFiles: string[], maxLines: number,
|
|
333
|
+
function findViolations(workspaceRoot: string, changedFiles: string[], maxLines: number, mode: ValidationMode): FileViolation[] {
|
|
332
334
|
const violations: FileViolation[] = [];
|
|
333
335
|
|
|
334
336
|
for (const file of changedFiles) {
|
|
@@ -342,8 +344,8 @@ function findViolations(workspaceRoot: string, changedFiles: string[], maxLines:
|
|
|
342
344
|
// Skip files under the limit
|
|
343
345
|
if (lineCount <= maxLines) continue;
|
|
344
346
|
|
|
345
|
-
// When
|
|
346
|
-
if (
|
|
347
|
+
// When mode is STRICT, ignore all disable comments
|
|
348
|
+
if (mode === 'STRICT') {
|
|
347
349
|
violations.push({ file, lines: lineCount });
|
|
348
350
|
continue;
|
|
349
351
|
}
|
|
@@ -427,7 +429,7 @@ function getTodayDateString(): string {
|
|
|
427
429
|
* Report violations to console
|
|
428
430
|
*/
|
|
429
431
|
// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections
|
|
430
|
-
function reportViolations(violations: FileViolation[], maxLines: number,
|
|
432
|
+
function reportViolations(violations: FileViolation[], maxLines: number, mode: ValidationMode): void {
|
|
431
433
|
console.error('');
|
|
432
434
|
console.error('❌ YOU MUST FIX THIS AND NOT be more than ' + maxLines + ' lines of code per file');
|
|
433
435
|
console.error(' as it slows down IDEs AND is VERY VERY EASY to refactor.');
|
|
@@ -452,8 +454,8 @@ function reportViolations(violations: FileViolation[], maxLines: number, forceLi
|
|
|
452
454
|
}
|
|
453
455
|
console.error('');
|
|
454
456
|
|
|
455
|
-
// Only show escape hatch instructions when
|
|
456
|
-
if (
|
|
457
|
+
// Only show escape hatch instructions when mode is not STRICT
|
|
458
|
+
if (mode !== 'STRICT') {
|
|
457
459
|
console.error(' You can disable this error, but you will be forced to fix again in 1 month');
|
|
458
460
|
console.error(' since 99% of files can be less than ' + maxLines + ' lines of code.');
|
|
459
461
|
console.error('');
|
|
@@ -461,7 +463,7 @@ function reportViolations(violations: FileViolation[], maxLines: number, forceLi
|
|
|
461
463
|
console.error(` // webpieces-disable max-lines-modified-files ${getTodayDateString()} -- [your reason]`);
|
|
462
464
|
console.error('');
|
|
463
465
|
} else {
|
|
464
|
-
console.error(' ⚠️
|
|
466
|
+
console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');
|
|
465
467
|
console.error(' You MUST refactor to reduce file size.');
|
|
466
468
|
console.error('');
|
|
467
469
|
}
|
|
@@ -473,7 +475,14 @@ export default async function runExecutor(
|
|
|
473
475
|
): Promise<ExecutorResult> {
|
|
474
476
|
const workspaceRoot = context.root;
|
|
475
477
|
const maxLines = options.max ?? 900;
|
|
476
|
-
const
|
|
478
|
+
const mode: ValidationMode = options.mode ?? 'NORMAL';
|
|
479
|
+
|
|
480
|
+
// Skip validation entirely if mode is OFF
|
|
481
|
+
if (mode === 'OFF') {
|
|
482
|
+
console.log('\n⏭️ Skipping modified files validation (validationMode: OFF)');
|
|
483
|
+
console.log('');
|
|
484
|
+
return { success: true };
|
|
485
|
+
}
|
|
477
486
|
|
|
478
487
|
let base = process.env['NX_BASE'];
|
|
479
488
|
|
|
@@ -495,9 +504,7 @@ export default async function runExecutor(
|
|
|
495
504
|
console.log(` Base: ${base}`);
|
|
496
505
|
console.log(' Comparing to: working tree (includes uncommitted changes)');
|
|
497
506
|
console.log(` Max lines for modified files: ${maxLines}`);
|
|
498
|
-
|
|
499
|
-
console.log(' Force limit: ENABLED (disable comments will be ignored)');
|
|
500
|
-
}
|
|
507
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);
|
|
501
508
|
console.log('');
|
|
502
509
|
|
|
503
510
|
try {
|
|
@@ -510,7 +517,7 @@ export default async function runExecutor(
|
|
|
510
517
|
|
|
511
518
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
512
519
|
|
|
513
|
-
const violations = findViolations(workspaceRoot, changedFiles, maxLines,
|
|
520
|
+
const violations = findViolations(workspaceRoot, changedFiles, maxLines, mode);
|
|
514
521
|
|
|
515
522
|
if (violations.length === 0) {
|
|
516
523
|
console.log('✅ All modified files are under ' + maxLines + ' lines');
|
|
@@ -518,7 +525,7 @@ export default async function runExecutor(
|
|
|
518
525
|
}
|
|
519
526
|
|
|
520
527
|
writeTmpInstructions(workspaceRoot);
|
|
521
|
-
reportViolations(violations, maxLines,
|
|
528
|
+
reportViolations(violations, maxLines, mode);
|
|
522
529
|
return { success: false };
|
|
523
530
|
} catch (err: unknown) {
|
|
524
531
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
"description": "Maximum number of lines allowed for modified files",
|
|
10
10
|
"default": 900
|
|
11
11
|
},
|
|
12
|
-
"
|
|
13
|
-
"type": "
|
|
14
|
-
"
|
|
15
|
-
"
|
|
12
|
+
"mode": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["STRICT", "NORMAL", "OFF"],
|
|
15
|
+
"description": "STRICT: limits enforced, no disable comments. NORMAL: disable comments work. OFF: skip validation.",
|
|
16
|
+
"default": "NORMAL"
|
|
16
17
|
}
|
|
17
18
|
},
|
|
18
19
|
"required": []
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
* The disable expires after 1 month from the date specified.
|
|
20
20
|
*/
|
|
21
21
|
import type { ExecutorContext } from '@nx/devkit';
|
|
22
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
22
23
|
export interface ValidateModifiedMethodsOptions {
|
|
23
24
|
max?: number;
|
|
25
|
+
mode?: ValidationMode;
|
|
24
26
|
}
|
|
25
27
|
export interface ExecutorResult {
|
|
26
28
|
success: boolean;
|
|
@@ -428,111 +428,80 @@ function findMethodsInFile(filePath, workspaceRoot) {
|
|
|
428
428
|
visit(sourceFile);
|
|
429
429
|
return methods;
|
|
430
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Check if a method has any changes within its line range
|
|
433
|
+
*/
|
|
434
|
+
function methodHasChanges(method, changedLineNumbers) {
|
|
435
|
+
for (let line = method.line; line <= method.endLine; line++) {
|
|
436
|
+
if (changedLineNumbers.has(line))
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Check a NEW method and return violation if applicable
|
|
443
|
+
*/
|
|
444
|
+
function checkNewMethodViolation(file, method, mode) {
|
|
445
|
+
const { type: disableType, isExpired, date: disableDate } = method.disableInfo;
|
|
446
|
+
if (mode === 'STRICT') {
|
|
447
|
+
// When mode is STRICT, skip NEW methods without escape (let validate-new-methods handle)
|
|
448
|
+
if (disableType === 'none')
|
|
449
|
+
return null;
|
|
450
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
451
|
+
}
|
|
452
|
+
if (disableType === 'full' && isExpired) {
|
|
453
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
454
|
+
}
|
|
455
|
+
if (disableType !== 'new-only')
|
|
456
|
+
return null;
|
|
457
|
+
if (isExpired) {
|
|
458
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
459
|
+
}
|
|
460
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Check a MODIFIED method and return violation if applicable
|
|
464
|
+
*/
|
|
465
|
+
function checkModifiedMethodViolation(file, method, mode) {
|
|
466
|
+
const { type: disableType, isExpired, date: disableDate } = method.disableInfo;
|
|
467
|
+
if (mode === 'STRICT') {
|
|
468
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
469
|
+
}
|
|
470
|
+
if (disableType === 'full' && isExpired) {
|
|
471
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
472
|
+
}
|
|
473
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
474
|
+
}
|
|
431
475
|
/**
|
|
432
476
|
* Find methods that exceed the 80-line limit.
|
|
433
|
-
*
|
|
434
|
-
* This validator checks:
|
|
435
|
-
* 1. NEW methods that have `max-lines-new-methods` escape (they passed 30-line check, now need 80-line check)
|
|
436
|
-
* 2. MODIFIED methods (existing methods with changes)
|
|
437
|
-
*
|
|
438
|
-
* Skips:
|
|
439
|
-
* - NEW methods without any escape (let validate-new-methods handle them first)
|
|
440
|
-
* - Methods with valid, non-expired `max-lines-modified` escape (ultimate escape hatch)
|
|
477
|
+
* Checks NEW methods with escape hatches and MODIFIED methods.
|
|
441
478
|
*/
|
|
442
|
-
|
|
443
|
-
function findViolations(workspaceRoot, changedFiles, base, maxLines) {
|
|
479
|
+
function findViolations(workspaceRoot, changedFiles, base, maxLines, mode) {
|
|
444
480
|
const violations = [];
|
|
445
481
|
for (const file of changedFiles) {
|
|
446
482
|
const diff = getFileDiff(workspaceRoot, file, base);
|
|
447
483
|
if (!diff)
|
|
448
484
|
continue;
|
|
449
|
-
// Find NEW methods from the diff
|
|
450
485
|
const newMethodNames = findNewMethodSignaturesInDiff(diff);
|
|
451
|
-
// Find which lines have changes
|
|
452
486
|
const changedLineNumbers = getChangedLineNumbers(diff);
|
|
453
487
|
if (changedLineNumbers.size === 0)
|
|
454
488
|
continue;
|
|
455
|
-
// Parse the current file to get all methods
|
|
456
489
|
const methods = findMethodsInFile(file, workspaceRoot);
|
|
457
490
|
for (const method of methods) {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
if (disableType === 'full' && !isExpired)
|
|
491
|
+
const { type: disableType, isExpired } = method.disableInfo;
|
|
492
|
+
// Skip methods with valid, non-expired full escape - unless mode is STRICT
|
|
493
|
+
if (mode !== 'STRICT' && disableType === 'full' && !isExpired)
|
|
462
494
|
continue;
|
|
463
|
-
// Skip methods under the limit
|
|
464
495
|
if (method.lines <= maxLines)
|
|
465
496
|
continue;
|
|
497
|
+
const isNewMethod = newMethodNames.has(method.name);
|
|
466
498
|
if (isNewMethod) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
// - If has expired disable → report as violation
|
|
471
|
-
if (disableType === 'full' && isExpired) {
|
|
472
|
-
// Expired full disable - report with expired info
|
|
473
|
-
violations.push({
|
|
474
|
-
file,
|
|
475
|
-
methodName: method.name,
|
|
476
|
-
line: method.line,
|
|
477
|
-
lines: method.lines,
|
|
478
|
-
expiredDisable: true,
|
|
479
|
-
expiredDate: disableDate,
|
|
480
|
-
});
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (disableType !== 'new-only')
|
|
484
|
-
continue;
|
|
485
|
-
if (isExpired) {
|
|
486
|
-
// Expired new-only disable - report with expired info
|
|
487
|
-
violations.push({
|
|
488
|
-
file,
|
|
489
|
-
methodName: method.name,
|
|
490
|
-
line: method.line,
|
|
491
|
-
lines: method.lines,
|
|
492
|
-
expiredDisable: true,
|
|
493
|
-
expiredDate: disableDate,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
else {
|
|
497
|
-
// New method with valid max-lines-new-methods escape - check against 80-line limit
|
|
498
|
-
violations.push({
|
|
499
|
-
file,
|
|
500
|
-
methodName: method.name,
|
|
501
|
-
line: method.line,
|
|
502
|
-
lines: method.lines,
|
|
503
|
-
});
|
|
504
|
-
}
|
|
499
|
+
const violation = checkNewMethodViolation(file, method, mode);
|
|
500
|
+
if (violation)
|
|
501
|
+
violations.push(violation);
|
|
505
502
|
}
|
|
506
|
-
else {
|
|
507
|
-
|
|
508
|
-
let hasChanges = false;
|
|
509
|
-
for (let line = method.line; line <= method.endLine; line++) {
|
|
510
|
-
if (changedLineNumbers.has(line)) {
|
|
511
|
-
hasChanges = true;
|
|
512
|
-
break;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
if (hasChanges) {
|
|
516
|
-
if (disableType === 'full' && isExpired) {
|
|
517
|
-
// Expired full disable - report with expired info
|
|
518
|
-
violations.push({
|
|
519
|
-
file,
|
|
520
|
-
methodName: method.name,
|
|
521
|
-
line: method.line,
|
|
522
|
-
lines: method.lines,
|
|
523
|
-
expiredDisable: true,
|
|
524
|
-
expiredDate: disableDate,
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
violations.push({
|
|
529
|
-
file,
|
|
530
|
-
methodName: method.name,
|
|
531
|
-
line: method.line,
|
|
532
|
-
lines: method.lines,
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
503
|
+
else if (methodHasChanges(method, changedLineNumbers)) {
|
|
504
|
+
violations.push(checkModifiedMethodViolation(file, method, mode));
|
|
536
505
|
}
|
|
537
506
|
}
|
|
538
507
|
}
|
|
@@ -573,7 +542,7 @@ function detectBase(workspaceRoot) {
|
|
|
573
542
|
* Report violations to console
|
|
574
543
|
*/
|
|
575
544
|
// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections
|
|
576
|
-
function reportViolations(violations, maxLines) {
|
|
545
|
+
function reportViolations(violations, maxLines, mode) {
|
|
577
546
|
console.error('');
|
|
578
547
|
console.error('❌ Modified methods exceed ' + maxLines + ' lines!');
|
|
579
548
|
console.error('');
|
|
@@ -598,16 +567,31 @@ function reportViolations(violations, maxLines) {
|
|
|
598
567
|
}
|
|
599
568
|
}
|
|
600
569
|
console.error('');
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
570
|
+
// Only show escape hatch instructions when mode is not STRICT
|
|
571
|
+
if (mode !== 'STRICT') {
|
|
572
|
+
console.error(' You can disable this error, but you will be forced to fix again in 1 month');
|
|
573
|
+
console.error(' since 99% of methods can be less than ' + maxLines + ' lines of code.');
|
|
574
|
+
console.error('');
|
|
575
|
+
console.error(' Use escape with DATE (expires in 1 month):');
|
|
576
|
+
console.error(` // webpieces-disable max-lines-modified ${getTodayDateString()} -- [your reason]`);
|
|
577
|
+
console.error('');
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');
|
|
581
|
+
console.error(' You MUST refactor to reduce method size.');
|
|
582
|
+
console.error('');
|
|
583
|
+
}
|
|
607
584
|
}
|
|
608
585
|
async function runExecutor(options, context) {
|
|
609
586
|
const workspaceRoot = context.root;
|
|
610
587
|
const maxLines = options.max ?? 80;
|
|
588
|
+
const mode = options.mode ?? 'NORMAL';
|
|
589
|
+
// Skip validation entirely if mode is OFF
|
|
590
|
+
if (mode === 'OFF') {
|
|
591
|
+
console.log('\n⏭️ Skipping modified method validation (validationMode: OFF)');
|
|
592
|
+
console.log('');
|
|
593
|
+
return { success: true };
|
|
594
|
+
}
|
|
611
595
|
let base = process.env['NX_BASE'];
|
|
612
596
|
if (!base) {
|
|
613
597
|
base = detectBase(workspaceRoot) ?? undefined;
|
|
@@ -625,6 +609,7 @@ async function runExecutor(options, context) {
|
|
|
625
609
|
console.log(` Base: ${base}`);
|
|
626
610
|
console.log(' Comparing to: working tree (includes uncommitted changes)');
|
|
627
611
|
console.log(` Max lines for modified methods: ${maxLines}`);
|
|
612
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);
|
|
628
613
|
console.log('');
|
|
629
614
|
try {
|
|
630
615
|
const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);
|
|
@@ -633,13 +618,13 @@ async function runExecutor(options, context) {
|
|
|
633
618
|
return { success: true };
|
|
634
619
|
}
|
|
635
620
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
636
|
-
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);
|
|
621
|
+
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, mode);
|
|
637
622
|
if (violations.length === 0) {
|
|
638
623
|
console.log('✅ All modified methods are under ' + maxLines + ' lines');
|
|
639
624
|
return { success: true };
|
|
640
625
|
}
|
|
641
626
|
writeTmpInstructions(workspaceRoot);
|
|
642
|
-
reportViolations(violations, maxLines);
|
|
627
|
+
reportViolations(violations, maxLines, mode);
|
|
643
628
|
return { success: false };
|
|
644
629
|
}
|
|
645
630
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-modified-methods/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Modified Methods Executor\n *\n * Validates that modified methods don't exceed a maximum line count (default 80).\n * This encourages gradual cleanup of legacy long methods - when you touch a method,\n * you must bring it under the limit.\n *\n * Combined with validate-new-methods (30 line limit), this creates a gradual\n * transition to cleaner code:\n * - New methods: strict 30 line limit\n * - Modified methods: lenient 80 line limit (cleanup when touched)\n * - Untouched methods: no limit (legacy allowed)\n *\n * Usage:\n * nx affected --target=validate-modified-methods --base=origin/main\n *\n * Escape hatch: Add webpieces-disable max-lines-modified comment with date and justification\n * Format: // webpieces-disable max-lines-modified 2025/01/15 -- [reason]\n * The disable expires after 1 month from the date specified.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\n\nexport interface ValidateModifiedMethodsOptions {\n max?: number;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface MethodViolation {\n file: string;\n methodName: string;\n line: number;\n lines: number;\n expiredDisable?: boolean;\n expiredDate?: string;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.methodsize.md';\n\nconst METHODSIZE_DOC_CONTENT = `# Instructions: Method Too Long\n\n## Requirement\n\n**~50% of the time**, you can stay under the \\`newMethodsMaxLines\\` limit from nx.json\nby extracting logical units into well-named methods.\n\n**~99% of the time**, you can stay under the \\`modifiedAndNewMethodsMaxLines\\` limit from nx.json.\nNearly all software can be written with methods under this size.\nTake the extra time to refactor - it's worth it for long-term maintainability.\n\n## The \"Table of Contents\" Principle\n\nGood code reads like a book's table of contents:\n- Chapter titles (method names) tell you WHAT happens\n- Reading chapter titles gives you the full story\n- You can dive into chapters (implementations) for details\n\n## Why Limit Method Sizes?\n\nMethods under reasonable limits are:\n- Easy to review in a single screen\n- Simple to understand without scrolling\n- Quick for AI to analyze and suggest improvements\n- More testable in isolation\n- Self-documenting through well-named extracted methods\n\n## Gradual Cleanup Strategy\n\nThis codebase uses a gradual cleanup approach:\n- **New methods**: Must be under \\`newMethodsMaxLines\\` from nx.json\n- **Modified methods**: Must be under \\`modifiedAndNewMethodsMaxLines\\` from nx.json\n- **Untouched methods**: No limit (legacy code is allowed until touched)\n\n## How to Refactor\n\nInstead of:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n // 100 lines of validation, transformation, saving, notifications...\n}\n\\`\\`\\`\n\nWrite:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n const validated = this.validateOrder(order);\n const transformed = this.applyBusinessRules(validated);\n const saved = await this.saveToDatabase(transformed);\n await this.notifyStakeholders(saved);\n return this.buildResult(saved);\n}\n\\`\\`\\`\n\nNow the main method is a \"table of contents\" - each line tells part of the story!\n\n## Patterns for Extraction\n\n### Pattern 1: Extract Loop Bodies\n\\`\\`\\`typescript\n// BEFORE\nfor (const item of items) {\n // 20 lines of processing\n}\n\n// AFTER\nfor (const item of items) {\n this.processItem(item);\n}\n\\`\\`\\`\n\n### Pattern 2: Extract Conditional Blocks\n\\`\\`\\`typescript\n// BEFORE\nif (isAdmin(user)) {\n // 15 lines of admin logic\n}\n\n// AFTER\nif (isAdmin(user)) {\n this.handleAdminUser(user);\n}\n\\`\\`\\`\n\n### Pattern 3: Extract Data Transformations\n\\`\\`\\`typescript\n// BEFORE\nconst result = {\n // 10+ lines of object construction\n};\n\n// AFTER\nconst result = this.buildResultObject(data);\n\\`\\`\\`\n\n## If Refactoring Is Not Feasible\n\nSometimes methods genuinely need to be longer (complex algorithms, state machines, etc.).\n\n**Escape hatch**: Add a webpieces-disable comment with DATE and justification:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-modified 2025/01/15 -- Complex state machine, splitting reduces clarity\nasync complexStateMachine(): Promise<void> {\n // ... longer method with justification\n}\n\\`\\`\\`\n\n**IMPORTANT**: The date format is yyyy/mm/dd. The disable will EXPIRE after 1 month from this date.\nAfter expiration, you must either fix the method or update the date to get another month.\nThis ensures that disable comments are reviewed periodically.\n\n## AI Agent Action Steps\n\n1. **READ** the method to understand its logical sections\n2. **IDENTIFY** logical units that can be extracted\n3. **EXTRACT** into well-named private methods\n4. **VERIFY** the main method now reads like a table of contents\n5. **IF NOT FEASIBLE**: Add webpieces-disable max-lines-modified comment with clear justification\n\n## Remember\n\n- Every method you write today will be read many times tomorrow\n- The best code explains itself through structure\n- When in doubt, extract and name it\n`;\n\n/**\n * Write the instructions documentation to tmp directory\n */\nfunction writeTmpInstructions(workspaceRoot: string): string {\n const tmpDir = path.join(workspaceRoot, TMP_DIR);\n const mdPath = path.join(tmpDir, TMP_MD_FILE);\n\n fs.mkdirSync(tmpDir, { recursive: true });\n fs.writeFileSync(mdPath, METHODSIZE_DOC_CONTENT);\n\n return mdPath;\n}\n\n/**\n * Get changed TypeScript files between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string): string[] {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n const output = execSync(`git diff --name-only ${base} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n return output\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n } catch {\n return [];\n }\n}\n\n/**\n * Get the diff content for a specific file between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getFileDiff(workspaceRoot: string, file: string, base: string): string {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n return execSync(`git diff ${base} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n } catch {\n return '';\n }\n}\n\n/**\n * Parse diff to find NEW method signatures.\n * Must handle: export function, async function, const/let arrow functions, class methods\n */\n// webpieces-disable max-lines-new-methods -- Regex patterns require inline documentation\nfunction findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n // Patterns to match method definitions (same as validate-new-methods)\n const patterns = [\n // [export] [async] function methodName( - most explicit, check first\n /^\\+\\s*(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(/,\n // [export] const/let methodName = [async] (\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/,\n // [export] const/let methodName = [async] function\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?function/,\n // class method: [async] methodName( - but NOT constructor, if, for, while, etc.\n /^\\+\\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 // Extract method name - now always in capture group 1\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 * Parse diff to find line numbers that have changes in the new file\n */\nfunction getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n\n let currentNewLine = 0;\n\n for (const line of lines) {\n // Parse hunk header: @@ -oldStart,oldCount +newStart,newCount @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentNewLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (currentNewLine === 0) continue;\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n // Added line\n changedLines.add(currentNewLine);\n currentNewLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Removed line - doesn't increment new line counter\n } else if (!line.startsWith('\\\\')) {\n // Context line (unchanged)\n currentNewLine++;\n }\n }\n\n return changedLines;\n}\n\n/**\n * Parse a date string in yyyy/mm/dd format and return a Date object.\n * Returns null if the format is invalid.\n */\nfunction parseDisableDate(dateStr: string): Date | null {\n // Match yyyy/mm/dd format\n const match = dateStr.match(/^(\\d{4})\\/(\\d{2})\\/(\\d{2})$/);\n if (!match) return null;\n\n const year = parseInt(match[1], 10);\n const month = parseInt(match[2], 10) - 1; // JS months are 0-indexed\n const day = parseInt(match[3], 10);\n\n const date = new Date(year, month, day);\n\n // Validate the date is valid (e.g., not Feb 30)\n if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Check if a date is within the last month (not expired).\n */\nfunction isDateWithinMonth(date: Date): boolean {\n const now = new Date();\n const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());\n return date >= oneMonthAgo;\n}\n\n/**\n * Get today's date in yyyy/mm/dd format for error messages\n */\nfunction getTodayDateString(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n return `${year}/${month}/${day}`;\n}\n\ninterface DisableInfo {\n type: 'full' | 'new-only' | 'none';\n isExpired: boolean;\n date?: string;\n}\n\n/**\n * Check what kind of webpieces-disable comment is present for a method.\n * Returns: DisableInfo with type, expiration status, and date\n * - 'full': max-lines-modified (ultimate escape, skips both validators)\n * - 'new-only': max-lines-new-methods (escaped 30-line check, still needs 80-line check)\n * - 'none': no escape hatch\n */\n// webpieces-disable max-lines-new-methods -- Complex validation logic with multiple escape hatch types\nfunction getDisableInfo(lines: string[], lineNumber: number): DisableInfo {\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')) {\n if (line.includes('max-lines-modified')) {\n // Check for date in format: max-lines-modified yyyy/mm/dd\n const dateMatch = line.match(/max-lines-modified\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - treat as expired (invalid)\n return { type: 'full', isExpired: true, date: undefined };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { type: 'full', isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format - treat as expired\n return { type: 'full', isExpired: true, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { type: 'full', isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { type: 'full', isExpired: false, date: dateStr };\n }\n if (line.includes('max-lines-new-methods')) {\n // Check for date in format: max-lines-new-methods yyyy/mm/dd\n const dateMatch = line.match(/max-lines-new-methods\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - treat as expired (invalid)\n return { type: 'new-only', isExpired: true, date: undefined };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { type: 'new-only', isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format - treat as expired\n return { type: 'new-only', isExpired: true, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { type: 'new-only', isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { type: 'new-only', isExpired: false, date: dateStr };\n }\n }\n }\n return { type: 'none', isExpired: false };\n}\n\ninterface MethodInfo {\n name: string;\n line: number;\n endLine: number;\n lines: number;\n disableInfo: DisableInfo;\n}\n\n/**\n * Parse a TypeScript file and find methods with their line counts\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\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 } 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 } 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 }\n }\n\n if (methodName && startLine !== undefined && endLine !== undefined) {\n methods.push({\n name: methodName,\n line: startLine,\n endLine: endLine,\n lines: endLine - startLine + 1,\n disableInfo: getDisableInfo(fileLines, startLine),\n });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return methods;\n}\n\n/**\n * Find methods that exceed the 80-line limit.\n *\n * This validator checks:\n * 1. NEW methods that have `max-lines-new-methods` escape (they passed 30-line check, now need 80-line check)\n * 2. MODIFIED methods (existing methods with changes)\n *\n * Skips:\n * - NEW methods without any escape (let validate-new-methods handle them first)\n * - Methods with valid, non-expired `max-lines-modified` escape (ultimate escape hatch)\n */\n// webpieces-disable max-lines-modified 2025/12/20 -- Core validation logic with multiple file operations\nfunction findViolations(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n maxLines: number\n): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base);\n if (!diff) continue;\n\n // Find NEW methods from the diff\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n\n // Find which lines have changes\n const changedLineNumbers = getChangedLineNumbers(diff);\n if (changedLineNumbers.size === 0) continue;\n\n // Parse the current file to get all methods\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n const isNewMethod = newMethodNames.has(method.name);\n const { type: disableType, isExpired, date: disableDate } = method.disableInfo;\n\n // Skip methods with valid, non-expired full escape (max-lines-modified)\n if (disableType === 'full' && !isExpired) continue;\n\n // Skip methods under the limit\n if (method.lines <= maxLines) continue;\n\n if (isNewMethod) {\n // For NEW methods:\n // - If has 'new-only' escape (non-expired) → check (they escaped 30-line, now need 80-line check)\n // - If has 'none' → skip (let validate-new-methods handle first)\n // - If has expired disable → report as violation\n if (disableType === 'full' && isExpired) {\n // Expired full disable - report with expired info\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n expiredDisable: true,\n expiredDate: disableDate,\n });\n continue;\n }\n if (disableType !== 'new-only') continue;\n\n if (isExpired) {\n // Expired new-only disable - report with expired info\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n expiredDisable: true,\n expiredDate: disableDate,\n });\n } else {\n // New method with valid max-lines-new-methods escape - check against 80-line limit\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n });\n }\n } else {\n // For MODIFIED methods: check if any changed line falls within method's range\n let hasChanges = false;\n for (let line = method.line; line <= method.endLine; line++) {\n if (changedLineNumbers.has(line)) {\n hasChanges = true;\n break;\n }\n }\n\n if (hasChanges) {\n if (disableType === 'full' && isExpired) {\n // Expired full disable - report with expired info\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n expiredDisable: true,\n expiredDate: disableDate,\n });\n } else {\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n });\n }\n }\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 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 {\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 {\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Report violations to console\n */\n// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections\nfunction reportViolations(violations: MethodViolation[], maxLines: number): void {\n console.error('');\n console.error('❌ Modified methods exceed ' + maxLines + ' lines!');\n console.error('');\n console.error('📚 When you modify a method, you must bring it under ' + maxLines + ' lines.');\n console.error(' This rule encourages GRADUAL cleanup so even though you did not cause it,');\n console.error(' you touched it, so you should fix now as part of your PR');\n console.error(' (this is for vibe coding and AI to fix as it touches things).');\n console.error(' You can refactor to stay under the limit 50% of the time. If not feasible, use the escape hatch.');\n console.error('');\n console.error(\n '⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️'\n );\n console.error('');\n\n for (const v of violations) {\n if (v.expiredDisable) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${maxLines})`);\n console.error(` ⏰ EXPIRED DISABLE: Your disable comment dated ${v.expiredDate ?? 'unknown'} has expired (>1 month old).`);\n console.error(` You must either FIX the method or UPDATE the date to get another month.`);\n } else {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${maxLines})`);\n }\n }\n console.error('');\n\n console.error(' You can disable this error, but you will be forced to fix again in 1 month');\n console.error(' since 99% of methods can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error(' Use escape with DATE (expires in 1 month):');\n console.error(` // webpieces-disable max-lines-modified ${getTodayDateString()} -- [your reason]`);\n console.error('');\n}\n\nexport default async function runExecutor(\n options: ValidateModifiedMethodsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 80;\n\n let base = process.env['NX_BASE'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping modified method validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-modified-methods --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Modified Method Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating Modified Method Sizes\\n');\n }\n\n console.log(` Base: ${base}`);\n console.log(' Comparing to: working tree (includes uncommitted changes)');\n console.log(` Max lines for modified methods: ${maxLines}`);\n console.log('');\n\n try {\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);\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 const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);\n\n if (violations.length === 0) {\n console.log('✅ All modified methods are under ' + maxLines + ' lines');\n return { success: true };\n }\n\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines);\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Modified method validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-modified-methods/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;AA2oBH,8BA+DC;;AAvsBD,iDAAyC;AACzC,+CAAyB;AACzB,mDAA6B;AAC7B,uDAAiC;AAsBjC,MAAM,OAAO,GAAG,eAAe,CAAC;AAChC,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAE9C,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6H9B,CAAC;AAEF;;GAEG;AACH,SAAS,oBAAoB,CAAC,aAAqB;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAE9C,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAEjD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,yBAAyB,CAAC,aAAqB,EAAE,IAAY;IAClE,IAAI,CAAC;QACD,gEAAgE;QAChE,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,wBAAwB,IAAI,oBAAoB,EAAE;YACtE,GAAG,EAAE,aAAa;YAClB,QAAQ,EAAE,OAAO;SACpB,CAAC,CAAC;QACH,OAAO,MAAM;aACR,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;IAChF,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,aAAqB,EAAE,IAAY,EAAE,IAAY;IAClE,IAAI,CAAC;QACD,gEAAgE;QAChE,OAAO,IAAA,wBAAQ,EAAC,YAAY,IAAI,QAAQ,IAAI,GAAG,EAAE;YAC7C,GAAG,EAAE,aAAa;YAClB,QAAQ,EAAE,OAAO;SACpB,CAAC,CAAC;IACP,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,yFAAyF;AACzF,SAAS,6BAA6B,CAAC,WAAmB;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEtC,sEAAsE;IACtE,MAAM,QAAQ,GAAG;QACb,qEAAqE;QACrE,wDAAwD;QACxD,4CAA4C;QAC5C,iEAAiE;QACjE,mDAAmD;QACnD,uEAAuE;QACvE,gFAAgF;QAChF,+BAA+B;KAClC,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,sDAAsD;oBACtD,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,qBAAqB,CAAC,WAAmB;IAC9C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,iEAAiE;QACjE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACtE,IAAI,SAAS,EAAE,CAAC;YACZ,cAAc,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,SAAS;QACb,CAAC;QAED,IAAI,cAAc,KAAK,CAAC;YAAE,SAAS;QAEnC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,aAAa;YACb,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACjC,cAAc,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,oDAAoD;QACxD,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,2BAA2B;YAC3B,cAAc,EAAE,CAAC;QACrB,CAAC;IACL,CAAC;IAED,OAAO,YAAY,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,OAAe;IACrC,0BAA0B;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,0BAA0B;IACpE,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEnC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAExC,gDAAgD;IAChD,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,EAAE,CAAC;QACrF,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAU;IACjC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACnF,OAAO,IAAI,IAAI,WAAW,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB;IACvB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;AACrC,CAAC;AAQD;;;;;;GAMG;AACH,uGAAuG;AACvG,SAAS,cAAc,CAAC,KAAe,EAAE,UAAkB;IACvD,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,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBACtC,0DAA0D;gBAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;gBAExF,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,6CAA6C;oBAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBAC9D,CAAC;gBAED,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAE7B,2BAA2B;gBAC3B,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;oBAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAC7D,CAAC;gBAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBACvC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACR,yCAAyC;oBACzC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAC5D,CAAC;gBAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3B,uCAAuC;oBACvC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAC5D,CAAC;gBAED,wBAAwB;gBACxB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC7D,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBACzC,6DAA6D;gBAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;gBAE3F,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,6CAA6C;oBAC7C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBAClE,CAAC;gBAED,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAE7B,2BAA2B;gBAC3B,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;oBAC3B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACjE,CAAC;gBAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBACvC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACR,yCAAyC;oBACzC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAChE,CAAC;gBAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3B,uCAAuC;oBACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAChE,CAAC;gBAED,wBAAwB;gBACxB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACjE,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC9C,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;QAEhC,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;QAC3B,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;QAC3B,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;YAC3B,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,EAAE,OAAO;gBAChB,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,CAAC;gBAC9B,WAAW,EAAE,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC;aACpD,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,gBAAgB,CAAC,MAAkB,EAAE,kBAA+B;IACzE,KAAK,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC1D,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,IAAY,EAAE,MAAkB,EAAE,IAAoB;IACnF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;IAE/E,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpB,yFAAyF;QACzF,IAAI,WAAW,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QACxC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACrF,CAAC;IAED,IAAI,WAAW,KAAK,MAAM,IAAI,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IACrI,CAAC;IACD,IAAI,WAAW,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAE5C,IAAI,SAAS,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IACrI,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,SAAS,4BAA4B,CAAC,IAAY,EAAE,MAAkB,EAAE,IAAoB;IACxF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;IAE/E,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACrF,CAAC;IACD,IAAI,WAAW,KAAK,MAAM,IAAI,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IACrI,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;AACrF,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CACnB,aAAqB,EACrB,YAAsB,EACtB,IAAY,EACZ,QAAgB,EAChB,IAAoB;IAEpB,MAAM,UAAU,GAAsB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,MAAM,cAAc,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,kBAAkB,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAE5C,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEvD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;YAE5D,2EAA2E;YAC3E,IAAI,IAAI,KAAK,QAAQ,IAAI,WAAW,KAAK,MAAM,IAAI,CAAC,SAAS;gBAAE,SAAS;YACxE,IAAI,MAAM,CAAC,KAAK,IAAI,QAAQ;gBAAE,SAAS;YAEvC,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAEpD,IAAI,WAAW,EAAE,CAAC;gBACd,MAAM,SAAS,GAAG,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC9D,IAAI,SAAS;oBAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAAC;gBACtD,UAAU,CAAC,IAAI,CAAC,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YACtE,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,aAAqB;IACrC,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,MAAM,CAAC;QACL,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,MAAM,CAAC;YACL,SAAS;QACb,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,oGAAoG;AACpG,SAAS,gBAAgB,CAAC,UAA6B,EAAE,QAAgB,EAAE,IAAoB;IAC3F,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,uDAAuD,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC9F,OAAO,CAAC,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAC9F,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;IAC7E,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAClF,OAAO,CAAC,KAAK,CAAC,qGAAqG,CAAC,CAAC;IACrH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CACT,2GAA2G,CAC9G,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElB,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,KAAK,gBAAgB,QAAQ,GAAG,CAAC,CAAC;YACnF,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC,WAAW,IAAI,SAAS,8BAA8B,CAAC,CAAC;YAC9H,OAAO,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;QACrG,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,KAAK,gBAAgB,QAAQ,GAAG,CAAC,CAAC;QACvF,CAAC;IACL,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElB,8DAA8D;IAC9D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,+EAA+E,CAAC,CAAC;QAC/F,OAAO,CAAC,KAAK,CAAC,2CAA2C,GAAG,QAAQ,GAAG,iBAAiB,CAAC,CAAC;QAC1F,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC/D,OAAO,CAAC,KAAK,CAAC,8CAA8C,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;QACrG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACrF,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;AACL,CAAC;AAEc,KAAK,UAAU,WAAW,CACrC,OAAuC,EACvC,OAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;IACnC,MAAM,IAAI,GAAmB,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;IAEtD,0CAA0C;IAC1C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAElC,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,0EAA0E,CAAC,CAAC;YACxF,OAAO,CAAC,GAAG,CAAC,yFAAyF,CAAC,CAAC;YACvG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAChF,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,CAAC;QACD,MAAM,YAAY,GAAG,yBAAyB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAEpE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;YAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,eAAe,YAAY,CAAC,MAAM,qBAAqB,CAAC,CAAC;QAErE,MAAM,UAAU,GAAG,cAAc,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAErF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,mCAAmC,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACpC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACrE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Validate Modified Methods Executor\n *\n * Validates that modified methods don't exceed a maximum line count (default 80).\n * This encourages gradual cleanup of legacy long methods - when you touch a method,\n * you must bring it under the limit.\n *\n * Combined with validate-new-methods (30 line limit), this creates a gradual\n * transition to cleaner code:\n * - New methods: strict 30 line limit\n * - Modified methods: lenient 80 line limit (cleanup when touched)\n * - Untouched methods: no limit (legacy allowed)\n *\n * Usage:\n * nx affected --target=validate-modified-methods --base=origin/main\n *\n * Escape hatch: Add webpieces-disable max-lines-modified comment with date and justification\n * Format: // webpieces-disable max-lines-modified 2025/01/15 -- [reason]\n * The disable expires after 1 month from the date specified.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\n\nexport type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';\n\nexport interface ValidateModifiedMethodsOptions {\n max?: number;\n mode?: ValidationMode;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface MethodViolation {\n file: string;\n methodName: string;\n line: number;\n lines: number;\n expiredDisable?: boolean;\n expiredDate?: string;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.methodsize.md';\n\nconst METHODSIZE_DOC_CONTENT = `# Instructions: Method Too Long\n\n## Requirement\n\n**~50% of the time**, you can stay under the \\`newMethodsMaxLines\\` limit from nx.json\nby extracting logical units into well-named methods.\n\n**~99% of the time**, you can stay under the \\`modifiedAndNewMethodsMaxLines\\` limit from nx.json.\nNearly all software can be written with methods under this size.\nTake the extra time to refactor - it's worth it for long-term maintainability.\n\n## The \"Table of Contents\" Principle\n\nGood code reads like a book's table of contents:\n- Chapter titles (method names) tell you WHAT happens\n- Reading chapter titles gives you the full story\n- You can dive into chapters (implementations) for details\n\n## Why Limit Method Sizes?\n\nMethods under reasonable limits are:\n- Easy to review in a single screen\n- Simple to understand without scrolling\n- Quick for AI to analyze and suggest improvements\n- More testable in isolation\n- Self-documenting through well-named extracted methods\n\n## Gradual Cleanup Strategy\n\nThis codebase uses a gradual cleanup approach:\n- **New methods**: Must be under \\`newMethodsMaxLines\\` from nx.json\n- **Modified methods**: Must be under \\`modifiedAndNewMethodsMaxLines\\` from nx.json\n- **Untouched methods**: No limit (legacy code is allowed until touched)\n\n## How to Refactor\n\nInstead of:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n // 100 lines of validation, transformation, saving, notifications...\n}\n\\`\\`\\`\n\nWrite:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n const validated = this.validateOrder(order);\n const transformed = this.applyBusinessRules(validated);\n const saved = await this.saveToDatabase(transformed);\n await this.notifyStakeholders(saved);\n return this.buildResult(saved);\n}\n\\`\\`\\`\n\nNow the main method is a \"table of contents\" - each line tells part of the story!\n\n## Patterns for Extraction\n\n### Pattern 1: Extract Loop Bodies\n\\`\\`\\`typescript\n// BEFORE\nfor (const item of items) {\n // 20 lines of processing\n}\n\n// AFTER\nfor (const item of items) {\n this.processItem(item);\n}\n\\`\\`\\`\n\n### Pattern 2: Extract Conditional Blocks\n\\`\\`\\`typescript\n// BEFORE\nif (isAdmin(user)) {\n // 15 lines of admin logic\n}\n\n// AFTER\nif (isAdmin(user)) {\n this.handleAdminUser(user);\n}\n\\`\\`\\`\n\n### Pattern 3: Extract Data Transformations\n\\`\\`\\`typescript\n// BEFORE\nconst result = {\n // 10+ lines of object construction\n};\n\n// AFTER\nconst result = this.buildResultObject(data);\n\\`\\`\\`\n\n## If Refactoring Is Not Feasible\n\nSometimes methods genuinely need to be longer (complex algorithms, state machines, etc.).\n\n**Escape hatch**: Add a webpieces-disable comment with DATE and justification:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-modified 2025/01/15 -- Complex state machine, splitting reduces clarity\nasync complexStateMachine(): Promise<void> {\n // ... longer method with justification\n}\n\\`\\`\\`\n\n**IMPORTANT**: The date format is yyyy/mm/dd. The disable will EXPIRE after 1 month from this date.\nAfter expiration, you must either fix the method or update the date to get another month.\nThis ensures that disable comments are reviewed periodically.\n\n## AI Agent Action Steps\n\n1. **READ** the method to understand its logical sections\n2. **IDENTIFY** logical units that can be extracted\n3. **EXTRACT** into well-named private methods\n4. **VERIFY** the main method now reads like a table of contents\n5. **IF NOT FEASIBLE**: Add webpieces-disable max-lines-modified comment with clear justification\n\n## Remember\n\n- Every method you write today will be read many times tomorrow\n- The best code explains itself through structure\n- When in doubt, extract and name it\n`;\n\n/**\n * Write the instructions documentation to tmp directory\n */\nfunction writeTmpInstructions(workspaceRoot: string): string {\n const tmpDir = path.join(workspaceRoot, TMP_DIR);\n const mdPath = path.join(tmpDir, TMP_MD_FILE);\n\n fs.mkdirSync(tmpDir, { recursive: true });\n fs.writeFileSync(mdPath, METHODSIZE_DOC_CONTENT);\n\n return mdPath;\n}\n\n/**\n * Get changed TypeScript files between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getChangedTypeScriptFiles(workspaceRoot: string, base: string): string[] {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n const output = execSync(`git diff --name-only ${base} -- '*.ts' '*.tsx'`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n return output\n .trim()\n .split('\\n')\n .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));\n } catch {\n return [];\n }\n}\n\n/**\n * Get the diff content for a specific file between base and working tree.\n * Uses `git diff base` (no three-dots) to match what `nx affected` does -\n * this includes both committed and uncommitted changes in one diff.\n */\nfunction getFileDiff(workspaceRoot: string, file: string, base: string): string {\n try {\n // Use two-dot diff (base to working tree) - same as nx affected\n return execSync(`git diff ${base} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n } catch {\n return '';\n }\n}\n\n/**\n * Parse diff to find NEW method signatures.\n * Must handle: export function, async function, const/let arrow functions, class methods\n */\n// webpieces-disable max-lines-new-methods -- Regex patterns require inline documentation\nfunction findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n // Patterns to match method definitions (same as validate-new-methods)\n const patterns = [\n // [export] [async] function methodName( - most explicit, check first\n /^\\+\\s*(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(/,\n // [export] const/let methodName = [async] (\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/,\n // [export] const/let methodName = [async] function\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?function/,\n // class method: [async] methodName( - but NOT constructor, if, for, while, etc.\n /^\\+\\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 // Extract method name - now always in capture group 1\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 * Parse diff to find line numbers that have changes in the new file\n */\nfunction getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n\n let currentNewLine = 0;\n\n for (const line of lines) {\n // Parse hunk header: @@ -oldStart,oldCount +newStart,newCount @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentNewLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (currentNewLine === 0) continue;\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n // Added line\n changedLines.add(currentNewLine);\n currentNewLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Removed line - doesn't increment new line counter\n } else if (!line.startsWith('\\\\')) {\n // Context line (unchanged)\n currentNewLine++;\n }\n }\n\n return changedLines;\n}\n\n/**\n * Parse a date string in yyyy/mm/dd format and return a Date object.\n * Returns null if the format is invalid.\n */\nfunction parseDisableDate(dateStr: string): Date | null {\n // Match yyyy/mm/dd format\n const match = dateStr.match(/^(\\d{4})\\/(\\d{2})\\/(\\d{2})$/);\n if (!match) return null;\n\n const year = parseInt(match[1], 10);\n const month = parseInt(match[2], 10) - 1; // JS months are 0-indexed\n const day = parseInt(match[3], 10);\n\n const date = new Date(year, month, day);\n\n // Validate the date is valid (e.g., not Feb 30)\n if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Check if a date is within the last month (not expired).\n */\nfunction isDateWithinMonth(date: Date): boolean {\n const now = new Date();\n const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());\n return date >= oneMonthAgo;\n}\n\n/**\n * Get today's date in yyyy/mm/dd format for error messages\n */\nfunction getTodayDateString(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n return `${year}/${month}/${day}`;\n}\n\ninterface DisableInfo {\n type: 'full' | 'new-only' | 'none';\n isExpired: boolean;\n date?: string;\n}\n\n/**\n * Check what kind of webpieces-disable comment is present for a method.\n * Returns: DisableInfo with type, expiration status, and date\n * - 'full': max-lines-modified (ultimate escape, skips both validators)\n * - 'new-only': max-lines-new-methods (escaped 30-line check, still needs 80-line check)\n * - 'none': no escape hatch\n */\n// webpieces-disable max-lines-new-methods -- Complex validation logic with multiple escape hatch types\nfunction getDisableInfo(lines: string[], lineNumber: number): DisableInfo {\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')) {\n if (line.includes('max-lines-modified')) {\n // Check for date in format: max-lines-modified yyyy/mm/dd\n const dateMatch = line.match(/max-lines-modified\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - treat as expired (invalid)\n return { type: 'full', isExpired: true, date: undefined };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { type: 'full', isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format - treat as expired\n return { type: 'full', isExpired: true, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { type: 'full', isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { type: 'full', isExpired: false, date: dateStr };\n }\n if (line.includes('max-lines-new-methods')) {\n // Check for date in format: max-lines-new-methods yyyy/mm/dd\n const dateMatch = line.match(/max-lines-new-methods\\s+(\\d{4}\\/\\d{2}\\/\\d{2}|XXXX\\/XX\\/XX)/);\n\n if (!dateMatch) {\n // No date found - treat as expired (invalid)\n return { type: 'new-only', isExpired: true, date: undefined };\n }\n\n const dateStr = dateMatch[1];\n\n // Secret permanent disable\n if (dateStr === 'XXXX/XX/XX') {\n return { type: 'new-only', isExpired: false, date: dateStr };\n }\n\n const date = parseDisableDate(dateStr);\n if (!date) {\n // Invalid date format - treat as expired\n return { type: 'new-only', isExpired: true, date: dateStr };\n }\n\n if (!isDateWithinMonth(date)) {\n // Date is expired (older than 1 month)\n return { type: 'new-only', isExpired: true, date: dateStr };\n }\n\n // Valid and not expired\n return { type: 'new-only', isExpired: false, date: dateStr };\n }\n }\n }\n return { type: 'none', isExpired: false };\n}\n\ninterface MethodInfo {\n name: string;\n line: number;\n endLine: number;\n lines: number;\n disableInfo: DisableInfo;\n}\n\n/**\n * Parse a TypeScript file and find methods with their line counts\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\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 } 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 } 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 }\n }\n\n if (methodName && startLine !== undefined && endLine !== undefined) {\n methods.push({\n name: methodName,\n line: startLine,\n endLine: endLine,\n lines: endLine - startLine + 1,\n disableInfo: getDisableInfo(fileLines, startLine),\n });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return methods;\n}\n\n/**\n * Check if a method has any changes within its line range\n */\nfunction methodHasChanges(method: MethodInfo, changedLineNumbers: Set<number>): boolean {\n for (let line = method.line; line <= method.endLine; line++) {\n if (changedLineNumbers.has(line)) return true;\n }\n return false;\n}\n\n/**\n * Check a NEW method and return violation if applicable\n */\nfunction checkNewMethodViolation(file: string, method: MethodInfo, mode: ValidationMode): MethodViolation | null {\n const { type: disableType, isExpired, date: disableDate } = method.disableInfo;\n\n if (mode === 'STRICT') {\n // When mode is STRICT, skip NEW methods without escape (let validate-new-methods handle)\n if (disableType === 'none') return null;\n return { file, methodName: method.name, line: method.line, lines: method.lines };\n }\n\n if (disableType === 'full' && isExpired) {\n return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };\n }\n if (disableType !== 'new-only') return null;\n\n if (isExpired) {\n return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };\n }\n return { file, methodName: method.name, line: method.line, lines: method.lines };\n}\n\n/**\n * Check a MODIFIED method and return violation if applicable\n */\nfunction checkModifiedMethodViolation(file: string, method: MethodInfo, mode: ValidationMode): MethodViolation {\n const { type: disableType, isExpired, date: disableDate } = method.disableInfo;\n\n if (mode === 'STRICT') {\n return { file, methodName: method.name, line: method.line, lines: method.lines };\n }\n if (disableType === 'full' && isExpired) {\n return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };\n }\n return { file, methodName: method.name, line: method.line, lines: method.lines };\n}\n\n/**\n * Find methods that exceed the 80-line limit.\n * Checks NEW methods with escape hatches and MODIFIED methods.\n */\nfunction findViolations(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n maxLines: number,\n mode: ValidationMode\n): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n const diff = getFileDiff(workspaceRoot, file, base);\n if (!diff) continue;\n\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n const changedLineNumbers = getChangedLineNumbers(diff);\n if (changedLineNumbers.size === 0) continue;\n\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n const { type: disableType, isExpired } = method.disableInfo;\n\n // Skip methods with valid, non-expired full escape - unless mode is STRICT\n if (mode !== 'STRICT' && disableType === 'full' && !isExpired) continue;\n if (method.lines <= maxLines) continue;\n\n const isNewMethod = newMethodNames.has(method.name);\n\n if (isNewMethod) {\n const violation = checkNewMethodViolation(file, method, mode);\n if (violation) violations.push(violation);\n } else if (methodHasChanges(method, changedLineNumbers)) {\n violations.push(checkModifiedMethodViolation(file, method, mode));\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 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 {\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 {\n // Ignore\n }\n }\n return null;\n}\n\n/**\n * Report violations to console\n */\n// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections\nfunction reportViolations(violations: MethodViolation[], maxLines: number, mode: ValidationMode): void {\n console.error('');\n console.error('❌ Modified methods exceed ' + maxLines + ' lines!');\n console.error('');\n console.error('📚 When you modify a method, you must bring it under ' + maxLines + ' lines.');\n console.error(' This rule encourages GRADUAL cleanup so even though you did not cause it,');\n console.error(' you touched it, so you should fix now as part of your PR');\n console.error(' (this is for vibe coding and AI to fix as it touches things).');\n console.error(' You can refactor to stay under the limit 50% of the time. If not feasible, use the escape hatch.');\n console.error('');\n console.error(\n '⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️'\n );\n console.error('');\n\n for (const v of violations) {\n if (v.expiredDisable) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${maxLines})`);\n console.error(` ⏰ EXPIRED DISABLE: Your disable comment dated ${v.expiredDate ?? 'unknown'} has expired (>1 month old).`);\n console.error(` You must either FIX the method or UPDATE the date to get another month.`);\n } else {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${maxLines})`);\n }\n }\n console.error('');\n\n // Only show escape hatch instructions when mode is not STRICT\n if (mode !== 'STRICT') {\n console.error(' You can disable this error, but you will be forced to fix again in 1 month');\n console.error(' since 99% of methods can be less than ' + maxLines + ' lines of code.');\n console.error('');\n console.error(' Use escape with DATE (expires in 1 month):');\n console.error(` // webpieces-disable max-lines-modified ${getTodayDateString()} -- [your reason]`);\n console.error('');\n } else {\n console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');\n console.error(' You MUST refactor to reduce method size.');\n console.error('');\n }\n}\n\nexport default async function runExecutor(\n options: ValidateModifiedMethodsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 80;\n const mode: ValidationMode = options.mode ?? 'NORMAL';\n\n // Skip validation entirely if mode is OFF\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping modified method validation (validationMode: OFF)');\n console.log('');\n return { success: true };\n }\n\n let base = process.env['NX_BASE'];\n\n if (!base) {\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping modified method validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-modified-methods --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating Modified Method Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating Modified Method Sizes\\n');\n }\n\n console.log(` Base: ${base}`);\n console.log(' Comparing to: working tree (includes uncommitted changes)');\n console.log(` Max lines for modified methods: ${maxLines}`);\n console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);\n console.log('');\n\n try {\n const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base);\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 const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, mode);\n\n if (violations.length === 0) {\n console.log('✅ All modified methods are under ' + maxLines + ' lines');\n return { success: true };\n }\n\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines, mode);\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Modified method validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -25,8 +25,11 @@ import * as fs from 'fs';
|
|
|
25
25
|
import * as path from 'path';
|
|
26
26
|
import * as ts from 'typescript';
|
|
27
27
|
|
|
28
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
29
|
+
|
|
28
30
|
export interface ValidateModifiedMethodsOptions {
|
|
29
31
|
max?: number;
|
|
32
|
+
mode?: ValidationMode;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
export interface ExecutorResult {
|
|
@@ -494,23 +497,64 @@ function findMethodsInFile(filePath: string, workspaceRoot: string): MethodInfo[
|
|
|
494
497
|
return methods;
|
|
495
498
|
}
|
|
496
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Check if a method has any changes within its line range
|
|
502
|
+
*/
|
|
503
|
+
function methodHasChanges(method: MethodInfo, changedLineNumbers: Set<number>): boolean {
|
|
504
|
+
for (let line = method.line; line <= method.endLine; line++) {
|
|
505
|
+
if (changedLineNumbers.has(line)) return true;
|
|
506
|
+
}
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Check a NEW method and return violation if applicable
|
|
512
|
+
*/
|
|
513
|
+
function checkNewMethodViolation(file: string, method: MethodInfo, mode: ValidationMode): MethodViolation | null {
|
|
514
|
+
const { type: disableType, isExpired, date: disableDate } = method.disableInfo;
|
|
515
|
+
|
|
516
|
+
if (mode === 'STRICT') {
|
|
517
|
+
// When mode is STRICT, skip NEW methods without escape (let validate-new-methods handle)
|
|
518
|
+
if (disableType === 'none') return null;
|
|
519
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (disableType === 'full' && isExpired) {
|
|
523
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
524
|
+
}
|
|
525
|
+
if (disableType !== 'new-only') return null;
|
|
526
|
+
|
|
527
|
+
if (isExpired) {
|
|
528
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
529
|
+
}
|
|
530
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Check a MODIFIED method and return violation if applicable
|
|
535
|
+
*/
|
|
536
|
+
function checkModifiedMethodViolation(file: string, method: MethodInfo, mode: ValidationMode): MethodViolation {
|
|
537
|
+
const { type: disableType, isExpired, date: disableDate } = method.disableInfo;
|
|
538
|
+
|
|
539
|
+
if (mode === 'STRICT') {
|
|
540
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
541
|
+
}
|
|
542
|
+
if (disableType === 'full' && isExpired) {
|
|
543
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines, expiredDisable: true, expiredDate: disableDate };
|
|
544
|
+
}
|
|
545
|
+
return { file, methodName: method.name, line: method.line, lines: method.lines };
|
|
546
|
+
}
|
|
547
|
+
|
|
497
548
|
/**
|
|
498
549
|
* Find methods that exceed the 80-line limit.
|
|
499
|
-
*
|
|
500
|
-
* This validator checks:
|
|
501
|
-
* 1. NEW methods that have `max-lines-new-methods` escape (they passed 30-line check, now need 80-line check)
|
|
502
|
-
* 2. MODIFIED methods (existing methods with changes)
|
|
503
|
-
*
|
|
504
|
-
* Skips:
|
|
505
|
-
* - NEW methods without any escape (let validate-new-methods handle them first)
|
|
506
|
-
* - Methods with valid, non-expired `max-lines-modified` escape (ultimate escape hatch)
|
|
550
|
+
* Checks NEW methods with escape hatches and MODIFIED methods.
|
|
507
551
|
*/
|
|
508
|
-
// webpieces-disable max-lines-modified 2025/12/20 -- Core validation logic with multiple file operations
|
|
509
552
|
function findViolations(
|
|
510
553
|
workspaceRoot: string,
|
|
511
554
|
changedFiles: string[],
|
|
512
555
|
base: string,
|
|
513
|
-
maxLines: number
|
|
556
|
+
maxLines: number,
|
|
557
|
+
mode: ValidationMode
|
|
514
558
|
): MethodViolation[] {
|
|
515
559
|
const violations: MethodViolation[] = [];
|
|
516
560
|
|
|
@@ -518,94 +562,26 @@ function findViolations(
|
|
|
518
562
|
const diff = getFileDiff(workspaceRoot, file, base);
|
|
519
563
|
if (!diff) continue;
|
|
520
564
|
|
|
521
|
-
// Find NEW methods from the diff
|
|
522
565
|
const newMethodNames = findNewMethodSignaturesInDiff(diff);
|
|
523
|
-
|
|
524
|
-
// Find which lines have changes
|
|
525
566
|
const changedLineNumbers = getChangedLineNumbers(diff);
|
|
526
567
|
if (changedLineNumbers.size === 0) continue;
|
|
527
568
|
|
|
528
|
-
// Parse the current file to get all methods
|
|
529
569
|
const methods = findMethodsInFile(file, workspaceRoot);
|
|
530
570
|
|
|
531
571
|
for (const method of methods) {
|
|
532
|
-
const
|
|
533
|
-
const { type: disableType, isExpired, date: disableDate } = method.disableInfo;
|
|
534
|
-
|
|
535
|
-
// Skip methods with valid, non-expired full escape (max-lines-modified)
|
|
536
|
-
if (disableType === 'full' && !isExpired) continue;
|
|
572
|
+
const { type: disableType, isExpired } = method.disableInfo;
|
|
537
573
|
|
|
538
|
-
// Skip methods
|
|
574
|
+
// Skip methods with valid, non-expired full escape - unless mode is STRICT
|
|
575
|
+
if (mode !== 'STRICT' && disableType === 'full' && !isExpired) continue;
|
|
539
576
|
if (method.lines <= maxLines) continue;
|
|
540
577
|
|
|
541
|
-
|
|
542
|
-
// For NEW methods:
|
|
543
|
-
// - If has 'new-only' escape (non-expired) → check (they escaped 30-line, now need 80-line check)
|
|
544
|
-
// - If has 'none' → skip (let validate-new-methods handle first)
|
|
545
|
-
// - If has expired disable → report as violation
|
|
546
|
-
if (disableType === 'full' && isExpired) {
|
|
547
|
-
// Expired full disable - report with expired info
|
|
548
|
-
violations.push({
|
|
549
|
-
file,
|
|
550
|
-
methodName: method.name,
|
|
551
|
-
line: method.line,
|
|
552
|
-
lines: method.lines,
|
|
553
|
-
expiredDisable: true,
|
|
554
|
-
expiredDate: disableDate,
|
|
555
|
-
});
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
if (disableType !== 'new-only') continue;
|
|
559
|
-
|
|
560
|
-
if (isExpired) {
|
|
561
|
-
// Expired new-only disable - report with expired info
|
|
562
|
-
violations.push({
|
|
563
|
-
file,
|
|
564
|
-
methodName: method.name,
|
|
565
|
-
line: method.line,
|
|
566
|
-
lines: method.lines,
|
|
567
|
-
expiredDisable: true,
|
|
568
|
-
expiredDate: disableDate,
|
|
569
|
-
});
|
|
570
|
-
} else {
|
|
571
|
-
// New method with valid max-lines-new-methods escape - check against 80-line limit
|
|
572
|
-
violations.push({
|
|
573
|
-
file,
|
|
574
|
-
methodName: method.name,
|
|
575
|
-
line: method.line,
|
|
576
|
-
lines: method.lines,
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
} else {
|
|
580
|
-
// For MODIFIED methods: check if any changed line falls within method's range
|
|
581
|
-
let hasChanges = false;
|
|
582
|
-
for (let line = method.line; line <= method.endLine; line++) {
|
|
583
|
-
if (changedLineNumbers.has(line)) {
|
|
584
|
-
hasChanges = true;
|
|
585
|
-
break;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
578
|
+
const isNewMethod = newMethodNames.has(method.name);
|
|
588
579
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
methodName: method.name,
|
|
595
|
-
line: method.line,
|
|
596
|
-
lines: method.lines,
|
|
597
|
-
expiredDisable: true,
|
|
598
|
-
expiredDate: disableDate,
|
|
599
|
-
});
|
|
600
|
-
} else {
|
|
601
|
-
violations.push({
|
|
602
|
-
file,
|
|
603
|
-
methodName: method.name,
|
|
604
|
-
line: method.line,
|
|
605
|
-
lines: method.lines,
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
}
|
|
580
|
+
if (isNewMethod) {
|
|
581
|
+
const violation = checkNewMethodViolation(file, method, mode);
|
|
582
|
+
if (violation) violations.push(violation);
|
|
583
|
+
} else if (methodHasChanges(method, changedLineNumbers)) {
|
|
584
|
+
violations.push(checkModifiedMethodViolation(file, method, mode));
|
|
609
585
|
}
|
|
610
586
|
}
|
|
611
587
|
}
|
|
@@ -649,7 +625,7 @@ function detectBase(workspaceRoot: string): string | null {
|
|
|
649
625
|
* Report violations to console
|
|
650
626
|
*/
|
|
651
627
|
// webpieces-disable max-lines-new-methods -- Error output formatting with multiple message sections
|
|
652
|
-
function reportViolations(violations: MethodViolation[], maxLines: number): void {
|
|
628
|
+
function reportViolations(violations: MethodViolation[], maxLines: number, mode: ValidationMode): void {
|
|
653
629
|
console.error('');
|
|
654
630
|
console.error('❌ Modified methods exceed ' + maxLines + ' lines!');
|
|
655
631
|
console.error('');
|
|
@@ -677,12 +653,19 @@ function reportViolations(violations: MethodViolation[], maxLines: number): void
|
|
|
677
653
|
}
|
|
678
654
|
console.error('');
|
|
679
655
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
656
|
+
// Only show escape hatch instructions when mode is not STRICT
|
|
657
|
+
if (mode !== 'STRICT') {
|
|
658
|
+
console.error(' You can disable this error, but you will be forced to fix again in 1 month');
|
|
659
|
+
console.error(' since 99% of methods can be less than ' + maxLines + ' lines of code.');
|
|
660
|
+
console.error('');
|
|
661
|
+
console.error(' Use escape with DATE (expires in 1 month):');
|
|
662
|
+
console.error(` // webpieces-disable max-lines-modified ${getTodayDateString()} -- [your reason]`);
|
|
663
|
+
console.error('');
|
|
664
|
+
} else {
|
|
665
|
+
console.error(' ⚠️ validationMode is STRICT - disable comments are NOT allowed.');
|
|
666
|
+
console.error(' You MUST refactor to reduce method size.');
|
|
667
|
+
console.error('');
|
|
668
|
+
}
|
|
686
669
|
}
|
|
687
670
|
|
|
688
671
|
export default async function runExecutor(
|
|
@@ -691,6 +674,14 @@ export default async function runExecutor(
|
|
|
691
674
|
): Promise<ExecutorResult> {
|
|
692
675
|
const workspaceRoot = context.root;
|
|
693
676
|
const maxLines = options.max ?? 80;
|
|
677
|
+
const mode: ValidationMode = options.mode ?? 'NORMAL';
|
|
678
|
+
|
|
679
|
+
// Skip validation entirely if mode is OFF
|
|
680
|
+
if (mode === 'OFF') {
|
|
681
|
+
console.log('\n⏭️ Skipping modified method validation (validationMode: OFF)');
|
|
682
|
+
console.log('');
|
|
683
|
+
return { success: true };
|
|
684
|
+
}
|
|
694
685
|
|
|
695
686
|
let base = process.env['NX_BASE'];
|
|
696
687
|
|
|
@@ -712,6 +703,7 @@ export default async function runExecutor(
|
|
|
712
703
|
console.log(` Base: ${base}`);
|
|
713
704
|
console.log(' Comparing to: working tree (includes uncommitted changes)');
|
|
714
705
|
console.log(` Max lines for modified methods: ${maxLines}`);
|
|
706
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored)' : ''}`);
|
|
715
707
|
console.log('');
|
|
716
708
|
|
|
717
709
|
try {
|
|
@@ -724,7 +716,7 @@ export default async function runExecutor(
|
|
|
724
716
|
|
|
725
717
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
726
718
|
|
|
727
|
-
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);
|
|
719
|
+
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, mode);
|
|
728
720
|
|
|
729
721
|
if (violations.length === 0) {
|
|
730
722
|
console.log('✅ All modified methods are under ' + maxLines + ' lines');
|
|
@@ -732,7 +724,7 @@ export default async function runExecutor(
|
|
|
732
724
|
}
|
|
733
725
|
|
|
734
726
|
writeTmpInstructions(workspaceRoot);
|
|
735
|
-
reportViolations(violations, maxLines);
|
|
727
|
+
reportViolations(violations, maxLines, mode);
|
|
736
728
|
return { success: false };
|
|
737
729
|
} catch (err: unknown) {
|
|
738
730
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
"type": "number",
|
|
9
9
|
"description": "Maximum number of lines allowed for modified methods",
|
|
10
10
|
"default": 80
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["STRICT", "NORMAL", "OFF"],
|
|
15
|
+
"description": "STRICT: limits enforced, no disable comments. NORMAL: disable comments work. OFF: skip validation.",
|
|
16
|
+
"default": "NORMAL"
|
|
11
17
|
}
|
|
12
18
|
},
|
|
13
19
|
"required": []
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -42,7 +42,7 @@ const DEFAULT_OPTIONS = {
|
|
|
42
42
|
newMethodsMaxLines: 30,
|
|
43
43
|
modifiedAndNewMethodsMaxLines: 80,
|
|
44
44
|
modifiedFilesMaxLines: 900,
|
|
45
|
-
|
|
45
|
+
validationMode: 'NORMAL',
|
|
46
46
|
},
|
|
47
47
|
features: {
|
|
48
48
|
generate: true,
|
|
@@ -198,10 +198,10 @@ function createWorkspaceTargetsWithoutPrefix(opts) {
|
|
|
198
198
|
targets['validate-new-methods'] = createValidateNewMethodsTarget(validations.newMethodsMaxLines);
|
|
199
199
|
}
|
|
200
200
|
if (validations.validateModifiedMethods) {
|
|
201
|
-
targets['validate-modified-methods'] = createValidateModifiedMethodsTarget(validations.modifiedAndNewMethodsMaxLines);
|
|
201
|
+
targets['validate-modified-methods'] = createValidateModifiedMethodsTarget(validations.modifiedAndNewMethodsMaxLines, validations.validationMode);
|
|
202
202
|
}
|
|
203
203
|
if (validations.validateModifiedFiles) {
|
|
204
|
-
targets['validate-modified-files'] = createValidateModifiedFilesTarget(validations.modifiedFilesMaxLines, validations.
|
|
204
|
+
targets['validate-modified-files'] = createValidateModifiedFilesTarget(validations.modifiedFilesMaxLines, validations.validationMode);
|
|
205
205
|
}
|
|
206
206
|
if (validations.validateVersionsLocked) {
|
|
207
207
|
targets['validate-versions-locked'] = createValidateVersionsLockedTarget();
|
|
@@ -245,10 +245,10 @@ function createWorkspaceTargets(opts) {
|
|
|
245
245
|
targets[`${prefix}validate-new-methods`] = createValidateNewMethodsTarget(opts.workspace.validations.newMethodsMaxLines);
|
|
246
246
|
}
|
|
247
247
|
if (opts.workspace.validations.validateModifiedMethods) {
|
|
248
|
-
targets[`${prefix}validate-modified-methods`] = createValidateModifiedMethodsTarget(opts.workspace.validations.modifiedAndNewMethodsMaxLines);
|
|
248
|
+
targets[`${prefix}validate-modified-methods`] = createValidateModifiedMethodsTarget(opts.workspace.validations.modifiedAndNewMethodsMaxLines, opts.workspace.validations.validationMode);
|
|
249
249
|
}
|
|
250
250
|
if (opts.workspace.validations.validateModifiedFiles) {
|
|
251
|
-
targets[`${prefix}validate-modified-files`] = createValidateModifiedFilesTarget(opts.workspace.validations.modifiedFilesMaxLines, opts.workspace.validations.
|
|
251
|
+
targets[`${prefix}validate-modified-files`] = createValidateModifiedFilesTarget(opts.workspace.validations.modifiedFilesMaxLines, opts.workspace.validations.validationMode);
|
|
252
252
|
}
|
|
253
253
|
return targets;
|
|
254
254
|
}
|
|
@@ -344,24 +344,24 @@ function createValidateNewMethodsTarget(maxLines) {
|
|
|
344
344
|
},
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
|
-
function createValidateModifiedMethodsTarget(maxLines) {
|
|
347
|
+
function createValidateModifiedMethodsTarget(maxLines, mode) {
|
|
348
348
|
return {
|
|
349
349
|
executor: '@webpieces/dev-config:validate-modified-methods',
|
|
350
350
|
cache: false, // Don't cache - depends on git state
|
|
351
351
|
inputs: ['default'],
|
|
352
|
-
options: { max: maxLines },
|
|
352
|
+
options: { max: maxLines, mode },
|
|
353
353
|
metadata: {
|
|
354
354
|
technologies: ['nx'],
|
|
355
355
|
description: `Validate new and modified methods do not exceed ${maxLines} lines (encourages gradual cleanup)`,
|
|
356
356
|
},
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
|
-
function createValidateModifiedFilesTarget(maxLines,
|
|
359
|
+
function createValidateModifiedFilesTarget(maxLines, mode) {
|
|
360
360
|
return {
|
|
361
361
|
executor: '@webpieces/dev-config:validate-modified-files',
|
|
362
362
|
cache: false, // Don't cache - depends on git state
|
|
363
363
|
inputs: ['default'],
|
|
364
|
-
options: { max: maxLines,
|
|
364
|
+
options: { max: maxLines, mode },
|
|
365
365
|
metadata: {
|
|
366
366
|
technologies: ['nx'],
|
|
367
367
|
description: `Validate modified files do not exceed ${maxLines} lines (encourages keeping files small)`,
|