@webpieces/dev-config 0.2.95 → 0.2.97

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 (181) hide show
  1. package/config/eslint/base.mjs +1 -1
  2. package/executors.json +6 -91
  3. package/package.json +6 -19
  4. package/{executors → src/executors}/help/executor.d.ts +4 -2
  5. package/src/executors/help/executor.js.map +1 -0
  6. package/src/executors/validate-eslint-sync/executor.js.map +1 -0
  7. package/{executors → src/executors}/validate-versions-locked/executor.js +2 -1
  8. package/src/executors/validate-versions-locked/executor.js.map +1 -0
  9. package/src/index.d.ts +1 -1
  10. package/src/index.js +1 -1
  11. package/src/index.js.map +1 -1
  12. package/src/plugin.d.ts +86 -0
  13. package/{plugin.js → src/plugin.js} +31 -15
  14. package/src/plugin.js.map +1 -0
  15. package/src/toError.d.ts +5 -0
  16. package/src/toError.js +37 -0
  17. package/src/toError.js.map +1 -0
  18. package/templates/eslint.webpieces.config.mjs +1 -1
  19. package/architecture/executors/diff-utils.d.ts +0 -24
  20. package/architecture/executors/diff-utils.js +0 -119
  21. package/architecture/executors/diff-utils.js.map +0 -1
  22. package/architecture/executors/diff-utils.ts +0 -127
  23. package/architecture/executors/generate/executor.d.ts +0 -16
  24. package/architecture/executors/generate/executor.js +0 -44
  25. package/architecture/executors/generate/executor.js.map +0 -1
  26. package/architecture/executors/generate/executor.ts +0 -59
  27. package/architecture/executors/generate/schema.json +0 -14
  28. package/architecture/executors/validate-architecture-unchanged/executor.d.ts +0 -17
  29. package/architecture/executors/validate-architecture-unchanged/executor.js +0 -229
  30. package/architecture/executors/validate-architecture-unchanged/executor.js.map +0 -1
  31. package/architecture/executors/validate-architecture-unchanged/executor.ts +0 -251
  32. package/architecture/executors/validate-architecture-unchanged/schema.json +0 -14
  33. package/architecture/executors/validate-code/executor.d.ts +0 -78
  34. package/architecture/executors/validate-code/executor.js +0 -243
  35. package/architecture/executors/validate-code/executor.js.map +0 -1
  36. package/architecture/executors/validate-code/executor.ts +0 -406
  37. package/architecture/executors/validate-code/schema.json +0 -227
  38. package/architecture/executors/validate-dtos/executor.d.ts +0 -42
  39. package/architecture/executors/validate-dtos/executor.js +0 -561
  40. package/architecture/executors/validate-dtos/executor.js.map +0 -1
  41. package/architecture/executors/validate-dtos/executor.ts +0 -689
  42. package/architecture/executors/validate-dtos/schema.json +0 -33
  43. package/architecture/executors/validate-modified-files/executor.d.ts +0 -25
  44. package/architecture/executors/validate-modified-files/executor.js +0 -501
  45. package/architecture/executors/validate-modified-files/executor.js.map +0 -1
  46. package/architecture/executors/validate-modified-files/executor.ts +0 -571
  47. package/architecture/executors/validate-modified-files/schema.json +0 -25
  48. package/architecture/executors/validate-modified-methods/executor.d.ts +0 -31
  49. package/architecture/executors/validate-modified-methods/executor.js +0 -694
  50. package/architecture/executors/validate-modified-methods/executor.js.map +0 -1
  51. package/architecture/executors/validate-modified-methods/executor.ts +0 -797
  52. package/architecture/executors/validate-modified-methods/schema.json +0 -25
  53. package/architecture/executors/validate-new-methods/executor.d.ts +0 -28
  54. package/architecture/executors/validate-new-methods/executor.js +0 -513
  55. package/architecture/executors/validate-new-methods/executor.js.map +0 -1
  56. package/architecture/executors/validate-new-methods/executor.ts +0 -584
  57. package/architecture/executors/validate-new-methods/schema.json +0 -25
  58. package/architecture/executors/validate-no-any-unknown/executor.d.ts +0 -42
  59. package/architecture/executors/validate-no-any-unknown/executor.js +0 -462
  60. package/architecture/executors/validate-no-any-unknown/executor.js.map +0 -1
  61. package/architecture/executors/validate-no-any-unknown/executor.ts +0 -540
  62. package/architecture/executors/validate-no-any-unknown/schema.json +0 -24
  63. package/architecture/executors/validate-no-architecture-cycles/executor.d.ts +0 -16
  64. package/architecture/executors/validate-no-architecture-cycles/executor.js +0 -48
  65. package/architecture/executors/validate-no-architecture-cycles/executor.js.map +0 -1
  66. package/architecture/executors/validate-no-architecture-cycles/executor.ts +0 -60
  67. package/architecture/executors/validate-no-architecture-cycles/schema.json +0 -8
  68. package/architecture/executors/validate-no-destructure/executor.d.ts +0 -52
  69. package/architecture/executors/validate-no-destructure/executor.js +0 -491
  70. package/architecture/executors/validate-no-destructure/executor.js.map +0 -1
  71. package/architecture/executors/validate-no-destructure/executor.ts +0 -578
  72. package/architecture/executors/validate-no-destructure/schema.json +0 -24
  73. package/architecture/executors/validate-no-direct-api-resolver/executor.d.ts +0 -47
  74. package/architecture/executors/validate-no-direct-api-resolver/executor.js +0 -566
  75. package/architecture/executors/validate-no-direct-api-resolver/executor.js.map +0 -1
  76. package/architecture/executors/validate-no-direct-api-resolver/executor.ts +0 -666
  77. package/architecture/executors/validate-no-direct-api-resolver/schema.json +0 -29
  78. package/architecture/executors/validate-no-inline-types/executor.d.ts +0 -91
  79. package/architecture/executors/validate-no-inline-types/executor.js +0 -669
  80. package/architecture/executors/validate-no-inline-types/executor.js.map +0 -1
  81. package/architecture/executors/validate-no-inline-types/executor.ts +0 -775
  82. package/architecture/executors/validate-no-inline-types/schema.json +0 -24
  83. package/architecture/executors/validate-no-skiplevel-deps/executor.d.ts +0 -19
  84. package/architecture/executors/validate-no-skiplevel-deps/executor.js +0 -227
  85. package/architecture/executors/validate-no-skiplevel-deps/executor.js.map +0 -1
  86. package/architecture/executors/validate-no-skiplevel-deps/executor.ts +0 -267
  87. package/architecture/executors/validate-no-skiplevel-deps/schema.json +0 -8
  88. package/architecture/executors/validate-packagejson/executor.d.ts +0 -16
  89. package/architecture/executors/validate-packagejson/executor.js +0 -57
  90. package/architecture/executors/validate-packagejson/executor.js.map +0 -1
  91. package/architecture/executors/validate-packagejson/executor.ts +0 -74
  92. package/architecture/executors/validate-packagejson/schema.json +0 -8
  93. package/architecture/executors/validate-prisma-converters/executor.d.ts +0 -60
  94. package/architecture/executors/validate-prisma-converters/executor.js +0 -634
  95. package/architecture/executors/validate-prisma-converters/executor.js.map +0 -1
  96. package/architecture/executors/validate-prisma-converters/executor.ts +0 -822
  97. package/architecture/executors/validate-prisma-converters/schema.json +0 -38
  98. package/architecture/executors/validate-return-types/executor.d.ts +0 -29
  99. package/architecture/executors/validate-return-types/executor.js +0 -439
  100. package/architecture/executors/validate-return-types/executor.js.map +0 -1
  101. package/architecture/executors/validate-return-types/executor.ts +0 -524
  102. package/architecture/executors/validate-return-types/schema.json +0 -24
  103. package/architecture/executors/visualize/executor.d.ts +0 -17
  104. package/architecture/executors/visualize/executor.js +0 -49
  105. package/architecture/executors/visualize/executor.js.map +0 -1
  106. package/architecture/executors/visualize/executor.ts +0 -63
  107. package/architecture/executors/visualize/schema.json +0 -14
  108. package/architecture/index.d.ts +0 -19
  109. package/architecture/index.js +0 -23
  110. package/architecture/index.js.map +0 -1
  111. package/architecture/index.ts +0 -20
  112. package/architecture/lib/graph-comparator.d.ts +0 -39
  113. package/architecture/lib/graph-comparator.js +0 -100
  114. package/architecture/lib/graph-comparator.js.map +0 -1
  115. package/architecture/lib/graph-comparator.ts +0 -141
  116. package/architecture/lib/graph-generator.d.ts +0 -19
  117. package/architecture/lib/graph-generator.js +0 -84
  118. package/architecture/lib/graph-generator.js.map +0 -1
  119. package/architecture/lib/graph-generator.ts +0 -97
  120. package/architecture/lib/graph-loader.d.ts +0 -31
  121. package/architecture/lib/graph-loader.js +0 -98
  122. package/architecture/lib/graph-loader.js.map +0 -1
  123. package/architecture/lib/graph-loader.ts +0 -116
  124. package/architecture/lib/graph-sorter.d.ts +0 -37
  125. package/architecture/lib/graph-sorter.js +0 -110
  126. package/architecture/lib/graph-sorter.js.map +0 -1
  127. package/architecture/lib/graph-sorter.ts +0 -137
  128. package/architecture/lib/graph-visualizer.d.ts +0 -29
  129. package/architecture/lib/graph-visualizer.js +0 -217
  130. package/architecture/lib/graph-visualizer.js.map +0 -1
  131. package/architecture/lib/graph-visualizer.ts +0 -231
  132. package/architecture/lib/package-validator.d.ts +0 -38
  133. package/architecture/lib/package-validator.js +0 -126
  134. package/architecture/lib/package-validator.js.map +0 -1
  135. package/architecture/lib/package-validator.ts +0 -170
  136. package/eslint-plugin/__tests__/catch-error-pattern.test.ts +0 -374
  137. package/eslint-plugin/__tests__/max-file-lines.test.ts +0 -207
  138. package/eslint-plugin/__tests__/max-method-lines.test.ts +0 -258
  139. package/eslint-plugin/__tests__/no-unmanaged-exceptions.test.ts +0 -359
  140. package/eslint-plugin/index.d.ts +0 -23
  141. package/eslint-plugin/index.js +0 -30
  142. package/eslint-plugin/index.js.map +0 -1
  143. package/eslint-plugin/index.ts +0 -29
  144. package/eslint-plugin/rules/catch-error-pattern.d.ts +0 -11
  145. package/eslint-plugin/rules/catch-error-pattern.js +0 -143
  146. package/eslint-plugin/rules/catch-error-pattern.js.map +0 -1
  147. package/eslint-plugin/rules/catch-error-pattern.ts +0 -246
  148. package/eslint-plugin/rules/enforce-architecture.d.ts +0 -15
  149. package/eslint-plugin/rules/enforce-architecture.js +0 -476
  150. package/eslint-plugin/rules/enforce-architecture.js.map +0 -1
  151. package/eslint-plugin/rules/enforce-architecture.ts +0 -543
  152. package/eslint-plugin/rules/max-file-lines.d.ts +0 -12
  153. package/eslint-plugin/rules/max-file-lines.js +0 -257
  154. package/eslint-plugin/rules/max-file-lines.js.map +0 -1
  155. package/eslint-plugin/rules/max-file-lines.ts +0 -272
  156. package/eslint-plugin/rules/max-method-lines.d.ts +0 -12
  157. package/eslint-plugin/rules/max-method-lines.js +0 -240
  158. package/eslint-plugin/rules/max-method-lines.js.map +0 -1
  159. package/eslint-plugin/rules/max-method-lines.ts +0 -287
  160. package/eslint-plugin/rules/no-unmanaged-exceptions.d.ts +0 -22
  161. package/eslint-plugin/rules/no-unmanaged-exceptions.js +0 -160
  162. package/eslint-plugin/rules/no-unmanaged-exceptions.js.map +0 -1
  163. package/eslint-plugin/rules/no-unmanaged-exceptions.ts +0 -179
  164. package/executors/help/executor.js.map +0 -1
  165. package/executors/help/executor.ts +0 -61
  166. package/executors/validate-eslint-sync/executor.js.map +0 -1
  167. package/executors/validate-eslint-sync/executor.ts +0 -87
  168. package/executors/validate-versions-locked/executor.js.map +0 -1
  169. package/executors/validate-versions-locked/executor.ts +0 -368
  170. package/plugin/README.md +0 -243
  171. package/plugin/index.d.ts +0 -4
  172. package/plugin/index.js +0 -8
  173. package/plugin/index.js.map +0 -1
  174. package/plugin/index.ts +0 -4
  175. /package/{executors → src/executors}/help/executor.js +0 -0
  176. /package/{executors → src/executors}/help/schema.json +0 -0
  177. /package/{executors → src/executors}/validate-eslint-sync/executor.d.ts +0 -0
  178. /package/{executors → src/executors}/validate-eslint-sync/executor.js +0 -0
  179. /package/{executors → src/executors}/validate-eslint-sync/schema.json +0 -0
  180. /package/{executors → src/executors}/validate-versions-locked/executor.d.ts +0 -0
  181. /package/{executors → src/executors}/validate-versions-locked/schema.json +0 -0
