@webpieces/nx-webpieces-rules 0.2.114 → 0.2.116
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/package.json +7 -3
- package/src/executors/validate-architecture-unchanged/executor.js +4 -148
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/executors/validate-no-skiplevel-deps/executor.js +4 -113
- package/src/executors/validate-no-skiplevel-deps/executor.js.map +1 -1
- package/src/lib/graph-generator.js +1 -1
- package/src/lib/graph-generator.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Nx-specific webpieces validation rules.
|
|
3
|
+
"version": "0.2.116",
|
|
4
|
+
"description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"executors": "./executors.json",
|
|
@@ -13,11 +13,15 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"src/**/*",
|
|
16
|
+
"templates/**/*",
|
|
16
17
|
"executors.json",
|
|
17
18
|
"README.md"
|
|
18
19
|
],
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@webpieces/
|
|
21
|
+
"@webpieces/ai-hook-rules": "0.2.116",
|
|
22
|
+
"@webpieces/code-rules": "0.2.116",
|
|
23
|
+
"@webpieces/eslint-rules": "0.2.116",
|
|
24
|
+
"@webpieces/rules-config": "0.2.116"
|
|
21
25
|
},
|
|
22
26
|
"peerDependencies": {
|
|
23
27
|
"@nx/devkit": ">=18.0.0"
|
|
@@ -10,163 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.default = runExecutor;
|
|
13
|
-
const
|
|
14
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
15
|
-
const path = tslib_1.__importStar(require("path"));
|
|
13
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
16
14
|
const graph_generator_1 = require("../../lib/graph-generator");
|
|
17
15
|
const graph_sorter_1 = require("../../lib/graph-sorter");
|
|
18
16
|
const graph_comparator_1 = require("../../lib/graph-comparator");
|
|
19
17
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
20
18
|
const toError_1 = require("../../toError");
|
|
21
|
-
const TMP_DIR = '.webpieces/instruct-ai';
|
|
22
19
|
const TMP_MD_FILE = 'webpieces.dependencies.md';
|
|
23
|
-
const DEPENDENCIES_DOC_CONTENT = `# Instructions: Architecture Dependency Violation
|
|
24
|
-
|
|
25
|
-
IN GENERAL, it is better to avoid these changes and find a different way by moving classes
|
|
26
|
-
around to existing packages you already depend on. It is not always avoidable though.
|
|
27
|
-
A clean dependency graph keeps you out of huge trouble later.
|
|
28
|
-
|
|
29
|
-
If you are a human, simply run these commands:
|
|
30
|
-
* nx run architecture:visualize - to see the new dependencies and validate that change is desired
|
|
31
|
-
* nx run architecture:generate - updates the dep graph
|
|
32
|
-
* git diff architecture/dependencies.json - to see the deps changes you made
|
|
33
|
-
|
|
34
|
-
**READ THIS FILE FIRST before making any changes!**
|
|
35
|
-
|
|
36
|
-
## ⚠️ CRITICAL WARNING ⚠️
|
|
37
|
-
|
|
38
|
-
**This is a VERY IMPORTANT change that has LARGE REPERCUSSIONS later!**
|
|
39
|
-
|
|
40
|
-
Adding new dependencies creates technical debt that compounds over time:
|
|
41
|
-
- Creates coupling between packages that may be hard to undo
|
|
42
|
-
- Can create circular dependency tangles
|
|
43
|
-
- Makes packages harder to test in isolation
|
|
44
|
-
- Increases build times and bundle sizes
|
|
45
|
-
- May force unnecessary upgrades across the codebase
|
|
46
|
-
|
|
47
|
-
**DO NOT add dependencies without senior developer approval!**
|
|
48
|
-
|
|
49
|
-
## Understanding the Error
|
|
50
|
-
|
|
51
|
-
You've attempted to import from a package that is not in your project's allowed dependencies.
|
|
52
|
-
The architecture enforces a layered dependency structure where:
|
|
53
|
-
- Level 0 packages are foundation packages with NO dependencies on other @webpieces packages
|
|
54
|
-
- Higher level packages can only depend on lower level packages
|
|
55
|
-
- All dependencies must be explicitly declared
|
|
56
|
-
|
|
57
|
-
## Steps to Resolve
|
|
58
|
-
|
|
59
|
-
### Step 1: Generate Current Dependency Graph
|
|
60
|
-
Run this command to see the current architecture:
|
|
61
|
-
\`\`\`bash
|
|
62
|
-
npx nx run architecture:generate
|
|
63
|
-
\`\`\`
|
|
64
|
-
This creates/updates \`architecture/dependencies.json\` showing all packages and their levels.
|
|
65
|
-
|
|
66
|
-
### Step 2: Analyze the Proposed Change
|
|
67
|
-
Ask yourself:
|
|
68
|
-
1. **Is this import truly necessary?** Can you refactor to avoid it?
|
|
69
|
-
2. **Should the code move instead?** Maybe the code belongs in a different package.
|
|
70
|
-
3. **Will this create a cycle?** Use \`npx nx graph\` to visualize dependencies.
|
|
71
|
-
4. **Can you use an interface/abstraction?** Define interface in lower-level package, implement in higher-level.
|
|
72
|
-
|
|
73
|
-
### Step 3: Get Senior Developer Approval
|
|
74
|
-
|
|
75
|
-
## 🛑 AI AGENTS: STOP HERE AND ASK FOR HUMAN APPROVAL! 🛑
|
|
76
|
-
|
|
77
|
-
**YOU MUST NOT PROCEED TO STEP 4 WITHOUT EXPLICIT HUMAN APPROVAL!**
|
|
78
|
-
|
|
79
|
-
**REQUIRED**: Discuss this architectural change with a senior developer before proceeding.
|
|
80
|
-
- Explain why the dependency is needed
|
|
81
|
-
- Show you've considered alternatives (Step 2)
|
|
82
|
-
- **WAIT for explicit approval before making ANY changes to project.json or package.json**
|
|
83
|
-
|
|
84
|
-
**AI Agent Instructions:**
|
|
85
|
-
1. Present your analysis from Step 2 to the human
|
|
86
|
-
2. Explain which package needs which dependency and why
|
|
87
|
-
3. ASK: "Do you approve adding this dependency?"
|
|
88
|
-
4. **DO NOT modify project.json or package.json until you receive explicit "yes" or approval**
|
|
89
|
-
|
|
90
|
-
### Step 4: If Approved, Add the Dependency
|
|
91
|
-
|
|
92
|
-
## ⛔ NEVER MODIFY THESE FILES WITHOUT HUMAN APPROVAL FROM STEP 3! ⛔
|
|
93
|
-
|
|
94
|
-
Only after receiving explicit human approval in Step 3, make these changes:
|
|
95
|
-
|
|
96
|
-
1. **Update project.json** - Add to \`build.dependsOn\`:
|
|
97
|
-
\`\`\`json
|
|
98
|
-
{
|
|
99
|
-
"targets": {
|
|
100
|
-
"build": {
|
|
101
|
-
"dependsOn": ["^build", "dep1:build", "NEW_PACKAGE:build"]
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
\`\`\`
|
|
106
|
-
|
|
107
|
-
2. **Update package.json** - Add to \`dependencies\`:
|
|
108
|
-
\`\`\`json
|
|
109
|
-
{
|
|
110
|
-
"dependencies": {
|
|
111
|
-
"@webpieces/NEW_PACKAGE": "*"
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
\`\`\`
|
|
115
|
-
|
|
116
|
-
### Step 5: Update Architecture Definition
|
|
117
|
-
Run this command to validate and update the architecture:
|
|
118
|
-
\`\`\`bash
|
|
119
|
-
npx nx run architecture:generate
|
|
120
|
-
\`\`\`
|
|
121
|
-
|
|
122
|
-
This will:
|
|
123
|
-
- Detect any cycles (which MUST be fixed before proceeding)
|
|
124
|
-
- Update \`architecture/dependencies.json\` with the new dependency
|
|
125
|
-
- Recalculate package levels
|
|
126
|
-
|
|
127
|
-
### Step 6: Verify No Cycles
|
|
128
|
-
\`\`\`bash
|
|
129
|
-
npx nx run architecture:validate-no-architecture-cycles
|
|
130
|
-
\`\`\`
|
|
131
|
-
|
|
132
|
-
If cycles are detected, you MUST refactor to break the cycle. Common strategies:
|
|
133
|
-
- Move shared code to a lower-level package
|
|
134
|
-
- Use dependency inversion (interfaces in low-level, implementations in high-level)
|
|
135
|
-
- Restructure package boundaries
|
|
136
|
-
|
|
137
|
-
## Alternative Solutions (Preferred over adding dependencies)
|
|
138
|
-
|
|
139
|
-
### Option A: Move the Code
|
|
140
|
-
If you need functionality from another package, consider moving that code to a shared lower-level package.
|
|
141
|
-
|
|
142
|
-
### Option B: Dependency Inversion
|
|
143
|
-
Define an interface in the lower-level package, implement it in the higher-level package:
|
|
144
|
-
\`\`\`typescript
|
|
145
|
-
// In foundation package (level 0)
|
|
146
|
-
export interface Logger { log(msg: string): void; }
|
|
147
|
-
|
|
148
|
-
// In higher-level package
|
|
149
|
-
export class ConsoleLogger implements Logger { ... }
|
|
150
|
-
\`\`\`
|
|
151
|
-
|
|
152
|
-
### Option C: Pass Dependencies as Parameters
|
|
153
|
-
Instead of importing, receive the dependency as a constructor or method parameter.
|
|
154
|
-
|
|
155
|
-
## Remember
|
|
156
|
-
- Every dependency you add today is technical debt for tomorrow
|
|
157
|
-
- The best dependency is the one you don't need
|
|
158
|
-
- When in doubt, refactor rather than add dependencies
|
|
159
|
-
`;
|
|
160
20
|
/**
|
|
161
|
-
* Write the instructions documentation to
|
|
21
|
+
* Write the instructions documentation to .webpieces/instruct-ai/.
|
|
22
|
+
* Sourced from @webpieces/rules-config.
|
|
162
23
|
*/
|
|
163
24
|
function writeTmpInstructionsFile(workspaceRoot) {
|
|
164
|
-
const
|
|
165
|
-
const mdPath = path.join(tmpDir, TMP_MD_FILE);
|
|
166
|
-
// Ensure tmp directory exists
|
|
167
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
168
|
-
// Write documentation MD file
|
|
169
|
-
fs.writeFileSync(mdPath, DEPENDENCIES_DOC_CONTENT);
|
|
25
|
+
const mdPath = (0, rules_config_1.writeTemplate)(workspaceRoot, TMP_MD_FILE);
|
|
170
26
|
return mdPath;
|
|
171
27
|
}
|
|
172
28
|
async function runExecutor(options, context) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-architecture-unchanged/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Architecture Unchanged Executor\n *\n * Validates that the current architecture graph matches the saved blessed graph.\n * This ensures no unapproved architecture changes have been made.\n *\n * Usage:\n * nx run architecture:validate-architecture-unchanged\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { generateGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { compareGraphs } from '../../lib/graph-comparator';\nimport { loadBlessedGraph, graphFileExists } from '../../lib/graph-loader';\nimport { toError } from '../../toError';\n\nexport interface ValidateArchitectureUnchangedOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst TMP_DIR = '.webpieces/instruct-ai';\nconst TMP_MD_FILE = 'webpieces.dependencies.md';\n\nconst DEPENDENCIES_DOC_CONTENT = `# Instructions: Architecture Dependency Violation\n\nIN GENERAL, it is better to avoid these changes and find a different way by moving classes\naround to existing packages you already depend on. It is not always avoidable though.\nA clean dependency graph keeps you out of huge trouble later.\n\nIf you are a human, simply run these commands:\n* nx run architecture:visualize - to see the new dependencies and validate that change is desired\n* nx run architecture:generate - updates the dep graph\n* git diff architecture/dependencies.json - to see the deps changes you made\n\n**READ THIS FILE FIRST before making any changes!**\n\n## ⚠️ CRITICAL WARNING ⚠️\n\n**This is a VERY IMPORTANT change that has LARGE REPERCUSSIONS later!**\n\nAdding new dependencies creates technical debt that compounds over time:\n- Creates coupling between packages that may be hard to undo\n- Can create circular dependency tangles\n- Makes packages harder to test in isolation\n- Increases build times and bundle sizes\n- May force unnecessary upgrades across the codebase\n\n**DO NOT add dependencies without senior developer approval!**\n\n## Understanding the Error\n\nYou've attempted to import from a package that is not in your project's allowed dependencies.\nThe architecture enforces a layered dependency structure where:\n- Level 0 packages are foundation packages with NO dependencies on other @webpieces packages\n- Higher level packages can only depend on lower level packages\n- All dependencies must be explicitly declared\n\n## Steps to Resolve\n\n### Step 1: Generate Current Dependency Graph\nRun this command to see the current architecture:\n\\`\\`\\`bash\nnpx nx run architecture:generate\n\\`\\`\\`\nThis creates/updates \\`architecture/dependencies.json\\` showing all packages and their levels.\n\n### Step 2: Analyze the Proposed Change\nAsk yourself:\n1. **Is this import truly necessary?** Can you refactor to avoid it?\n2. **Should the code move instead?** Maybe the code belongs in a different package.\n3. **Will this create a cycle?** Use \\`npx nx graph\\` to visualize dependencies.\n4. **Can you use an interface/abstraction?** Define interface in lower-level package, implement in higher-level.\n\n### Step 3: Get Senior Developer Approval\n\n## 🛑 AI AGENTS: STOP HERE AND ASK FOR HUMAN APPROVAL! 🛑\n\n**YOU MUST NOT PROCEED TO STEP 4 WITHOUT EXPLICIT HUMAN APPROVAL!**\n\n**REQUIRED**: Discuss this architectural change with a senior developer before proceeding.\n- Explain why the dependency is needed\n- Show you've considered alternatives (Step 2)\n- **WAIT for explicit approval before making ANY changes to project.json or package.json**\n\n**AI Agent Instructions:**\n1. Present your analysis from Step 2 to the human\n2. Explain which package needs which dependency and why\n3. ASK: \"Do you approve adding this dependency?\"\n4. **DO NOT modify project.json or package.json until you receive explicit \"yes\" or approval**\n\n### Step 4: If Approved, Add the Dependency\n\n## ⛔ NEVER MODIFY THESE FILES WITHOUT HUMAN APPROVAL FROM STEP 3! ⛔\n\nOnly after receiving explicit human approval in Step 3, make these changes:\n\n1. **Update project.json** - Add to \\`build.dependsOn\\`:\n \\`\\`\\`json\n {\n \"targets\": {\n \"build\": {\n \"dependsOn\": [\"^build\", \"dep1:build\", \"NEW_PACKAGE:build\"]\n }\n }\n }\n \\`\\`\\`\n\n2. **Update package.json** - Add to \\`dependencies\\`:\n \\`\\`\\`json\n {\n \"dependencies\": {\n \"@webpieces/NEW_PACKAGE\": \"*\"\n }\n }\n \\`\\`\\`\n\n### Step 5: Update Architecture Definition\nRun this command to validate and update the architecture:\n\\`\\`\\`bash\nnpx nx run architecture:generate\n\\`\\`\\`\n\nThis will:\n- Detect any cycles (which MUST be fixed before proceeding)\n- Update \\`architecture/dependencies.json\\` with the new dependency\n- Recalculate package levels\n\n### Step 6: Verify No Cycles\n\\`\\`\\`bash\nnpx nx run architecture:validate-no-architecture-cycles\n\\`\\`\\`\n\nIf cycles are detected, you MUST refactor to break the cycle. Common strategies:\n- Move shared code to a lower-level package\n- Use dependency inversion (interfaces in low-level, implementations in high-level)\n- Restructure package boundaries\n\n## Alternative Solutions (Preferred over adding dependencies)\n\n### Option A: Move the Code\nIf you need functionality from another package, consider moving that code to a shared lower-level package.\n\n### Option B: Dependency Inversion\nDefine an interface in the lower-level package, implement it in the higher-level package:\n\\`\\`\\`typescript\n// In foundation package (level 0)\nexport interface Logger { log(msg: string): void; }\n\n// In higher-level package\nexport class ConsoleLogger implements Logger { ... }\n\\`\\`\\`\n\n### Option C: Pass Dependencies as Parameters\nInstead of importing, receive the dependency as a constructor or method parameter.\n\n## Remember\n- Every dependency you add today is technical debt for tomorrow\n- The best dependency is the one you don't need\n- When in doubt, refactor rather than add dependencies\n`;\n\n/**\n * Write the instructions documentation to tmp directory\n */\nfunction writeTmpInstructionsFile(workspaceRoot: string): string {\n const tmpDir = path.join(workspaceRoot, TMP_DIR);\n const mdPath = path.join(tmpDir, TMP_MD_FILE);\n\n // Ensure tmp directory exists\n fs.mkdirSync(tmpDir, { recursive: true });\n\n // Write documentation MD file\n fs.writeFileSync(mdPath, DEPENDENCIES_DOC_CONTENT);\n\n return mdPath;\n}\n\nexport default async function runExecutor(\n options: ValidateArchitectureUnchangedOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n🔍 Validating Architecture Unchanged\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Check if saved graph exists\n if (!graphFileExists(workspaceRoot, graphPath)) {\n console.error('❌ No saved graph found at architecture/dependencies.json');\n console.error('');\n console.error('To initialize:');\n console.error(' 1. Run: nx run architecture:generate');\n console.error(' 2. Run: nx run architecture:visualize');\n console.error(' 3. Manually inspect the generated graph to confirm it is the desired architecture');\n console.error(' 4. Commit architecture/dependencies.json');\n return { success: false };\n }\n\n // Step 1: Generate current graph from project.json files\n console.log('📊 Generating current dependency graph...');\n const rawGraph = await generateGraph();\n\n // Step 2: Topological sort (to get enhanced graph with levels)\n console.log('🔄 Computing topological layers...');\n const currentGraph = sortGraphTopologically(rawGraph);\n\n // Step 3: Load saved graph\n console.log('📂 Loading saved graph...');\n const savedGraph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!savedGraph) {\n console.error('❌ Could not load saved graph');\n return { success: false };\n }\n\n // Step 4: Compare graphs\n console.log('🔍 Comparing current graph to saved graph...');\n const comparison = compareGraphs(currentGraph, savedGraph);\n\n if (comparison.identical) {\n console.log('✅ Architecture unchanged - current graph matches saved graph');\n return { success: true };\n } else {\n // Write instructions file for AI agent\n const mdPath = writeTmpInstructionsFile(workspaceRoot);\n\n console.error('❌ Architecture has changed since last update!');\n console.error('\\nDifferences:');\n console.error(comparison.summary);\n console.error('');\n console.error('⚠️ *** Refer to ' + mdPath + ' for instructions on how to fix *** ⚠️');\n console.error('');\n console.error('To fix:');\n console.error(' 1. Review the changes above');\n console.error(' 2. If intentional, ASK USER to run: nx run architecture:generate since this is a critical change');\n console.error(' 3. Commit the updated architecture/dependencies.json');\n return { success: false };\n }\n } catch (err: unknown) {\n const error = toError(err);\n console.error('❌ Architecture validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-architecture-unchanged/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Architecture Unchanged Executor\n *\n * Validates that the current architecture graph matches the saved blessed graph.\n * This ensures no unapproved architecture changes have been made.\n *\n * Usage:\n * nx run architecture:validate-architecture-unchanged\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { writeTemplate } from '@webpieces/rules-config';\nimport { generateGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { compareGraphs } from '../../lib/graph-comparator';\nimport { loadBlessedGraph, graphFileExists } from '../../lib/graph-loader';\nimport { toError } from '../../toError';\n\nexport interface ValidateArchitectureUnchangedOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst TMP_MD_FILE = 'webpieces.dependencies.md';\n\n/**\n * Write the instructions documentation to .webpieces/instruct-ai/.\n * Sourced from @webpieces/rules-config.\n */\nfunction writeTmpInstructionsFile(workspaceRoot: string): string {\n const mdPath = writeTemplate(workspaceRoot, TMP_MD_FILE);\n\n return mdPath;\n}\n\nexport default async function runExecutor(\n options: ValidateArchitectureUnchangedOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n🔍 Validating Architecture Unchanged\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Check if saved graph exists\n if (!graphFileExists(workspaceRoot, graphPath)) {\n console.error('❌ No saved graph found at architecture/dependencies.json');\n console.error('');\n console.error('To initialize:');\n console.error(' 1. Run: nx run architecture:generate');\n console.error(' 2. Run: nx run architecture:visualize');\n console.error(' 3. Manually inspect the generated graph to confirm it is the desired architecture');\n console.error(' 4. Commit architecture/dependencies.json');\n return { success: false };\n }\n\n // Step 1: Generate current graph from project.json files\n console.log('📊 Generating current dependency graph...');\n const rawGraph = await generateGraph();\n\n // Step 2: Topological sort (to get enhanced graph with levels)\n console.log('🔄 Computing topological layers...');\n const currentGraph = sortGraphTopologically(rawGraph);\n\n // Step 3: Load saved graph\n console.log('📂 Loading saved graph...');\n const savedGraph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!savedGraph) {\n console.error('❌ Could not load saved graph');\n return { success: false };\n }\n\n // Step 4: Compare graphs\n console.log('🔍 Comparing current graph to saved graph...');\n const comparison = compareGraphs(currentGraph, savedGraph);\n\n if (comparison.identical) {\n console.log('✅ Architecture unchanged - current graph matches saved graph');\n return { success: true };\n } else {\n // Write instructions file for AI agent\n const mdPath = writeTmpInstructionsFile(workspaceRoot);\n\n console.error('❌ Architecture has changed since last update!');\n console.error('\\nDifferences:');\n console.error(comparison.summary);\n console.error('');\n console.error('⚠️ *** Refer to ' + mdPath + ' for instructions on how to fix *** ⚠️');\n console.error('');\n console.error('To fix:');\n console.error(' 1. Review the changes above');\n console.error(' 2. If intentional, ASK USER to run: nx run architecture:generate since this is a critical change');\n console.error(' 3. Commit the updated architecture/dependencies.json');\n return { success: false };\n }\n } catch (err: unknown) {\n const error = toError(err);\n console.error('❌ Architecture validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -13,114 +13,9 @@
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.default = runExecutor;
|
|
16
|
-
const tslib_1 = require("tslib");
|
|
17
16
|
const graph_generator_1 = require("../../lib/graph-generator");
|
|
18
|
-
const
|
|
19
|
-
const path = tslib_1.__importStar(require("path"));
|
|
17
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
20
18
|
const toError_1 = require("../../toError");
|
|
21
|
-
const TRANSITIVE_DEPS_DOC = `# AI Agent Instructions: Redundant Transitive Dependency Violation
|
|
22
|
-
|
|
23
|
-
**READ THIS FILE FIRST before making any changes!**
|
|
24
|
-
|
|
25
|
-
## Why This Rule Exists
|
|
26
|
-
|
|
27
|
-
This rule keeps the architecture dependency graph **CLEAN and SIMPLE**.
|
|
28
|
-
|
|
29
|
-
When you run \`npx nx run architecture:visualize\`, it generates a visual diagram of all
|
|
30
|
-
package dependencies. Without this rule, you end up with a tangled mess of 100+ lines
|
|
31
|
-
where everything depends on everything - making it impossible to understand.
|
|
32
|
-
|
|
33
|
-
**Clean graphs = easier understanding for humans AND AI agents.**
|
|
34
|
-
|
|
35
|
-
## Understanding the Error
|
|
36
|
-
|
|
37
|
-
You have a **redundant transitive dependency**. This means:
|
|
38
|
-
|
|
39
|
-
1. Project A directly depends on Project C
|
|
40
|
-
2. BUT Project A also depends on Project B
|
|
41
|
-
3. AND Project B already brings in Project C (transitively)
|
|
42
|
-
|
|
43
|
-
Therefore, Project A's direct dependency on C is **redundant** - it's already available
|
|
44
|
-
through B. This extra line clutters the dependency graph.
|
|
45
|
-
|
|
46
|
-
**Example:**
|
|
47
|
-
\`\`\`
|
|
48
|
-
http-server depends on: [http-routing, http-filters, core-util]
|
|
49
|
-
^^^^^^^^^ ^^^^^^^^
|
|
50
|
-
REDUNDANT! REDUNDANT!
|
|
51
|
-
|
|
52
|
-
Why? Because http-routing already brings in:
|
|
53
|
-
- http-filters (direct)
|
|
54
|
-
- core-util (via http-api)
|
|
55
|
-
\`\`\`
|
|
56
|
-
|
|
57
|
-
## How to Fix
|
|
58
|
-
|
|
59
|
-
### Step 1: Identify the Redundant Dependency
|
|
60
|
-
|
|
61
|
-
Look at the error message. It tells you:
|
|
62
|
-
- Which project has the problem
|
|
63
|
-
- Which dependency is redundant
|
|
64
|
-
- Which other dependency already brings it in
|
|
65
|
-
|
|
66
|
-
### Step 2: Remove from project.json
|
|
67
|
-
|
|
68
|
-
Remove the redundant dependency from \`build.dependsOn\`:
|
|
69
|
-
|
|
70
|
-
\`\`\`json
|
|
71
|
-
{
|
|
72
|
-
"targets": {
|
|
73
|
-
"build": {
|
|
74
|
-
"dependsOn": [
|
|
75
|
-
"^build",
|
|
76
|
-
"http-routing:build"
|
|
77
|
-
// REMOVE: "http-filters:build" <-- redundant, http-routing brings it in
|
|
78
|
-
// REMOVE: "core-util:build" <-- redundant, http-routing brings it in
|
|
79
|
-
]
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
\`\`\`
|
|
84
|
-
|
|
85
|
-
### Step 3: Remove from package.json
|
|
86
|
-
|
|
87
|
-
Remove the redundant dependency from \`dependencies\`:
|
|
88
|
-
|
|
89
|
-
\`\`\`json
|
|
90
|
-
{
|
|
91
|
-
"dependencies": {
|
|
92
|
-
"@webpieces/http-routing": "*"
|
|
93
|
-
// REMOVE: "@webpieces/http-filters": "*" <-- redundant
|
|
94
|
-
// REMOVE: "@webpieces/core-util": "*" <-- redundant
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
\`\`\`
|
|
98
|
-
|
|
99
|
-
### Step 4: Regenerate Architecture
|
|
100
|
-
|
|
101
|
-
\`\`\`bash
|
|
102
|
-
npx nx run architecture:generate
|
|
103
|
-
\`\`\`
|
|
104
|
-
|
|
105
|
-
### Step 5: Verify
|
|
106
|
-
|
|
107
|
-
\`\`\`bash
|
|
108
|
-
npm run build-all
|
|
109
|
-
\`\`\`
|
|
110
|
-
|
|
111
|
-
## Important Notes
|
|
112
|
-
|
|
113
|
-
- You DON'T lose access to the transitive dependency - it's still available through the parent
|
|
114
|
-
- This is about keeping the DECLARED dependencies minimal and clean
|
|
115
|
-
- The actual runtime/compile behavior is unchanged
|
|
116
|
-
- TypeScript will still find the types through the transitive path
|
|
117
|
-
|
|
118
|
-
## Remember
|
|
119
|
-
|
|
120
|
-
- Fewer lines in the graph = easier to understand
|
|
121
|
-
- Only declare what you DIRECTLY need that isn't already transitively available
|
|
122
|
-
- When in doubt, check with \`npx nx run architecture:visualize\`
|
|
123
|
-
`;
|
|
124
19
|
/**
|
|
125
20
|
* Compute all transitive dependencies for a project
|
|
126
21
|
*/
|
|
@@ -174,17 +69,13 @@ function findRedundantDeps(project, graph) {
|
|
|
174
69
|
* Write documentation file when violations are found
|
|
175
70
|
*/
|
|
176
71
|
function writeDocFile(workspaceRoot) {
|
|
177
|
-
const docPath = path.join(workspaceRoot, '.webpieces', 'instruct-ai', 'webpieces.transitivedeps.md');
|
|
178
|
-
const docDir = path.dirname(docPath);
|
|
179
72
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
180
73
|
try {
|
|
181
|
-
|
|
182
|
-
fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');
|
|
74
|
+
(0, rules_config_1.writeTemplate)(workspaceRoot, 'webpieces.transitivedeps.md');
|
|
183
75
|
}
|
|
184
76
|
catch (err) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
console.warn(`Could not write documentation file: ${docPath}`);
|
|
77
|
+
const error = (0, toError_1.toError)(err);
|
|
78
|
+
console.warn('Could not write webpieces.transitivedeps.md:', error.message);
|
|
188
79
|
}
|
|
189
80
|
}
|
|
190
81
|
async function runExecutor(_options, context) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/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';\nimport { toError } from '../../toError';\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 const transitiveEntries = Array.from(transitiveByDep.entries());\n for (const dep of directDeps) {\n for (const entry of transitiveEntries) {\n const otherDep = entry[0];\n const otherTransitive = entry[1];\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, '.webpieces', 'instruct-ai', 'webpieces.transitivedeps.md');\n const docDir = path.dirname(docPath);\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.mkdirSync(docDir, { recursive: true });\n fs.writeFileSync(docPath, TRANSITIVE_DEPS_DOC, 'utf-8');\n } catch (err: unknown) {\n //const error = toError(err);\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 // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\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 .webpieces/instruct-ai/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 .webpieces/instruct-ai/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: unknown) {\n const error = toError(err);\n console.error('❌ Skip-level validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/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 { writeTemplate } from '@webpieces/rules-config';\nimport { toError } from '../../toError';\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\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 const transitiveEntries = Array.from(transitiveByDep.entries());\n for (const dep of directDeps) {\n for (const entry of transitiveEntries) {\n const otherDep = entry[0];\n const otherTransitive = entry[1];\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 // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n writeTemplate(workspaceRoot, 'webpieces.transitivedeps.md');\n } catch (err: unknown) {\n const error = toError(err);\n console.warn('Could not write webpieces.transitivedeps.md:', error.message);\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 // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\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 .webpieces/instruct-ai/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 .webpieces/instruct-ai/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: unknown) {\n const error = toError(err);\n console.error('❌ Skip-level validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -13,7 +13,7 @@ const devkit_1 = require("@nx/devkit");
|
|
|
13
13
|
/**
|
|
14
14
|
* Projects to exclude from graph validation (tools, configs, etc.)
|
|
15
15
|
*/
|
|
16
|
-
const EXCLUDED_PROJECTS = new Set([]);
|
|
16
|
+
const EXCLUDED_PROJECTS = new Set(['architecture']);
|
|
17
17
|
/**
|
|
18
18
|
* Extract project dependencies from project.json's build.dependsOn and implicitDependencies
|
|
19
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-generator.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-generator.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAiDH,4CAiBC;AAKD,wCAYC;AAKD,sCAGC;AAzFD,uCAIoB;AAEpB;;GAEG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS,
|
|
1
|
+
{"version":3,"file":"graph-generator.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-generator.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAiDH,4CAiBC;AAKD,wCAYC;AAKD,sCAGC;AAzFD,uCAIoB;AAEpB;;GAEG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS,CAAC,cAAc,CAAC,CAAC,CAAC;AAE5D;;GAEG;AACH,SAAS,wBAAwB,CAAC,aAAmC;IACjE,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,+BAA+B;IAC/B,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,WAAW,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC1B,0DAA0D;gBAC1D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBAC3C,IAAI,KAAK,EAAE,CAAC;oBACR,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACxB,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,yCAAyC;IACzC,IAAI,aAAa,CAAC,oBAAoB,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAC1F,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,oBAAoB,EAAE,CAAC;YACnD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,gBAAgB;IAClC,MAAM,YAAY,GAAG,MAAM,IAAA,gCAAuB,GAAE,CAAC;IACrD,MAAM,cAAc,GAAG,IAAA,kDAAyC,EAAC,YAAY,CAAC,CAAC;IAC/E,MAAM,OAAO,GAA6B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjF,0CAA0C;QAC1C,IAAI,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACrC,SAAS;QACb,CAAC;QAED,4DAA4D;QAC5D,MAAM,IAAI,GAAG,wBAAwB,CAAC,aAAa,CAAC,CAAC;QACrD,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;IAChC,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,QAAkC;IAC7D,MAAM,MAAM,GAA6B,EAAE,CAAC;IAE5C,KAAK,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,4EAA4E;QAC5E,MAAM,eAAe,GAAG,WAAW,CAAC;QACpC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAEpC,MAAM,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAC9C,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,aAAa;IAC/B,MAAM,QAAQ,GAAG,MAAM,gBAAgB,EAAE,CAAC;IAC1C,OAAO,cAAc,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC","sourcesContent":["/**\n * Graph Generator\n *\n * Generates dependency graph from project.json files in the workspace.\n * Reads build.dependsOn and implicitDependencies to determine project relationships.\n */\n\nimport {\n createProjectGraphAsync,\n readProjectsConfigurationFromProjectGraph,\n ProjectConfiguration,\n} from '@nx/devkit';\n\n/**\n * Projects to exclude from graph validation (tools, configs, etc.)\n */\nconst EXCLUDED_PROJECTS = new Set<string>(['architecture']);\n\n/**\n * Extract project dependencies from project.json's build.dependsOn and implicitDependencies\n */\nfunction extractBuildDependencies(projectConfig: ProjectConfiguration): string[] {\n const deps: string[] = [];\n\n // 1. Read from build.dependsOn\n const buildTarget = projectConfig.targets?.['build'];\n if (buildTarget && buildTarget.dependsOn) {\n for (const dep of buildTarget.dependsOn) {\n if (typeof dep === 'string') {\n // Format: \"project-name:build\" or just \"build\" (for self)\n const match = dep.match(/^([^:]+):build$/);\n if (match) {\n deps.push(match[1]);\n }\n }\n }\n }\n\n // 2. Also read from implicitDependencies\n if (projectConfig.implicitDependencies && Array.isArray(projectConfig.implicitDependencies)) {\n for (const dep of projectConfig.implicitDependencies) {\n if (typeof dep === 'string' && !deps.includes(dep)) {\n deps.push(dep);\n }\n }\n }\n\n return deps.sort();\n}\n\n/**\n * Generate raw dependency graph from project.json files\n * Returns: { projectName: [dependencyNames] }\n */\nexport async function generateRawGraph(): Promise<Record<string, string[]>> {\n const projectGraph = await createProjectGraphAsync();\n const projectsConfig = readProjectsConfigurationFromProjectGraph(projectGraph);\n const rawDeps: Record<string, string[]> = {};\n\n for (const [projectName, projectConfig] of Object.entries(projectsConfig.projects)) {\n // Skip excluded projects (tools, plugins)\n if (EXCLUDED_PROJECTS.has(projectName)) {\n continue;\n }\n\n // Extract dependencies from build.dependsOn in project.json\n const deps = extractBuildDependencies(projectConfig);\n rawDeps[projectName] = deps;\n }\n\n return rawDeps;\n}\n\n/**\n * Transform project names (sorting dependencies only - no scope transformation)\n */\nexport function transformGraph(rawGraph: Record<string, string[]>): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n\n for (const [projectName, deps] of Object.entries(rawGraph)) {\n // Use project names as-is - don't force @webpieces scope on client projects\n const transformedName = projectName;\n const transformedDeps = deps.sort();\n\n result[transformedName] = transformedDeps;\n }\n\n return result;\n}\n\n/**\n * Generate complete dependency graph with transformations\n */\nexport async function generateGraph(): Promise<Record<string, string[]>> {\n const rawGraph = await generateRawGraph();\n return transformGraph(rawGraph);\n}\n"]}
|