@webpieces/dev-config 0.2.65 → 0.2.67
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-code/executor.d.ts +13 -0
- package/architecture/executors/validate-code/executor.js +36 -0
- package/architecture/executors/validate-code/executor.js.map +1 -0
- package/architecture/executors/validate-code/executor.ts +66 -0
- package/architecture/executors/validate-code/schema.json +34 -0
- package/architecture/executors/validate-new-methods/executor.d.ts +1 -0
- package/architecture/executors/validate-new-methods/executor.js +60 -16
- package/architecture/executors/validate-new-methods/executor.js.map +1 -1
- package/architecture/executors/validate-new-methods/executor.ts +66 -16
- package/architecture/executors/validate-new-methods/schema.json +6 -2
- package/executors.json +5 -0
- package/package.json +1 -1
- package/plugin.js +28 -22
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
3
|
+
export interface ValidateCodeOptions {
|
|
4
|
+
mode?: ValidationMode;
|
|
5
|
+
newMethodsMaxLines?: number;
|
|
6
|
+
strictNewMethodMaxLines?: number;
|
|
7
|
+
modifiedMethodsMaxLines?: number;
|
|
8
|
+
modifiedFilesMaxLines?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ExecutorResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
}
|
|
13
|
+
export default function runExecutor(options: ValidateCodeOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = runExecutor;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const executor_1 = tslib_1.__importDefault(require("../validate-new-methods/executor"));
|
|
6
|
+
const executor_2 = tslib_1.__importDefault(require("../validate-modified-methods/executor"));
|
|
7
|
+
const executor_3 = tslib_1.__importDefault(require("../validate-modified-files/executor"));
|
|
8
|
+
async function runExecutor(options, context) {
|
|
9
|
+
const mode = options.mode ?? 'NORMAL';
|
|
10
|
+
if (mode === 'OFF') {
|
|
11
|
+
console.log('\n⏭️ Skipping all code validations (validationMode: OFF)\n');
|
|
12
|
+
return { success: true };
|
|
13
|
+
}
|
|
14
|
+
console.log('\n📏 Running Code Validations\n');
|
|
15
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored for modified code)' : ''}`);
|
|
16
|
+
console.log(` New methods max: ${options.newMethodsMaxLines ?? 30} lines (soft limit)`);
|
|
17
|
+
if (options.strictNewMethodMaxLines) {
|
|
18
|
+
console.log(` New methods max: ${options.strictNewMethodMaxLines} lines (hard limit, no escape)`);
|
|
19
|
+
}
|
|
20
|
+
console.log(` Modified methods max: ${options.modifiedMethodsMaxLines ?? 80} lines`);
|
|
21
|
+
console.log(` Modified files max: ${options.modifiedFilesMaxLines ?? 900} lines`);
|
|
22
|
+
console.log('');
|
|
23
|
+
// Run all three validators sequentially to avoid interleaved output
|
|
24
|
+
const newMethodsResult = await (0, executor_1.default)({ max: options.newMethodsMaxLines ?? 30, strictMax: options.strictNewMethodMaxLines, mode }, context);
|
|
25
|
+
const modifiedMethodsResult = await (0, executor_2.default)({ max: options.modifiedMethodsMaxLines ?? 80, mode }, context);
|
|
26
|
+
const modifiedFilesResult = await (0, executor_3.default)({ max: options.modifiedFilesMaxLines ?? 900, mode }, context);
|
|
27
|
+
const allSuccess = newMethodsResult.success && modifiedMethodsResult.success && modifiedFilesResult.success;
|
|
28
|
+
if (allSuccess) {
|
|
29
|
+
console.log('\n✅ All code validations passed\n');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log('\n❌ Some code validations failed\n');
|
|
33
|
+
}
|
|
34
|
+
return { success: allSuccess };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-code/executor.ts"],"names":[],"mappings":";;AAmBA,8BA8CC;;AAhED,wFAAqE;AACrE,6FAA+E;AAC/E,2FAA2E;AAgB5D,KAAK,UAAU,WAAW,CACrC,OAA4B,EAC5B,OAAwB;IAExB,MAAM,IAAI,GAAmB,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;IAEtD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtH,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,kBAAkB,IAAI,EAAE,qBAAqB,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,uBAAuB,gCAAgC,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,4BAA4B,OAAO,CAAC,uBAAuB,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,0BAA0B,OAAO,CAAC,qBAAqB,IAAI,GAAG,QAAQ,CAAC,CAAC;IACpF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,MAAM,IAAA,kBAAqB,EAChD,EAAE,GAAG,EAAE,OAAO,CAAC,kBAAkB,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,uBAAuB,EAAE,IAAI,EAAE,EAC3F,OAAO,CACV,CAAC;IAEF,MAAM,qBAAqB,GAAG,MAAM,IAAA,kBAA0B,EAC1D,EAAE,GAAG,EAAE,OAAO,CAAC,uBAAuB,IAAI,EAAE,EAAE,IAAI,EAAE,EACpD,OAAO,CACV,CAAC;IAEF,MAAM,mBAAmB,GAAG,MAAM,IAAA,kBAAwB,EACtD,EAAE,GAAG,EAAE,OAAO,CAAC,qBAAqB,IAAI,GAAG,EAAE,IAAI,EAAE,EACnD,OAAO,CACV,CAAC;IAEF,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,IAAI,qBAAqB,CAAC,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC;IAE5G,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import { ExecutorContext } from '@nx/devkit';\nimport runNewMethodsExecutor from '../validate-new-methods/executor';\nimport runModifiedMethodsExecutor from '../validate-modified-methods/executor';\nimport runModifiedFilesExecutor from '../validate-modified-files/executor';\n\nexport type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';\n\nexport interface ValidateCodeOptions {\n mode?: ValidationMode;\n newMethodsMaxLines?: number;\n strictNewMethodMaxLines?: number;\n modifiedMethodsMaxLines?: number;\n modifiedFilesMaxLines?: number;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n options: ValidateCodeOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const mode: ValidationMode = options.mode ?? 'NORMAL';\n\n if (mode === 'OFF') {\n console.log('\\n⏭️ Skipping all code validations (validationMode: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored for modified code)' : ''}`);\n console.log(` New methods max: ${options.newMethodsMaxLines ?? 30} lines (soft limit)`);\n if (options.strictNewMethodMaxLines) {\n console.log(` New methods max: ${options.strictNewMethodMaxLines} lines (hard limit, no escape)`);\n }\n console.log(` Modified methods max: ${options.modifiedMethodsMaxLines ?? 80} lines`);\n console.log(` Modified files max: ${options.modifiedFilesMaxLines ?? 900} lines`);\n console.log('');\n\n // Run all three validators sequentially to avoid interleaved output\n const newMethodsResult = await runNewMethodsExecutor(\n { max: options.newMethodsMaxLines ?? 30, strictMax: options.strictNewMethodMaxLines, mode },\n context\n );\n\n const modifiedMethodsResult = await runModifiedMethodsExecutor(\n { max: options.modifiedMethodsMaxLines ?? 80, mode },\n context\n );\n\n const modifiedFilesResult = await runModifiedFilesExecutor(\n { max: options.modifiedFilesMaxLines ?? 900, mode },\n context\n );\n\n const allSuccess = newMethodsResult.success && modifiedMethodsResult.success && modifiedFilesResult.success;\n\n if (allSuccess) {\n console.log('\\n✅ All code validations passed\\n');\n } else {\n console.log('\\n❌ Some code validations failed\\n');\n }\n\n return { success: allSuccess };\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import runNewMethodsExecutor from '../validate-new-methods/executor';
|
|
3
|
+
import runModifiedMethodsExecutor from '../validate-modified-methods/executor';
|
|
4
|
+
import runModifiedFilesExecutor from '../validate-modified-files/executor';
|
|
5
|
+
|
|
6
|
+
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
7
|
+
|
|
8
|
+
export interface ValidateCodeOptions {
|
|
9
|
+
mode?: ValidationMode;
|
|
10
|
+
newMethodsMaxLines?: number;
|
|
11
|
+
strictNewMethodMaxLines?: number;
|
|
12
|
+
modifiedMethodsMaxLines?: number;
|
|
13
|
+
modifiedFilesMaxLines?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ExecutorResult {
|
|
17
|
+
success: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default async function runExecutor(
|
|
21
|
+
options: ValidateCodeOptions,
|
|
22
|
+
context: ExecutorContext
|
|
23
|
+
): Promise<ExecutorResult> {
|
|
24
|
+
const mode: ValidationMode = options.mode ?? 'NORMAL';
|
|
25
|
+
|
|
26
|
+
if (mode === 'OFF') {
|
|
27
|
+
console.log('\n⏭️ Skipping all code validations (validationMode: OFF)\n');
|
|
28
|
+
return { success: true };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('\n📏 Running Code Validations\n');
|
|
32
|
+
console.log(` Validation mode: ${mode}${mode === 'STRICT' ? ' (disable comments ignored for modified code)' : ''}`);
|
|
33
|
+
console.log(` New methods max: ${options.newMethodsMaxLines ?? 30} lines (soft limit)`);
|
|
34
|
+
if (options.strictNewMethodMaxLines) {
|
|
35
|
+
console.log(` New methods max: ${options.strictNewMethodMaxLines} lines (hard limit, no escape)`);
|
|
36
|
+
}
|
|
37
|
+
console.log(` Modified methods max: ${options.modifiedMethodsMaxLines ?? 80} lines`);
|
|
38
|
+
console.log(` Modified files max: ${options.modifiedFilesMaxLines ?? 900} lines`);
|
|
39
|
+
console.log('');
|
|
40
|
+
|
|
41
|
+
// Run all three validators sequentially to avoid interleaved output
|
|
42
|
+
const newMethodsResult = await runNewMethodsExecutor(
|
|
43
|
+
{ max: options.newMethodsMaxLines ?? 30, strictMax: options.strictNewMethodMaxLines, mode },
|
|
44
|
+
context
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const modifiedMethodsResult = await runModifiedMethodsExecutor(
|
|
48
|
+
{ max: options.modifiedMethodsMaxLines ?? 80, mode },
|
|
49
|
+
context
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const modifiedFilesResult = await runModifiedFilesExecutor(
|
|
53
|
+
{ max: options.modifiedFilesMaxLines ?? 900, mode },
|
|
54
|
+
context
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const allSuccess = newMethodsResult.success && modifiedMethodsResult.success && modifiedFilesResult.success;
|
|
58
|
+
|
|
59
|
+
if (allSuccess) {
|
|
60
|
+
console.log('\n✅ All code validations passed\n');
|
|
61
|
+
} else {
|
|
62
|
+
console.log('\n❌ Some code validations failed\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { success: allSuccess };
|
|
66
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "Validate Code Executor",
|
|
4
|
+
"description": "Combined validation for new methods, modified methods, and file sizes. Configure via targetDefaults in nx.json for runtime options (no cache issues).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"mode": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["STRICT", "NORMAL", "OFF"],
|
|
10
|
+
"description": "OFF: skip all validations. NORMAL: enforce limits, disable comments work. STRICT: enforce limits, no disable comments for modified code.",
|
|
11
|
+
"default": "NORMAL"
|
|
12
|
+
},
|
|
13
|
+
"newMethodsMaxLines": {
|
|
14
|
+
"type": "number",
|
|
15
|
+
"description": "Soft limit: Maximum lines for NEW methods (can be bypassed with disable comment)",
|
|
16
|
+
"default": 30
|
|
17
|
+
},
|
|
18
|
+
"strictNewMethodMaxLines": {
|
|
19
|
+
"type": "number",
|
|
20
|
+
"description": "Hard limit: Absolute maximum lines for NEW methods (CANNOT be bypassed). If not set, no hard limit is enforced."
|
|
21
|
+
},
|
|
22
|
+
"modifiedMethodsMaxLines": {
|
|
23
|
+
"type": "number",
|
|
24
|
+
"description": "Maximum lines for MODIFIED methods",
|
|
25
|
+
"default": 80
|
|
26
|
+
},
|
|
27
|
+
"modifiedFilesMaxLines": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "Maximum lines for MODIFIED files",
|
|
30
|
+
"default": 900
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"required": []
|
|
34
|
+
}
|
|
@@ -19,6 +19,7 @@ import type { ExecutorContext } from '@nx/devkit';
|
|
|
19
19
|
export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
20
20
|
export interface ValidateNewMethodsOptions {
|
|
21
21
|
max?: number;
|
|
22
|
+
strictMax?: number;
|
|
22
23
|
mode?: ValidationMode;
|
|
23
24
|
}
|
|
24
25
|
export interface ExecutorResult {
|
|
@@ -304,7 +304,7 @@ function findMethodsInFile(filePath, workspaceRoot) {
|
|
|
304
304
|
/**
|
|
305
305
|
* Find new methods that exceed the line limit
|
|
306
306
|
*/
|
|
307
|
-
function findViolations(workspaceRoot, changedFiles, base, maxLines) {
|
|
307
|
+
function findViolations(workspaceRoot, changedFiles, base, maxLines, strictMaxLines) {
|
|
308
308
|
const violations = [];
|
|
309
309
|
for (const file of changedFiles) {
|
|
310
310
|
// Get the diff to find which methods are NEW (not just modified)
|
|
@@ -315,14 +315,30 @@ function findViolations(workspaceRoot, changedFiles, base, maxLines) {
|
|
|
315
315
|
// Parse the current file to get method line counts
|
|
316
316
|
const methods = findMethodsInFile(file, workspaceRoot);
|
|
317
317
|
for (const method of methods) {
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
if (!newMethodNames.has(method.name))
|
|
319
|
+
continue;
|
|
320
|
+
// Check hard limit first (if defined) - NO escape possible
|
|
321
|
+
if (strictMaxLines && method.lines > strictMaxLines) {
|
|
320
322
|
violations.push({
|
|
321
323
|
file,
|
|
322
324
|
methodName: method.name,
|
|
323
325
|
line: method.line,
|
|
324
326
|
lines: method.lines,
|
|
325
327
|
isNew: true,
|
|
328
|
+
isHardLimit: true,
|
|
329
|
+
limit: strictMaxLines,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// Check soft limit - can be escaped with disable comment
|
|
333
|
+
else if (method.lines > maxLines && !method.hasDisableComment) {
|
|
334
|
+
violations.push({
|
|
335
|
+
file,
|
|
336
|
+
methodName: method.name,
|
|
337
|
+
line: method.line,
|
|
338
|
+
lines: method.lines,
|
|
339
|
+
isNew: true,
|
|
340
|
+
isHardLimit: false,
|
|
341
|
+
limit: maxLines,
|
|
326
342
|
});
|
|
327
343
|
}
|
|
328
344
|
}
|
|
@@ -366,9 +382,12 @@ function detectBase(workspaceRoot) {
|
|
|
366
382
|
/**
|
|
367
383
|
* Report violations to the user with helpful instructions
|
|
368
384
|
*/
|
|
369
|
-
|
|
385
|
+
// webpieces-disable max-lines-new-methods -- Console output formatting requires distinct sections for hard/soft violations
|
|
386
|
+
function reportViolations(violations, maxLines, strictMaxLines) {
|
|
387
|
+
const hardViolations = violations.filter((v) => v.isHardLimit);
|
|
388
|
+
const softViolations = violations.filter((v) => !v.isHardLimit);
|
|
370
389
|
console.error('');
|
|
371
|
-
console.error('❌ New methods exceed
|
|
390
|
+
console.error('❌ New methods exceed line limits!');
|
|
372
391
|
console.error('');
|
|
373
392
|
console.error('📚 Methods should read like a "table of contents" - each method call');
|
|
374
393
|
console.error(' describes a larger piece of work. You can refactor');
|
|
@@ -376,18 +395,37 @@ function reportViolations(violations, maxLines) {
|
|
|
376
395
|
console.error('');
|
|
377
396
|
console.error('⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️');
|
|
378
397
|
console.error('');
|
|
379
|
-
|
|
380
|
-
console.error(
|
|
381
|
-
console.error(
|
|
398
|
+
if (hardViolations.length > 0) {
|
|
399
|
+
console.error('🚫 HARD LIMIT VIOLATIONS (cannot be bypassed with disable comment):');
|
|
400
|
+
console.error('');
|
|
401
|
+
for (const v of hardViolations) {
|
|
402
|
+
console.error(` ❌ ${v.file}:${v.line}`);
|
|
403
|
+
console.error(` Method: ${v.methodName} (${v.lines} lines, hard max: ${strictMaxLines})`);
|
|
404
|
+
}
|
|
405
|
+
console.error('');
|
|
406
|
+
console.error(' These methods MUST be refactored - no escape hatch available.');
|
|
407
|
+
console.error('');
|
|
408
|
+
}
|
|
409
|
+
if (softViolations.length > 0) {
|
|
410
|
+
console.error('⚠️ SOFT LIMIT VIOLATIONS (can be bypassed with disable comment):');
|
|
411
|
+
console.error('');
|
|
412
|
+
for (const v of softViolations) {
|
|
413
|
+
console.error(` ❌ ${v.file}:${v.line}`);
|
|
414
|
+
console.error(` Method: ${v.methodName} (${v.lines} lines, soft max: ${maxLines})`);
|
|
415
|
+
}
|
|
416
|
+
console.error('');
|
|
417
|
+
console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');
|
|
418
|
+
console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');
|
|
419
|
+
if (strictMaxLines) {
|
|
420
|
+
console.error(` NOTE: Even with escape, you cannot exceed the hard limit of ${strictMaxLines} lines.`);
|
|
421
|
+
}
|
|
422
|
+
console.error('');
|
|
382
423
|
}
|
|
383
|
-
console.error('');
|
|
384
|
-
console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');
|
|
385
|
-
console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');
|
|
386
|
-
console.error('');
|
|
387
424
|
}
|
|
388
425
|
async function runExecutor(options, context) {
|
|
389
426
|
const workspaceRoot = context.root;
|
|
390
427
|
const maxLines = options.max ?? 30;
|
|
428
|
+
const strictMaxLines = options.strictMax;
|
|
391
429
|
const mode = options.mode ?? 'NORMAL';
|
|
392
430
|
// Skip validation entirely if mode is OFF
|
|
393
431
|
if (mode === 'OFF') {
|
|
@@ -415,7 +453,10 @@ async function runExecutor(options, context) {
|
|
|
415
453
|
}
|
|
416
454
|
console.log(` Base: ${base}`);
|
|
417
455
|
console.log(` Comparing to: working tree (includes uncommitted changes)`);
|
|
418
|
-
console.log(`
|
|
456
|
+
console.log(` Soft limit for new methods: ${maxLines} lines (can escape with disable comment)`);
|
|
457
|
+
if (strictMaxLines) {
|
|
458
|
+
console.log(` Hard limit for new methods: ${strictMaxLines} lines (NO escape possible)`);
|
|
459
|
+
}
|
|
419
460
|
console.log('');
|
|
420
461
|
try {
|
|
421
462
|
// Get changed TypeScript files (base to working tree, like nx affected)
|
|
@@ -426,14 +467,17 @@ async function runExecutor(options, context) {
|
|
|
426
467
|
}
|
|
427
468
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
428
469
|
// Find violations
|
|
429
|
-
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);
|
|
470
|
+
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, strictMaxLines);
|
|
430
471
|
if (violations.length === 0) {
|
|
431
|
-
|
|
472
|
+
const limitMsg = strictMaxLines
|
|
473
|
+
? `soft limit (${maxLines}) or hard limit (${strictMaxLines})`
|
|
474
|
+
: `${maxLines} lines`;
|
|
475
|
+
console.log('✅ All new methods are within ' + limitMsg);
|
|
432
476
|
return { success: true };
|
|
433
477
|
}
|
|
434
478
|
// Write instructions file and report violations
|
|
435
479
|
writeTmpInstructions(workspaceRoot);
|
|
436
|
-
reportViolations(violations, maxLines);
|
|
480
|
+
reportViolations(violations, maxLines, strictMaxLines);
|
|
437
481
|
return { success: false };
|
|
438
482
|
}
|
|
439
483
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-new-methods/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate New Methods Executor\n *\n * Validates that newly added methods don't exceed a maximum line count.\n * Runs in affected mode when:\n * 1. NX_BASE environment variable is set (via nx affected), OR\n * 2. Auto-detects base by finding merge-base with origin/main\n *\n * This validator encourages writing methods that read like a \"table of contents\"\n * where each method call describes a larger piece of work.\n *\n * Usage:\n * nx affected --target=validate-new-methods --base=origin/main\n * OR: runs automatically via build's architecture:validate-complete dependency\n *\n * Escape hatch: Add webpieces-disable max-lines-new-methods comment with justification\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 ValidateNewMethodsOptions {\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 isNew: boolean;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.methodsize.md';\n\nconst METHODSIZE_DOC_CONTENT = `# Instructions: New 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.\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 New Methods?\n\nMethods under the limit 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\nExtracting logical units into well-named methods makes code more readable for both\nAI and humans.\n\n## How to Refactor\n\nInstead of:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n // 50 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 justification:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-new-methods -- Complex state machine, splitting reduces clarity\nasync complexStateMachine(): Promise<void> {\n // ... longer method with justification\n}\n\\`\\`\\`\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-new-methods 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 newly added method signatures\n */\nfunction findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n // Patterns to match method definitions\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 * Check if a line contains a webpieces-disable comment that exempts from new method validation.\n * Both max-lines-new-methods AND max-lines-modified are accepted here.\n * - max-lines-new-methods: Exempts from 30-line check, still checked by 80-line validator\n * - max-lines-modified: Exempts from both validators (ultimate escape hatch)\n */\nfunction hasDisableComment(lines: string[], lineNumber: number): boolean {\n // Check the line before the method (lineNumber is 1-indexed, array is 0-indexed)\n // We need to check a few lines before in case there's JSDoc or decorators\n const startCheck = Math.max(0, lineNumber - 5);\n for (let i = lineNumber - 2; i >= startCheck; i--) {\n const line = lines[i]?.trim() ?? '';\n // Stop if we hit another function/class/etc\n if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {\n break;\n }\n if (line.includes('webpieces-disable')) {\n // Either escape hatch exempts from the 30-line new method check\n if (line.includes('max-lines-new-methods') || line.includes('max-lines-modified')) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Parse a TypeScript file and find methods with their line counts\n */\nfunction findMethodsInFile(\n filePath: string,\n workspaceRoot: string\n): Array<{ name: string; line: number; lines: number; hasDisableComment: boolean }> {\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: Array<{ name: string; line: number; lines: number; hasDisableComment: boolean }> = [];\n\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 // Check if it's assigned to a variable\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 lines: endLine - startLine + 1,\n hasDisableComment: hasDisableComment(fileLines, startLine),\n });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return methods;\n}\n\n/**\n * Find new methods that exceed the line limit\n */\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 // Get the diff to find which methods are NEW (not just modified)\n const diff = getFileDiff(workspaceRoot, file, base);\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n\n if (newMethodNames.size === 0) continue;\n\n // Parse the current file to get method line counts\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n // Only check NEW methods that don't have webpieces-disable comment\n if (newMethodNames.has(method.name) && method.lines > maxLines && !method.hasDisableComment) {\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n isNew: true,\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 * This allows the executor to run even when NX_BASE isn't set (e.g., via dependsOn).\n */\nfunction detectBase(workspaceRoot: string): string | null {\n try {\n // First, try to get merge-base with origin/main\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 // origin/main might not exist, try main\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 - will return null\n }\n }\n return null;\n}\n\n/**\n * Report violations to the user with helpful instructions\n */\nfunction reportViolations(violations: MethodViolation[], maxLines: number): void {\n console.error('');\n console.error('❌ New methods exceed ' + maxLines + ' lines!');\n console.error('');\n console.error('📚 Methods should read like a \"table of contents\" - each method call');\n console.error(' describes a larger piece of work. You can refactor');\n console.error(' to stay under ' + maxLines + ' lines 50% of the time.');\n console.error('');\n console.error('⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️');\n console.error('');\n\n for (const v of violations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, max: ${maxLines})`);\n }\n console.error('');\n console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');\n console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');\n console.error('');\n}\n\nexport default async function runExecutor(\n options: ValidateNewMethodsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 30;\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 new method validation (validationMode: OFF)');\n console.log('');\n return { success: true };\n }\n\n // Check if running in affected mode via NX_BASE, or auto-detect\n // We use NX_BASE as the base, and compare to WORKING TREE (not NX_HEAD)\n // This matches what `nx affected` does - it compares base to working tree\n let base = process.env['NX_BASE'];\n\n if (!base) {\n // Try to auto-detect base from git merge-base\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping new method validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-new-methods --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating New Method Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating New 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 new methods: ${maxLines}`);\n console.log('');\n\n try {\n // Get changed TypeScript files (base to working tree, like nx affected)\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 // Find violations\n const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);\n\n if (violations.length === 0) {\n console.log('✅ All new methods are under ' + maxLines + ' lines');\n return { success: true };\n }\n\n // Write instructions file and report violations\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines);\n\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ New 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-new-methods/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate New Methods Executor\n *\n * Validates that newly added methods don't exceed a maximum line count.\n * Runs in affected mode when:\n * 1. NX_BASE environment variable is set (via nx affected), OR\n * 2. Auto-detects base by finding merge-base with origin/main\n *\n * This validator encourages writing methods that read like a \"table of contents\"\n * where each method call describes a larger piece of work.\n *\n * Usage:\n * nx affected --target=validate-new-methods --base=origin/main\n * OR: runs automatically via build's architecture:validate-complete dependency\n *\n * Escape hatch: Add webpieces-disable max-lines-new-methods comment with justification\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 ValidateNewMethodsOptions {\n max?: number;\n strictMax?: 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 isNew: boolean;\n isHardLimit: boolean;\n limit: number;\n}\n\nconst TMP_DIR = 'tmp/webpieces';\nconst TMP_MD_FILE = 'webpieces.methodsize.md';\n\nconst METHODSIZE_DOC_CONTENT = `# Instructions: New 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.\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 New Methods?\n\nMethods under the limit 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\nExtracting logical units into well-named methods makes code more readable for both\nAI and humans.\n\n## How to Refactor\n\nInstead of:\n\\`\\`\\`typescript\nasync processOrder(order: Order): Promise<Result> {\n // 50 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 justification:\n\n\\`\\`\\`typescript\n// webpieces-disable max-lines-new-methods -- Complex state machine, splitting reduces clarity\nasync complexStateMachine(): Promise<void> {\n // ... longer method with justification\n}\n\\`\\`\\`\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-new-methods 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 newly added method signatures\n */\nfunction findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n // Patterns to match method definitions\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 * Check if a line contains a webpieces-disable comment that exempts from new method validation.\n * Both max-lines-new-methods AND max-lines-modified are accepted here.\n * - max-lines-new-methods: Exempts from 30-line check, still checked by 80-line validator\n * - max-lines-modified: Exempts from both validators (ultimate escape hatch)\n */\nfunction hasDisableComment(lines: string[], lineNumber: number): boolean {\n // Check the line before the method (lineNumber is 1-indexed, array is 0-indexed)\n // We need to check a few lines before in case there's JSDoc or decorators\n const startCheck = Math.max(0, lineNumber - 5);\n for (let i = lineNumber - 2; i >= startCheck; i--) {\n const line = lines[i]?.trim() ?? '';\n // Stop if we hit another function/class/etc\n if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {\n break;\n }\n if (line.includes('webpieces-disable')) {\n // Either escape hatch exempts from the 30-line new method check\n if (line.includes('max-lines-new-methods') || line.includes('max-lines-modified')) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Parse a TypeScript file and find methods with their line counts\n */\nfunction findMethodsInFile(\n filePath: string,\n workspaceRoot: string\n): Array<{ name: string; line: number; lines: number; hasDisableComment: boolean }> {\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: Array<{ name: string; line: number; lines: number; hasDisableComment: boolean }> = [];\n\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 // Check if it's assigned to a variable\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 lines: endLine - startLine + 1,\n hasDisableComment: hasDisableComment(fileLines, startLine),\n });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return methods;\n}\n\n/**\n * Find new methods that exceed the line limit\n */\nfunction findViolations(\n workspaceRoot: string,\n changedFiles: string[],\n base: string,\n maxLines: number,\n strictMaxLines?: number\n): MethodViolation[] {\n const violations: MethodViolation[] = [];\n\n for (const file of changedFiles) {\n // Get the diff to find which methods are NEW (not just modified)\n const diff = getFileDiff(workspaceRoot, file, base);\n const newMethodNames = findNewMethodSignaturesInDiff(diff);\n\n if (newMethodNames.size === 0) continue;\n\n // Parse the current file to get method line counts\n const methods = findMethodsInFile(file, workspaceRoot);\n\n for (const method of methods) {\n if (!newMethodNames.has(method.name)) continue;\n\n // Check hard limit first (if defined) - NO escape possible\n if (strictMaxLines && method.lines > strictMaxLines) {\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n isNew: true,\n isHardLimit: true,\n limit: strictMaxLines,\n });\n }\n // Check soft limit - can be escaped with disable comment\n else if (method.lines > maxLines && !method.hasDisableComment) {\n violations.push({\n file,\n methodName: method.name,\n line: method.line,\n lines: method.lines,\n isNew: true,\n isHardLimit: false,\n limit: maxLines,\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 * This allows the executor to run even when NX_BASE isn't set (e.g., via dependsOn).\n */\nfunction detectBase(workspaceRoot: string): string | null {\n try {\n // First, try to get merge-base with origin/main\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 // origin/main might not exist, try main\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 - will return null\n }\n }\n return null;\n}\n\n/**\n * Report violations to the user with helpful instructions\n */\n// webpieces-disable max-lines-new-methods -- Console output formatting requires distinct sections for hard/soft violations\nfunction reportViolations(violations: MethodViolation[], maxLines: number, strictMaxLines?: number): void {\n const hardViolations = violations.filter((v) => v.isHardLimit);\n const softViolations = violations.filter((v) => !v.isHardLimit);\n\n console.error('');\n console.error('❌ New methods exceed line limits!');\n console.error('');\n console.error('📚 Methods should read like a \"table of contents\" - each method call');\n console.error(' describes a larger piece of work. You can refactor');\n console.error(' to stay under ' + maxLines + ' lines 50% of the time.');\n console.error('');\n console.error('⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️');\n console.error('');\n\n if (hardViolations.length > 0) {\n console.error('🚫 HARD LIMIT VIOLATIONS (cannot be bypassed with disable comment):');\n console.error('');\n for (const v of hardViolations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, hard max: ${strictMaxLines})`);\n }\n console.error('');\n console.error(' These methods MUST be refactored - no escape hatch available.');\n console.error('');\n }\n\n if (softViolations.length > 0) {\n console.error('⚠️ SOFT LIMIT VIOLATIONS (can be bypassed with disable comment):');\n console.error('');\n for (const v of softViolations) {\n console.error(` ❌ ${v.file}:${v.line}`);\n console.error(` Method: ${v.methodName} (${v.lines} lines, soft max: ${maxLines})`);\n }\n console.error('');\n console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');\n console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');\n if (strictMaxLines) {\n console.error(` NOTE: Even with escape, you cannot exceed the hard limit of ${strictMaxLines} lines.`);\n }\n console.error('');\n }\n}\n\nexport default async function runExecutor(\n options: ValidateNewMethodsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const maxLines = options.max ?? 30;\n const strictMaxLines = options.strictMax;\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 new method validation (validationMode: OFF)');\n console.log('');\n return { success: true };\n }\n\n // Check if running in affected mode via NX_BASE, or auto-detect\n // We use NX_BASE as the base, and compare to WORKING TREE (not NX_HEAD)\n // This matches what `nx affected` does - it compares base to working tree\n let base = process.env['NX_BASE'];\n\n if (!base) {\n // Try to auto-detect base from git merge-base\n base = detectBase(workspaceRoot) ?? undefined;\n\n if (!base) {\n console.log('\\n⏭️ Skipping new method validation (could not detect base branch)');\n console.log(' To run explicitly: nx affected --target=validate-new-methods --base=origin/main');\n console.log('');\n return { success: true };\n }\n\n console.log('\\n📏 Validating New Method Sizes (auto-detected base)\\n');\n } else {\n console.log('\\n📏 Validating New Method Sizes\\n');\n }\n\n console.log(` Base: ${base}`);\n console.log(` Comparing to: working tree (includes uncommitted changes)`);\n console.log(` Soft limit for new methods: ${maxLines} lines (can escape with disable comment)`);\n if (strictMaxLines) {\n console.log(` Hard limit for new methods: ${strictMaxLines} lines (NO escape possible)`);\n }\n console.log('');\n\n try {\n // Get changed TypeScript files (base to working tree, like nx affected)\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 // Find violations\n const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, strictMaxLines);\n\n if (violations.length === 0) {\n const limitMsg = strictMaxLines\n ? `soft limit (${maxLines}) or hard limit (${strictMaxLines})`\n : `${maxLines} lines`;\n console.log('✅ All new methods are within ' + limitMsg);\n return { success: true };\n }\n\n // Write instructions file and report violations\n writeTmpInstructions(workspaceRoot);\n reportViolations(violations, maxLines, strictMaxLines);\n\n return { success: false };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ New method validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -26,6 +26,7 @@ export type ValidationMode = 'STRICT' | 'NORMAL' | 'OFF';
|
|
|
26
26
|
|
|
27
27
|
export interface ValidateNewMethodsOptions {
|
|
28
28
|
max?: number;
|
|
29
|
+
strictMax?: number;
|
|
29
30
|
mode?: ValidationMode;
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -39,6 +40,8 @@ interface MethodViolation {
|
|
|
39
40
|
line: number;
|
|
40
41
|
lines: number;
|
|
41
42
|
isNew: boolean;
|
|
43
|
+
isHardLimit: boolean;
|
|
44
|
+
limit: number;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
const TMP_DIR = 'tmp/webpieces';
|
|
@@ -344,7 +347,8 @@ function findViolations(
|
|
|
344
347
|
workspaceRoot: string,
|
|
345
348
|
changedFiles: string[],
|
|
346
349
|
base: string,
|
|
347
|
-
maxLines: number
|
|
350
|
+
maxLines: number,
|
|
351
|
+
strictMaxLines?: number
|
|
348
352
|
): MethodViolation[] {
|
|
349
353
|
const violations: MethodViolation[] = [];
|
|
350
354
|
|
|
@@ -359,14 +363,30 @@ function findViolations(
|
|
|
359
363
|
const methods = findMethodsInFile(file, workspaceRoot);
|
|
360
364
|
|
|
361
365
|
for (const method of methods) {
|
|
362
|
-
|
|
363
|
-
|
|
366
|
+
if (!newMethodNames.has(method.name)) continue;
|
|
367
|
+
|
|
368
|
+
// Check hard limit first (if defined) - NO escape possible
|
|
369
|
+
if (strictMaxLines && method.lines > strictMaxLines) {
|
|
370
|
+
violations.push({
|
|
371
|
+
file,
|
|
372
|
+
methodName: method.name,
|
|
373
|
+
line: method.line,
|
|
374
|
+
lines: method.lines,
|
|
375
|
+
isNew: true,
|
|
376
|
+
isHardLimit: true,
|
|
377
|
+
limit: strictMaxLines,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
// Check soft limit - can be escaped with disable comment
|
|
381
|
+
else if (method.lines > maxLines && !method.hasDisableComment) {
|
|
364
382
|
violations.push({
|
|
365
383
|
file,
|
|
366
384
|
methodName: method.name,
|
|
367
385
|
line: method.line,
|
|
368
386
|
lines: method.lines,
|
|
369
387
|
isNew: true,
|
|
388
|
+
isHardLimit: false,
|
|
389
|
+
limit: maxLines,
|
|
370
390
|
});
|
|
371
391
|
}
|
|
372
392
|
}
|
|
@@ -413,9 +433,13 @@ function detectBase(workspaceRoot: string): string | null {
|
|
|
413
433
|
/**
|
|
414
434
|
* Report violations to the user with helpful instructions
|
|
415
435
|
*/
|
|
416
|
-
|
|
436
|
+
// webpieces-disable max-lines-new-methods -- Console output formatting requires distinct sections for hard/soft violations
|
|
437
|
+
function reportViolations(violations: MethodViolation[], maxLines: number, strictMaxLines?: number): void {
|
|
438
|
+
const hardViolations = violations.filter((v) => v.isHardLimit);
|
|
439
|
+
const softViolations = violations.filter((v) => !v.isHardLimit);
|
|
440
|
+
|
|
417
441
|
console.error('');
|
|
418
|
-
console.error('❌ New methods exceed
|
|
442
|
+
console.error('❌ New methods exceed line limits!');
|
|
419
443
|
console.error('');
|
|
420
444
|
console.error('📚 Methods should read like a "table of contents" - each method call');
|
|
421
445
|
console.error(' describes a larger piece of work. You can refactor');
|
|
@@ -424,14 +448,33 @@ function reportViolations(violations: MethodViolation[], maxLines: number): void
|
|
|
424
448
|
console.error('⚠️ *** READ tmp/webpieces/webpieces.methodsize.md for detailed guidance on how to fix this easily *** ⚠️');
|
|
425
449
|
console.error('');
|
|
426
450
|
|
|
427
|
-
|
|
428
|
-
console.error(
|
|
429
|
-
console.error(
|
|
451
|
+
if (hardViolations.length > 0) {
|
|
452
|
+
console.error('🚫 HARD LIMIT VIOLATIONS (cannot be bypassed with disable comment):');
|
|
453
|
+
console.error('');
|
|
454
|
+
for (const v of hardViolations) {
|
|
455
|
+
console.error(` ❌ ${v.file}:${v.line}`);
|
|
456
|
+
console.error(` Method: ${v.methodName} (${v.lines} lines, hard max: ${strictMaxLines})`);
|
|
457
|
+
}
|
|
458
|
+
console.error('');
|
|
459
|
+
console.error(' These methods MUST be refactored - no escape hatch available.');
|
|
460
|
+
console.error('');
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (softViolations.length > 0) {
|
|
464
|
+
console.error('⚠️ SOFT LIMIT VIOLATIONS (can be bypassed with disable comment):');
|
|
465
|
+
console.error('');
|
|
466
|
+
for (const v of softViolations) {
|
|
467
|
+
console.error(` ❌ ${v.file}:${v.line}`);
|
|
468
|
+
console.error(` Method: ${v.methodName} (${v.lines} lines, soft max: ${maxLines})`);
|
|
469
|
+
}
|
|
470
|
+
console.error('');
|
|
471
|
+
console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');
|
|
472
|
+
console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');
|
|
473
|
+
if (strictMaxLines) {
|
|
474
|
+
console.error(` NOTE: Even with escape, you cannot exceed the hard limit of ${strictMaxLines} lines.`);
|
|
475
|
+
}
|
|
476
|
+
console.error('');
|
|
430
477
|
}
|
|
431
|
-
console.error('');
|
|
432
|
-
console.error(' If you REALLY REALLY need more than ' + maxLines + ' lines, this happens 50% of the time,');
|
|
433
|
-
console.error(' so use escape: // webpieces-disable max-lines-new-methods -- [your reason]');
|
|
434
|
-
console.error('');
|
|
435
478
|
}
|
|
436
479
|
|
|
437
480
|
export default async function runExecutor(
|
|
@@ -440,6 +483,7 @@ export default async function runExecutor(
|
|
|
440
483
|
): Promise<ExecutorResult> {
|
|
441
484
|
const workspaceRoot = context.root;
|
|
442
485
|
const maxLines = options.max ?? 30;
|
|
486
|
+
const strictMaxLines = options.strictMax;
|
|
443
487
|
const mode: ValidationMode = options.mode ?? 'NORMAL';
|
|
444
488
|
|
|
445
489
|
// Skip validation entirely if mode is OFF
|
|
@@ -472,7 +516,10 @@ export default async function runExecutor(
|
|
|
472
516
|
|
|
473
517
|
console.log(` Base: ${base}`);
|
|
474
518
|
console.log(` Comparing to: working tree (includes uncommitted changes)`);
|
|
475
|
-
console.log(`
|
|
519
|
+
console.log(` Soft limit for new methods: ${maxLines} lines (can escape with disable comment)`);
|
|
520
|
+
if (strictMaxLines) {
|
|
521
|
+
console.log(` Hard limit for new methods: ${strictMaxLines} lines (NO escape possible)`);
|
|
522
|
+
}
|
|
476
523
|
console.log('');
|
|
477
524
|
|
|
478
525
|
try {
|
|
@@ -487,16 +534,19 @@ export default async function runExecutor(
|
|
|
487
534
|
console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
|
|
488
535
|
|
|
489
536
|
// Find violations
|
|
490
|
-
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines);
|
|
537
|
+
const violations = findViolations(workspaceRoot, changedFiles, base, maxLines, strictMaxLines);
|
|
491
538
|
|
|
492
539
|
if (violations.length === 0) {
|
|
493
|
-
|
|
540
|
+
const limitMsg = strictMaxLines
|
|
541
|
+
? `soft limit (${maxLines}) or hard limit (${strictMaxLines})`
|
|
542
|
+
: `${maxLines} lines`;
|
|
543
|
+
console.log('✅ All new methods are within ' + limitMsg);
|
|
494
544
|
return { success: true };
|
|
495
545
|
}
|
|
496
546
|
|
|
497
547
|
// Write instructions file and report violations
|
|
498
548
|
writeTmpInstructions(workspaceRoot);
|
|
499
|
-
reportViolations(violations, maxLines);
|
|
549
|
+
reportViolations(violations, maxLines, strictMaxLines);
|
|
500
550
|
|
|
501
551
|
return { success: false };
|
|
502
552
|
} catch (err: unknown) {
|
|
@@ -6,13 +6,17 @@
|
|
|
6
6
|
"properties": {
|
|
7
7
|
"max": {
|
|
8
8
|
"type": "number",
|
|
9
|
-
"description": "
|
|
9
|
+
"description": "Soft limit: Maximum lines for new methods (can be bypassed with disable comment)",
|
|
10
10
|
"default": 30
|
|
11
11
|
},
|
|
12
|
+
"strictMax": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"description": "Hard limit: Absolute maximum lines for new methods (CANNOT be bypassed with disable comment). If not set, no hard limit is enforced."
|
|
15
|
+
},
|
|
12
16
|
"mode": {
|
|
13
17
|
"type": "string",
|
|
14
18
|
"enum": ["STRICT", "NORMAL", "OFF"],
|
|
15
|
-
"description": "OFF: skip validation. NORMAL/STRICT: validate (disable comments
|
|
19
|
+
"description": "OFF: skip validation. NORMAL/STRICT: validate (disable comments work for soft limit only).",
|
|
16
20
|
"default": "NORMAL"
|
|
17
21
|
}
|
|
18
22
|
},
|
package/executors.json
CHANGED
|
@@ -59,6 +59,11 @@
|
|
|
59
59
|
"implementation": "./architecture/executors/validate-modified-files/executor",
|
|
60
60
|
"schema": "./architecture/executors/validate-modified-files/schema.json",
|
|
61
61
|
"description": "Validate modified files don't exceed max line count"
|
|
62
|
+
},
|
|
63
|
+
"validate-code": {
|
|
64
|
+
"implementation": "./architecture/executors/validate-code/executor",
|
|
65
|
+
"schema": "./architecture/executors/validate-code/schema.json",
|
|
66
|
+
"description": "Combined validation for new methods, modified methods, and file sizes"
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
}
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -156,12 +156,10 @@ function buildValidationTargetsList(validations) {
|
|
|
156
156
|
targets.push('validate-no-skiplevel-deps');
|
|
157
157
|
if (validations.validatePackageJson)
|
|
158
158
|
targets.push('validate-packagejson');
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (validations.validateModifiedFiles)
|
|
164
|
-
targets.push('validate-modified-files');
|
|
159
|
+
// Use combined validate-code instead of 3 separate targets
|
|
160
|
+
if (validations.validateNewMethods || validations.validateModifiedMethods || validations.validateModifiedFiles) {
|
|
161
|
+
targets.push('validate-code');
|
|
162
|
+
}
|
|
165
163
|
if (validations.validateVersionsLocked)
|
|
166
164
|
targets.push('validate-versions-locked');
|
|
167
165
|
return targets;
|
|
@@ -194,14 +192,10 @@ function createWorkspaceTargetsWithoutPrefix(opts) {
|
|
|
194
192
|
if (validations.validatePackageJson) {
|
|
195
193
|
targets['validate-packagejson'] = createValidatePackageJsonTarget();
|
|
196
194
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
targets['validate-modified-methods'] = createValidateModifiedMethodsTarget(validations.modifiedAndNewMethodsMaxLines, validations.validationMode);
|
|
202
|
-
}
|
|
203
|
-
if (validations.validateModifiedFiles) {
|
|
204
|
-
targets['validate-modified-files'] = createValidateModifiedFilesTarget(validations.modifiedFilesMaxLines, validations.validationMode);
|
|
195
|
+
// Use combined validate-code instead of 3 separate targets
|
|
196
|
+
// Options come from targetDefaults in nx.json (applied at runtime, no cache issues)
|
|
197
|
+
if (validations.validateNewMethods || validations.validateModifiedMethods || validations.validateModifiedFiles) {
|
|
198
|
+
targets['validate-code'] = createValidateCodeTarget();
|
|
205
199
|
}
|
|
206
200
|
if (validations.validateVersionsLocked) {
|
|
207
201
|
targets['validate-versions-locked'] = createValidateVersionsLockedTarget();
|
|
@@ -241,14 +235,10 @@ function createWorkspaceTargets(opts) {
|
|
|
241
235
|
if (opts.workspace.validations.validatePackageJson) {
|
|
242
236
|
targets[`${prefix}validate-packagejson`] = createValidatePackageJsonTarget();
|
|
243
237
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
targets[`${prefix}validate-modified-methods`] = createValidateModifiedMethodsTarget(opts.workspace.validations.modifiedAndNewMethodsMaxLines, opts.workspace.validations.validationMode);
|
|
249
|
-
}
|
|
250
|
-
if (opts.workspace.validations.validateModifiedFiles) {
|
|
251
|
-
targets[`${prefix}validate-modified-files`] = createValidateModifiedFilesTarget(opts.workspace.validations.modifiedFilesMaxLines, opts.workspace.validations.validationMode);
|
|
238
|
+
// Use combined validate-code instead of 3 separate targets
|
|
239
|
+
// Options come from targetDefaults in nx.json (applied at runtime, no cache issues)
|
|
240
|
+
if (opts.workspace.validations.validateNewMethods || opts.workspace.validations.validateModifiedMethods || opts.workspace.validations.validateModifiedFiles) {
|
|
241
|
+
targets[`${prefix}validate-code`] = createValidateCodeTarget();
|
|
252
242
|
}
|
|
253
243
|
return targets;
|
|
254
244
|
}
|
|
@@ -368,6 +358,22 @@ function createValidateModifiedFilesTarget(maxLines, mode) {
|
|
|
368
358
|
},
|
|
369
359
|
};
|
|
370
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Create combined validate-code target
|
|
363
|
+
* Options come from targetDefaults in nx.json (applied at runtime, no cache issues)
|
|
364
|
+
*/
|
|
365
|
+
function createValidateCodeTarget() {
|
|
366
|
+
return {
|
|
367
|
+
executor: '@webpieces/dev-config:validate-code',
|
|
368
|
+
cache: false, // Don't cache - depends on git state
|
|
369
|
+
inputs: ['default'],
|
|
370
|
+
// No options here - they come from targetDefaults at runtime
|
|
371
|
+
metadata: {
|
|
372
|
+
technologies: ['nx'],
|
|
373
|
+
description: 'Combined validation for new methods, modified methods, and file sizes',
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
}
|
|
371
377
|
function createValidateVersionsLockedTarget() {
|
|
372
378
|
return {
|
|
373
379
|
executor: '@webpieces/dev-config:validate-versions-locked',
|