@webpieces/nx-webpieces-rules 0.0.1
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/LICENSE +373 -0
- package/executors.json +124 -0
- package/package.json +36 -0
- package/src/executor-result.ts +7 -0
- package/src/executors/generate/executor.ts +61 -0
- package/src/executors/generate/schema.json +14 -0
- package/src/executors/help/executor.ts +63 -0
- package/src/executors/help/schema.json +7 -0
- package/src/executors/validate-architecture-unchanged/executor.ts +253 -0
- package/src/executors/validate-architecture-unchanged/schema.json +14 -0
- package/src/executors/validate-catch-error-pattern/executor.ts +11 -0
- package/src/executors/validate-catch-error-pattern/schema.json +24 -0
- package/src/executors/validate-code/executor.ts +11 -0
- package/src/executors/validate-code/schema.json +287 -0
- package/src/executors/validate-dtos/executor.ts +11 -0
- package/src/executors/validate-dtos/schema.json +33 -0
- package/src/executors/validate-eslint-sync/executor.ts +87 -0
- package/src/executors/validate-eslint-sync/schema.json +7 -0
- package/src/executors/validate-modified-files/executor.ts +11 -0
- package/src/executors/validate-modified-files/schema.json +25 -0
- package/src/executors/validate-modified-methods/executor.ts +11 -0
- package/src/executors/validate-modified-methods/schema.json +25 -0
- package/src/executors/validate-new-methods/executor.ts +11 -0
- package/src/executors/validate-new-methods/schema.json +25 -0
- package/src/executors/validate-no-any-unknown/executor.ts +11 -0
- package/src/executors/validate-no-any-unknown/schema.json +24 -0
- package/src/executors/validate-no-architecture-cycles/executor.ts +63 -0
- package/src/executors/validate-no-architecture-cycles/schema.json +8 -0
- package/src/executors/validate-no-destructure/executor.ts +11 -0
- package/src/executors/validate-no-destructure/schema.json +24 -0
- package/src/executors/validate-no-direct-api-resolver/executor.ts +11 -0
- package/src/executors/validate-no-direct-api-resolver/schema.json +29 -0
- package/src/executors/validate-no-implicit-any/executor.ts +11 -0
- package/src/executors/validate-no-implicit-any/schema.json +24 -0
- package/src/executors/validate-no-inline-types/executor.ts +11 -0
- package/src/executors/validate-no-inline-types/schema.json +24 -0
- package/src/executors/validate-no-skiplevel-deps/executor.ts +274 -0
- package/src/executors/validate-no-skiplevel-deps/schema.json +8 -0
- package/src/executors/validate-no-unmanaged-exceptions/executor.ts +11 -0
- package/src/executors/validate-no-unmanaged-exceptions/schema.json +24 -0
- package/src/executors/validate-packagejson/executor.ts +76 -0
- package/src/executors/validate-packagejson/schema.json +8 -0
- package/src/executors/validate-prisma-converters/executor.ts +11 -0
- package/src/executors/validate-prisma-converters/schema.json +38 -0
- package/src/executors/validate-return-types/executor.ts +11 -0
- package/src/executors/validate-return-types/schema.json +24 -0
- package/src/executors/validate-ts-in-src/executor.ts +283 -0
- package/src/executors/validate-ts-in-src/schema.json +25 -0
- package/src/executors/validate-versions-locked/executor.ts +376 -0
- package/src/executors/validate-versions-locked/schema.json +8 -0
- package/src/executors/visualize/executor.ts +65 -0
- package/src/executors/visualize/schema.json +14 -0
- package/src/index.ts +9 -0
- package/src/lib/graph-comparator.ts +154 -0
- package/src/lib/graph-generator.ts +97 -0
- package/src/lib/graph-loader.ts +119 -0
- package/src/lib/graph-sorter.ts +137 -0
- package/src/lib/graph-visualizer.ts +253 -0
- package/src/lib/package-validator.ts +184 -0
- package/src/plugin.ts +666 -0
- package/src/toError.ts +36 -0
|
@@ -0,0 +1,287 @@
|
|
|
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
|
+
"methodMaxLimit": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"description": "Configuration for method line limit validation",
|
|
10
|
+
"properties": {
|
|
11
|
+
"limit": {
|
|
12
|
+
"type": "number",
|
|
13
|
+
"description": "Maximum lines allowed for methods",
|
|
14
|
+
"default": 80
|
|
15
|
+
},
|
|
16
|
+
"mode": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "OFF: skip validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
|
|
19
|
+
"default": "NEW_AND_MODIFIED_METHODS"
|
|
20
|
+
},
|
|
21
|
+
"disableAllowed": {
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
24
|
+
"default": true
|
|
25
|
+
},
|
|
26
|
+
"ignoreModifiedUntilEpoch": {
|
|
27
|
+
"type": "number",
|
|
28
|
+
"description": "Epoch seconds. Until this time, skip modified-method validation (new methods still enforced). When expired, normal mode resumes. Omit when not needed."
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"fileMaxLimit": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"description": "Configuration for file line limit validation",
|
|
35
|
+
"properties": {
|
|
36
|
+
"limit": {
|
|
37
|
+
"type": "number",
|
|
38
|
+
"description": "Maximum lines for modified files",
|
|
39
|
+
"default": 900
|
|
40
|
+
},
|
|
41
|
+
"mode": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "OFF: skip validation. MODIFIED_FILES: all code in modified files.",
|
|
44
|
+
"default": "MODIFIED_FILES"
|
|
45
|
+
},
|
|
46
|
+
"disableAllowed": {
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
49
|
+
"default": true
|
|
50
|
+
},
|
|
51
|
+
"ignoreModifiedUntilEpoch": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"description": "Epoch seconds. Until this time, skip modified-file validation. When expired, normal mode resumes. Omit when not needed."
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"requireReturnType": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"description": "Configuration for return type validation",
|
|
60
|
+
"properties": {
|
|
61
|
+
"mode": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "OFF: skip return type validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
|
|
64
|
+
"default": "OFF"
|
|
65
|
+
},
|
|
66
|
+
"disableAllowed": {
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
69
|
+
"default": true
|
|
70
|
+
},
|
|
71
|
+
"ignoreModifiedUntilEpoch": {
|
|
72
|
+
"type": "number",
|
|
73
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"noInlineTypeLiterals": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"description": "Configuration for no-inline-types validation",
|
|
80
|
+
"properties": {
|
|
81
|
+
"mode": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "OFF: skip validation. NEW_METHODS: only new methods. NEW_AND_MODIFIED_METHODS: new + modified methods. MODIFIED_FILES: all in modified files. Disallows inline type literals like { x: number } - use named types instead.",
|
|
84
|
+
"default": "OFF"
|
|
85
|
+
},
|
|
86
|
+
"disableAllowed": {
|
|
87
|
+
"type": "boolean",
|
|
88
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
89
|
+
"default": true
|
|
90
|
+
},
|
|
91
|
+
"ignoreModifiedUntilEpoch": {
|
|
92
|
+
"type": "number",
|
|
93
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"noAnyUnknown": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"description": "Configuration for no-any-unknown validation",
|
|
100
|
+
"properties": {
|
|
101
|
+
"mode": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows `any` and `unknown` TypeScript keywords.",
|
|
104
|
+
"default": "OFF"
|
|
105
|
+
},
|
|
106
|
+
"disableAllowed": {
|
|
107
|
+
"type": "boolean",
|
|
108
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
109
|
+
"default": true
|
|
110
|
+
},
|
|
111
|
+
"ignoreModifiedUntilEpoch": {
|
|
112
|
+
"type": "number",
|
|
113
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"noImplicitAny": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"description": "Configuration for no-implicit-any validation (TS7006/7005/7018/etc via the TypeScript compiler).",
|
|
120
|
+
"properties": {
|
|
121
|
+
"mode": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only implicit-any on changed lines. MODIFIED_FILES: all implicit-any in modified files.",
|
|
124
|
+
"default": "OFF"
|
|
125
|
+
},
|
|
126
|
+
"disableAllowed": {
|
|
127
|
+
"type": "boolean",
|
|
128
|
+
"description": "Whether // webpieces-disable no-implicit-any comments work. When false, no escape hatch.",
|
|
129
|
+
"default": true
|
|
130
|
+
},
|
|
131
|
+
"ignoreModifiedUntilEpoch": {
|
|
132
|
+
"type": "number",
|
|
133
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"validateDtos": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"description": "Validate DTO fields match Prisma Dbo model fields. Prevents AI from inventing field names.",
|
|
140
|
+
"properties": {
|
|
141
|
+
"mode": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"description": "OFF: skip validation. MODIFIED_CLASS: only validate Dto classes with changed lines. MODIFIED_FILES: validate all Dtos in modified files.",
|
|
144
|
+
"default": "OFF"
|
|
145
|
+
},
|
|
146
|
+
"disableAllowed": {
|
|
147
|
+
"type": "boolean",
|
|
148
|
+
"description": "Whether @deprecated field exemption works. When false, all fields must match.",
|
|
149
|
+
"default": true
|
|
150
|
+
},
|
|
151
|
+
"prismaSchemaPath": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"description": "Relative path from workspace root to schema.prisma"
|
|
154
|
+
},
|
|
155
|
+
"dtoSourcePaths": {
|
|
156
|
+
"type": "array",
|
|
157
|
+
"items": { "type": "string" },
|
|
158
|
+
"description": "Array of directories (relative to workspace root) containing Dto files"
|
|
159
|
+
},
|
|
160
|
+
"ignoreModifiedUntilEpoch": {
|
|
161
|
+
"type": "number",
|
|
162
|
+
"description": "Epoch seconds. Until this time, skip DTO validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"prismaConverter": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"description": "Validate Prisma converter methods follow scalable patterns: correct Dbo parameter, no async, no standalone functions, Dto creation only in converter directories.",
|
|
169
|
+
"properties": {
|
|
170
|
+
"mode": {
|
|
171
|
+
"type": "string",
|
|
172
|
+
"description": "OFF: skip validation. NEW_AND_MODIFIED_METHODS: validate new/modified methods in converters + changed lines in non-converters. MODIFIED_FILES: validate all methods in modified files.",
|
|
173
|
+
"default": "OFF"
|
|
174
|
+
},
|
|
175
|
+
"disableAllowed": {
|
|
176
|
+
"type": "boolean",
|
|
177
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
178
|
+
"default": true
|
|
179
|
+
},
|
|
180
|
+
"schemaPath": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"description": "Relative path from workspace root to schema.prisma"
|
|
183
|
+
},
|
|
184
|
+
"convertersPaths": {
|
|
185
|
+
"type": "array",
|
|
186
|
+
"items": { "type": "string" },
|
|
187
|
+
"description": "Array of directories (relative to workspace root) containing converter files"
|
|
188
|
+
},
|
|
189
|
+
"enforcePaths": {
|
|
190
|
+
"type": "array",
|
|
191
|
+
"items": { "type": "string" },
|
|
192
|
+
"description": "Array of directory prefixes (relative to workspace root) to scope validation to. When set, only files under these paths are checked. When empty/omitted, all changed files are checked."
|
|
193
|
+
},
|
|
194
|
+
"ignoreModifiedUntilEpoch": {
|
|
195
|
+
"type": "number",
|
|
196
|
+
"description": "Epoch seconds. Until this time, skip prisma-converter validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
"noDestructure": {
|
|
201
|
+
"type": "object",
|
|
202
|
+
"description": "Validate no destructuring patterns are used. Destructuring hurts code traceability.",
|
|
203
|
+
"properties": {
|
|
204
|
+
"mode": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows destructuring patterns.",
|
|
207
|
+
"default": "OFF"
|
|
208
|
+
},
|
|
209
|
+
"disableAllowed": {
|
|
210
|
+
"type": "boolean",
|
|
211
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
212
|
+
"default": true
|
|
213
|
+
},
|
|
214
|
+
"ignoreModifiedUntilEpoch": {
|
|
215
|
+
"type": "number",
|
|
216
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"catchErrorPattern": {
|
|
221
|
+
"type": "object",
|
|
222
|
+
"description": "Validate catch blocks follow standardized pattern: catch (err: unknown) { const error = toError(err); }",
|
|
223
|
+
"properties": {
|
|
224
|
+
"mode": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only catch blocks on changed lines. MODIFIED_FILES: all catch blocks in modified files.",
|
|
227
|
+
"default": "OFF"
|
|
228
|
+
},
|
|
229
|
+
"disableAllowed": {
|
|
230
|
+
"type": "boolean",
|
|
231
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
232
|
+
"default": true
|
|
233
|
+
},
|
|
234
|
+
"ignoreModifiedUntilEpoch": {
|
|
235
|
+
"type": "number",
|
|
236
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"noUnmanagedExceptions": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"description": "Validate try/catch blocks are not used outside chokepoints. Exceptions should bubble to filters/globalErrorHandlers.",
|
|
243
|
+
"properties": {
|
|
244
|
+
"mode": {
|
|
245
|
+
"type": "string",
|
|
246
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. MODIFIED_FILES: all in modified files. Disallows try/catch blocks.",
|
|
247
|
+
"default": "OFF"
|
|
248
|
+
},
|
|
249
|
+
"disableAllowed": {
|
|
250
|
+
"type": "boolean",
|
|
251
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
252
|
+
"default": true
|
|
253
|
+
},
|
|
254
|
+
"ignoreModifiedUntilEpoch": {
|
|
255
|
+
"type": "number",
|
|
256
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"noDirectApiInResolver": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"description": "Validate resolvers use services (not APIs) and components subscribe to service observables (not route.snapshot.data).",
|
|
263
|
+
"properties": {
|
|
264
|
+
"mode": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "OFF: skip validation. MODIFIED_CODE: only changed lines in diff. NEW_AND_MODIFIED_METHODS: violations in new/modified method/route scopes. MODIFIED_FILES: all in modified files.",
|
|
267
|
+
"default": "OFF"
|
|
268
|
+
},
|
|
269
|
+
"disableAllowed": {
|
|
270
|
+
"type": "boolean",
|
|
271
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
272
|
+
"default": true
|
|
273
|
+
},
|
|
274
|
+
"ignoreModifiedUntilEpoch": {
|
|
275
|
+
"type": "number",
|
|
276
|
+
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
277
|
+
},
|
|
278
|
+
"enforcePaths": {
|
|
279
|
+
"type": "array",
|
|
280
|
+
"items": { "type": "string" },
|
|
281
|
+
"description": "Array of directory prefixes (relative to workspace root) to scope validation to. When set, only files under these paths are checked. When empty/omitted, all changed files are checked."
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"required": []
|
|
287
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { ExecutorResult } from '../../executor-result';
|
|
3
|
+
import { validateDtos } from '@webpieces/code-rules';
|
|
4
|
+
|
|
5
|
+
export default async function runExecutor(
|
|
6
|
+
// webpieces-disable no-any-unknown -- options are passed through to code-rules validators
|
|
7
|
+
options: Record<string, unknown>,
|
|
8
|
+
context: ExecutorContext,
|
|
9
|
+
): Promise<ExecutorResult> {
|
|
10
|
+
return validateDtos(options, context.root);
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "Validate DTOs Executor",
|
|
4
|
+
"description": "Validate DTO fields match Prisma Dbo model fields. Ensures AI agents don't invent field names.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"mode": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["OFF", "MODIFIED_CLASS", "MODIFIED_FILES"],
|
|
10
|
+
"description": "OFF: skip validation. MODIFIED_CLASS: only validate Dto classes with changed lines. MODIFIED_FILES: validate all Dtos in modified files.",
|
|
11
|
+
"default": "OFF"
|
|
12
|
+
},
|
|
13
|
+
"prismaSchemaPath": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Relative path from workspace root to schema.prisma"
|
|
16
|
+
},
|
|
17
|
+
"dtoSourcePaths": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": { "type": "string" },
|
|
20
|
+
"description": "Array of directories (relative to workspace root) containing Dto files"
|
|
21
|
+
},
|
|
22
|
+
"disableAllowed": {
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"description": "Whether @deprecated field exemption works. When false, all fields must match.",
|
|
25
|
+
"default": true
|
|
26
|
+
},
|
|
27
|
+
"ignoreModifiedUntilEpoch": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "Epoch seconds. Until this time, skip DTO validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required": []
|
|
33
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
|
|
6
|
+
export interface ValidateEslintSyncOptions {}
|
|
7
|
+
|
|
8
|
+
export interface ExecutorResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function calculateHash(content: string): string {
|
|
13
|
+
return createHash('sha256').update(content).digest('hex');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizeContent(content: string): string {
|
|
17
|
+
// Normalize line endings and trim whitespace
|
|
18
|
+
return content.replace(/\r\n/g, '\n').trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default async function validateEslintSyncExecutor(
|
|
22
|
+
options: ValidateEslintSyncOptions,
|
|
23
|
+
context: ExecutorContext
|
|
24
|
+
): Promise<ExecutorResult> {
|
|
25
|
+
const workspaceRoot = context.root;
|
|
26
|
+
|
|
27
|
+
const templatePath = join(workspaceRoot, 'packages/tooling/nx-webpieces-rules/templates/eslint.webpieces.config.mjs');
|
|
28
|
+
const workspacePath = join(workspaceRoot, 'eslint.webpieces.config.mjs');
|
|
29
|
+
|
|
30
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
31
|
+
try {
|
|
32
|
+
const templateContent = readFileSync(templatePath, 'utf-8');
|
|
33
|
+
const workspaceContent = readFileSync(workspacePath, 'utf-8');
|
|
34
|
+
|
|
35
|
+
const templateRules = extractRulesSection(templateContent);
|
|
36
|
+
const workspaceRules = extractRulesSection(workspaceContent);
|
|
37
|
+
|
|
38
|
+
const templateHash = calculateHash(normalizeContent(templateRules));
|
|
39
|
+
const workspaceHash = calculateHash(normalizeContent(workspaceRules));
|
|
40
|
+
|
|
41
|
+
if (templateHash !== workspaceHash) {
|
|
42
|
+
printValidationError(templatePath, workspacePath);
|
|
43
|
+
return { success: false };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('ā
ESLint configuration sync validated - rules match!');
|
|
47
|
+
return { success: true };
|
|
48
|
+
|
|
49
|
+
} catch (err: unknown) {
|
|
50
|
+
// Error occurred during validation - log and fail
|
|
51
|
+
// eslint-disable-next-line @webpieces/catch-error-pattern
|
|
52
|
+
console.error('ā Error validating ESLint sync:', err);
|
|
53
|
+
return { success: false };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function printValidationError(templatePath: string, workspacePath: string): void {
|
|
58
|
+
console.error('');
|
|
59
|
+
console.error('ā ESLint configuration sync validation FAILED');
|
|
60
|
+
console.error('');
|
|
61
|
+
console.error('The @webpieces ESLint rules must be identical in both files:');
|
|
62
|
+
console.error(` 1. ${templatePath}`);
|
|
63
|
+
console.error(` 2. ${workspacePath}`);
|
|
64
|
+
console.error('');
|
|
65
|
+
console.error('These files must have identical rules sections so that:');
|
|
66
|
+
console.error(' - External clients get the same rules we use internally');
|
|
67
|
+
console.error(' - We "eat our own dog food" - same rules for everyone');
|
|
68
|
+
console.error('');
|
|
69
|
+
console.error('To fix:');
|
|
70
|
+
console.error(' 1. Modify the rules in ONE file (recommend: templates/eslint.webpieces.config.mjs)');
|
|
71
|
+
console.error(' 2. Copy the rules section to the other file');
|
|
72
|
+
console.error(' 3. Keep import statements different (template uses npm, workspace uses loadWorkspaceRules)');
|
|
73
|
+
console.error('');
|
|
74
|
+
console.error('Customization for webpieces workspace goes in: eslint.config.mjs');
|
|
75
|
+
console.error('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function extractRulesSection(content: string): string {
|
|
79
|
+
// Extract everything between "export default [" and the final "];"
|
|
80
|
+
// This includes the rules configuration
|
|
81
|
+
const match = content.match(/export default \[([\s\S]*)\];/);
|
|
82
|
+
if (!match) {
|
|
83
|
+
throw new Error('Could not extract rules section - export default not found');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return match[1].trim();
|
|
87
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { ExecutorResult } from '../../executor-result';
|
|
3
|
+
import { validateModifiedFiles } from '@webpieces/code-rules';
|
|
4
|
+
|
|
5
|
+
export default async function runExecutor(
|
|
6
|
+
// webpieces-disable no-any-unknown -- options are passed through to code-rules validators
|
|
7
|
+
options: Record<string, unknown>,
|
|
8
|
+
context: ExecutorContext,
|
|
9
|
+
): Promise<ExecutorResult> {
|
|
10
|
+
return validateModifiedFiles(options, context.root);
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "Validate Modified Files Executor",
|
|
4
|
+
"description": "Validates that modified files don't exceed a maximum line count. Encourages keeping files small and focused.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"limit": {
|
|
8
|
+
"type": "number",
|
|
9
|
+
"description": "Maximum number of lines allowed for modified files",
|
|
10
|
+
"default": 900
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["OFF", "MODIFIED_FILES"],
|
|
15
|
+
"description": "OFF: skip validation. MODIFIED_FILES: validate all code in modified files.",
|
|
16
|
+
"default": "MODIFIED_FILES"
|
|
17
|
+
},
|
|
18
|
+
"disableAllowed": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
21
|
+
"default": true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": []
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { ExecutorResult } from '../../executor-result';
|
|
3
|
+
import { validateModifiedMethods } from '@webpieces/code-rules';
|
|
4
|
+
|
|
5
|
+
export default async function runExecutor(
|
|
6
|
+
// webpieces-disable no-any-unknown -- options are passed through to code-rules validators
|
|
7
|
+
options: Record<string, unknown>,
|
|
8
|
+
context: ExecutorContext,
|
|
9
|
+
): Promise<ExecutorResult> {
|
|
10
|
+
return validateModifiedMethods(options, context.root);
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "Validate Modified Methods Executor",
|
|
4
|
+
"description": "Validates that modified methods don't exceed a maximum line count. Encourages gradual cleanup of legacy long methods.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"limit": {
|
|
8
|
+
"type": "number",
|
|
9
|
+
"description": "Maximum lines allowed for modified methods",
|
|
10
|
+
"default": 80
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
|
|
15
|
+
"description": "OFF: skip validation. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
|
|
16
|
+
"default": "NEW_AND_MODIFIED_METHODS"
|
|
17
|
+
},
|
|
18
|
+
"disableAllowed": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
21
|
+
"default": true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": []
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { ExecutorResult } from '../../executor-result';
|
|
3
|
+
import { validateNewMethods } from '@webpieces/code-rules';
|
|
4
|
+
|
|
5
|
+
export default async function runExecutor(
|
|
6
|
+
// webpieces-disable no-any-unknown -- options are passed through to code-rules validators
|
|
7
|
+
options: Record<string, unknown>,
|
|
8
|
+
context: ExecutorContext,
|
|
9
|
+
): Promise<ExecutorResult> {
|
|
10
|
+
return validateNewMethods(options, context.root);
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "Validate New Methods Executor",
|
|
4
|
+
"description": "Validates that newly added methods don't exceed a maximum line count. Only runs in affected mode (when NX_BASE is set).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"limit": {
|
|
8
|
+
"type": "number",
|
|
9
|
+
"description": "Maximum lines allowed for new methods",
|
|
10
|
+
"default": 80
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
|
|
15
|
+
"description": "OFF: skip validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all methods in modified files.",
|
|
16
|
+
"default": "NEW_AND_MODIFIED_METHODS"
|
|
17
|
+
},
|
|
18
|
+
"disableAllowed": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
21
|
+
"default": true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": []
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { ExecutorResult } from '../../executor-result';
|
|
3
|
+
import { validateNoAnyUnknown } from '@webpieces/code-rules';
|
|
4
|
+
|
|
5
|
+
export default async function runExecutor(
|
|
6
|
+
// webpieces-disable no-any-unknown -- options are passed through to code-rules validators
|
|
7
|
+
options: Record<string, unknown>,
|
|
8
|
+
context: ExecutorContext,
|
|
9
|
+
): Promise<ExecutorResult> {
|
|
10
|
+
return validateNoAnyUnknown(options, context.root);
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
|
|
11
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
12
|
+
import { generateGraph } from '../../lib/graph-generator';
|
|
13
|
+
import { sortGraphTopologically } from '../../lib/graph-sorter';
|
|
14
|
+
import { toError } from '../../toError';
|
|
15
|
+
|
|
16
|
+
export interface ValidateNoCyclesOptions {
|
|
17
|
+
// No options needed
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ExecutorResult {
|
|
21
|
+
success: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default async function runExecutor(
|
|
25
|
+
_options: ValidateNoCyclesOptions,
|
|
26
|
+
_context: ExecutorContext
|
|
27
|
+
): Promise<ExecutorResult> {
|
|
28
|
+
console.log('\nš Validating No Circular Dependencies\n');
|
|
29
|
+
|
|
30
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
31
|
+
try {
|
|
32
|
+
// Step 1: Generate current graph from project.json files
|
|
33
|
+
console.log('š Generating dependency graph from project.json files...');
|
|
34
|
+
const rawGraph = await generateGraph();
|
|
35
|
+
|
|
36
|
+
// Step 2: Topological sort (validates acyclic)
|
|
37
|
+
console.log('š Checking for cycles (topological sort)...');
|
|
38
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
39
|
+
try {
|
|
40
|
+
sortGraphTopologically(rawGraph);
|
|
41
|
+
console.log('ā
No circular dependencies detected!');
|
|
42
|
+
|
|
43
|
+
// Print summary
|
|
44
|
+
const projectCount = Object.keys(rawGraph).length;
|
|
45
|
+
console.log(`\nš Summary: ${projectCount} projects, all acyclic`);
|
|
46
|
+
|
|
47
|
+
return { success: true };
|
|
48
|
+
} catch (err: unknown) {
|
|
49
|
+
const error = toError(err);
|
|
50
|
+
console.error('ā Circular dependency detected!');
|
|
51
|
+
console.error(error.message);
|
|
52
|
+
console.error('\nTo fix:');
|
|
53
|
+
console.error(' 1. Review the cycle above');
|
|
54
|
+
console.error(' 2. Break the cycle by refactoring dependencies');
|
|
55
|
+
console.error(' 3. Run this check again');
|
|
56
|
+
return { success: false };
|
|
57
|
+
}
|
|
58
|
+
} catch (err: unknown) {
|
|
59
|
+
const error = toError(err);
|
|
60
|
+
console.error('ā Cycle validation failed:', error.message);
|
|
61
|
+
return { success: false };
|
|
62
|
+
}
|
|
63
|
+
}
|