@webpieces/dev-config 0.2.17 ā 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/architecture/executors/generate/executor.d.ts +17 -0
- package/architecture/executors/generate/executor.js +67 -0
- package/architecture/executors/generate/executor.js.map +1 -0
- package/architecture/executors/generate/executor.ts +83 -0
- package/architecture/executors/generate/schema.json +14 -0
- package/architecture/executors/validate-architecture-unchanged/executor.d.ts +17 -0
- package/architecture/executors/validate-architecture-unchanged/executor.js +65 -0
- package/architecture/executors/validate-architecture-unchanged/executor.js.map +1 -0
- package/architecture/executors/validate-architecture-unchanged/executor.ts +81 -0
- package/architecture/executors/validate-architecture-unchanged/schema.json +14 -0
- package/architecture/executors/validate-no-cycles/executor.d.ts +16 -0
- package/architecture/executors/validate-no-cycles/executor.js +48 -0
- package/architecture/executors/validate-no-cycles/executor.js.map +1 -0
- package/architecture/executors/validate-no-cycles/executor.ts +60 -0
- package/architecture/executors/validate-no-cycles/schema.json +8 -0
- package/architecture/executors/validate-no-skiplevel-deps/executor.d.ts +19 -0
- package/architecture/executors/validate-no-skiplevel-deps/executor.js +227 -0
- package/architecture/executors/validate-no-skiplevel-deps/executor.js.map +1 -0
- package/architecture/executors/validate-no-skiplevel-deps/executor.ts +267 -0
- package/architecture/executors/validate-no-skiplevel-deps/schema.json +8 -0
- package/architecture/executors/visualize/executor.d.ts +17 -0
- package/architecture/executors/visualize/executor.js +49 -0
- package/architecture/executors/visualize/executor.js.map +1 -0
- package/architecture/executors/visualize/executor.ts +63 -0
- package/architecture/executors/visualize/schema.json +14 -0
- package/architecture/index.d.ts +19 -0
- package/architecture/index.js +23 -0
- package/architecture/index.js.map +1 -0
- package/architecture/index.ts +20 -0
- package/architecture/lib/graph-comparator.d.ts +39 -0
- package/architecture/lib/graph-comparator.js +100 -0
- package/architecture/lib/graph-comparator.js.map +1 -0
- package/architecture/lib/graph-comparator.ts +141 -0
- package/architecture/lib/graph-generator.d.ts +19 -0
- package/architecture/lib/graph-generator.js +88 -0
- package/architecture/lib/graph-generator.js.map +1 -0
- package/architecture/lib/graph-generator.ts +102 -0
- package/architecture/lib/graph-loader.d.ts +31 -0
- package/architecture/lib/graph-loader.js +70 -0
- package/architecture/lib/graph-loader.js.map +1 -0
- package/architecture/lib/graph-loader.ts +82 -0
- package/architecture/lib/graph-sorter.d.ts +37 -0
- package/architecture/lib/graph-sorter.js +110 -0
- package/architecture/lib/graph-sorter.js.map +1 -0
- package/architecture/lib/graph-sorter.ts +137 -0
- package/architecture/lib/graph-visualizer.d.ts +29 -0
- package/architecture/lib/graph-visualizer.js +209 -0
- package/architecture/lib/graph-visualizer.js.map +1 -0
- package/architecture/lib/graph-visualizer.ts +222 -0
- package/architecture/lib/package-validator.d.ts +38 -0
- package/architecture/lib/package-validator.js +105 -0
- package/architecture/lib/package-validator.js.map +1 -0
- package/architecture/lib/package-validator.ts +144 -0
- package/config/eslint/base.mjs +6 -0
- package/eslint-plugin/__tests__/max-file-lines.test.ts +207 -0
- package/eslint-plugin/__tests__/max-method-lines.test.ts +258 -0
- package/eslint-plugin/__tests__/no-unmanaged-exceptions.test.ts +359 -0
- package/eslint-plugin/index.d.ts +11 -0
- package/eslint-plugin/index.js +15 -0
- package/eslint-plugin/index.js.map +1 -1
- package/eslint-plugin/index.ts +15 -0
- package/eslint-plugin/rules/enforce-architecture.d.ts +15 -0
- package/eslint-plugin/rules/enforce-architecture.js +406 -0
- package/eslint-plugin/rules/enforce-architecture.js.map +1 -0
- package/eslint-plugin/rules/enforce-architecture.ts +469 -0
- package/eslint-plugin/rules/max-file-lines.d.ts +12 -0
- package/eslint-plugin/rules/max-file-lines.js +257 -0
- package/eslint-plugin/rules/max-file-lines.js.map +1 -0
- package/eslint-plugin/rules/max-file-lines.ts +272 -0
- package/eslint-plugin/rules/max-method-lines.d.ts +12 -0
- package/eslint-plugin/rules/max-method-lines.js +240 -0
- package/eslint-plugin/rules/max-method-lines.js.map +1 -0
- package/eslint-plugin/rules/max-method-lines.ts +287 -0
- package/eslint-plugin/rules/no-unmanaged-exceptions.d.ts +22 -0
- package/eslint-plugin/rules/no-unmanaged-exceptions.js +605 -0
- package/eslint-plugin/rules/no-unmanaged-exceptions.js.map +1 -0
- package/eslint-plugin/rules/no-unmanaged-exceptions.ts +621 -0
- package/executors.json +29 -0
- package/package.json +13 -3
- package/plugins/circular-deps/index.d.ts +8 -0
- package/plugins/circular-deps/index.js +14 -0
- package/plugins/circular-deps/index.js.map +1 -0
- package/plugins/circular-deps/index.ts +9 -0
- package/plugins/circular-deps/plugin.d.ts +32 -0
- package/plugins/circular-deps/plugin.js +73 -0
- package/plugins/circular-deps/plugin.js.map +1 -0
- package/plugins/circular-deps/plugin.ts +83 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validate No Skip-Level Dependencies Executor
|
|
4
|
+
*
|
|
5
|
+
* Validates that no project has redundant transitive dependencies.
|
|
6
|
+
* If project A depends on B, and B transitively brings in C, then A should NOT
|
|
7
|
+
* also directly depend on C (it's redundant and clutters the dependency graph).
|
|
8
|
+
*
|
|
9
|
+
* This keeps the architecture graph clean for visualization and human understanding.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* nx run architecture:validate-no-skiplevel-deps
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.default = runExecutor;
|
|
16
|
+
const tslib_1 = require("tslib");
|
|
17
|
+
const graph_generator_1 = require("../../lib/graph-generator");
|
|
18
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
19
|
+
const path = tslib_1.__importStar(require("path"));
|
|
20
|
+
const TRANSITIVE_DEPS_DOC = `# AI Agent Instructions: Redundant Transitive Dependency Violation
|
|
21
|
+
|
|
22
|
+
**READ THIS FILE FIRST before making any changes!**
|
|
23
|
+
|
|
24
|
+
## Why This Rule Exists
|
|
25
|
+
|
|
26
|
+
This rule keeps the architecture dependency graph **CLEAN and SIMPLE**.
|
|
27
|
+
|
|
28
|
+
When you run \`npx nx run architecture:visualize\`, it generates a visual diagram of all
|
|
29
|
+
package dependencies. Without this rule, you end up with a tangled mess of 100+ lines
|
|
30
|
+
where everything depends on everything - making it impossible to understand.
|
|
31
|
+
|
|
32
|
+
**Clean graphs = easier understanding for humans AND AI agents.**
|
|
33
|
+
|
|
34
|
+
## Understanding the Error
|
|
35
|
+
|
|
36
|
+
You have a **redundant transitive dependency**. This means:
|
|
37
|
+
|
|
38
|
+
1. Project A directly depends on Project C
|
|
39
|
+
2. BUT Project A also depends on Project B
|
|
40
|
+
3. AND Project B already brings in Project C (transitively)
|
|
41
|
+
|
|
42
|
+
Therefore, Project A's direct dependency on C is **redundant** - it's already available
|
|
43
|
+
through B. This extra line clutters the dependency graph.
|
|
44
|
+
|
|
45
|
+
**Example:**
|
|
46
|
+
\`\`\`
|
|
47
|
+
http-server depends on: [http-routing, http-filters, core-util]
|
|
48
|
+
^^^^^^^^^ ^^^^^^^^
|
|
49
|
+
REDUNDANT! REDUNDANT!
|
|
50
|
+
|
|
51
|
+
Why? Because http-routing already brings in:
|
|
52
|
+
- http-filters (direct)
|
|
53
|
+
- core-util (via http-api)
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
## How to Fix
|
|
57
|
+
|
|
58
|
+
### Step 1: Identify the Redundant Dependency
|
|
59
|
+
|
|
60
|
+
Look at the error message. It tells you:
|
|
61
|
+
- Which project has the problem
|
|
62
|
+
- Which dependency is redundant
|
|
63
|
+
- Which other dependency already brings it in
|
|
64
|
+
|
|
65
|
+
### Step 2: Remove from project.json
|
|
66
|
+
|
|
67
|
+
Remove the redundant dependency from \`build.dependsOn\`:
|
|
68
|
+
|
|
69
|
+
\`\`\`json
|
|
70
|
+
{
|
|
71
|
+
"targets": {
|
|
72
|
+
"build": {
|
|
73
|
+
"dependsOn": [
|
|
74
|
+
"^build",
|
|
75
|
+
"http-routing:build"
|
|
76
|
+
// REMOVE: "http-filters:build" <-- redundant, http-routing brings it in
|
|
77
|
+
// REMOVE: "core-util:build" <-- redundant, http-routing brings it in
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
### Step 3: Remove from package.json
|
|
85
|
+
|
|
86
|
+
Remove the redundant dependency from \`dependencies\`:
|
|
87
|
+
|
|
88
|
+
\`\`\`json
|
|
89
|
+
{
|
|
90
|
+
"dependencies": {
|
|
91
|
+
"@webpieces/http-routing": "*"
|
|
92
|
+
// REMOVE: "@webpieces/http-filters": "*" <-- redundant
|
|
93
|
+
// REMOVE: "@webpieces/core-util": "*" <-- redundant
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
### Step 4: Regenerate Architecture
|
|
99
|
+
|
|
100
|
+
\`\`\`bash
|
|
101
|
+
npx nx run architecture:generate
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
### Step 5: Verify
|
|
105
|
+
|
|
106
|
+
\`\`\`bash
|
|
107
|
+
npm run build-all
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
## Important Notes
|
|
111
|
+
|
|
112
|
+
- You DON'T lose access to the transitive dependency - it's still available through the parent
|
|
113
|
+
- This is about keeping the DECLARED dependencies minimal and clean
|
|
114
|
+
- The actual runtime/compile behavior is unchanged
|
|
115
|
+
- TypeScript will still find the types through the transitive path
|
|
116
|
+
|
|
117
|
+
## Remember
|
|
118
|
+
|
|
119
|
+
- Fewer lines in the graph = easier to understand
|
|
120
|
+
- Only declare what you DIRECTLY need that isn't already transitively available
|
|
121
|
+
- When in doubt, check with \`npx nx run architecture:visualize\`
|
|
122
|
+
`;
|
|
123
|
+
/**
|
|
124
|
+
* Compute all transitive dependencies for a project
|
|
125
|
+
*/
|
|
126
|
+
function computeTransitiveDeps(project, graph, visited = new Set()) {
|
|
127
|
+
const result = new Set();
|
|
128
|
+
if (visited.has(project)) {
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
visited.add(project);
|
|
132
|
+
const directDeps = graph[project] || [];
|
|
133
|
+
for (const dep of directDeps) {
|
|
134
|
+
result.add(dep);
|
|
135
|
+
// Recursively get transitive deps
|
|
136
|
+
const transitive = computeTransitiveDeps(dep, graph, visited);
|
|
137
|
+
for (const t of transitive) {
|
|
138
|
+
result.add(t);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Find redundant dependencies for a project
|
|
145
|
+
*/
|
|
146
|
+
function findRedundantDeps(project, graph) {
|
|
147
|
+
const redundant = [];
|
|
148
|
+
const directDeps = graph[project] || [];
|
|
149
|
+
// For each direct dependency, compute what it transitively brings in
|
|
150
|
+
const transitiveByDep = new Map();
|
|
151
|
+
for (const dep of directDeps) {
|
|
152
|
+
transitiveByDep.set(dep, computeTransitiveDeps(dep, graph));
|
|
153
|
+
}
|
|
154
|
+
// Check if any direct dependency is already brought in by another
|
|
155
|
+
for (const dep of directDeps) {
|
|
156
|
+
for (const [otherDep, otherTransitive] of transitiveByDep) {
|
|
157
|
+
if (otherDep !== dep && otherTransitive.has(dep)) {
|
|
158
|
+
redundant.push({
|
|
159
|
+
project,
|
|
160
|
+
redundantDep: dep,
|
|
161
|
+
alreadyBroughtInBy: otherDep,
|
|
162
|
+
});
|
|
163
|
+
break; // Only report once per redundant dep
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return redundant;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Write documentation file when violations are found
|
|
171
|
+
*/
|
|
172
|
+
function writeDocFile(workspaceRoot) {
|
|
173
|
+
const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.transitivedeps.md');
|
|
174
|
+
const docDir = path.dirname(docPath);
|
|
175
|
+
try {
|
|
176
|
+
fs.mkdirSync(docDir, { recursive: true });
|
|
177
|
+
fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
void err;
|
|
181
|
+
console.warn(`Could not write documentation file: ${docPath}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function runExecutor(_options, context) {
|
|
185
|
+
console.log('\nš Validating No Skip-Level Dependencies\n');
|
|
186
|
+
try {
|
|
187
|
+
// Step 1: Generate current graph from project.json files
|
|
188
|
+
console.log('š Generating dependency graph from project.json files...');
|
|
189
|
+
const graph = await (0, graph_generator_1.generateGraph)();
|
|
190
|
+
// Step 2: Find all redundant dependencies
|
|
191
|
+
console.log('š Checking for redundant transitive dependencies...');
|
|
192
|
+
const allRedundant = [];
|
|
193
|
+
for (const project of Object.keys(graph)) {
|
|
194
|
+
const redundant = findRedundantDeps(project, graph);
|
|
195
|
+
allRedundant.push(...redundant);
|
|
196
|
+
}
|
|
197
|
+
if (allRedundant.length === 0) {
|
|
198
|
+
console.log('ā
No redundant transitive dependencies detected!');
|
|
199
|
+
console.log('\nš Graph is clean and minimal.');
|
|
200
|
+
return { success: true };
|
|
201
|
+
}
|
|
202
|
+
// Write documentation file
|
|
203
|
+
const workspaceRoot = context.root || process.cwd();
|
|
204
|
+
writeDocFile(workspaceRoot);
|
|
205
|
+
// Report violations
|
|
206
|
+
console.error('\nā Redundant transitive dependencies detected!\n');
|
|
207
|
+
console.error('ā ļø AI Agent: READ tmp/webpieces/webpieces.transitivedeps.md for fix instructions!\n');
|
|
208
|
+
for (const r of allRedundant) {
|
|
209
|
+
console.error(` ${r.project}:`);
|
|
210
|
+
console.error(` āā "${r.redundantDep}" is REDUNDANT`);
|
|
211
|
+
console.error(` (already brought in by "${r.alreadyBroughtInBy}")\n`);
|
|
212
|
+
}
|
|
213
|
+
console.error('\nTo fix:');
|
|
214
|
+
console.error(' 1. READ tmp/webpieces/webpieces.transitivedeps.md');
|
|
215
|
+
console.error(' 2. Remove redundant deps from project.json build.dependsOn');
|
|
216
|
+
console.error(' 3. Remove redundant deps from package.json dependencies');
|
|
217
|
+
console.error(' 4. Run: npx nx run architecture:generate');
|
|
218
|
+
console.error(' 5. Run: npm run build-all');
|
|
219
|
+
return { success: false };
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
223
|
+
console.error('ā Skip-level validation failed:', error.message);
|
|
224
|
+
return { success: false };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/validate-no-skiplevel-deps/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate No Skip-Level Dependencies Executor\n *\n * Validates that no project has redundant transitive dependencies.\n * If project A depends on B, and B transitively brings in C, then A should NOT\n * also directly depend on C (it's redundant and clutters the dependency graph).\n *\n * This keeps the architecture graph clean for visualization and human understanding.\n *\n * Usage:\n * nx run architecture:validate-no-skiplevel-deps\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { generateGraph } from '../../lib/graph-generator';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateNoSkipLevelDepsOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\ninterface RedundantDep {\n project: string;\n redundantDep: string;\n alreadyBroughtInBy: string;\n}\n\nconst TRANSITIVE_DEPS_DOC = `# AI Agent Instructions: Redundant Transitive Dependency Violation\n\n**READ THIS FILE FIRST before making any changes!**\n\n## Why This Rule Exists\n\nThis rule keeps the architecture dependency graph **CLEAN and SIMPLE**.\n\nWhen you run \\`npx nx run architecture:visualize\\`, it generates a visual diagram of all\npackage dependencies. Without this rule, you end up with a tangled mess of 100+ lines\nwhere everything depends on everything - making it impossible to understand.\n\n**Clean graphs = easier understanding for humans AND AI agents.**\n\n## Understanding the Error\n\nYou have a **redundant transitive dependency**. This means:\n\n1. Project A directly depends on Project C\n2. BUT Project A also depends on Project B\n3. AND Project B already brings in Project C (transitively)\n\nTherefore, Project A's direct dependency on C is **redundant** - it's already available\nthrough B. This extra line clutters the dependency graph.\n\n**Example:**\n\\`\\`\\`\nhttp-server depends on: [http-routing, http-filters, core-util]\n ^^^^^^^^^ ^^^^^^^^\n REDUNDANT! REDUNDANT!\n\nWhy? Because http-routing already brings in:\n - http-filters (direct)\n - core-util (via http-api)\n\\`\\`\\`\n\n## How to Fix\n\n### Step 1: Identify the Redundant Dependency\n\nLook at the error message. It tells you:\n- Which project has the problem\n- Which dependency is redundant\n- Which other dependency already brings it in\n\n### Step 2: Remove from project.json\n\nRemove the redundant dependency from \\`build.dependsOn\\`:\n\n\\`\\`\\`json\n{\n \"targets\": {\n \"build\": {\n \"dependsOn\": [\n \"^build\",\n \"http-routing:build\"\n // REMOVE: \"http-filters:build\" <-- redundant, http-routing brings it in\n // REMOVE: \"core-util:build\" <-- redundant, http-routing brings it in\n ]\n }\n }\n}\n\\`\\`\\`\n\n### Step 3: Remove from package.json\n\nRemove the redundant dependency from \\`dependencies\\`:\n\n\\`\\`\\`json\n{\n \"dependencies\": {\n \"@webpieces/http-routing\": \"*\"\n // REMOVE: \"@webpieces/http-filters\": \"*\" <-- redundant\n // REMOVE: \"@webpieces/core-util\": \"*\" <-- redundant\n }\n}\n\\`\\`\\`\n\n### Step 4: Regenerate Architecture\n\n\\`\\`\\`bash\nnpx nx run architecture:generate\n\\`\\`\\`\n\n### Step 5: Verify\n\n\\`\\`\\`bash\nnpm run build-all\n\\`\\`\\`\n\n## Important Notes\n\n- You DON'T lose access to the transitive dependency - it's still available through the parent\n- This is about keeping the DECLARED dependencies minimal and clean\n- The actual runtime/compile behavior is unchanged\n- TypeScript will still find the types through the transitive path\n\n## Remember\n\n- Fewer lines in the graph = easier to understand\n- Only declare what you DIRECTLY need that isn't already transitively available\n- When in doubt, check with \\`npx nx run architecture:visualize\\`\n`;\n\n/**\n * Compute all transitive dependencies for a project\n */\nfunction computeTransitiveDeps(\n project: string,\n graph: Record<string, string[]>,\n visited: Set<string> = new Set()\n): Set<string> {\n const result = new Set<string>();\n\n if (visited.has(project)) {\n return result;\n }\n visited.add(project);\n\n const directDeps = graph[project] || [];\n for (const dep of directDeps) {\n result.add(dep);\n // Recursively get transitive deps\n const transitive = computeTransitiveDeps(dep, graph, visited);\n for (const t of transitive) {\n result.add(t);\n }\n }\n\n return result;\n}\n\n/**\n * Find redundant dependencies for a project\n */\nfunction findRedundantDeps(\n project: string,\n graph: Record<string, string[]>\n): RedundantDep[] {\n const redundant: RedundantDep[] = [];\n const directDeps = graph[project] || [];\n\n // For each direct dependency, compute what it transitively brings in\n const transitiveByDep = new Map<string, Set<string>>();\n for (const dep of directDeps) {\n transitiveByDep.set(dep, computeTransitiveDeps(dep, graph));\n }\n\n // Check if any direct dependency is already brought in by another\n for (const dep of directDeps) {\n for (const [otherDep, otherTransitive] of transitiveByDep) {\n if (otherDep !== dep && otherTransitive.has(dep)) {\n redundant.push({\n project,\n redundantDep: dep,\n alreadyBroughtInBy: otherDep,\n });\n break; // Only report once per redundant dep\n }\n }\n }\n\n return redundant;\n}\n\n/**\n * Write documentation file when violations are found\n */\nfunction writeDocFile(workspaceRoot: string): void {\n const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.transitivedeps.md');\n const docDir = path.dirname(docPath);\n\n try {\n fs.mkdirSync(docDir, { recursive: true });\n fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');\n } catch (err: any) {\n void err;\n console.warn(`Could not write documentation file: ${docPath}`);\n }\n}\n\nexport default async function runExecutor(\n _options: ValidateNoSkipLevelDepsOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n console.log('\\nš Validating No Skip-Level 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 graph = await generateGraph();\n\n // Step 2: Find all redundant dependencies\n console.log('š Checking for redundant transitive dependencies...');\n const allRedundant: RedundantDep[] = [];\n\n for (const project of Object.keys(graph)) {\n const redundant = findRedundantDeps(project, graph);\n allRedundant.push(...redundant);\n }\n\n if (allRedundant.length === 0) {\n console.log('ā
No redundant transitive dependencies detected!');\n console.log('\\nš Graph is clean and minimal.');\n return { success: true };\n }\n\n // Write documentation file\n const workspaceRoot = context.root || process.cwd();\n writeDocFile(workspaceRoot);\n\n // Report violations\n console.error('\\nā Redundant transitive dependencies detected!\\n');\n console.error('ā ļø AI Agent: READ tmp/webpieces/webpieces.transitivedeps.md for fix instructions!\\n');\n\n for (const r of allRedundant) {\n console.error(` ${r.project}:`);\n console.error(` āā \"${r.redundantDep}\" is REDUNDANT`);\n console.error(` (already brought in by \"${r.alreadyBroughtInBy}\")\\n`);\n }\n\n console.error('\\nTo fix:');\n console.error(' 1. READ tmp/webpieces/webpieces.transitivedeps.md');\n console.error(' 2. Remove redundant deps from project.json build.dependsOn');\n console.error(' 3. Remove redundant deps from package.json dependencies');\n console.error(' 4. Run: npx nx run architecture:generate');\n console.error(' 5. Run: npm run build-all');\n\n return { success: false };\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('ā Skip-level validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -0,0 +1,267 @@
|
|
|
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
|
+
|
|
19
|
+
export interface ValidateNoSkipLevelDepsOptions {
|
|
20
|
+
// No options needed
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ExecutorResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface RedundantDep {
|
|
28
|
+
project: string;
|
|
29
|
+
redundantDep: string;
|
|
30
|
+
alreadyBroughtInBy: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const TRANSITIVE_DEPS_DOC = `# AI Agent Instructions: Redundant Transitive Dependency Violation
|
|
34
|
+
|
|
35
|
+
**READ THIS FILE FIRST before making any changes!**
|
|
36
|
+
|
|
37
|
+
## Why This Rule Exists
|
|
38
|
+
|
|
39
|
+
This rule keeps the architecture dependency graph **CLEAN and SIMPLE**.
|
|
40
|
+
|
|
41
|
+
When you run \`npx nx run architecture:visualize\`, it generates a visual diagram of all
|
|
42
|
+
package dependencies. Without this rule, you end up with a tangled mess of 100+ lines
|
|
43
|
+
where everything depends on everything - making it impossible to understand.
|
|
44
|
+
|
|
45
|
+
**Clean graphs = easier understanding for humans AND AI agents.**
|
|
46
|
+
|
|
47
|
+
## Understanding the Error
|
|
48
|
+
|
|
49
|
+
You have a **redundant transitive dependency**. This means:
|
|
50
|
+
|
|
51
|
+
1. Project A directly depends on Project C
|
|
52
|
+
2. BUT Project A also depends on Project B
|
|
53
|
+
3. AND Project B already brings in Project C (transitively)
|
|
54
|
+
|
|
55
|
+
Therefore, Project A's direct dependency on C is **redundant** - it's already available
|
|
56
|
+
through B. This extra line clutters the dependency graph.
|
|
57
|
+
|
|
58
|
+
**Example:**
|
|
59
|
+
\`\`\`
|
|
60
|
+
http-server depends on: [http-routing, http-filters, core-util]
|
|
61
|
+
^^^^^^^^^ ^^^^^^^^
|
|
62
|
+
REDUNDANT! REDUNDANT!
|
|
63
|
+
|
|
64
|
+
Why? Because http-routing already brings in:
|
|
65
|
+
- http-filters (direct)
|
|
66
|
+
- core-util (via http-api)
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
## How to Fix
|
|
70
|
+
|
|
71
|
+
### Step 1: Identify the Redundant Dependency
|
|
72
|
+
|
|
73
|
+
Look at the error message. It tells you:
|
|
74
|
+
- Which project has the problem
|
|
75
|
+
- Which dependency is redundant
|
|
76
|
+
- Which other dependency already brings it in
|
|
77
|
+
|
|
78
|
+
### Step 2: Remove from project.json
|
|
79
|
+
|
|
80
|
+
Remove the redundant dependency from \`build.dependsOn\`:
|
|
81
|
+
|
|
82
|
+
\`\`\`json
|
|
83
|
+
{
|
|
84
|
+
"targets": {
|
|
85
|
+
"build": {
|
|
86
|
+
"dependsOn": [
|
|
87
|
+
"^build",
|
|
88
|
+
"http-routing:build"
|
|
89
|
+
// REMOVE: "http-filters:build" <-- redundant, http-routing brings it in
|
|
90
|
+
// REMOVE: "core-util:build" <-- redundant, http-routing brings it in
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
### Step 3: Remove from package.json
|
|
98
|
+
|
|
99
|
+
Remove the redundant dependency from \`dependencies\`:
|
|
100
|
+
|
|
101
|
+
\`\`\`json
|
|
102
|
+
{
|
|
103
|
+
"dependencies": {
|
|
104
|
+
"@webpieces/http-routing": "*"
|
|
105
|
+
// REMOVE: "@webpieces/http-filters": "*" <-- redundant
|
|
106
|
+
// REMOVE: "@webpieces/core-util": "*" <-- redundant
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
### Step 4: Regenerate Architecture
|
|
112
|
+
|
|
113
|
+
\`\`\`bash
|
|
114
|
+
npx nx run architecture:generate
|
|
115
|
+
\`\`\`
|
|
116
|
+
|
|
117
|
+
### Step 5: Verify
|
|
118
|
+
|
|
119
|
+
\`\`\`bash
|
|
120
|
+
npm run build-all
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
## Important Notes
|
|
124
|
+
|
|
125
|
+
- You DON'T lose access to the transitive dependency - it's still available through the parent
|
|
126
|
+
- This is about keeping the DECLARED dependencies minimal and clean
|
|
127
|
+
- The actual runtime/compile behavior is unchanged
|
|
128
|
+
- TypeScript will still find the types through the transitive path
|
|
129
|
+
|
|
130
|
+
## Remember
|
|
131
|
+
|
|
132
|
+
- Fewer lines in the graph = easier to understand
|
|
133
|
+
- Only declare what you DIRECTLY need that isn't already transitively available
|
|
134
|
+
- When in doubt, check with \`npx nx run architecture:visualize\`
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Compute all transitive dependencies for a project
|
|
139
|
+
*/
|
|
140
|
+
function computeTransitiveDeps(
|
|
141
|
+
project: string,
|
|
142
|
+
graph: Record<string, string[]>,
|
|
143
|
+
visited: Set<string> = new Set()
|
|
144
|
+
): Set<string> {
|
|
145
|
+
const result = new Set<string>();
|
|
146
|
+
|
|
147
|
+
if (visited.has(project)) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
visited.add(project);
|
|
151
|
+
|
|
152
|
+
const directDeps = graph[project] || [];
|
|
153
|
+
for (const dep of directDeps) {
|
|
154
|
+
result.add(dep);
|
|
155
|
+
// Recursively get transitive deps
|
|
156
|
+
const transitive = computeTransitiveDeps(dep, graph, visited);
|
|
157
|
+
for (const t of transitive) {
|
|
158
|
+
result.add(t);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Find redundant dependencies for a project
|
|
167
|
+
*/
|
|
168
|
+
function findRedundantDeps(
|
|
169
|
+
project: string,
|
|
170
|
+
graph: Record<string, string[]>
|
|
171
|
+
): RedundantDep[] {
|
|
172
|
+
const redundant: RedundantDep[] = [];
|
|
173
|
+
const directDeps = graph[project] || [];
|
|
174
|
+
|
|
175
|
+
// For each direct dependency, compute what it transitively brings in
|
|
176
|
+
const transitiveByDep = new Map<string, Set<string>>();
|
|
177
|
+
for (const dep of directDeps) {
|
|
178
|
+
transitiveByDep.set(dep, computeTransitiveDeps(dep, graph));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check if any direct dependency is already brought in by another
|
|
182
|
+
for (const dep of directDeps) {
|
|
183
|
+
for (const [otherDep, otherTransitive] of transitiveByDep) {
|
|
184
|
+
if (otherDep !== dep && otherTransitive.has(dep)) {
|
|
185
|
+
redundant.push({
|
|
186
|
+
project,
|
|
187
|
+
redundantDep: dep,
|
|
188
|
+
alreadyBroughtInBy: otherDep,
|
|
189
|
+
});
|
|
190
|
+
break; // Only report once per redundant dep
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return redundant;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Write documentation file when violations are found
|
|
200
|
+
*/
|
|
201
|
+
function writeDocFile(workspaceRoot: string): void {
|
|
202
|
+
const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.transitivedeps.md');
|
|
203
|
+
const docDir = path.dirname(docPath);
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
fs.mkdirSync(docDir, { recursive: true });
|
|
207
|
+
fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');
|
|
208
|
+
} catch (err: any) {
|
|
209
|
+
void err;
|
|
210
|
+
console.warn(`Could not write documentation file: ${docPath}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export default async function runExecutor(
|
|
215
|
+
_options: ValidateNoSkipLevelDepsOptions,
|
|
216
|
+
context: ExecutorContext
|
|
217
|
+
): Promise<ExecutorResult> {
|
|
218
|
+
console.log('\nš Validating No Skip-Level Dependencies\n');
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// Step 1: Generate current graph from project.json files
|
|
222
|
+
console.log('š Generating dependency graph from project.json files...');
|
|
223
|
+
const graph = await generateGraph();
|
|
224
|
+
|
|
225
|
+
// Step 2: Find all redundant dependencies
|
|
226
|
+
console.log('š Checking for redundant transitive dependencies...');
|
|
227
|
+
const allRedundant: RedundantDep[] = [];
|
|
228
|
+
|
|
229
|
+
for (const project of Object.keys(graph)) {
|
|
230
|
+
const redundant = findRedundantDeps(project, graph);
|
|
231
|
+
allRedundant.push(...redundant);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (allRedundant.length === 0) {
|
|
235
|
+
console.log('ā
No redundant transitive dependencies detected!');
|
|
236
|
+
console.log('\nš Graph is clean and minimal.');
|
|
237
|
+
return { success: true };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Write documentation file
|
|
241
|
+
const workspaceRoot = context.root || process.cwd();
|
|
242
|
+
writeDocFile(workspaceRoot);
|
|
243
|
+
|
|
244
|
+
// Report violations
|
|
245
|
+
console.error('\nā Redundant transitive dependencies detected!\n');
|
|
246
|
+
console.error('ā ļø AI Agent: READ tmp/webpieces/webpieces.transitivedeps.md for fix instructions!\n');
|
|
247
|
+
|
|
248
|
+
for (const r of allRedundant) {
|
|
249
|
+
console.error(` ${r.project}:`);
|
|
250
|
+
console.error(` āā "${r.redundantDep}" is REDUNDANT`);
|
|
251
|
+
console.error(` (already brought in by "${r.alreadyBroughtInBy}")\n`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.error('\nTo fix:');
|
|
255
|
+
console.error(' 1. READ tmp/webpieces/webpieces.transitivedeps.md');
|
|
256
|
+
console.error(' 2. Remove redundant deps from project.json build.dependsOn');
|
|
257
|
+
console.error(' 3. Remove redundant deps from package.json dependencies');
|
|
258
|
+
console.error(' 4. Run: npx nx run architecture:generate');
|
|
259
|
+
console.error(' 5. Run: npm run build-all');
|
|
260
|
+
|
|
261
|
+
return { success: false };
|
|
262
|
+
} catch (err: any) {
|
|
263
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
264
|
+
console.error('ā Skip-level validation failed:', error.message);
|
|
265
|
+
return { success: false };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visualize Executor
|
|
3
|
+
*
|
|
4
|
+
* Generates visual representations of the architecture graph (DOT + HTML)
|
|
5
|
+
* and opens the visualization in a browser.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* nx run architecture:visualize
|
|
9
|
+
*/
|
|
10
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
11
|
+
export interface VisualizeExecutorOptions {
|
|
12
|
+
graphPath?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ExecutorResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
}
|
|
17
|
+
export default function runExecutor(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Visualize Executor
|
|
4
|
+
*
|
|
5
|
+
* Generates visual representations of the architecture graph (DOT + HTML)
|
|
6
|
+
* and opens the visualization in a browser.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* nx run architecture:visualize
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.default = runExecutor;
|
|
13
|
+
const graph_loader_1 = require("../../lib/graph-loader");
|
|
14
|
+
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
15
|
+
async function runExecutor(options, context) {
|
|
16
|
+
const { graphPath } = options;
|
|
17
|
+
const workspaceRoot = context.root;
|
|
18
|
+
console.log('\nšØ Architecture Visualization\n');
|
|
19
|
+
try {
|
|
20
|
+
// Load the saved graph
|
|
21
|
+
console.log('š Loading saved graph...');
|
|
22
|
+
const graph = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
23
|
+
if (!graph) {
|
|
24
|
+
console.error('ā No saved graph found at architecture/dependencies.json');
|
|
25
|
+
console.error(' Run: nx run architecture:generate first');
|
|
26
|
+
return { success: false };
|
|
27
|
+
}
|
|
28
|
+
// Generate visualization
|
|
29
|
+
console.log('šØ Generating visualization...');
|
|
30
|
+
const { dotPath, htmlPath } = (0, graph_visualizer_1.writeVisualization)(graph, workspaceRoot);
|
|
31
|
+
console.log(`ā
Generated: ${dotPath}`);
|
|
32
|
+
console.log(`ā
Generated: ${htmlPath}`);
|
|
33
|
+
// Try to open in browser
|
|
34
|
+
console.log('\nš Opening visualization in browser...');
|
|
35
|
+
if ((0, graph_visualizer_1.openVisualization)(htmlPath)) {
|
|
36
|
+
console.log('ā
Browser opened');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log(`ā ļø Could not auto-open. Open manually: ${htmlPath}`);
|
|
40
|
+
}
|
|
41
|
+
return { success: true };
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
45
|
+
console.error('ā Visualization failed:', error.message);
|
|
46
|
+
return { success: false };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/dev-config/architecture/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAcH,8BAwCC;AAnDD,yDAA0D;AAC1D,iEAAmF;AAUpE,KAAK,UAAU,WAAW,CACrC,OAAiC,EACjC,OAAwB;IAExB,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9B,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAEjD,IAAI,CAAC;QACD,uBAAuB;QACvB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAEzD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAC1E,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAA,qCAAkB,EAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,EAAE,CAAC,CAAC;QAExC,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,IAAI,IAAA,oCAAiB,EAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,2CAA2C,QAAQ,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,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,yBAAyB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACxD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Visualize Executor\n *\n * Generates visual representations of the architecture graph (DOT + HTML)\n * and opens the visualization in a browser.\n *\n * Usage:\n * nx run architecture:visualize\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\nimport { writeVisualization, openVisualization } from '../../lib/graph-visualizer';\n\nexport interface VisualizeExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n options: VisualizeExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const { graphPath } = options;\n const workspaceRoot = context.root;\n\n console.log('\\nšØ Architecture Visualization\\n');\n\n try {\n // Load the saved graph\n console.log('š Loading saved graph...');\n const graph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!graph) {\n console.error('ā No saved graph found at architecture/dependencies.json');\n console.error(' Run: nx run architecture:generate first');\n return { success: false };\n }\n\n // Generate visualization\n console.log('šØ Generating visualization...');\n const { dotPath, htmlPath } = writeVisualization(graph, workspaceRoot);\n console.log(`ā
Generated: ${dotPath}`);\n console.log(`ā
Generated: ${htmlPath}`);\n\n // Try to open in browser\n console.log('\\nš Opening visualization in browser...');\n if (openVisualization(htmlPath)) {\n console.log('ā
Browser opened');\n } else {\n console.log(`ā ļø Could not auto-open. Open manually: ${htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n console.error('ā Visualization failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visualize Executor
|
|
3
|
+
*
|
|
4
|
+
* Generates visual representations of the architecture graph (DOT + HTML)
|
|
5
|
+
* and opens the visualization in a browser.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* nx run architecture:visualize
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
12
|
+
import { loadBlessedGraph } from '../../lib/graph-loader';
|
|
13
|
+
import { writeVisualization, openVisualization } from '../../lib/graph-visualizer';
|
|
14
|
+
|
|
15
|
+
export interface VisualizeExecutorOptions {
|
|
16
|
+
graphPath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ExecutorResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default async function runExecutor(
|
|
24
|
+
options: VisualizeExecutorOptions,
|
|
25
|
+
context: ExecutorContext
|
|
26
|
+
): Promise<ExecutorResult> {
|
|
27
|
+
const { graphPath } = options;
|
|
28
|
+
const workspaceRoot = context.root;
|
|
29
|
+
|
|
30
|
+
console.log('\nšØ Architecture Visualization\n');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Load the saved graph
|
|
34
|
+
console.log('š Loading saved graph...');
|
|
35
|
+
const graph = loadBlessedGraph(workspaceRoot, graphPath);
|
|
36
|
+
|
|
37
|
+
if (!graph) {
|
|
38
|
+
console.error('ā No saved graph found at architecture/dependencies.json');
|
|
39
|
+
console.error(' Run: nx run architecture:generate first');
|
|
40
|
+
return { success: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Generate visualization
|
|
44
|
+
console.log('šØ Generating visualization...');
|
|
45
|
+
const { dotPath, htmlPath } = writeVisualization(graph, workspaceRoot);
|
|
46
|
+
console.log(`ā
Generated: ${dotPath}`);
|
|
47
|
+
console.log(`ā
Generated: ${htmlPath}`);
|
|
48
|
+
|
|
49
|
+
// Try to open in browser
|
|
50
|
+
console.log('\nš Opening visualization in browser...');
|
|
51
|
+
if (openVisualization(htmlPath)) {
|
|
52
|
+
console.log('ā
Browser opened');
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`ā ļø Could not auto-open. Open manually: ${htmlPath}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { success: true };
|
|
58
|
+
} catch (err: unknown) {
|
|
59
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
60
|
+
console.error('ā Visualization failed:', error.message);
|
|
61
|
+
return { success: false };
|
|
62
|
+
}
|
|
63
|
+
}
|