@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.
Files changed (61) hide show
  1. package/LICENSE +373 -0
  2. package/executors.json +124 -0
  3. package/package.json +36 -0
  4. package/src/executor-result.ts +7 -0
  5. package/src/executors/generate/executor.ts +61 -0
  6. package/src/executors/generate/schema.json +14 -0
  7. package/src/executors/help/executor.ts +63 -0
  8. package/src/executors/help/schema.json +7 -0
  9. package/src/executors/validate-architecture-unchanged/executor.ts +253 -0
  10. package/src/executors/validate-architecture-unchanged/schema.json +14 -0
  11. package/src/executors/validate-catch-error-pattern/executor.ts +11 -0
  12. package/src/executors/validate-catch-error-pattern/schema.json +24 -0
  13. package/src/executors/validate-code/executor.ts +11 -0
  14. package/src/executors/validate-code/schema.json +287 -0
  15. package/src/executors/validate-dtos/executor.ts +11 -0
  16. package/src/executors/validate-dtos/schema.json +33 -0
  17. package/src/executors/validate-eslint-sync/executor.ts +87 -0
  18. package/src/executors/validate-eslint-sync/schema.json +7 -0
  19. package/src/executors/validate-modified-files/executor.ts +11 -0
  20. package/src/executors/validate-modified-files/schema.json +25 -0
  21. package/src/executors/validate-modified-methods/executor.ts +11 -0
  22. package/src/executors/validate-modified-methods/schema.json +25 -0
  23. package/src/executors/validate-new-methods/executor.ts +11 -0
  24. package/src/executors/validate-new-methods/schema.json +25 -0
  25. package/src/executors/validate-no-any-unknown/executor.ts +11 -0
  26. package/src/executors/validate-no-any-unknown/schema.json +24 -0
  27. package/src/executors/validate-no-architecture-cycles/executor.ts +63 -0
  28. package/src/executors/validate-no-architecture-cycles/schema.json +8 -0
  29. package/src/executors/validate-no-destructure/executor.ts +11 -0
  30. package/src/executors/validate-no-destructure/schema.json +24 -0
  31. package/src/executors/validate-no-direct-api-resolver/executor.ts +11 -0
  32. package/src/executors/validate-no-direct-api-resolver/schema.json +29 -0
  33. package/src/executors/validate-no-implicit-any/executor.ts +11 -0
  34. package/src/executors/validate-no-implicit-any/schema.json +24 -0
  35. package/src/executors/validate-no-inline-types/executor.ts +11 -0
  36. package/src/executors/validate-no-inline-types/schema.json +24 -0
  37. package/src/executors/validate-no-skiplevel-deps/executor.ts +274 -0
  38. package/src/executors/validate-no-skiplevel-deps/schema.json +8 -0
  39. package/src/executors/validate-no-unmanaged-exceptions/executor.ts +11 -0
  40. package/src/executors/validate-no-unmanaged-exceptions/schema.json +24 -0
  41. package/src/executors/validate-packagejson/executor.ts +76 -0
  42. package/src/executors/validate-packagejson/schema.json +8 -0
  43. package/src/executors/validate-prisma-converters/executor.ts +11 -0
  44. package/src/executors/validate-prisma-converters/schema.json +38 -0
  45. package/src/executors/validate-return-types/executor.ts +11 -0
  46. package/src/executors/validate-return-types/schema.json +24 -0
  47. package/src/executors/validate-ts-in-src/executor.ts +283 -0
  48. package/src/executors/validate-ts-in-src/schema.json +25 -0
  49. package/src/executors/validate-versions-locked/executor.ts +376 -0
  50. package/src/executors/validate-versions-locked/schema.json +8 -0
  51. package/src/executors/visualize/executor.ts +65 -0
  52. package/src/executors/visualize/schema.json +14 -0
  53. package/src/index.ts +9 -0
  54. package/src/lib/graph-comparator.ts +154 -0
  55. package/src/lib/graph-generator.ts +97 -0
  56. package/src/lib/graph-loader.ts +119 -0
  57. package/src/lib/graph-sorter.ts +137 -0
  58. package/src/lib/graph-visualizer.ts +253 -0
  59. package/src/lib/package-validator.ts +184 -0
  60. package/src/plugin.ts +666 -0
  61. package/src/toError.ts +36 -0
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateNoDestructure } 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 validateNoDestructure(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Destructure Executor",
4
+ "description": "Validates that destructuring patterns 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 destructuring patterns.",
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,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateNoDirectApiResolver } 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 validateNoDirectApiResolver(options, context.root);
11
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Direct API in Resolver Executor",
4
+ "description": "Validates resolvers use services (not APIs) and components subscribe to service observables (not route.snapshot.data). Uses LINE-BASED detection for git diff filtering.",
5
+ "type": "object",
6
+ "properties": {
7
+ "mode": {
8
+ "type": "string",
9
+ "enum": ["OFF", "MODIFIED_CODE", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
10
+ "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.",
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
+ "enforcePaths": {
23
+ "type": "array",
24
+ "items": { "type": "string" },
25
+ "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."
26
+ }
27
+ },
28
+ "required": []
29
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateNoImplicitAny } 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 validateNoImplicitAny(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Implicit Any Executor",
4
+ "description": "Validates that no function parameter, variable, or property has an implicit `any` type. Uses the TypeScript compiler (with noImplicitAny override) to detect TS7006/7005/7018/etc violations on changed lines only.",
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 implicit-any on changed lines in diff. MODIFIED_FILES: all implicit-any in modified files.",
11
+ "default": "OFF"
12
+ },
13
+ "disableAllowed": {
14
+ "type": "boolean",
15
+ "description": "Whether // webpieces-disable no-implicit-any 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,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateNoInlineTypes } 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 validateNoInlineTypes(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Inline Types Executor",
4
+ "description": "Validates that inline type literals are not used - prefer named types/interfaces/classes.",
5
+ "type": "object",
6
+ "properties": {
7
+ "mode": {
8
+ "type": "string",
9
+ "enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
10
+ "description": "OFF: skip validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. MODIFIED_FILES: all inline types in modified files.",
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,274 @@
1
+ /**
2
+ * Validate No Skip-Level Dependencies Executor
3
+ *
4
+ * Validates that no project has redundant transitive dependencies.
5
+ * If project A depends on B, and B transitively brings in C, then A should NOT
6
+ * also directly depend on C (it's redundant and clutters the dependency graph).
7
+ *
8
+ * This keeps the architecture graph clean for visualization and human understanding.
9
+ *
10
+ * Usage:
11
+ * nx run architecture:validate-no-skiplevel-deps
12
+ */
13
+
14
+ import type { ExecutorContext } from '@nx/devkit';
15
+ import { generateGraph } from '../../lib/graph-generator';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import { toError } from '../../toError';
19
+
20
+ export interface ValidateNoSkipLevelDepsOptions {
21
+ // No options needed
22
+ }
23
+
24
+ export interface ExecutorResult {
25
+ success: boolean;
26
+ }
27
+
28
+ interface RedundantDep {
29
+ project: string;
30
+ redundantDep: string;
31
+ alreadyBroughtInBy: string;
32
+ }
33
+
34
+ const TRANSITIVE_DEPS_DOC = `# AI Agent Instructions: Redundant Transitive Dependency Violation
35
+
36
+ **READ THIS FILE FIRST before making any changes!**
37
+
38
+ ## Why This Rule Exists
39
+
40
+ This rule keeps the architecture dependency graph **CLEAN and SIMPLE**.
41
+
42
+ When you run \`npx nx run architecture:visualize\`, it generates a visual diagram of all
43
+ package dependencies. Without this rule, you end up with a tangled mess of 100+ lines
44
+ where everything depends on everything - making it impossible to understand.
45
+
46
+ **Clean graphs = easier understanding for humans AND AI agents.**
47
+
48
+ ## Understanding the Error
49
+
50
+ You have a **redundant transitive dependency**. This means:
51
+
52
+ 1. Project A directly depends on Project C
53
+ 2. BUT Project A also depends on Project B
54
+ 3. AND Project B already brings in Project C (transitively)
55
+
56
+ Therefore, Project A's direct dependency on C is **redundant** - it's already available
57
+ through B. This extra line clutters the dependency graph.
58
+
59
+ **Example:**
60
+ \`\`\`
61
+ http-server depends on: [http-routing, http-filters, core-util]
62
+ ^^^^^^^^^ ^^^^^^^^
63
+ REDUNDANT! REDUNDANT!
64
+
65
+ Why? Because http-routing already brings in:
66
+ - http-filters (direct)
67
+ - core-util (via http-api)
68
+ \`\`\`
69
+
70
+ ## How to Fix
71
+
72
+ ### Step 1: Identify the Redundant Dependency
73
+
74
+ Look at the error message. It tells you:
75
+ - Which project has the problem
76
+ - Which dependency is redundant
77
+ - Which other dependency already brings it in
78
+
79
+ ### Step 2: Remove from project.json
80
+
81
+ Remove the redundant dependency from \`build.dependsOn\`:
82
+
83
+ \`\`\`json
84
+ {
85
+ "targets": {
86
+ "build": {
87
+ "dependsOn": [
88
+ "^build",
89
+ "http-routing:build"
90
+ // REMOVE: "http-filters:build" <-- redundant, http-routing brings it in
91
+ // REMOVE: "core-util:build" <-- redundant, http-routing brings it in
92
+ ]
93
+ }
94
+ }
95
+ }
96
+ \`\`\`
97
+
98
+ ### Step 3: Remove from package.json
99
+
100
+ Remove the redundant dependency from \`dependencies\`:
101
+
102
+ \`\`\`json
103
+ {
104
+ "dependencies": {
105
+ "@webpieces/http-routing": "*"
106
+ // REMOVE: "@webpieces/http-filters": "*" <-- redundant
107
+ // REMOVE: "@webpieces/core-util": "*" <-- redundant
108
+ }
109
+ }
110
+ \`\`\`
111
+
112
+ ### Step 4: Regenerate Architecture
113
+
114
+ \`\`\`bash
115
+ npx nx run architecture:generate
116
+ \`\`\`
117
+
118
+ ### Step 5: Verify
119
+
120
+ \`\`\`bash
121
+ npm run build-all
122
+ \`\`\`
123
+
124
+ ## Important Notes
125
+
126
+ - You DON'T lose access to the transitive dependency - it's still available through the parent
127
+ - This is about keeping the DECLARED dependencies minimal and clean
128
+ - The actual runtime/compile behavior is unchanged
129
+ - TypeScript will still find the types through the transitive path
130
+
131
+ ## Remember
132
+
133
+ - Fewer lines in the graph = easier to understand
134
+ - Only declare what you DIRECTLY need that isn't already transitively available
135
+ - When in doubt, check with \`npx nx run architecture:visualize\`
136
+ `;
137
+
138
+ /**
139
+ * Compute all transitive dependencies for a project
140
+ */
141
+ function computeTransitiveDeps(
142
+ project: string,
143
+ graph: Record<string, string[]>,
144
+ visited: Set<string> = new Set()
145
+ ): Set<string> {
146
+ const result = new Set<string>();
147
+
148
+ if (visited.has(project)) {
149
+ return result;
150
+ }
151
+ visited.add(project);
152
+
153
+ const directDeps = graph[project] || [];
154
+ for (const dep of directDeps) {
155
+ result.add(dep);
156
+ // Recursively get transitive deps
157
+ const transitive = computeTransitiveDeps(dep, graph, visited);
158
+ for (const t of transitive) {
159
+ result.add(t);
160
+ }
161
+ }
162
+
163
+ return result;
164
+ }
165
+
166
+ /**
167
+ * Find redundant dependencies for a project
168
+ */
169
+ function findRedundantDeps(
170
+ project: string,
171
+ graph: Record<string, string[]>
172
+ ): RedundantDep[] {
173
+ const redundant: RedundantDep[] = [];
174
+ const directDeps = graph[project] || [];
175
+
176
+ // For each direct dependency, compute what it transitively brings in
177
+ const transitiveByDep = new Map<string, Set<string>>();
178
+ for (const dep of directDeps) {
179
+ transitiveByDep.set(dep, computeTransitiveDeps(dep, graph));
180
+ }
181
+
182
+ // Check if any direct dependency is already brought in by another
183
+ const transitiveEntries = Array.from(transitiveByDep.entries());
184
+ for (const dep of directDeps) {
185
+ for (const entry of transitiveEntries) {
186
+ const otherDep = entry[0];
187
+ const otherTransitive = entry[1];
188
+ if (otherDep !== dep && otherTransitive.has(dep)) {
189
+ redundant.push({
190
+ project,
191
+ redundantDep: dep,
192
+ alreadyBroughtInBy: otherDep,
193
+ });
194
+ break; // Only report once per redundant dep
195
+ }
196
+ }
197
+ }
198
+
199
+ return redundant;
200
+ }
201
+
202
+ /**
203
+ * Write documentation file when violations are found
204
+ */
205
+ function writeDocFile(workspaceRoot: string): void {
206
+ const docPath = path.join(workspaceRoot, '.webpieces', 'instruct-ai', 'webpieces.transitivedeps.md');
207
+ const docDir = path.dirname(docPath);
208
+
209
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
210
+ try {
211
+ fs.mkdirSync(docDir, { recursive: true });
212
+ fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');
213
+ } catch (err: unknown) {
214
+ //const error = toError(err);
215
+ void err;
216
+ console.warn(`Could not write documentation file: ${docPath}`);
217
+ }
218
+ }
219
+
220
+ export default async function runExecutor(
221
+ _options: ValidateNoSkipLevelDepsOptions,
222
+ context: ExecutorContext
223
+ ): Promise<ExecutorResult> {
224
+ console.log('\nšŸ”„ Validating No Skip-Level Dependencies\n');
225
+
226
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
227
+ try {
228
+ // Step 1: Generate current graph from project.json files
229
+ console.log('šŸ“Š Generating dependency graph from project.json files...');
230
+ const graph = await generateGraph();
231
+
232
+ // Step 2: Find all redundant dependencies
233
+ console.log('šŸ” Checking for redundant transitive dependencies...');
234
+ const allRedundant: RedundantDep[] = [];
235
+
236
+ for (const project of Object.keys(graph)) {
237
+ const redundant = findRedundantDeps(project, graph);
238
+ allRedundant.push(...redundant);
239
+ }
240
+
241
+ if (allRedundant.length === 0) {
242
+ console.log('āœ… No redundant transitive dependencies detected!');
243
+ console.log('\nšŸ“ˆ Graph is clean and minimal.');
244
+ return { success: true };
245
+ }
246
+
247
+ // Write documentation file
248
+ const workspaceRoot = context.root || process.cwd();
249
+ writeDocFile(workspaceRoot);
250
+
251
+ // Report violations
252
+ console.error('\nāŒ Redundant transitive dependencies detected!\n');
253
+ console.error('āš ļø AI Agent: READ .webpieces/instruct-ai/webpieces.transitivedeps.md for fix instructions!\n');
254
+
255
+ for (const r of allRedundant) {
256
+ console.error(` ${r.project}:`);
257
+ console.error(` └─ "${r.redundantDep}" is REDUNDANT`);
258
+ console.error(` (already brought in by "${r.alreadyBroughtInBy}")\n`);
259
+ }
260
+
261
+ console.error('\nTo fix:');
262
+ console.error(' 1. READ .webpieces/instruct-ai/webpieces.transitivedeps.md');
263
+ console.error(' 2. Remove redundant deps from project.json build.dependsOn');
264
+ console.error(' 3. Remove redundant deps from package.json dependencies');
265
+ console.error(' 4. Run: npx nx run architecture:generate');
266
+ console.error(' 5. Run: npm run build-all');
267
+
268
+ return { success: false };
269
+ } catch (err: unknown) {
270
+ const error = toError(err);
271
+ console.error('āŒ Skip-level validation failed:', error.message);
272
+ return { success: false };
273
+ }
274
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Skip-Level Dependencies Executor",
4
+ "description": "Validates that no project has redundant transitive dependencies",
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": []
8
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateNoUnmanagedExceptions } 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 validateNoUnmanagedExceptions(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate No Unmanaged Exceptions Executor",
4
+ "description": "Validates that try/catch blocks are not used outside chokepoints. 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 try/catch blocks.",
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,76 @@
1
+ /**
2
+ * Validate Package.json Executor
3
+ *
4
+ * Validates that package.json dependencies match project.json build dependencies.
5
+ * This ensures the two sources of truth don't drift apart.
6
+ *
7
+ * Usage:
8
+ * nx run architecture:validate-packagejson
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 { validatePackageJsonDependencies } from '../../lib/package-validator';
15
+ import { toError } from '../../toError';
16
+
17
+ export interface ValidatePackageJsonOptions {
18
+ // No options needed for now
19
+ }
20
+
21
+ export interface ExecutorResult {
22
+ success: boolean;
23
+ }
24
+
25
+ export default async function runExecutor(
26
+ options: ValidatePackageJsonOptions,
27
+ context: ExecutorContext
28
+ ): Promise<ExecutorResult> {
29
+ const workspaceRoot = context.root;
30
+
31
+ console.log('\nšŸ“¦ Validating Package.json Dependencies\n');
32
+
33
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
34
+ try {
35
+ // Step 1: Generate current graph from project.json files
36
+ console.log('šŸ“Š Generating dependency graph from project.json files...');
37
+ const rawGraph = await generateGraph();
38
+
39
+ // Step 2: Topological sort (to get enhanced graph with levels)
40
+ console.log('šŸ”„ Computing topological layers...');
41
+ const enhancedGraph = sortGraphTopologically(rawGraph);
42
+
43
+ // Step 3: Validate package.json dependencies match
44
+ console.log('šŸ“¦ Validating package.json dependencies match project.json...');
45
+ const packageValidation = await validatePackageJsonDependencies(enhancedGraph, workspaceRoot);
46
+
47
+ if (!packageValidation.valid) {
48
+ console.error('āŒ Package.json validation failed!');
49
+ console.error('\nErrors:');
50
+ for (const error of packageValidation.errors) {
51
+ console.error(` ${error}`);
52
+ }
53
+ console.error('\nTo fix:');
54
+ console.error(' 1. Review the missing dependencies above');
55
+ console.error(' 2. Add the missing dependencies to the respective package.json files');
56
+ console.error(' 3. Ensure dependencies in package.json match build.dependsOn in project.json');
57
+ return { success: false };
58
+ }
59
+
60
+ console.log('āœ… Package.json dependencies match project.json');
61
+
62
+ // Print summary
63
+ const validProjects = packageValidation.projectResults.filter(r => r.valid).length;
64
+ const totalProjects = packageValidation.projectResults.length;
65
+ console.log(`\nšŸ“ˆ Validation Summary:`);
66
+ console.log(` Projects validated: ${totalProjects}`);
67
+ console.log(` Valid: ${validProjects}`);
68
+ console.log(` Invalid: ${totalProjects - validProjects}`);
69
+
70
+ return { success: true };
71
+ } catch (err: unknown) {
72
+ const error = toError(err);
73
+ console.error('āŒ Package.json validation failed:', error.message);
74
+ return { success: false };
75
+ }
76
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Package.json Executor",
4
+ "description": "Validates that package.json dependencies match project.json build dependencies",
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": []
8
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validatePrismaConverters } 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 validatePrismaConverters(options, context.root);
11
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Prisma Converters Executor",
4
+ "description": "Validates that Prisma converter methods follow scalable patterns: correct Dbo parameter, no async, no standalone functions, and Dto creation only in converter directories.",
5
+ "type": "object",
6
+ "properties": {
7
+ "mode": {
8
+ "type": "string",
9
+ "enum": ["OFF", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
10
+ "description": "OFF: skip validation. NEW_AND_MODIFIED_METHODS: validate methods that are new or have changed lines. MODIFIED_FILES: validate all methods in modified files.",
11
+ "default": "OFF"
12
+ },
13
+ "schemaPath": {
14
+ "type": "string",
15
+ "description": "Relative path from workspace root to schema.prisma"
16
+ },
17
+ "convertersPaths": {
18
+ "type": "array",
19
+ "items": { "type": "string" },
20
+ "description": "Array of directories (relative to workspace root) containing converter files"
21
+ },
22
+ "enforcePaths": {
23
+ "type": "array",
24
+ "items": { "type": "string" },
25
+ "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."
26
+ },
27
+ "disableAllowed": {
28
+ "type": "boolean",
29
+ "description": "Whether disable comments work. When false, no escape hatch.",
30
+ "default": true
31
+ },
32
+ "ignoreModifiedUntilEpoch": {
33
+ "type": "number",
34
+ "description": "Epoch seconds. Until this time, skip prisma-converter validation entirely. When expired, normal mode resumes. Omit when not needed."
35
+ }
36
+ },
37
+ "required": []
38
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateReturnTypes } 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 validateReturnTypes(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Return Types Executor",
4
+ "description": "Validates that methods have explicit return type annotations for better code readability.",
5
+ "type": "object",
6
+ "properties": {
7
+ "mode": {
8
+ "type": "string",
9
+ "enum": ["OFF", "NEW_METHODS", "NEW_AND_MODIFIED_METHODS", "MODIFIED_FILES"],
10
+ "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.",
11
+ "default": "NEW_METHODS"
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
+ }