@webpieces/dev-config 0.2.94 → 0.2.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/config/eslint/base.mjs +1 -1
  2. package/executors.json +6 -91
  3. package/package.json +6 -19
  4. package/{executors → src/executors}/help/executor.d.ts +4 -2
  5. package/src/executors/help/executor.js.map +1 -0
  6. package/{executors → src/executors}/validate-eslint-sync/executor.d.ts +3 -2
  7. package/src/executors/validate-eslint-sync/executor.js.map +1 -0
  8. package/{executors → src/executors}/validate-versions-locked/executor.js +5 -3
  9. package/src/executors/validate-versions-locked/executor.js.map +1 -0
  10. package/src/generators/init/generator.js.map +1 -1
  11. package/src/index.d.ts +1 -1
  12. package/src/index.js +1 -1
  13. package/src/index.js.map +1 -1
  14. package/src/plugin.d.ts +86 -0
  15. package/{plugin.js → src/plugin.js} +31 -15
  16. package/src/plugin.js.map +1 -0
  17. package/src/toError.d.ts +5 -0
  18. package/src/toError.js +37 -0
  19. package/src/toError.js.map +1 -0
  20. package/templates/eslint.webpieces.config.mjs +1 -1
  21. package/templates/webpieces.exceptions.md +15 -15
  22. package/architecture/executors/diff-utils.d.ts +0 -24
  23. package/architecture/executors/diff-utils.js +0 -119
  24. package/architecture/executors/diff-utils.js.map +0 -1
  25. package/architecture/executors/diff-utils.ts +0 -127
  26. package/architecture/executors/generate/executor.d.ts +0 -16
  27. package/architecture/executors/generate/executor.js +0 -44
  28. package/architecture/executors/generate/executor.js.map +0 -1
  29. package/architecture/executors/generate/executor.ts +0 -59
  30. package/architecture/executors/generate/schema.json +0 -14
  31. package/architecture/executors/validate-architecture-unchanged/executor.d.ts +0 -17
  32. package/architecture/executors/validate-architecture-unchanged/executor.js +0 -229
  33. package/architecture/executors/validate-architecture-unchanged/executor.js.map +0 -1
  34. package/architecture/executors/validate-architecture-unchanged/executor.ts +0 -251
  35. package/architecture/executors/validate-architecture-unchanged/schema.json +0 -14
  36. package/architecture/executors/validate-code/executor.d.ts +0 -78
  37. package/architecture/executors/validate-code/executor.js +0 -243
  38. package/architecture/executors/validate-code/executor.js.map +0 -1
  39. package/architecture/executors/validate-code/executor.ts +0 -406
  40. package/architecture/executors/validate-code/schema.json +0 -227
  41. package/architecture/executors/validate-dtos/executor.d.ts +0 -42
  42. package/architecture/executors/validate-dtos/executor.js +0 -561
  43. package/architecture/executors/validate-dtos/executor.js.map +0 -1
  44. package/architecture/executors/validate-dtos/executor.ts +0 -689
  45. package/architecture/executors/validate-dtos/schema.json +0 -33
  46. package/architecture/executors/validate-modified-files/executor.d.ts +0 -25
  47. package/architecture/executors/validate-modified-files/executor.js +0 -501
  48. package/architecture/executors/validate-modified-files/executor.js.map +0 -1
  49. package/architecture/executors/validate-modified-files/executor.ts +0 -571
  50. package/architecture/executors/validate-modified-files/schema.json +0 -25
  51. package/architecture/executors/validate-modified-methods/executor.d.ts +0 -31
  52. package/architecture/executors/validate-modified-methods/executor.js +0 -694
  53. package/architecture/executors/validate-modified-methods/executor.js.map +0 -1
  54. package/architecture/executors/validate-modified-methods/executor.ts +0 -797
  55. package/architecture/executors/validate-modified-methods/schema.json +0 -25
  56. package/architecture/executors/validate-new-methods/executor.d.ts +0 -28
  57. package/architecture/executors/validate-new-methods/executor.js +0 -513
  58. package/architecture/executors/validate-new-methods/executor.js.map +0 -1
  59. package/architecture/executors/validate-new-methods/executor.ts +0 -584
  60. package/architecture/executors/validate-new-methods/schema.json +0 -25
  61. package/architecture/executors/validate-no-any-unknown/executor.d.ts +0 -42
  62. package/architecture/executors/validate-no-any-unknown/executor.js +0 -462
  63. package/architecture/executors/validate-no-any-unknown/executor.js.map +0 -1
  64. package/architecture/executors/validate-no-any-unknown/executor.ts +0 -540
  65. package/architecture/executors/validate-no-any-unknown/schema.json +0 -24
  66. package/architecture/executors/validate-no-architecture-cycles/executor.d.ts +0 -16
  67. package/architecture/executors/validate-no-architecture-cycles/executor.js +0 -48
  68. package/architecture/executors/validate-no-architecture-cycles/executor.js.map +0 -1
  69. package/architecture/executors/validate-no-architecture-cycles/executor.ts +0 -60
  70. package/architecture/executors/validate-no-architecture-cycles/schema.json +0 -8
  71. package/architecture/executors/validate-no-destructure/executor.d.ts +0 -52
  72. package/architecture/executors/validate-no-destructure/executor.js +0 -491
  73. package/architecture/executors/validate-no-destructure/executor.js.map +0 -1
  74. package/architecture/executors/validate-no-destructure/executor.ts +0 -578
  75. package/architecture/executors/validate-no-destructure/schema.json +0 -24
  76. package/architecture/executors/validate-no-direct-api-resolver/executor.d.ts +0 -47
  77. package/architecture/executors/validate-no-direct-api-resolver/executor.js +0 -566
  78. package/architecture/executors/validate-no-direct-api-resolver/executor.js.map +0 -1
  79. package/architecture/executors/validate-no-direct-api-resolver/executor.ts +0 -666
  80. package/architecture/executors/validate-no-direct-api-resolver/schema.json +0 -29
  81. package/architecture/executors/validate-no-inline-types/executor.d.ts +0 -91
  82. package/architecture/executors/validate-no-inline-types/executor.js +0 -669
  83. package/architecture/executors/validate-no-inline-types/executor.js.map +0 -1
  84. package/architecture/executors/validate-no-inline-types/executor.ts +0 -775
  85. package/architecture/executors/validate-no-inline-types/schema.json +0 -24
  86. package/architecture/executors/validate-no-skiplevel-deps/executor.d.ts +0 -19
  87. package/architecture/executors/validate-no-skiplevel-deps/executor.js +0 -227
  88. package/architecture/executors/validate-no-skiplevel-deps/executor.js.map +0 -1
  89. package/architecture/executors/validate-no-skiplevel-deps/executor.ts +0 -267
  90. package/architecture/executors/validate-no-skiplevel-deps/schema.json +0 -8
  91. package/architecture/executors/validate-packagejson/executor.d.ts +0 -16
  92. package/architecture/executors/validate-packagejson/executor.js +0 -57
  93. package/architecture/executors/validate-packagejson/executor.js.map +0 -1
  94. package/architecture/executors/validate-packagejson/executor.ts +0 -74
  95. package/architecture/executors/validate-packagejson/schema.json +0 -8
  96. package/architecture/executors/validate-prisma-converters/executor.d.ts +0 -60
  97. package/architecture/executors/validate-prisma-converters/executor.js +0 -634
  98. package/architecture/executors/validate-prisma-converters/executor.js.map +0 -1
  99. package/architecture/executors/validate-prisma-converters/executor.ts +0 -822
  100. package/architecture/executors/validate-prisma-converters/schema.json +0 -38
  101. package/architecture/executors/validate-return-types/executor.d.ts +0 -29
  102. package/architecture/executors/validate-return-types/executor.js +0 -439
  103. package/architecture/executors/validate-return-types/executor.js.map +0 -1
  104. package/architecture/executors/validate-return-types/executor.ts +0 -524
  105. package/architecture/executors/validate-return-types/schema.json +0 -24
  106. package/architecture/executors/visualize/executor.d.ts +0 -17
  107. package/architecture/executors/visualize/executor.js +0 -49
  108. package/architecture/executors/visualize/executor.js.map +0 -1
  109. package/architecture/executors/visualize/executor.ts +0 -63
  110. package/architecture/executors/visualize/schema.json +0 -14
  111. package/architecture/index.d.ts +0 -19
  112. package/architecture/index.js +0 -23
  113. package/architecture/index.js.map +0 -1
  114. package/architecture/index.ts +0 -20
  115. package/architecture/lib/graph-comparator.d.ts +0 -39
  116. package/architecture/lib/graph-comparator.js +0 -100
  117. package/architecture/lib/graph-comparator.js.map +0 -1
  118. package/architecture/lib/graph-comparator.ts +0 -141
  119. package/architecture/lib/graph-generator.d.ts +0 -19
  120. package/architecture/lib/graph-generator.js +0 -84
  121. package/architecture/lib/graph-generator.js.map +0 -1
  122. package/architecture/lib/graph-generator.ts +0 -97
  123. package/architecture/lib/graph-loader.d.ts +0 -31
  124. package/architecture/lib/graph-loader.js +0 -98
  125. package/architecture/lib/graph-loader.js.map +0 -1
  126. package/architecture/lib/graph-loader.ts +0 -116
  127. package/architecture/lib/graph-sorter.d.ts +0 -37
  128. package/architecture/lib/graph-sorter.js +0 -110
  129. package/architecture/lib/graph-sorter.js.map +0 -1
  130. package/architecture/lib/graph-sorter.ts +0 -137
  131. package/architecture/lib/graph-visualizer.d.ts +0 -29
  132. package/architecture/lib/graph-visualizer.js +0 -217
  133. package/architecture/lib/graph-visualizer.js.map +0 -1
  134. package/architecture/lib/graph-visualizer.ts +0 -231
  135. package/architecture/lib/package-validator.d.ts +0 -38
  136. package/architecture/lib/package-validator.js +0 -126
  137. package/architecture/lib/package-validator.js.map +0 -1
  138. package/architecture/lib/package-validator.ts +0 -170
  139. package/eslint-plugin/__tests__/catch-error-pattern.test.ts +0 -359
  140. package/eslint-plugin/__tests__/max-file-lines.test.ts +0 -207
  141. package/eslint-plugin/__tests__/max-method-lines.test.ts +0 -258
  142. package/eslint-plugin/__tests__/no-unmanaged-exceptions.test.ts +0 -359
  143. package/eslint-plugin/index.d.ts +0 -23
  144. package/eslint-plugin/index.js +0 -30
  145. package/eslint-plugin/index.js.map +0 -1
  146. package/eslint-plugin/index.ts +0 -29
  147. package/eslint-plugin/rules/catch-error-pattern.d.ts +0 -11
  148. package/eslint-plugin/rules/catch-error-pattern.js +0 -196
  149. package/eslint-plugin/rules/catch-error-pattern.js.map +0 -1
  150. package/eslint-plugin/rules/catch-error-pattern.ts +0 -281
  151. package/eslint-plugin/rules/enforce-architecture.d.ts +0 -15
  152. package/eslint-plugin/rules/enforce-architecture.js +0 -476
  153. package/eslint-plugin/rules/enforce-architecture.js.map +0 -1
  154. package/eslint-plugin/rules/enforce-architecture.ts +0 -543
  155. package/eslint-plugin/rules/max-file-lines.d.ts +0 -12
  156. package/eslint-plugin/rules/max-file-lines.js +0 -257
  157. package/eslint-plugin/rules/max-file-lines.js.map +0 -1
  158. package/eslint-plugin/rules/max-file-lines.ts +0 -272
  159. package/eslint-plugin/rules/max-method-lines.d.ts +0 -12
  160. package/eslint-plugin/rules/max-method-lines.js +0 -240
  161. package/eslint-plugin/rules/max-method-lines.js.map +0 -1
  162. package/eslint-plugin/rules/max-method-lines.ts +0 -287
  163. package/eslint-plugin/rules/no-unmanaged-exceptions.d.ts +0 -22
  164. package/eslint-plugin/rules/no-unmanaged-exceptions.js +0 -160
  165. package/eslint-plugin/rules/no-unmanaged-exceptions.js.map +0 -1
  166. package/eslint-plugin/rules/no-unmanaged-exceptions.ts +0 -179
  167. package/executors/help/executor.js.map +0 -1
  168. package/executors/help/executor.ts +0 -61
  169. package/executors/validate-eslint-sync/executor.js.map +0 -1
  170. package/executors/validate-eslint-sync/executor.ts +0 -83
  171. package/executors/validate-versions-locked/executor.js.map +0 -1
  172. package/executors/validate-versions-locked/executor.ts +0 -367
  173. package/plugin/README.md +0 -243
  174. package/plugin/index.d.ts +0 -4
  175. package/plugin/index.js +0 -8
  176. package/plugin/index.js.map +0 -1
  177. package/plugin/index.ts +0 -4
  178. /package/{executors → src/executors}/help/executor.js +0 -0
  179. /package/{executors → src/executors}/help/schema.json +0 -0
  180. /package/{executors → src/executors}/validate-eslint-sync/executor.js +0 -0
  181. /package/{executors → src/executors}/validate-eslint-sync/schema.json +0 -0
  182. /package/{executors → src/executors}/validate-versions-locked/executor.d.ts +0 -0
  183. /package/{executors → src/executors}/validate-versions-locked/schema.json +0 -0