@@ -1,543 +0,0 @@
1
- /**
2
- * ESLint rule to enforce architecture boundaries
3
- *
4
- * Validates that imports from @webpieces/* packages comply with the
5
- * blessed dependency graph in .graphs/dependencies.json
6
- *
7
- * Supports transitive dependencies: if A depends on B and B depends on C,
8
- * then A can import from C.
9
- *
10
- * Configuration:
11
- * '@webpieces/enforce-architecture': 'error'
12
- */
13
-
14
- import type { Rule } from 'eslint';
15
- import * as fs from 'fs';
16
- import * as path from 'path';
17
- import { toError } from '../../toError';
18
-
19
- const DEPENDENCIES_DOC_CONTENT = `# Instructions: Architecture Dependency Violation
20
-
21
- IN GENERAL, it is better to avoid these changes and find a different way by moving classes
22
- around to existing packages you already depend on. It is not always avoidable though.
23
- A clean dependency graph keeps you out of huge trouble later.
24
-
25
- If you are a human, simply run these commands:
26
- * nx run architecture:visualize - to see the new dependencies and validate that change is desired
27
- * nx run architecture:generate - updates the dep graph
28
- * git diff architecture/dependencies.json - to see the deps changes you made
29
-
30
- **READ THIS FILE FIRST before making any changes!**
31
-
32
- ## ⚠️ CRITICAL WARNING ⚠️
33
-
34
- **This is a VERY IMPORTANT change that has LARGE REPERCUSSIONS later!**
35
-
36
- Adding new dependencies creates technical debt that compounds over time:
37
- - Creates coupling between packages that may be hard to undo
38
- - Can create circular dependency tangles
39
- - Makes packages harder to test in isolation
40
- - Increases build times and bundle sizes
41
- - May force unnecessary upgrades across the codebase
42
-
43
- **DO NOT add dependencies without senior developer approval!**
44
-
45
- ## Understanding the Error
46
-
47
- You've attempted to import from a package that is not in your project's allowed dependencies.
48
- The architecture enforces a layered dependency structure where:
49
- - Level 0 packages are foundation packages with NO dependencies on other @webpieces packages
50
- - Higher level packages can only depend on lower level packages
51
- - All dependencies must be explicitly declared
52
-
53
- ## Steps to Resolve
54
-
55
- ### Step 1: Generate Current Dependency Graph
56
- Run this command to see the current architecture:
57
- \`\`\`bash
58
- npx nx run architecture:generate
59
- \`\`\`
60
- This creates/updates \`architecture/dependencies.json\` showing all packages and their levels.
61
-
62
- ### Step 2: Analyze the Proposed Change
63
- Ask yourself:
64
- 1. **Is this import truly necessary?** Can you refactor to avoid it?
65
- 2. **Should the code move instead?** Maybe the code belongs in a different package.
66
- 3. **Will this create a cycle?** Use \`npx nx graph\` to visualize dependencies.
67
- 4. **Can you use an interface/abstraction?** Define interface in lower-level package, implement in higher-level.
68
-
69
- ### Step 3: Get Senior Developer Approval
70
-
71
- ## 🛑 AI AGENTS: STOP HERE AND ASK FOR HUMAN APPROVAL! 🛑
72
-
73
- **YOU MUST NOT PROCEED TO STEP 4 WITHOUT EXPLICIT HUMAN APPROVAL!**
74
-
75
- **REQUIRED**: Discuss this architectural change with a senior developer before proceeding.
76
- - Explain why the dependency is needed
77
- - Show you've considered alternatives (Step 2)
78
- - **WAIT for explicit approval before making ANY changes to project.json or package.json**
79
-
80
- **AI Agent Instructions:**
81
- 1. Present your analysis from Step 2 to the human
82
- 2. Explain which package needs which dependency and why
83
- 3. ASK: "Do you approve adding this dependency?"
84
- 4. **DO NOT modify project.json or package.json until you receive explicit "yes" or approval**
85
-
86
- ### Step 4: If Approved, Add the Dependency
87
-
88
- ## ⛔ NEVER MODIFY THESE FILES WITHOUT HUMAN APPROVAL FROM STEP 3! ⛔
89
-
90
- Only after receiving explicit human approval in Step 3, make these changes:
91
-
92
- 1. **Update project.json** - Add to \`build.dependsOn\`:
93
- \`\`\`json
94
- {
95
- "targets": {
96
- "build": {
97
- "dependsOn": ["^build", "dep1:build", "NEW_PACKAGE:build"]
98
- }
99
- }
100
- }
101
- \`\`\`
102
-
103
- 2. **Update package.json** - Add to \`dependencies\`:
104
- \`\`\`json
105
- {
106
- "dependencies": {
107
- "@webpieces/NEW_PACKAGE": "*"
108
- }
109
- }
110
- \`\`\`
111
-
112
- ### Step 5: Update Architecture Definition
113
- Run this command to validate and update the architecture:
114
- \`\`\`bash
115
- npx nx run architecture:generate
116
- \`\`\`
117
-
118
- This will:
119
- - Detect any cycles (which MUST be fixed before proceeding)
120
- - Update \`architecture/dependencies.json\` with the new dependency
121
- - Recalculate package levels
122
-
123
- ### Step 6: Verify No Cycles
124
- \`\`\`bash
125
- npx nx run architecture:validate-no-architecture-cycles
126
- \`\`\`
127
-
128
- If cycles are detected, you MUST refactor to break the cycle. Common strategies:
129
- - Move shared code to a lower-level package
130
- - Use dependency inversion (interfaces in low-level, implementations in high-level)
131
- - Restructure package boundaries
132
-
133
- ## Alternative Solutions (Preferred over adding dependencies)
134
-
135
- ### Option A: Move the Code
136
- If you need functionality from another package, consider moving that code to a shared lower-level package.
137
-
138
- ### Option B: Dependency Inversion
139
- Define an interface in the lower-level package, implement it in the higher-level package:
140
- \`\`\`typescript
141
- // In foundation package (level 0)
142
- export interface Logger { log(msg: string): void; }
143
-
144
- // In higher-level package
145
- export class ConsoleLogger implements Logger { ... }
146
- \`\`\`
147
-
148
- ### Option C: Pass Dependencies as Parameters
149
- Instead of importing, receive the dependency as a constructor or method parameter.
150
-
151
- ## Remember
152
- - Every dependency you add today is technical debt for tomorrow
153
- - The best dependency is the one you don't need
154
- - When in doubt, refactor rather than add dependencies
155
- `;
156
-
157
- // Module-level flag to prevent redundant file creation
158
- let dependenciesDocCreated = false;
159
-
160
- /**
161
- * Ensure a documentation file exists at the given path.
162
- */
163
- function ensureDocFile(docPath: string, content: string): boolean {
164
- try {
165
- fs.mkdirSync(path.dirname(docPath), { recursive: true });
166
- fs.writeFileSync(docPath, content, 'utf-8');
167
- return true;
168
- } catch (err: unknown) {
169
- void err;
170
- console.warn(`[webpieces] Could not create doc file: ${docPath}`);
171
- return false;
172
- }
173
- }
174
-
175
- /**
176
- * Ensure the dependencies documentation file exists.
177
- * Called when an architecture violation is detected.
178
- */
179
- function ensureDependenciesDoc(workspaceRoot: string): void {
180
- if (dependenciesDocCreated) return;
181
- const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.dependencies.md');
182
- if (ensureDocFile(docPath, DEPENDENCIES_DOC_CONTENT)) {
183
- dependenciesDocCreated = true;
184
- }
185
- }
186
-
187
- /**
188
- * Graph entry format from .graphs/dependencies.json
189
- */
190
- interface GraphEntry {
191
- level: number;
192
- dependsOn: string[];
193
- }
194
-
195
- type EnhancedGraph = Record<string, GraphEntry>;
196
-
197
- /**
198
- * Project mapping entry
199
- */
200
- interface ProjectMapping {
201
- root: string;
202
- name: string;
203
- }
204
-
205
- // Cache for blessed graph (loaded once per lint run)
206
- let cachedGraph: EnhancedGraph | null = null;
207
- let cachedGraphPath: string | null = null;
208
-
209
- // Cache for project mappings
210
- let cachedProjectMappings: ProjectMapping[] | null = null;
211
-
212
- /**
213
- * Find workspace root by walking up from file location
214
- */
215
- function findWorkspaceRoot(startPath: string): string {
216
- let currentDir = path.dirname(startPath);
217
-
218
- for (let i = 0; i < 20; i++) {
219
- const packagePath = path.join(currentDir, 'package.json');
220
- if (fs.existsSync(packagePath)) {
221
- try {
222
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
223
- if (pkg.workspaces || pkg.name === 'webpieces-ts') {
224
- return currentDir;
225
- }
226
- } catch (err: unknown) {
227
- //const error = toError(err);
228
- void err;
229
- }
230
- }
231
-
232
- const parent = path.dirname(currentDir);
233
- if (parent === currentDir) break;
234
- currentDir = parent;
235
- }
236
-
237
- return process.cwd();
238
- }
239
-
240
- /**
241
- * Load blessed graph from architecture/dependencies.json
242
- */
243
- function loadBlessedGraph(workspaceRoot: string): EnhancedGraph | null {
244
- const graphPath = path.join(workspaceRoot, 'architecture', 'dependencies.json');
245
-
246
- // Return cached if same path
247
- if (cachedGraphPath === graphPath && cachedGraph !== null) {
248
- return cachedGraph;
249
- }
250
-
251
- if (!fs.existsSync(graphPath)) {
252
- return null;
253
- }
254
-
255
- try {
256
- const content = fs.readFileSync(graphPath, 'utf-8');
257
- cachedGraph = JSON.parse(content) as EnhancedGraph;
258
- cachedGraphPath = graphPath;
259
- return cachedGraph;
260
- } catch (err: unknown) {
261
- const error = toError(err);
262
- console.error(`[ESLint @webpieces/enforce-architecture] Could not load graph: ${error.message}`);
263
- return null;
264
- }
265
- }
266
-
267
- /**
268
- * Build set of all workspace package names (from package.json files)
269
- * Used to detect workspace imports (works for any scope or unscoped)
270
- */
271
- function buildWorkspacePackageNames(workspaceRoot: string): Set<string> {
272
- const packageNames = new Set<string>();
273
- const mappings = buildProjectMappings(workspaceRoot);
274
-
275
- for (const mapping of mappings) {
276
- const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');
277
- if (fs.existsSync(pkgJsonPath)) {
278
- try {
279
- const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
280
- if (pkgJson.name) {
281
- packageNames.add(pkgJson.name);
282
- }
283
- } catch {
284
- // Ignore parse errors
285
- }
286
- }
287
- }
288
-
289
- return packageNames;
290
- }
291
-
292
- /**
293
- * Check if an import path is a workspace project
294
- * Works for scoped (@scope/name) or unscoped (name) packages
295
- */
296
- function isWorkspaceImport(importPath: string, workspaceRoot: string): boolean {
297
- const workspacePackages = buildWorkspacePackageNames(workspaceRoot);
298
- return workspacePackages.has(importPath);
299
- }
300
-
301
- /**
302
- * Get project name from package name
303
- * e.g., '@webpieces/client' → 'client', 'apis' → 'apis'
304
- */
305
- function getProjectNameFromPackageName(packageName: string, workspaceRoot: string): string {
306
- const mappings = buildProjectMappings(workspaceRoot);
307
-
308
- // Try to find by reading package.json files
309
- for (const mapping of mappings) {
310
- const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');
311
- if (fs.existsSync(pkgJsonPath)) {
312
- try {
313
- const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
314
- if (pkgJson.name === packageName) {
315
- return mapping.name; // Return project name
316
- }
317
- } catch {
318
- // Ignore parse errors
319
- }
320
- }
321
- }
322
-
323
- // Fallback: return package name as-is (might be unscoped project name)
324
- return packageName;
325
- }
326
-
327
- /**
328
- * Build project mappings from project.json files in workspace
329
- */
330
- function buildProjectMappings(workspaceRoot: string): ProjectMapping[] {
331
- if (cachedProjectMappings !== null) {
332
- return cachedProjectMappings;
333
- }
334
-
335
- const mappings: ProjectMapping[] = [];
336
-
337
- // Scan common locations for project.json files
338
- const searchDirs = ['packages', 'apps', 'libs', 'libraries', 'services'];
339
-
340
- for (const searchDir of searchDirs) {
341
- const searchPath = path.join(workspaceRoot, searchDir);
342
- if (!fs.existsSync(searchPath)) continue;
343
-
344
- scanForProjects(searchPath, workspaceRoot, mappings);
345
- }
346
-
347
- // Sort by path length (longest first) for more specific matching
348
- mappings.sort((a, b) => b.root.length - a.root.length);
349
-
350
- cachedProjectMappings = mappings;
351
- return mappings;
352
- }
353
-
354
- /**
355
- * Recursively scan for project.json files
356
- */
357
- function scanForProjects(
358
- dir: string,
359
- workspaceRoot: string,
360
- mappings: ProjectMapping[]
361
- ): void {
362
- try {
363
- const entries = fs.readdirSync(dir, { withFileTypes: true });
364
-
365
- for (const entry of entries) {
366
- const fullPath = path.join(dir, entry.name);
367
-
368
- if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
369
- // Check for project.json in this directory
370
- const projectJsonPath = path.join(fullPath, 'project.json');
371
- if (fs.existsSync(projectJsonPath)) {
372
- try {
373
- const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
374
- const projectRoot = path.relative(workspaceRoot, fullPath);
375
-
376
- // Use project name from project.json as-is (no scope forcing)
377
- const projectName = projectJson.name || entry.name;
378
-
379
- mappings.push({
380
- root: projectRoot,
381
- name: projectName,
382
- });
383
- } catch (err: unknown) {
384
- //const error = toError(err);
385
- void err;
386
- }
387
- }
388
-
389
- // Continue scanning subdirectories
390
- scanForProjects(fullPath, workspaceRoot, mappings);
391
- }
392
- }
393
- } catch (err: unknown) {
394
- //const error = toError(err);
395
- void err;
396
- }
397
- }
398
-
399
- /**
400
- * Get project name from file path
401
- */
402
- function getProjectFromFile(filePath: string, workspaceRoot: string): string | null {
403
- const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
404
- const mappings = buildProjectMappings(workspaceRoot);
405
-
406
- for (const mapping of mappings) {
407
- if (relativePath.startsWith(mapping.root + '/') || relativePath.startsWith(mapping.root)) {
408
- return mapping.name;
409
- }
410
- }
411
-
412
- return null;
413
- }
414
-
415
- /**
416
- * Compute all transitive dependencies for a project
417
- */
418
- function computeTransitiveDependencies(project: string, graph: EnhancedGraph): Set<string> {
419
- const result = new Set<string>();
420
- const visited = new Set<string>();
421
-
422
- function visit(currentProject: string): void {
423
- if (visited.has(currentProject)) return;
424
- visited.add(currentProject);
425
-
426
- const entry = graph[currentProject];
427
- if (!entry || !entry.dependsOn) return;
428
-
429
- for (const dep of entry.dependsOn) {
430
- result.add(dep);
431
- visit(dep);
432
- }
433
- }
434
-
435
- visit(project);
436
- return result;
437
- }
438
-
439
- const rule: Rule.RuleModule = {
440
- meta: {
441
- type: 'problem',
442
- docs: {
443
- description: 'Enforce blessed architecture dependencies',
444
- category: 'Best Practices',
445
- recommended: true,
446
- url: 'https://github.com/deanhiller/webpieces-ts',
447
- },
448
- messages: {
449
- illegalImport:
450
- "⚠️ AI Agent: READ tmp/webpieces/webpieces.dependencies.md for resolution steps!\n\n" +
451
- "Import '{{imported}}' violates architecture boundaries.\n\n" +
452
- "Project '{{project}}' (level {{level}}) can only import from:\n" +
453
- '{{allowedList}}',
454
- noGraph:
455
- 'No architecture graph found at architecture/dependencies.json\n' +
456
- 'Run: nx run architecture:validate --mode=update',
457
- },
458
- schema: [],
459
- },
460
-
461
- create(context: Rule.RuleContext): Rule.RuleListener {
462
- const filename = context.filename || context.getFilename();
463
- const workspaceRoot = findWorkspaceRoot(filename);
464
-
465
- return {
466
- ImportDeclaration(node: any): void {
467
- const importPath = node.source.value as string;
468
-
469
- // Check if this is a workspace import (works for any scope or unscoped)
470
- if (!isWorkspaceImport(importPath, workspaceRoot)) {
471
- return; // Not a workspace import, skip validation
472
- }
473
-
474
- // Determine which project this file belongs to
475
- const sourceProject = getProjectFromFile(filename, workspaceRoot);
476
- if (!sourceProject) {
477
- // File not in any known project (e.g., tools/, scripts/)
478
- return;
479
- }
480
-
481
- // Convert import (package name) to project name
482
- const targetProject = getProjectNameFromPackageName(importPath, workspaceRoot);
483
-
484
- // Self-import is always allowed
485
- if (targetProject === sourceProject) {
486
- return;
487
- }
488
-
489
- // Load blessed graph
490
- const graph = loadBlessedGraph(workspaceRoot);
491
- if (!graph) {
492
- // No graph file - warn but don't fail (allows gradual adoption)
493
- return;
494
- }
495
-
496
- // Get project entry
497
- const projectEntry = graph[sourceProject];
498
- if (!projectEntry) {
499
- // Project not in graph (new project?) - allow
500
- return;
501
- }
502
-
503
- // Compute allowed dependencies (direct + transitive)
504
- const allowedDeps = computeTransitiveDependencies(sourceProject, graph);
505
-
506
- // Check if import is allowed (use project name, not package name)
507
- if (!allowedDeps.has(targetProject)) {
508
- // Write documentation file for AI/developer to read
509
- ensureDependenciesDoc(workspaceRoot);
510
-
511
- // Build list of all allowed deps (direct + transitive) sorted by level
512
- const allAllowedDeps = Array.from(allowedDeps);
513
- // Sort by level (highest first) then alphabetically
514
- allAllowedDeps.sort((a, b) => {
515
- const levelA = graph[a]?.level ?? 0;
516
- const levelB = graph[b]?.level ?? 0;
517
- if (levelB !== levelA) return levelB - levelA;
518
- return a.localeCompare(b);
519
- });
520
- const allowedList =
521
- allAllowedDeps.length > 0
522
- ? allAllowedDeps
523
- .map((dep) => ` - ${dep} (level ${graph[dep]?.level ?? '?'})`)
524
- .join('\n')
525
- : ' (none - this is a foundation project)';
526
-
527
- context.report({
528
- node: node.source,
529
- messageId: 'illegalImport',
530
- data: {
531
- imported: importPath,
532
- project: sourceProject,
533
- level: String(projectEntry.level),
534
- allowedList: allowedList,
535
- },
536
- });
537
- }
538
- },
539
- };
540
- },
541
- };
542
-
543
- export = rule;
@@ -1,12 +0,0 @@
1
- /**
2
- * ESLint rule to enforce maximum file length
3
- *
4
- * Enforces a configurable maximum line count for files.
5
- * Default: 700 lines
6
- *
7
- * Configuration:
8
- * '@webpieces/max-file-lines': ['error', { max: 700 }]
9
- */
10
- import type { Rule } from 'eslint';
11
- declare const rule: Rule.RuleModule;
12
- export = rule;