@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,63 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+
3
+ export interface HelpExecutorOptions {}
4
+
5
+ type HelpExecutorResult = { success: true };
6
+
7
+ export default async function helpExecutor(
8
+ options: HelpExecutorOptions,
9
+ context: ExecutorContext
10
+ ): Promise<HelpExecutorResult> {
11
+ // ANSI color codes
12
+ const GREEN = '\x1b[32m\x1b[1m';
13
+ const BOLD = '\x1b[1m';
14
+ const RESET = '\x1b[0m';
15
+
16
+ console.log('');
17
+ console.log(`${GREEN}šŸ’” @webpieces/nx-webpieces-rules - Available Commands${RESET}`);
18
+ console.log('');
19
+ console.log(`${BOLD}šŸ“ Available npm scripts (convenient shortcuts):${RESET}`);
20
+ console.log('');
21
+ console.log(' Architecture graph:');
22
+ console.log(' npm run arch:generate # Generate dependency graph');
23
+ console.log(' npm run arch:visualize # Visualize dependency graph');
24
+ console.log('');
25
+ console.log(' Validation:');
26
+ console.log(' npm run arch:validate # Quick validation (no-cycles + no-skiplevel-deps)');
27
+ console.log(' npm run arch:validate-all # Full arch validation (+ unchanged check)');
28
+ console.log(' npm run arch:check-circular # Check all projects for circular deps');
29
+ console.log(' npm run arch:check-circular-affected # Check affected projects only');
30
+ console.log(' npm run arch:validate-complete # Complete validation (arch + circular)');
31
+ console.log('');
32
+ console.log(`${BOLD}šŸ“ Available Nx targets:${RESET}`);
33
+ console.log('');
34
+ console.log(' Workspace-level architecture validation:');
35
+ console.log(' nx run architecture:generate # Generate dependency graph');
36
+ console.log(' nx run architecture:visualize # Visualize dependency graph');
37
+ console.log(' nx run architecture:validate-no-architecture-cycles # Check for circular project dependencies');
38
+ console.log(' nx run architecture:validate-no-skiplevel-deps # Check for redundant dependencies');
39
+ console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');
40
+ console.log('');
41
+ console.log(' Per-project file import cycle checking:');
42
+ console.log(' nx run <project>:validate-no-file-import-cycles # Check project for file import cycles');
43
+ console.log(' nx affected --target=validate-no-file-import-cycles # Check all affected projects');
44
+ console.log(' nx run-many --target=validate-no-file-import-cycles --all # Check all projects');
45
+ console.log('');
46
+ console.log(' Per-project CI target (lint + build + test):');
47
+ console.log(' nx run <project>:ci # Run lint, build, test together');
48
+ console.log(' nx run-many --target=ci --all # Run ci for all projects');
49
+ console.log('');
50
+ console.log(' Execution order (test waits for build via targetDefaults):');
51
+ console.log(' ci (nx:noop)');
52
+ console.log(' ā”œā”€ā”€ lint ─────────────────┐');
53
+ console.log(' ā”œā”€ā”€ build ────────────────┼── run in parallel');
54
+ console.log(' └── test ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜');
55
+ console.log(' └── depends on build (waits)');
56
+ console.log('');
57
+ console.log(`${GREEN}šŸ’” Quick start:${RESET}`);
58
+ console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the graph first`);
59
+ console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);
60
+ console.log('');
61
+
62
+ return { success: true };
63
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Help Executor",
4
+ "description": "Display help for @webpieces/nx-webpieces-rules commands and targets",
5
+ "type": "object",
6
+ "properties": {}
7
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Validate Architecture Unchanged Executor
3
+ *
4
+ * Validates that the current architecture graph matches the saved blessed graph.
5
+ * This ensures no unapproved architecture changes have been made.
6
+ *
7
+ * Usage:
8
+ * nx run architecture:validate-architecture-unchanged
9
+ */
10
+
11
+ import type { ExecutorContext } from '@nx/devkit';
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { generateGraph } from '../../lib/graph-generator';
15
+ import { sortGraphTopologically } from '../../lib/graph-sorter';
16
+ import { compareGraphs } from '../../lib/graph-comparator';
17
+ import { loadBlessedGraph, graphFileExists } from '../../lib/graph-loader';
18
+ import { toError } from '../../toError';
19
+
20
+ export interface ValidateArchitectureUnchangedOptions {
21
+ graphPath?: string;
22
+ }
23
+
24
+ export interface ExecutorResult {
25
+ success: boolean;
26
+ }
27
+
28
+ const TMP_DIR = '.webpieces/instruct-ai';
29
+ const TMP_MD_FILE = 'webpieces.dependencies.md';
30
+
31
+ const DEPENDENCIES_DOC_CONTENT = `# Instructions: Architecture Dependency Violation
32
+
33
+ IN GENERAL, it is better to avoid these changes and find a different way by moving classes
34
+ around to existing packages you already depend on. It is not always avoidable though.
35
+ A clean dependency graph keeps you out of huge trouble later.
36
+
37
+ If you are a human, simply run these commands:
38
+ * nx run architecture:visualize - to see the new dependencies and validate that change is desired
39
+ * nx run architecture:generate - updates the dep graph
40
+ * git diff architecture/dependencies.json - to see the deps changes you made
41
+
42
+ **READ THIS FILE FIRST before making any changes!**
43
+
44
+ ## āš ļø CRITICAL WARNING āš ļø
45
+
46
+ **This is a VERY IMPORTANT change that has LARGE REPERCUSSIONS later!**
47
+
48
+ Adding new dependencies creates technical debt that compounds over time:
49
+ - Creates coupling between packages that may be hard to undo
50
+ - Can create circular dependency tangles
51
+ - Makes packages harder to test in isolation
52
+ - Increases build times and bundle sizes
53
+ - May force unnecessary upgrades across the codebase
54
+
55
+ **DO NOT add dependencies without senior developer approval!**
56
+
57
+ ## Understanding the Error
58
+
59
+ You've attempted to import from a package that is not in your project's allowed dependencies.
60
+ The architecture enforces a layered dependency structure where:
61
+ - Level 0 packages are foundation packages with NO dependencies on other @webpieces packages
62
+ - Higher level packages can only depend on lower level packages
63
+ - All dependencies must be explicitly declared
64
+
65
+ ## Steps to Resolve
66
+
67
+ ### Step 1: Generate Current Dependency Graph
68
+ Run this command to see the current architecture:
69
+ \`\`\`bash
70
+ npx nx run architecture:generate
71
+ \`\`\`
72
+ This creates/updates \`architecture/dependencies.json\` showing all packages and their levels.
73
+
74
+ ### Step 2: Analyze the Proposed Change
75
+ Ask yourself:
76
+ 1. **Is this import truly necessary?** Can you refactor to avoid it?
77
+ 2. **Should the code move instead?** Maybe the code belongs in a different package.
78
+ 3. **Will this create a cycle?** Use \`npx nx graph\` to visualize dependencies.
79
+ 4. **Can you use an interface/abstraction?** Define interface in lower-level package, implement in higher-level.
80
+
81
+ ### Step 3: Get Senior Developer Approval
82
+
83
+ ## šŸ›‘ AI AGENTS: STOP HERE AND ASK FOR HUMAN APPROVAL! šŸ›‘
84
+
85
+ **YOU MUST NOT PROCEED TO STEP 4 WITHOUT EXPLICIT HUMAN APPROVAL!**
86
+
87
+ **REQUIRED**: Discuss this architectural change with a senior developer before proceeding.
88
+ - Explain why the dependency is needed
89
+ - Show you've considered alternatives (Step 2)
90
+ - **WAIT for explicit approval before making ANY changes to project.json or package.json**
91
+
92
+ **AI Agent Instructions:**
93
+ 1. Present your analysis from Step 2 to the human
94
+ 2. Explain which package needs which dependency and why
95
+ 3. ASK: "Do you approve adding this dependency?"
96
+ 4. **DO NOT modify project.json or package.json until you receive explicit "yes" or approval**
97
+
98
+ ### Step 4: If Approved, Add the Dependency
99
+
100
+ ## ā›” NEVER MODIFY THESE FILES WITHOUT HUMAN APPROVAL FROM STEP 3! ā›”
101
+
102
+ Only after receiving explicit human approval in Step 3, make these changes:
103
+
104
+ 1. **Update project.json** - Add to \`build.dependsOn\`:
105
+ \`\`\`json
106
+ {
107
+ "targets": {
108
+ "build": {
109
+ "dependsOn": ["^build", "dep1:build", "NEW_PACKAGE:build"]
110
+ }
111
+ }
112
+ }
113
+ \`\`\`
114
+
115
+ 2. **Update package.json** - Add to \`dependencies\`:
116
+ \`\`\`json
117
+ {
118
+ "dependencies": {
119
+ "@webpieces/NEW_PACKAGE": "*"
120
+ }
121
+ }
122
+ \`\`\`
123
+
124
+ ### Step 5: Update Architecture Definition
125
+ Run this command to validate and update the architecture:
126
+ \`\`\`bash
127
+ npx nx run architecture:generate
128
+ \`\`\`
129
+
130
+ This will:
131
+ - Detect any cycles (which MUST be fixed before proceeding)
132
+ - Update \`architecture/dependencies.json\` with the new dependency
133
+ - Recalculate package levels
134
+
135
+ ### Step 6: Verify No Cycles
136
+ \`\`\`bash
137
+ npx nx run architecture:validate-no-architecture-cycles
138
+ \`\`\`
139
+
140
+ If cycles are detected, you MUST refactor to break the cycle. Common strategies:
141
+ - Move shared code to a lower-level package
142
+ - Use dependency inversion (interfaces in low-level, implementations in high-level)
143
+ - Restructure package boundaries
144
+
145
+ ## Alternative Solutions (Preferred over adding dependencies)
146
+
147
+ ### Option A: Move the Code
148
+ If you need functionality from another package, consider moving that code to a shared lower-level package.
149
+
150
+ ### Option B: Dependency Inversion
151
+ Define an interface in the lower-level package, implement it in the higher-level package:
152
+ \`\`\`typescript
153
+ // In foundation package (level 0)
154
+ export interface Logger { log(msg: string): void; }
155
+
156
+ // In higher-level package
157
+ export class ConsoleLogger implements Logger { ... }
158
+ \`\`\`
159
+
160
+ ### Option C: Pass Dependencies as Parameters
161
+ Instead of importing, receive the dependency as a constructor or method parameter.
162
+
163
+ ## Remember
164
+ - Every dependency you add today is technical debt for tomorrow
165
+ - The best dependency is the one you don't need
166
+ - When in doubt, refactor rather than add dependencies
167
+ `;
168
+
169
+ /**
170
+ * Write the instructions documentation to tmp directory
171
+ */
172
+ function writeTmpInstructionsFile(workspaceRoot: string): string {
173
+ const tmpDir = path.join(workspaceRoot, TMP_DIR);
174
+ const mdPath = path.join(tmpDir, TMP_MD_FILE);
175
+
176
+ // Ensure tmp directory exists
177
+ fs.mkdirSync(tmpDir, { recursive: true });
178
+
179
+ // Write documentation MD file
180
+ fs.writeFileSync(mdPath, DEPENDENCIES_DOC_CONTENT);
181
+
182
+ return mdPath;
183
+ }
184
+
185
+ export default async function runExecutor(
186
+ options: ValidateArchitectureUnchangedOptions,
187
+ context: ExecutorContext
188
+ ): Promise<ExecutorResult> {
189
+ const graphPath = options.graphPath;
190
+ const workspaceRoot = context.root;
191
+
192
+ console.log('\nšŸ” Validating Architecture Unchanged\n');
193
+
194
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
195
+ try {
196
+ // Check if saved graph exists
197
+ if (!graphFileExists(workspaceRoot, graphPath)) {
198
+ console.error('āŒ No saved graph found at architecture/dependencies.json');
199
+ console.error('');
200
+ console.error('To initialize:');
201
+ console.error(' 1. Run: nx run architecture:generate');
202
+ console.error(' 2. Run: nx run architecture:visualize');
203
+ console.error(' 3. Manually inspect the generated graph to confirm it is the desired architecture');
204
+ console.error(' 4. Commit architecture/dependencies.json');
205
+ return { success: false };
206
+ }
207
+
208
+ // Step 1: Generate current graph from project.json files
209
+ console.log('šŸ“Š Generating current dependency graph...');
210
+ const rawGraph = await generateGraph();
211
+
212
+ // Step 2: Topological sort (to get enhanced graph with levels)
213
+ console.log('šŸ”„ Computing topological layers...');
214
+ const currentGraph = sortGraphTopologically(rawGraph);
215
+
216
+ // Step 3: Load saved graph
217
+ console.log('šŸ“‚ Loading saved graph...');
218
+ const savedGraph = loadBlessedGraph(workspaceRoot, graphPath);
219
+
220
+ if (!savedGraph) {
221
+ console.error('āŒ Could not load saved graph');
222
+ return { success: false };
223
+ }
224
+
225
+ // Step 4: Compare graphs
226
+ console.log('šŸ” Comparing current graph to saved graph...');
227
+ const comparison = compareGraphs(currentGraph, savedGraph);
228
+
229
+ if (comparison.identical) {
230
+ console.log('āœ… Architecture unchanged - current graph matches saved graph');
231
+ return { success: true };
232
+ } else {
233
+ // Write instructions file for AI agent
234
+ const mdPath = writeTmpInstructionsFile(workspaceRoot);
235
+
236
+ console.error('āŒ Architecture has changed since last update!');
237
+ console.error('\nDifferences:');
238
+ console.error(comparison.summary);
239
+ console.error('');
240
+ console.error('āš ļø *** Refer to ' + mdPath + ' for instructions on how to fix *** āš ļø');
241
+ console.error('');
242
+ console.error('To fix:');
243
+ console.error(' 1. Review the changes above');
244
+ console.error(' 2. If intentional, ASK USER to run: nx run architecture:generate since this is a critical change');
245
+ console.error(' 3. Commit the updated architecture/dependencies.json');
246
+ return { success: false };
247
+ }
248
+ } catch (err: unknown) {
249
+ const error = toError(err);
250
+ console.error('āŒ Architecture validation failed:', error.message);
251
+ return { success: false };
252
+ }
253
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Architecture Unchanged Executor",
4
+ "description": "Validates that the current architecture matches the saved graph",
5
+ "type": "object",
6
+ "properties": {
7
+ "graphPath": {
8
+ "type": "string",
9
+ "description": "Path to the graph file (relative to workspace root)",
10
+ "default": "architecture/dependencies.json"
11
+ }
12
+ },
13
+ "required": []
14
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateCatchErrorPattern } 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 validateCatchErrorPattern(options, context.root);
11
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "title": "Validate Catch Error Pattern Executor",
4
+ "description": "Validates that catch blocks follow the standardized pattern: catch (err: unknown) { const error = toError(err); }",
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 catch blocks on changed lines in diff. MODIFIED_FILES: all catch blocks 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,11 @@
1
+ import type { ExecutorContext } from '@nx/devkit';
2
+ import { ExecutorResult } from '../../executor-result';
3
+ import { validateCode } 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 validateCode(options, context.root);
11
+ }