@@ -1,540 +0,0 @@
1
- /**
2
- * Validate No Any Unknown Executor
3
- *
4
- * Validates that `any` and `unknown` TypeScript keywords are not used.
5
- * Uses LINE-BASED detection (not method-based) for git diff filtering.
6
- *
7
- * ============================================================================
8
- * VIOLATIONS (BAD) - These patterns are flagged:
9
- * ============================================================================
10
- *
11
- * - const x: any = ...
12
- * - function foo(arg: any): any { }
13
- * - const data = response as any;
14
- * - type T = any;
15
- * - const x: unknown = ...
16
- * - function foo(arg: unknown): unknown { }
17
- *
18
- * ============================================================================
19
- * MODES (LINE-BASED)
20
- * ============================================================================
21
- * - OFF: Skip validation entirely
22
- * - MODIFIED_CODE: Flag any/unknown on changed lines (lines in diff hunks)
23
- * - MODIFIED_FILES: Flag ALL any/unknown in files that were modified
24
- *
25
- * ============================================================================
26
- * ESCAPE HATCH
27
- * ============================================================================
28
- * Add comment above the violation:
29
- * // webpieces-disable no-any-unknown -- [your justification]
30
- * const x: any = ...;
31
- */
32
-
33
- import type { ExecutorContext } from '@nx/devkit';
34
- import { execSync } from 'child_process';
35
- import * as fs from 'fs';
36
- import * as path from 'path';
37
- import * as ts from 'typescript';
38
-
39
- export type NoAnyUnknownMode = 'OFF' | 'MODIFIED_CODE' | 'MODIFIED_FILES';
40
-
41
- export interface ValidateNoAnyUnknownOptions {
42
- mode?: NoAnyUnknownMode;
43
- disableAllowed?: boolean;
44
- ignoreModifiedUntilEpoch?: number;
45
- }
46
-
47
- export interface ExecutorResult {
48
- success: boolean;
49
- }
50
-
51
- interface AnyUnknownViolation {
52
- file: string;
53
- line: number;
54
- column: number;
55
- keyword: 'any' | 'unknown';
56
- context: string;
57
- }
58
-
59
- /**
60
- * Get changed TypeScript files between base and head (or working tree if head not specified).
61
- */
62
- // webpieces-disable max-lines-new-methods -- Git command handling with untracked files requires multiple code paths
63
- function getChangedTypeScriptFiles(workspaceRoot: string, base: string, head?: string): string[] {
64
- try {
65
- const diffTarget = head ? `${base} ${head}` : base;
66
- const output = execSync(`git diff --name-only ${diffTarget} -- '*.ts' '*.tsx'`, {
67
- cwd: workspaceRoot,
68
- encoding: 'utf-8',
69
- });
70
- const changedFiles = output
71
- .trim()
72
- .split('\n')
73
- .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
74
-
75
- if (!head) {
76
- try {
77
- const untrackedOutput = execSync(`git ls-files --others --exclude-standard '*.ts' '*.tsx'`, {
78
- cwd: workspaceRoot,
79
- encoding: 'utf-8',
80
- });
81
- const untrackedFiles = untrackedOutput
82
- .trim()
83
- .split('\n')
84
- .filter((f) => f && !f.includes('.spec.ts') && !f.includes('.test.ts'));
85
- const allFiles = new Set([...changedFiles, ...untrackedFiles]);
86
- return Array.from(allFiles);
87
- } catch {
88
- return changedFiles;
89
- }
90
- }
91
-
92
- return changedFiles;
93
- } catch {
94
- return [];
95
- }
96
- }
97
-
98
- /**
99
- * Get the diff content for a specific file.
100
- */
101
- function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {
102
- try {
103
- const diffTarget = head ? `${base} ${head}` : base;
104
- const diff = execSync(`git diff ${diffTarget} -- "${file}"`, {
105
- cwd: workspaceRoot,
106
- encoding: 'utf-8',
107
- });
108
-
109
- if (!diff && !head) {
110
- const fullPath = path.join(workspaceRoot, file);
111
- if (fs.existsSync(fullPath)) {
112
- const isUntracked = execSync(`git ls-files --others --exclude-standard "${file}"`, {
113
- cwd: workspaceRoot,
114
- encoding: 'utf-8',
115
- }).trim();
116
-
117
- if (isUntracked) {
118
- const content = fs.readFileSync(fullPath, 'utf-8');
119
- const lines = content.split('\n');
120
- return lines.map((line) => `+${line}`).join('\n');
121
- }
122
- }
123
- }
124
-
125
- return diff;
126
- } catch {
127
- return '';
128
- }
129
- }
130
-
131
- /**
132
- * Parse diff to extract changed line numbers (additions only - lines starting with +).
133
- */
134
- function getChangedLineNumbers(diffContent: string): Set<number> {
135
- const changedLines = new Set<number>();
136
- const lines = diffContent.split('\n');
137
- let currentLine = 0;
138
-
139
- for (const line of lines) {
140
- const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
141
- if (hunkMatch) {
142
- currentLine = parseInt(hunkMatch[1], 10);
143
- continue;
144
- }
145
-
146
- if (line.startsWith('+') && !line.startsWith('+++')) {
147
- changedLines.add(currentLine);
148
- currentLine++;
149
- } else if (line.startsWith('-') && !line.startsWith('---')) {
150
- // Deletions don't increment line number
151
- } else {
152
- currentLine++;
153
- }
154
- }
155
-
156
- return changedLines;
157
- }
158
-
159
- /**
160
- * Check if a line contains a webpieces-disable comment for no-any-unknown.
161
- */
162
- function hasDisableComment(lines: string[], lineNumber: number): boolean {
163
- const startCheck = Math.max(0, lineNumber - 5);
164
- for (let i = lineNumber - 2; i >= startCheck; i--) {
165
- const line = lines[i]?.trim() ?? '';
166
- if (line.startsWith('function ') || line.startsWith('class ') || line.endsWith('}')) {
167
- break;
168
- }
169
- if (line.includes('webpieces-disable') && line.includes('no-any-unknown')) {
170
- return true;
171
- }
172
- }
173
- return false;
174
- }
175
-
176
- /**
177
- * Get a description of the context where the any/unknown keyword appears.
178
- */
179
- // webpieces-disable max-lines-new-methods -- Context detection requires checking many AST node types
180
- function getViolationContext(node: ts.Node, sourceFile: ts.SourceFile): string {
181
- try {
182
- let current: ts.Node = node;
183
- while (current.parent) {
184
- const parent = current.parent;
185
- if (ts.isParameter(parent)) {
186
- return 'parameter type';
187
- }
188
- if (ts.isFunctionDeclaration(parent) || ts.isMethodDeclaration(parent) || ts.isArrowFunction(parent)) {
189
- if (parent.type === current) {
190
- return 'return type';
191
- }
192
- }
193
- if (ts.isVariableDeclaration(parent)) {
194
- return 'variable type';
195
- }
196
- if (ts.isPropertyDeclaration(parent) || ts.isPropertySignature(parent)) {
197
- return 'property type';
198
- }
199
- if (ts.isAsExpression(parent)) {
200
- return 'type assertion';
201
- }
202
- if (ts.isTypeAliasDeclaration(parent)) {
203
- return 'type alias';
204
- }
205
- if (ts.isTypeReferenceNode(parent)) {
206
- return 'generic argument';
207
- }
208
- if (ts.isArrayTypeNode(parent)) {
209
- return 'array element type';
210
- }
211
- if (ts.isUnionTypeNode(parent) || ts.isIntersectionTypeNode(parent)) {
212
- return 'union/intersection type';
213
- }
214
- current = parent;
215
- }
216
- return 'type position';
217
- } catch {
218
- return 'type position';
219
- }
220
- }
221
-
222
- interface AnyUnknownInfo {
223
- line: number;
224
- column: number;
225
- keyword: 'any' | 'unknown';
226
- context: string;
227
- hasDisableComment: boolean;
228
- }
229
-
230
- /**
231
- * Check if a node is in a catch clause variable declaration.
232
- * This allows `catch (err: any)` and `catch (err: unknown)` patterns.
233
- */
234
- function isInCatchClause(node: ts.Node): boolean {
235
- let current: ts.Node | undefined = node.parent;
236
- while (current) {
237
- if (ts.isCatchClause(current)) {
238
- // We're somewhere in a catch clause - check if we're in the variable declaration
239
- const catchClause = current as ts.CatchClause;
240
- if (catchClause.variableDeclaration) {
241
- // Walk back up from the original node to see if we're part of the variable declaration
242
- let checkNode: ts.Node | undefined = node.parent;
243
- while (checkNode && checkNode !== current) {
244
- if (checkNode === catchClause.variableDeclaration) {
245
- return true;
246
- }
247
- checkNode = checkNode.parent;
248
- }
249
- }
250
- }
251
- current = current.parent;
252
- }
253
- return false;
254
- }
255
-
256
- /**
257
- * Find all `any` and `unknown` keywords in a file using AST.
258
- */
259
- // webpieces-disable max-lines-new-methods -- AST traversal with nested visitor function for keyword detection
260
- function findAnyUnknownInFile(filePath: string, workspaceRoot: string): AnyUnknownInfo[] {
261
- const fullPath = path.join(workspaceRoot, filePath);
262
- if (!fs.existsSync(fullPath)) return [];
263
-
264
- const content = fs.readFileSync(fullPath, 'utf-8');
265
- const fileLines = content.split('\n');
266
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
267
-
268
- const violations: AnyUnknownInfo[] = [];
269
-
270
- // webpieces-disable max-lines-new-methods -- AST visitor needs to handle both any and unknown keywords with full context detection
271
- function visit(node: ts.Node): void {
272
- try {
273
- // Detect `any` keyword
274
- if (node.kind === ts.SyntaxKind.AnyKeyword) {
275
- // Skip catch clause variable types: catch (err: any) is allowed
276
- if (isInCatchClause(node)) {
277
- ts.forEachChild(node, visit);
278
- return;
279
- }
280
-
281
- const startPos = node.getStart(sourceFile);
282
- if (startPos >= 0) {
283
- const pos = sourceFile.getLineAndCharacterOfPosition(startPos);
284
- const line = pos.line + 1;
285
- const column = pos.character + 1;
286
- const context = getViolationContext(node, sourceFile);
287
- const disabled = hasDisableComment(fileLines, line);
288
-
289
- violations.push({
290
- line,
291
- column,
292
- keyword: 'any',
293
- context,
294
- hasDisableComment: disabled,
295
- });
296
- }
297
- }
298
-
299
- // Detect `unknown` keyword
300
- if (node.kind === ts.SyntaxKind.UnknownKeyword) {
301
- // Skip catch clause variable types: catch (err: unknown) is allowed
302
- if (isInCatchClause(node)) {
303
- ts.forEachChild(node, visit);
304
- return;
305
- }
306
-
307
- const startPos = node.getStart(sourceFile);
308
- if (startPos >= 0) {
309
- const pos = sourceFile.getLineAndCharacterOfPosition(startPos);
310
- const line = pos.line + 1;
311
- const column = pos.character + 1;
312
- const context = getViolationContext(node, sourceFile);
313
- const disabled = hasDisableComment(fileLines, line);
314
-
315
- violations.push({
316
- line,
317
- column,
318
- keyword: 'unknown',
319
- context,
320
- hasDisableComment: disabled,
321
- });
322
- }
323
- }
324
- } catch {
325
- // Skip nodes that cause errors during analysis
326
- }
327
-
328
- ts.forEachChild(node, visit);
329
- }
330
-
331
- visit(sourceFile);
332
- return violations;
333
- }
334
-
335
- /**
336
- * MODIFIED_CODE mode: Flag violations on changed lines in diff hunks.
337
- * This is LINE-BASED detection.
338
- */
339
- // webpieces-disable max-lines-new-methods -- File iteration with diff parsing and line filtering
340
- function findViolationsForModifiedCode(
341
- workspaceRoot: string,
342
- changedFiles: string[],
343
- base: string,
344
- head: string | undefined,
345
- disableAllowed: boolean
346
- ): AnyUnknownViolation[] {
347
- const violations: AnyUnknownViolation[] = [];
348
-
349
- for (const file of changedFiles) {
350
- const diff = getFileDiff(workspaceRoot, file, base, head);
351
- const changedLines = getChangedLineNumbers(diff);
352
-
353
- if (changedLines.size === 0) continue;
354
-
355
- const allViolations = findAnyUnknownInFile(file, workspaceRoot);
356
-
357
- for (const v of allViolations) {
358
- if (disableAllowed && v.hasDisableComment) continue;
359
- // LINE-BASED: Only include if the violation is on a changed line
360
- if (!changedLines.has(v.line)) continue;
361
-
362
- violations.push({
363
- file,
364
- line: v.line,
365
- column: v.column,
366
- keyword: v.keyword,
367
- context: v.context,
368
- });
369
- }
370
- }
371
-
372
- return violations;
373
- }
374
-
375
- /**
376
- * MODIFIED_FILES mode: Flag ALL violations in files that were modified.
377
- */
378
- function findViolationsForModifiedFiles(workspaceRoot: string, changedFiles: string[], disableAllowed: boolean): AnyUnknownViolation[] {
379
- const violations: AnyUnknownViolation[] = [];
380
-
381
- for (const file of changedFiles) {
382
- const allViolations = findAnyUnknownInFile(file, workspaceRoot);
383
-
384
- for (const v of allViolations) {
385
- if (disableAllowed && v.hasDisableComment) continue;
386
-
387
- violations.push({
388
- file,
389
- line: v.line,
390
- column: v.column,
391
- keyword: v.keyword,
392
- context: v.context,
393
- });
394
- }
395
- }
396
-
397
- return violations;
398
- }
399
-
400
- /**
401
- * Auto-detect the base branch by finding the merge-base with origin/main.
402
- */
403
- function detectBase(workspaceRoot: string): string | null {
404
- try {
405
- const mergeBase = execSync('git merge-base HEAD origin/main', {
406
- cwd: workspaceRoot,
407
- encoding: 'utf-8',
408
- stdio: ['pipe', 'pipe', 'pipe'],
409
- }).trim();
410
-
411
- if (mergeBase) {
412
- return mergeBase;
413
- }
414
- } catch {
415
- try {
416
- const mergeBase = execSync('git merge-base HEAD main', {
417
- cwd: workspaceRoot,
418
- encoding: 'utf-8',
419
- stdio: ['pipe', 'pipe', 'pipe'],
420
- }).trim();
421
-
422
- if (mergeBase) {
423
- return mergeBase;
424
- }
425
- } catch {
426
- // Ignore
427
- }
428
- }
429
- return null;
430
- }
431
-
432
- /**
433
- * Report violations to console.
434
- */
435
- function reportViolations(violations: AnyUnknownViolation[], mode: NoAnyUnknownMode): void {
436
- console.error('');
437
- console.error('❌ `any` and `unknown` keywords found! Use specific types instead.');
438
- console.error('');
439
- console.error('📚 Avoiding any/unknown improves type safety:');
440
- console.error('');
441
- console.error(' BAD: const data: any = fetchData();');
442
- console.error(' GOOD: const data: UserData = fetchData();');
443
- console.error('');
444
- console.error(' BAD: function process(input: unknown): unknown { }');
445
- console.error(' GOOD: function process(input: ValidInput): ValidOutput { }');
446
- console.error('');
447
-
448
- for (const v of violations) {
449
- console.error(` ❌ ${v.file}:${v.line}:${v.column}`);
450
- console.error(` \`${v.keyword}\` keyword in ${v.context}`);
451
- }
452
- console.error('');
453
-
454
- console.error(' To fix: Replace with specific types or interfaces');
455
- console.error('');
456
- console.error(' Escape hatch (use sparingly):');
457
- console.error(' // webpieces-disable no-any-unknown -- [your reason]');
458
- console.error('');
459
- console.error(` Current mode: ${mode}`);
460
- console.error('');
461
- }
462
-
463
- /**
464
- * Resolve mode considering ignoreModifiedUntilEpoch override.
465
- * When active, downgrades to OFF. When expired, logs a warning.
466
- */
467
- function resolveMode(normalMode: NoAnyUnknownMode, epoch: number | undefined): NoAnyUnknownMode {
468
- if (epoch === undefined || normalMode === 'OFF') {
469
- return normalMode;
470
- }
471
- const nowSeconds = Date.now() / 1000;
472
- if (nowSeconds < epoch) {
473
- const expiresDate = new Date(epoch * 1000).toISOString().split('T')[0];
474
- console.log(`\n⏭️ Skipping no-any-unknown validation (ignoreModifiedUntilEpoch active, expires: ${expiresDate})`);
475
- console.log('');
476
- return 'OFF';
477
- }
478
- return normalMode;
479
- }
480
-
481
- export default async function runExecutor(
482
- options: ValidateNoAnyUnknownOptions,
483
- context: ExecutorContext
484
- ): Promise<ExecutorResult> {
485
- const workspaceRoot = context.root;
486
- const mode: NoAnyUnknownMode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch);
487
- const disableAllowed = options.disableAllowed ?? true;
488
-
489
- if (mode === 'OFF') {
490
- console.log('\n⏭️ Skipping no-any-unknown validation (mode: OFF)');
491
- console.log('');
492
- return { success: true };
493
- }
494
-
495
- console.log('\n📏 Validating No Any/Unknown\n');
496
- console.log(` Mode: ${mode}`);
497
-
498
- let base = process.env['NX_BASE'];
499
- const head = process.env['NX_HEAD'];
500
-
501
- if (!base) {
502
- base = detectBase(workspaceRoot) ?? undefined;
503
-
504
- if (!base) {
505
- console.log('\n⏭️ Skipping no-any-unknown validation (could not detect base branch)');
506
- console.log('');
507
- return { success: true };
508
- }
509
- }
510
-
511
- console.log(` Base: ${base}`);
512
- console.log(` Head: ${head ?? 'working tree (includes uncommitted changes)'}`);
513
- console.log('');
514
-
515
- const changedFiles = getChangedTypeScriptFiles(workspaceRoot, base, head);
516
-
517
- if (changedFiles.length === 0) {
518
- console.log('✅ No TypeScript files changed');
519
- return { success: true };
520
- }
521
-
522
- console.log(`📂 Checking ${changedFiles.length} changed file(s)...`);
523
-
524
- let violations: AnyUnknownViolation[] = [];
525
-
526
- if (mode === 'MODIFIED_CODE') {
527
- violations = findViolationsForModifiedCode(workspaceRoot, changedFiles, base, head, disableAllowed);
528
- } else if (mode === 'MODIFIED_FILES') {
529
- violations = findViolationsForModifiedFiles(workspaceRoot, changedFiles, disableAllowed);
530
- }
531
-
532
- if (violations.length === 0) {
533
- console.log('✅ No any/unknown keywords found');
534
- return { success: true };
535
- }
536
-
537
- reportViolations(violations, mode);
538
-
539
- return { success: false };
540
- }
@@ -1,24 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/schema",
3
- "title": "Validate No Any Unknown Executor",
4
- "description": "Validates that `any` and `unknown` TypeScript keywords are not used. Uses LINE-BASED detection for git diff filtering.",
5
- "type": "object",
6
- "properties": {
7
- "mode": {
8
- "type": "string",
9
- "enum": ["OFF", "MODIFIED_CODE", "MODIFIED_FILES"],
10
- "description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows `any` and `unknown` TypeScript keywords.",
11
- "default": "OFF"
12
- },
13
- "disableAllowed": {
14
- "type": "boolean",
15
- "description": "Whether disable comments work. When false, no escape hatch.",
16
- "default": true
17
- },
18
- "ignoreModifiedUntilEpoch": {
19
- "type": "number",
20
- "description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
21
- }
22
- },
23
- "required": []
24
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Validate No Architecture Cycles Executor
3
- *
4
- * Validates that the architecture dependency graph has no circular dependencies.
5
- * This is a fast check that only validates acyclicity at the project level.
6
- *
7
- * Usage:
8
- * nx run architecture:validate-no-architecture-cycles
9
- */
10
- import type { ExecutorContext } from '@nx/devkit';
11
- export interface ValidateNoCyclesOptions {
12
- }
13
- export interface ExecutorResult {
14
- success: boolean;
15
- }
16
- export default function runExecutor(_options: ValidateNoCyclesOptions, _context: ExecutorContext): Promise<ExecutorResult>;
@@ -1,48 +0,0 @@
1
- "use strict";
2
- /**
3
- * Validate No Architecture Cycles Executor
4
- *
5
- * Validates that the architecture dependency graph has no circular dependencies.
6
- * This is a fast check that only validates acyclicity at the project level.
7
- *
8
- * Usage:
9
- * nx run architecture:validate-no-architecture-cycles
10
- */
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.default = runExecutor;
13
- const graph_generator_1 = require("../../lib/graph-generator");
14
- const graph_sorter_1 = require("../../lib/graph-sorter");
15
- async function runExecutor(_options, _context) {
16
- console.log('\n🔄 Validating No Circular Dependencies\n');
17
- try {
18
- // Step 1: Generate current graph from project.json files
19
- console.log('📊 Generating dependency graph from project.json files...');
20
- const rawGraph = await (0, graph_generator_1.generateGraph)();
21
- // Step 2: Topological sort (validates acyclic)
22
- console.log('🔄 Checking for cycles (topological sort)...');
23
- try {
24
- (0, graph_sorter_1.sortGraphTopologically)(rawGraph);
25
- console.log('✅ No circular dependencies detected!');
26
- // Print summary
27
- const projectCount = Object.keys(rawGraph).length;
28
- console.log(`\n📈 Summary: ${projectCount} projects, all acyclic`);
29
- return { success: true };
30
- }
31
- catch (err) {
32
- const error = err instanceof Error ? err : new Error(String(err));
33
- console.error('❌ Circular dependency detected!');
34
- console.error(error.message);
35
- console.error('\nTo fix:');
36
- console.error(' 1. Review the cycle above');
37
- console.error(' 2. Break the cycle by refactoring dependencies');
38
- console.error(' 3. Run this check again');
39
- return { success: false };
40
- }
41
- }
42
- catch (err) {
43
- const error = err instanceof Error ? err : new Error(String(err));
44
- console.error('❌ Cycle validation failed:', error.message);
45
- return { success: false };
46
- }
47
- }
48
- //# sourceMappingURL=executor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-no-architecture-cycles/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAcH,8BAqCC;AAhDD,+DAA0D;AAC1D,yDAAgE;AAUjD,KAAK,UAAU,WAAW,CACrC,QAAiC,EACjC,QAAyB;IAEzB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAE1D,IAAI,CAAC;QACD,yDAAyD;QACzD,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,MAAM,IAAA,+BAAa,GAAE,CAAC;QAEvC,+CAA+C;QAC/C,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC5D,IAAI,CAAC;YACD,IAAA,qCAAsB,EAAC,QAAQ,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;YAEpD,gBAAgB;YAChB,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,iBAAiB,YAAY,wBAAwB,CAAC,CAAC;YAEnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACjD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;YAClE,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;IACL,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,4BAA4B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Validate No Architecture Cycles Executor\n *\n * Validates that the architecture dependency graph has no circular dependencies.\n * This is a fast check that only validates acyclicity at the project level.\n *\n * Usage:\n * nx run architecture:validate-no-architecture-cycles\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { generateGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\n\nexport interface ValidateNoCyclesOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: ValidateNoCyclesOptions,\n _context: ExecutorContext\n): Promise<ExecutorResult> {\n console.log('\\n🔄 Validating No Circular Dependencies\\n');\n\n try {\n // Step 1: Generate current graph from project.json files\n console.log('📊 Generating dependency graph from project.json files...');\n const rawGraph = await generateGraph();\n\n // Step 2: Topological sort (validates acyclic)\n console.log('🔄 Checking for cycles (topological sort)...');\n try {\n sortGraphTopologically(rawGraph);\n console.log('✅ No circular dependencies detected!');\n\n // Print summary\n const projectCount = Object.keys(rawGraph).length;\n console.log(`\\n📈 Summary: ${projectCount} projects, all acyclic`);\n\n return { success: true };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Circular dependency detected!');\n console.error(error.message);\n console.error('\\nTo fix:');\n console.error(' 1. Review the cycle above');\n console.error(' 2. Break the cycle by refactoring dependencies');\n console.error(' 3. Run this check again');\n return { success: false };\n }\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('❌ Cycle validation failed:', error.message);\n return { success: false };\n }\n}\n"]}