@webpieces/dev-config 0.2.29 → 0.2.31

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.
@@ -1 +1 @@
1
- {"version":3,"file":"enforce-architecture.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/eslint-plugin/rules/enforce-architecture.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ESLint rule to enforce architecture boundaries\n *\n * Validates that imports from @webpieces/* packages comply with the\n * blessed dependency graph in .graphs/dependencies.json\n *\n * Supports transitive dependencies: if A depends on B and B depends on C,\n * then A can import from C.\n *\n * Configuration:\n * '@webpieces/enforce-architecture': 'error'\n */\n\nimport type { Rule } from 'eslint';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst DEPENDENCIES_DOC_CONTENT = `# AI Agent Instructions: Architecture Dependency Violation\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:validate --mode=update\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\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// Module-level flag to prevent redundant file creation\nlet dependenciesDocCreated = false;\n\n/**\n * Ensure a documentation file exists at the given path.\n */\nfunction ensureDocFile(docPath: string, content: string): boolean {\n try {\n fs.mkdirSync(path.dirname(docPath), { recursive: true });\n fs.writeFileSync(docPath, content, 'utf-8');\n return true;\n } catch (err: any) {\n void err;\n console.warn(`[webpieces] Could not create doc file: ${docPath}`);\n return false;\n }\n}\n\n/**\n * Ensure the dependencies documentation file exists.\n * Called when an architecture violation is detected.\n */\nfunction ensureDependenciesDoc(workspaceRoot: string): void {\n if (dependenciesDocCreated) return;\n const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.dependencies.md');\n if (ensureDocFile(docPath, DEPENDENCIES_DOC_CONTENT)) {\n dependenciesDocCreated = true;\n }\n}\n\n/**\n * Graph entry format from .graphs/dependencies.json\n */\ninterface GraphEntry {\n level: number;\n dependsOn: string[];\n}\n\ntype EnhancedGraph = Record<string, GraphEntry>;\n\n/**\n * Project mapping entry\n */\ninterface ProjectMapping {\n root: string;\n name: string;\n}\n\n// Cache for blessed graph (loaded once per lint run)\nlet cachedGraph: EnhancedGraph | null = null;\nlet cachedGraphPath: string | null = null;\n\n// Cache for project mappings\nlet cachedProjectMappings: ProjectMapping[] | null = null;\n\n/**\n * Find workspace root by walking up from file location\n */\nfunction findWorkspaceRoot(startPath: string): string {\n let currentDir = path.dirname(startPath);\n\n for (let i = 0; i < 20; i++) {\n const packagePath = path.join(currentDir, 'package.json');\n if (fs.existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));\n if (pkg.workspaces || pkg.name === 'webpieces-ts') {\n return currentDir;\n }\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n }\n\n const parent = path.dirname(currentDir);\n if (parent === currentDir) break;\n currentDir = parent;\n }\n\n return process.cwd();\n}\n\n/**\n * Load blessed graph from architecture/dependencies.json\n */\nfunction loadBlessedGraph(workspaceRoot: string): EnhancedGraph | null {\n const graphPath = path.join(workspaceRoot, 'architecture', 'dependencies.json');\n\n // Return cached if same path\n if (cachedGraphPath === graphPath && cachedGraph !== null) {\n return cachedGraph;\n }\n\n if (!fs.existsSync(graphPath)) {\n return null;\n }\n\n try {\n const content = fs.readFileSync(graphPath, 'utf-8');\n cachedGraph = JSON.parse(content) as EnhancedGraph;\n cachedGraphPath = graphPath;\n return cachedGraph;\n } catch (err: any) {\n //const error = toError(err);\n // err is used below\n console.error(`[ESLint @webpieces/enforce-architecture] Could not load graph: ${err}`);\n return null;\n }\n}\n\n/**\n * Build project mappings from project.json files in workspace\n */\nfunction buildProjectMappings(workspaceRoot: string): ProjectMapping[] {\n if (cachedProjectMappings !== null) {\n return cachedProjectMappings;\n }\n\n const mappings: ProjectMapping[] = [];\n\n // Scan common locations for project.json files\n const searchDirs = ['packages', 'apps', 'libs', 'libraries', 'services'];\n\n for (const searchDir of searchDirs) {\n const searchPath = path.join(workspaceRoot, searchDir);\n if (!fs.existsSync(searchPath)) continue;\n\n scanForProjects(searchPath, workspaceRoot, mappings);\n }\n\n // Sort by path length (longest first) for more specific matching\n mappings.sort((a, b) => b.root.length - a.root.length);\n\n cachedProjectMappings = mappings;\n return mappings;\n}\n\n/**\n * Recursively scan for project.json files\n */\nfunction scanForProjects(\n dir: string,\n workspaceRoot: string,\n mappings: ProjectMapping[]\n): void {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n // Check for project.json in this directory\n const projectJsonPath = path.join(fullPath, 'project.json');\n if (fs.existsSync(projectJsonPath)) {\n try {\n const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));\n const projectRoot = path.relative(workspaceRoot, fullPath);\n\n // Determine project name\n let projectName = projectJson.name || entry.name;\n\n // Add @webpieces/ prefix if not present\n if (!projectName.startsWith('@webpieces/')) {\n projectName = `@webpieces/${projectName}`;\n }\n\n mappings.push({\n root: projectRoot,\n name: projectName,\n });\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n }\n\n // Continue scanning subdirectories\n scanForProjects(fullPath, workspaceRoot, mappings);\n }\n }\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n}\n\n/**\n * Get project name from file path\n */\nfunction getProjectFromFile(filePath: string, workspaceRoot: string): string | null {\n const relativePath = path.relative(workspaceRoot, filePath).replace(/\\\\/g, '/');\n const mappings = buildProjectMappings(workspaceRoot);\n\n for (const mapping of mappings) {\n if (relativePath.startsWith(mapping.root + '/') || relativePath.startsWith(mapping.root)) {\n return mapping.name;\n }\n }\n\n return null;\n}\n\n/**\n * Compute all transitive dependencies for a project\n */\nfunction computeTransitiveDependencies(project: string, graph: EnhancedGraph): Set<string> {\n const result = new Set<string>();\n const visited = new Set<string>();\n\n function visit(currentProject: string): void {\n if (visited.has(currentProject)) return;\n visited.add(currentProject);\n\n const entry = graph[currentProject];\n if (!entry || !entry.dependsOn) return;\n\n for (const dep of entry.dependsOn) {\n result.add(dep);\n visit(dep);\n }\n }\n\n visit(project);\n return result;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Enforce blessed architecture dependencies',\n category: 'Best Practices',\n recommended: true,\n url: 'https://github.com/deanhiller/webpieces-ts',\n },\n messages: {\n illegalImport:\n \"⚠️ AI Agent: READ tmp/webpieces/webpieces.dependencies.md for resolution steps!\\n\\n\" +\n \"Import '{{imported}}' violates architecture boundaries.\\n\\n\" +\n \"Project '{{project}}' (level {{level}}) can only import from:\\n\" +\n '{{allowedList}}',\n noGraph:\n 'No architecture graph found at architecture/dependencies.json\\n' +\n 'Run: nx run architecture:validate --mode=update',\n },\n schema: [],\n },\n\n create(context: Rule.RuleContext): Rule.RuleListener {\n const filename = context.filename || context.getFilename();\n const workspaceRoot = findWorkspaceRoot(filename);\n\n return {\n ImportDeclaration(node: any): void {\n const importPath = node.source.value as string;\n\n // Only check @webpieces/* imports\n if (!importPath.startsWith('@webpieces/')) {\n return;\n }\n\n // Determine which project this file belongs to\n const project = getProjectFromFile(filename, workspaceRoot);\n if (!project) {\n // File not in any known project (e.g., tools/, scripts/)\n return;\n }\n\n // Self-import is always allowed\n if (importPath === project) {\n return;\n }\n\n // Load blessed graph\n const graph = loadBlessedGraph(workspaceRoot);\n if (!graph) {\n // No graph file - warn but don't fail (allows gradual adoption)\n // Uncomment below to enforce graph existence:\n // context.report({ node: node.source, messageId: 'noGraph' });\n return;\n }\n\n // Get project entry\n const projectEntry = graph[project];\n if (!projectEntry) {\n // Project not in graph (new project?) - allow\n return;\n }\n\n // Compute allowed dependencies (direct + transitive)\n const allowedDeps = computeTransitiveDependencies(project, graph);\n\n // Check if import is allowed\n if (!allowedDeps.has(importPath)) {\n // Write documentation file for AI/developer to read\n ensureDependenciesDoc(workspaceRoot);\n\n const directDeps = projectEntry.dependsOn || [];\n const allowedList =\n directDeps.length > 0\n ? directDeps.map((dep) => ` - ${dep}`).join('\\n') +\n '\\n (and their transitive dependencies)'\n : ' (none - this is a foundation project)';\n\n context.report({\n node: node.source,\n messageId: 'illegalImport',\n data: {\n imported: importPath,\n project: project,\n level: String(projectEntry.level),\n allowedList: allowedList,\n },\n });\n }\n },\n };\n },\n};\n\nexport = rule;\n"]}
1
+ {"version":3,"file":"enforce-architecture.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/eslint-plugin/rules/enforce-architecture.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ESLint rule to enforce architecture boundaries\n *\n * Validates that imports from @webpieces/* packages comply with the\n * blessed dependency graph in .graphs/dependencies.json\n *\n * Supports transitive dependencies: if A depends on B and B depends on C,\n * then A can import from C.\n *\n * Configuration:\n * '@webpieces/enforce-architecture': 'error'\n */\n\nimport type { Rule } from 'eslint';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst DEPENDENCIES_DOC_CONTENT = `# AI Agent Instructions: Architecture Dependency Violation\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:validate --mode=update\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\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// Module-level flag to prevent redundant file creation\nlet dependenciesDocCreated = false;\n\n/**\n * Ensure a documentation file exists at the given path.\n */\nfunction ensureDocFile(docPath: string, content: string): boolean {\n try {\n fs.mkdirSync(path.dirname(docPath), { recursive: true });\n fs.writeFileSync(docPath, content, 'utf-8');\n return true;\n } catch (err: any) {\n void err;\n console.warn(`[webpieces] Could not create doc file: ${docPath}`);\n return false;\n }\n}\n\n/**\n * Ensure the dependencies documentation file exists.\n * Called when an architecture violation is detected.\n */\nfunction ensureDependenciesDoc(workspaceRoot: string): void {\n if (dependenciesDocCreated) return;\n const docPath = path.join(workspaceRoot, 'tmp', 'webpieces', 'webpieces.dependencies.md');\n if (ensureDocFile(docPath, DEPENDENCIES_DOC_CONTENT)) {\n dependenciesDocCreated = true;\n }\n}\n\n/**\n * Graph entry format from .graphs/dependencies.json\n */\ninterface GraphEntry {\n level: number;\n dependsOn: string[];\n}\n\ntype EnhancedGraph = Record<string, GraphEntry>;\n\n/**\n * Project mapping entry\n */\ninterface ProjectMapping {\n root: string;\n name: string;\n}\n\n// Cache for blessed graph (loaded once per lint run)\nlet cachedGraph: EnhancedGraph | null = null;\nlet cachedGraphPath: string | null = null;\n\n// Cache for project mappings\nlet cachedProjectMappings: ProjectMapping[] | null = null;\n\n/**\n * Find workspace root by walking up from file location\n */\nfunction findWorkspaceRoot(startPath: string): string {\n let currentDir = path.dirname(startPath);\n\n for (let i = 0; i < 20; i++) {\n const packagePath = path.join(currentDir, 'package.json');\n if (fs.existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));\n if (pkg.workspaces || pkg.name === 'webpieces-ts') {\n return currentDir;\n }\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n }\n\n const parent = path.dirname(currentDir);\n if (parent === currentDir) break;\n currentDir = parent;\n }\n\n return process.cwd();\n}\n\n/**\n * Load blessed graph from architecture/dependencies.json\n */\nfunction loadBlessedGraph(workspaceRoot: string): EnhancedGraph | null {\n const graphPath = path.join(workspaceRoot, 'architecture', 'dependencies.json');\n\n // Return cached if same path\n if (cachedGraphPath === graphPath && cachedGraph !== null) {\n return cachedGraph;\n }\n\n if (!fs.existsSync(graphPath)) {\n return null;\n }\n\n try {\n const content = fs.readFileSync(graphPath, 'utf-8');\n cachedGraph = JSON.parse(content) as EnhancedGraph;\n cachedGraphPath = graphPath;\n return cachedGraph;\n } catch (err: any) {\n //const error = toError(err);\n // err is used below\n console.error(`[ESLint @webpieces/enforce-architecture] Could not load graph: ${err}`);\n return null;\n }\n}\n\n/**\n * Build set of all workspace package names (from package.json files)\n * Used to detect workspace imports (works for any scope or unscoped)\n */\nfunction buildWorkspacePackageNames(workspaceRoot: string): Set<string> {\n const packageNames = new Set<string>();\n const mappings = buildProjectMappings(workspaceRoot);\n\n for (const mapping of mappings) {\n const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');\n if (fs.existsSync(pkgJsonPath)) {\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));\n if (pkgJson.name) {\n packageNames.add(pkgJson.name);\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n\n return packageNames;\n}\n\n/**\n * Check if an import path is a workspace project\n * Works for scoped (@scope/name) or unscoped (name) packages\n */\nfunction isWorkspaceImport(importPath: string, workspaceRoot: string): boolean {\n const workspacePackages = buildWorkspacePackageNames(workspaceRoot);\n return workspacePackages.has(importPath);\n}\n\n/**\n * Get project name from package name\n * e.g., '@webpieces/client' → 'client', 'apis' → 'apis'\n */\nfunction getProjectNameFromPackageName(packageName: string, workspaceRoot: string): string {\n const mappings = buildProjectMappings(workspaceRoot);\n\n // Try to find by reading package.json files\n for (const mapping of mappings) {\n const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');\n if (fs.existsSync(pkgJsonPath)) {\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));\n if (pkgJson.name === packageName) {\n return mapping.name; // Return project name\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n\n // Fallback: return package name as-is (might be unscoped project name)\n return packageName;\n}\n\n/**\n * Build project mappings from project.json files in workspace\n */\nfunction buildProjectMappings(workspaceRoot: string): ProjectMapping[] {\n if (cachedProjectMappings !== null) {\n return cachedProjectMappings;\n }\n\n const mappings: ProjectMapping[] = [];\n\n // Scan common locations for project.json files\n const searchDirs = ['packages', 'apps', 'libs', 'libraries', 'services'];\n\n for (const searchDir of searchDirs) {\n const searchPath = path.join(workspaceRoot, searchDir);\n if (!fs.existsSync(searchPath)) continue;\n\n scanForProjects(searchPath, workspaceRoot, mappings);\n }\n\n // Sort by path length (longest first) for more specific matching\n mappings.sort((a, b) => b.root.length - a.root.length);\n\n cachedProjectMappings = mappings;\n return mappings;\n}\n\n/**\n * Recursively scan for project.json files\n */\nfunction scanForProjects(\n dir: string,\n workspaceRoot: string,\n mappings: ProjectMapping[]\n): void {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n // Check for project.json in this directory\n const projectJsonPath = path.join(fullPath, 'project.json');\n if (fs.existsSync(projectJsonPath)) {\n try {\n const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));\n const projectRoot = path.relative(workspaceRoot, fullPath);\n\n // Use project name from project.json as-is (no scope forcing)\n const projectName = projectJson.name || entry.name;\n\n mappings.push({\n root: projectRoot,\n name: projectName,\n });\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n }\n\n // Continue scanning subdirectories\n scanForProjects(fullPath, workspaceRoot, mappings);\n }\n }\n } catch (err: any) {\n //const error = toError(err);\n void err;\n }\n}\n\n/**\n * Get project name from file path\n */\nfunction getProjectFromFile(filePath: string, workspaceRoot: string): string | null {\n const relativePath = path.relative(workspaceRoot, filePath).replace(/\\\\/g, '/');\n const mappings = buildProjectMappings(workspaceRoot);\n\n for (const mapping of mappings) {\n if (relativePath.startsWith(mapping.root + '/') || relativePath.startsWith(mapping.root)) {\n return mapping.name;\n }\n }\n\n return null;\n}\n\n/**\n * Compute all transitive dependencies for a project\n */\nfunction computeTransitiveDependencies(project: string, graph: EnhancedGraph): Set<string> {\n const result = new Set<string>();\n const visited = new Set<string>();\n\n function visit(currentProject: string): void {\n if (visited.has(currentProject)) return;\n visited.add(currentProject);\n\n const entry = graph[currentProject];\n if (!entry || !entry.dependsOn) return;\n\n for (const dep of entry.dependsOn) {\n result.add(dep);\n visit(dep);\n }\n }\n\n visit(project);\n return result;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Enforce blessed architecture dependencies',\n category: 'Best Practices',\n recommended: true,\n url: 'https://github.com/deanhiller/webpieces-ts',\n },\n messages: {\n illegalImport:\n \"⚠️ AI Agent: READ tmp/webpieces/webpieces.dependencies.md for resolution steps!\\n\\n\" +\n \"Import '{{imported}}' violates architecture boundaries.\\n\\n\" +\n \"Project '{{project}}' (level {{level}}) can only import from:\\n\" +\n '{{allowedList}}',\n noGraph:\n 'No architecture graph found at architecture/dependencies.json\\n' +\n 'Run: nx run architecture:validate --mode=update',\n },\n schema: [],\n },\n\n create(context: Rule.RuleContext): Rule.RuleListener {\n const filename = context.filename || context.getFilename();\n const workspaceRoot = findWorkspaceRoot(filename);\n\n return {\n ImportDeclaration(node: any): void {\n const importPath = node.source.value as string;\n\n // Check if this is a workspace import (works for any scope or unscoped)\n if (!isWorkspaceImport(importPath, workspaceRoot)) {\n return; // Not a workspace import, skip validation\n }\n\n // Determine which project this file belongs to\n const sourceProject = getProjectFromFile(filename, workspaceRoot);\n if (!sourceProject) {\n // File not in any known project (e.g., tools/, scripts/)\n return;\n }\n\n // Convert import (package name) to project name\n const targetProject = getProjectNameFromPackageName(importPath, workspaceRoot);\n\n // Self-import is always allowed\n if (targetProject === sourceProject) {\n return;\n }\n\n // Load blessed graph\n const graph = loadBlessedGraph(workspaceRoot);\n if (!graph) {\n // No graph file - warn but don't fail (allows gradual adoption)\n return;\n }\n\n // Get project entry\n const projectEntry = graph[sourceProject];\n if (!projectEntry) {\n // Project not in graph (new project?) - allow\n return;\n }\n\n // Compute allowed dependencies (direct + transitive)\n const allowedDeps = computeTransitiveDependencies(sourceProject, graph);\n\n // Check if import is allowed (use project name, not package name)\n if (!allowedDeps.has(targetProject)) {\n // Write documentation file for AI/developer to read\n ensureDependenciesDoc(workspaceRoot);\n\n const directDeps = projectEntry.dependsOn || [];\n const allowedList =\n directDeps.length > 0\n ? directDeps.map((dep) => ` - ${dep}`).join('\\n') +\n '\\n (and their transitive dependencies)'\n : ' (none - this is a foundation project)';\n\n context.report({\n node: node.source,\n messageId: 'illegalImport',\n data: {\n imported: importPath,\n project: sourceProject,\n level: String(projectEntry.level),\n allowedList: allowedList,\n },\n });\n }\n },\n };\n },\n};\n\nexport = rule;\n"]}
@@ -255,6 +255,66 @@ function loadBlessedGraph(workspaceRoot: string): EnhancedGraph | null {
255
255
  }
256
256
  }
257
257
 
258
+ /**
259
+ * Build set of all workspace package names (from package.json files)
260
+ * Used to detect workspace imports (works for any scope or unscoped)
261
+ */
262
+ function buildWorkspacePackageNames(workspaceRoot: string): Set<string> {
263
+ const packageNames = new Set<string>();
264
+ const mappings = buildProjectMappings(workspaceRoot);
265
+
266
+ for (const mapping of mappings) {
267
+ const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');
268
+ if (fs.existsSync(pkgJsonPath)) {
269
+ try {
270
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
271
+ if (pkgJson.name) {
272
+ packageNames.add(pkgJson.name);
273
+ }
274
+ } catch {
275
+ // Ignore parse errors
276
+ }
277
+ }
278
+ }
279
+
280
+ return packageNames;
281
+ }
282
+
283
+ /**
284
+ * Check if an import path is a workspace project
285
+ * Works for scoped (@scope/name) or unscoped (name) packages
286
+ */
287
+ function isWorkspaceImport(importPath: string, workspaceRoot: string): boolean {
288
+ const workspacePackages = buildWorkspacePackageNames(workspaceRoot);
289
+ return workspacePackages.has(importPath);
290
+ }
291
+
292
+ /**
293
+ * Get project name from package name
294
+ * e.g., '@webpieces/client' → 'client', 'apis' → 'apis'
295
+ */
296
+ function getProjectNameFromPackageName(packageName: string, workspaceRoot: string): string {
297
+ const mappings = buildProjectMappings(workspaceRoot);
298
+
299
+ // Try to find by reading package.json files
300
+ for (const mapping of mappings) {
301
+ const pkgJsonPath = path.join(workspaceRoot, mapping.root, 'package.json');
302
+ if (fs.existsSync(pkgJsonPath)) {
303
+ try {
304
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
305
+ if (pkgJson.name === packageName) {
306
+ return mapping.name; // Return project name
307
+ }
308
+ } catch {
309
+ // Ignore parse errors
310
+ }
311
+ }
312
+ }
313
+
314
+ // Fallback: return package name as-is (might be unscoped project name)
315
+ return packageName;
316
+ }
317
+
258
318
  /**
259
319
  * Build project mappings from project.json files in workspace
260
320
  */
@@ -304,13 +364,8 @@ function scanForProjects(
304
364
  const projectJson = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
305
365
  const projectRoot = path.relative(workspaceRoot, fullPath);
306
366
 
307
- // Determine project name
308
- let projectName = projectJson.name || entry.name;
309
-
310
- // Add @webpieces/ prefix if not present
311
- if (!projectName.startsWith('@webpieces/')) {
312
- projectName = `@webpieces/${projectName}`;
313
- }
367
+ // Use project name from project.json as-is (no scope forcing)
368
+ const projectName = projectJson.name || entry.name;
314
369
 
315
370
  mappings.push({
316
371
  root: projectRoot,
@@ -402,20 +457,23 @@ const rule: Rule.RuleModule = {
402
457
  ImportDeclaration(node: any): void {
403
458
  const importPath = node.source.value as string;
404
459
 
405
- // Only check @webpieces/* imports
406
- if (!importPath.startsWith('@webpieces/')) {
407
- return;
460
+ // Check if this is a workspace import (works for any scope or unscoped)
461
+ if (!isWorkspaceImport(importPath, workspaceRoot)) {
462
+ return; // Not a workspace import, skip validation
408
463
  }
409
464
 
410
465
  // Determine which project this file belongs to
411
- const project = getProjectFromFile(filename, workspaceRoot);
412
- if (!project) {
466
+ const sourceProject = getProjectFromFile(filename, workspaceRoot);
467
+ if (!sourceProject) {
413
468
  // File not in any known project (e.g., tools/, scripts/)
414
469
  return;
415
470
  }
416
471
 
472
+ // Convert import (package name) to project name
473
+ const targetProject = getProjectNameFromPackageName(importPath, workspaceRoot);
474
+
417
475
  // Self-import is always allowed
418
- if (importPath === project) {
476
+ if (targetProject === sourceProject) {
419
477
  return;
420
478
  }
421
479
 
@@ -423,23 +481,21 @@ const rule: Rule.RuleModule = {
423
481
  const graph = loadBlessedGraph(workspaceRoot);
424
482
  if (!graph) {
425
483
  // No graph file - warn but don't fail (allows gradual adoption)
426
- // Uncomment below to enforce graph existence:
427
- // context.report({ node: node.source, messageId: 'noGraph' });
428
484
  return;
429
485
  }
430
486
 
431
487
  // Get project entry
432
- const projectEntry = graph[project];
488
+ const projectEntry = graph[sourceProject];
433
489
  if (!projectEntry) {
434
490
  // Project not in graph (new project?) - allow
435
491
  return;
436
492
  }
437
493
 
438
494
  // Compute allowed dependencies (direct + transitive)
439
- const allowedDeps = computeTransitiveDependencies(project, graph);
495
+ const allowedDeps = computeTransitiveDependencies(sourceProject, graph);
440
496
 
441
- // Check if import is allowed
442
- if (!allowedDeps.has(importPath)) {
497
+ // Check if import is allowed (use project name, not package name)
498
+ if (!allowedDeps.has(targetProject)) {
443
499
  // Write documentation file for AI/developer to read
444
500
  ensureDependenciesDoc(workspaceRoot);
445
501
 
@@ -455,7 +511,7 @@ const rule: Rule.RuleModule = {
455
511
  messageId: 'illegalImport',
456
512
  data: {
457
513
  imported: importPath,
458
- project: project,
514
+ project: sourceProject,
459
515
  level: String(projectEntry.level),
460
516
  allowedList: allowedList,
461
517
  },
@@ -25,11 +25,11 @@ async function helpExecutor(options, context) {
25
25
  console.log(`${BOLD}📝 Available Nx targets:${RESET}`);
26
26
  console.log('');
27
27
  console.log(' Workspace-level architecture validation:');
28
- console.log(' nx run .:arch:generate # Generate dependency graph');
29
- console.log(' nx run .:arch:visualize # Visualize dependency graph');
30
- console.log(' nx run .:arch:validate-no-cycles # Check for circular dependencies');
31
- console.log(' nx run .:arch:validate-no-skiplevel-deps # Check for redundant dependencies');
32
- console.log(' nx run .:arch:validate-architecture-unchanged # Validate against blessed graph');
28
+ console.log(' nx run architecture:generate # Generate dependency graph');
29
+ console.log(' nx run architecture:visualize # Visualize dependency graph');
30
+ console.log(' nx run architecture:validate-no-cycles # Check for circular dependencies');
31
+ console.log(' nx run architecture:validate-no-skiplevel-deps # Check for redundant dependencies');
32
+ console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');
33
33
  console.log('');
34
34
  console.log(' Per-project circular dependency checking:');
35
35
  console.log(' nx run <project>:check-circular-deps # Check project for circular deps');
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/executors/help/executor.ts"],"names":[],"mappings":";;AAIA,+BA6CC;AA7Cc,KAAK,UAAU,YAAY,CACtC,OAA4B,EAC5B,OAAwB;IAExB,mBAAmB;IACnB,MAAM,KAAK,GAAG,iBAAiB,CAAC;IAChC,MAAM,IAAI,GAAG,SAAS,CAAC;IACvB,MAAM,KAAK,GAAG,SAAS,CAAC;IAExB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,gDAAgD,KAAK,EAAE,CAAC,CAAC;IAC7E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,mDAAmD,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,+FAA+F,CAAC,CAAC;IAC7G,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACrG,OAAO,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;IACzF,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAC;IAClG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,2BAA2B,KAAK,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAC;IAC9F,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;IAC/F,OAAO,CAAC,GAAG,CAAC,sFAAsF,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACrG,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;IACnG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,sFAAsF,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,wBAAwB,KAAK,uCAAuC,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,iCAAiC,KAAK,6BAA6B,CAAC,CAAC;IAC3F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["import type { ExecutorContext } from '@nx/devkit';\n\nexport interface HelpExecutorOptions {}\n\nexport default async function helpExecutor(\n options: HelpExecutorOptions,\n context: ExecutorContext\n): Promise<{ success: true }> {\n // ANSI color codes\n const GREEN = '\\x1b[32m\\x1b[1m';\n const BOLD = '\\x1b[1m';\n const RESET = '\\x1b[0m';\n\n console.log('');\n console.log(`${GREEN}💡 @webpieces/dev-config - Available Commands${RESET}`);\n console.log('');\n console.log(`${BOLD}📝 Available npm scripts (convenient shortcuts):${RESET}`);\n console.log('');\n console.log(' Architecture graph:');\n console.log(' npm run arch:generate # Generate dependency graph');\n console.log(' npm run arch:visualize # Visualize dependency graph');\n console.log('');\n console.log(' Validation:');\n console.log(' npm run arch:validate # Quick validation (no-cycles + no-skiplevel-deps)');\n console.log(' npm run arch:validate-all # Full arch validation (+ unchanged check)');\n console.log(' npm run arch:check-circular # Check all projects for circular deps');\n console.log(' npm run arch:check-circular-affected # Check affected projects only');\n console.log(' npm run arch:validate-complete # Complete validation (arch + circular)');\n console.log('');\n console.log(`${BOLD}📝 Available Nx targets:${RESET}`);\n console.log('');\n console.log(' Workspace-level architecture validation:');\n console.log(' nx run .:arch:generate # Generate dependency graph');\n console.log(' nx run .:arch:visualize # Visualize dependency graph');\n console.log(' nx run .:arch:validate-no-cycles # Check for circular dependencies');\n console.log(' nx run .:arch:validate-no-skiplevel-deps # Check for redundant dependencies');\n console.log(' nx run .:arch:validate-architecture-unchanged # Validate against blessed graph');\n console.log('');\n console.log(' Per-project circular dependency checking:');\n console.log(' nx run <project>:check-circular-deps # Check project for circular deps');\n console.log(' nx affected --target=check-circular-deps # Check all affected projects');\n console.log(' nx run-many --target=check-circular-deps --all # Check all projects');\n console.log('');\n console.log(`${GREEN}💡 Quick start:${RESET}`);\n console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the graph first`);\n console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);\n console.log('');\n\n return { success: true };\n}\n"]}
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../packages/tooling/dev-config/executors/help/executor.ts"],"names":[],"mappings":";;AAIA,+BA6CC;AA7Cc,KAAK,UAAU,YAAY,CACtC,OAA4B,EAC5B,OAAwB;IAExB,mBAAmB;IACnB,MAAM,KAAK,GAAG,iBAAiB,CAAC;IAChC,MAAM,IAAI,GAAG,SAAS,CAAC;IACvB,MAAM,KAAK,GAAG,SAAS,CAAC;IAExB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,gDAAgD,KAAK,EAAE,CAAC,CAAC;IAC7E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,mDAAmD,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,+FAA+F,CAAC,CAAC;IAC7G,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACrG,OAAO,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;IACzF,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAC;IAClG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,2BAA2B,KAAK,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,sFAAsF,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACrG,OAAO,CAAC,GAAG,CAAC,4FAA4F,CAAC,CAAC;IAC1G,OAAO,CAAC,GAAG,CAAC,6FAA6F,CAAC,CAAC;IAC3G,OAAO,CAAC,GAAG,CAAC,2FAA2F,CAAC,CAAC;IACzG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,sFAAsF,CAAC,CAAC;IACpG,OAAO,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,kBAAkB,KAAK,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,wBAAwB,KAAK,uCAAuC,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,iCAAiC,KAAK,6BAA6B,CAAC,CAAC;IAC3F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["import type { ExecutorContext } from '@nx/devkit';\n\nexport interface HelpExecutorOptions {}\n\nexport default async function helpExecutor(\n options: HelpExecutorOptions,\n context: ExecutorContext\n): Promise<{ success: true }> {\n // ANSI color codes\n const GREEN = '\\x1b[32m\\x1b[1m';\n const BOLD = '\\x1b[1m';\n const RESET = '\\x1b[0m';\n\n console.log('');\n console.log(`${GREEN}💡 @webpieces/dev-config - Available Commands${RESET}`);\n console.log('');\n console.log(`${BOLD}📝 Available npm scripts (convenient shortcuts):${RESET}`);\n console.log('');\n console.log(' Architecture graph:');\n console.log(' npm run arch:generate # Generate dependency graph');\n console.log(' npm run arch:visualize # Visualize dependency graph');\n console.log('');\n console.log(' Validation:');\n console.log(' npm run arch:validate # Quick validation (no-cycles + no-skiplevel-deps)');\n console.log(' npm run arch:validate-all # Full arch validation (+ unchanged check)');\n console.log(' npm run arch:check-circular # Check all projects for circular deps');\n console.log(' npm run arch:check-circular-affected # Check affected projects only');\n console.log(' npm run arch:validate-complete # Complete validation (arch + circular)');\n console.log('');\n console.log(`${BOLD}📝 Available Nx targets:${RESET}`);\n console.log('');\n console.log(' Workspace-level architecture validation:');\n console.log(' nx run architecture:generate # Generate dependency graph');\n console.log(' nx run architecture:visualize # Visualize dependency graph');\n console.log(' nx run architecture:validate-no-cycles # Check for circular dependencies');\n console.log(' nx run architecture:validate-no-skiplevel-deps # Check for redundant dependencies');\n console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');\n console.log('');\n console.log(' Per-project circular dependency checking:');\n console.log(' nx run <project>:check-circular-deps # Check project for circular deps');\n console.log(' nx affected --target=check-circular-deps # Check all affected projects');\n console.log(' nx run-many --target=check-circular-deps --all # Check all projects');\n console.log('');\n console.log(`${GREEN}💡 Quick start:${RESET}`);\n console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the graph first`);\n console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);\n console.log('');\n\n return { success: true };\n}\n"]}
@@ -30,11 +30,11 @@ export default async function helpExecutor(
30
30
  console.log(`${BOLD}📝 Available Nx targets:${RESET}`);
31
31
  console.log('');
32
32
  console.log(' Workspace-level architecture validation:');
33
- console.log(' nx run .:arch:generate # Generate dependency graph');
34
- console.log(' nx run .:arch:visualize # Visualize dependency graph');
35
- console.log(' nx run .:arch:validate-no-cycles # Check for circular dependencies');
36
- console.log(' nx run .:arch:validate-no-skiplevel-deps # Check for redundant dependencies');
37
- console.log(' nx run .:arch:validate-architecture-unchanged # Validate against blessed graph');
33
+ console.log(' nx run architecture:generate # Generate dependency graph');
34
+ console.log(' nx run architecture:visualize # Visualize dependency graph');
35
+ console.log(' nx run architecture:validate-no-cycles # Check for circular dependencies');
36
+ console.log(' nx run architecture:validate-no-skiplevel-deps # Check for redundant dependencies');
37
+ console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');
38
38
  console.log('');
39
39
  console.log(' Per-project circular dependency checking:');
40
40
  console.log(' nx run <project>:check-circular-deps # Check project for circular deps');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/dev-config",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "Development configuration, scripts, and patterns for WebPieces projects",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/plugin/README.md CHANGED
@@ -69,19 +69,19 @@ You can also run targets directly with Nx:
69
69
 
70
70
  ```bash
71
71
  # Generate the dependency graph
72
- nx run .:arch:generate
72
+ nx run architecture:generate
73
73
 
74
74
  # Visualize the graph in your browser
75
- nx run .:arch:visualize
75
+ nx run architecture:visualize
76
76
 
77
77
  # Validate no circular dependencies
78
- nx run .:arch:validate-no-cycles
78
+ nx run architecture:validate-no-cycles
79
79
 
80
80
  # Validate against blessed graph (for CI)
81
- nx run .:arch:validate-architecture-unchanged
81
+ nx run architecture:validate-architecture-unchanged
82
82
 
83
83
  # Check for redundant dependencies
84
- nx run .:arch:validate-no-skiplevel-deps
84
+ nx run architecture:validate-no-skiplevel-deps
85
85
  ```
86
86
 
87
87
  ### Per-Project Circular Dependency Checking
@@ -114,7 +114,7 @@ Configure the plugin in `nx.json`:
114
114
  },
115
115
  "workspace": {
116
116
  "enabled": true,
117
- "targetPrefix": "arch:",
117
+ "targetPrefix": "architecture:",
118
118
  "validations": {
119
119
  "noCycles": true,
120
120
  "noSkipLevelDeps": true,
package/plugin.js CHANGED
@@ -63,68 +63,105 @@ function normalizeOptions(options) {
63
63
  workspace,
64
64
  };
65
65
  }
66
+ async function createNodesFunction(projectFiles, options, context) {
67
+ const opts = normalizeOptions(options);
68
+ const results = [];
69
+ // Add workspace-level architecture targets
70
+ addArchitectureProject(results, projectFiles, opts, context);
71
+ // Add per-project circular-deps targets
72
+ addCircularDepsTargets(results, projectFiles, opts, context);
73
+ return results;
74
+ }
75
+ function addArchitectureProject(results, projectFiles, opts, context) {
76
+ if (!opts.workspace.enabled)
77
+ return;
78
+ const archDirPath = (0, path_1.join)(context.workspaceRoot, 'architecture');
79
+ if (!(0, fs_1.existsSync)(archDirPath))
80
+ return;
81
+ const workspaceTargets = createWorkspaceTargetsWithoutPrefix(opts);
82
+ if (Object.keys(workspaceTargets).length === 0)
83
+ return;
84
+ const result = {
85
+ projects: {
86
+ architecture: {
87
+ name: 'architecture',
88
+ root: 'architecture',
89
+ targets: workspaceTargets,
90
+ },
91
+ },
92
+ };
93
+ const firstProjectFile = projectFiles[0];
94
+ if (firstProjectFile) {
95
+ results.push([firstProjectFile, result]);
96
+ }
97
+ }
98
+ function addCircularDepsTargets(results, projectFiles, opts, context) {
99
+ if (!opts.circularDeps.enabled)
100
+ return;
101
+ for (const projectFile of projectFiles) {
102
+ if (!projectFile.endsWith('project.json'))
103
+ continue;
104
+ const projectRoot = (0, path_1.dirname)(projectFile);
105
+ if (projectRoot === '.')
106
+ continue;
107
+ if (isExcluded(projectRoot, opts.circularDeps.excludePatterns))
108
+ continue;
109
+ const srcDir = (0, path_1.join)(context.workspaceRoot, projectRoot, 'src');
110
+ if (!(0, fs_1.existsSync)(srcDir))
111
+ continue;
112
+ const targetName = opts.circularDeps.targetName;
113
+ const checkCircularDepsTarget = createCircularDepsTarget(projectRoot, targetName);
114
+ const result = {
115
+ projects: {
116
+ [projectRoot]: {
117
+ targets: {
118
+ [targetName]: checkCircularDepsTarget,
119
+ },
120
+ },
121
+ },
122
+ };
123
+ results.push([projectFile, result]);
124
+ }
125
+ }
66
126
  /**
67
127
  * Nx V2 Inference Plugin
68
- * Matches project.json files and creates architecture + circular-deps targets
128
+ * Matches project.json files to create targets
69
129
  */
70
130
  exports.createNodesV2 = [
71
- // Pattern to match: look for project.json files
131
+ // Pattern to match project.json files
72
132
  '**/project.json',
73
133
  // Inference function
74
- async (projectFiles, options, context) => {
75
- const opts = normalizeOptions(options);
76
- const results = [];
77
- // Phase 1: Add workspace-level architecture targets to root
78
- if (opts.workspace.enabled) {
79
- const rootProject = projectFiles.find((f) => (0, path_1.dirname)(f) === '.');
80
- if (rootProject) {
81
- const workspaceTargets = createWorkspaceTargets(opts);
82
- if (Object.keys(workspaceTargets).length > 0) {
83
- const result = {
84
- projects: {
85
- '.': {
86
- targets: workspaceTargets,
87
- },
88
- },
89
- };
90
- results.push([rootProject, result]);
91
- }
92
- }
93
- }
94
- // Phase 2: Add per-project circular-deps targets
95
- if (opts.circularDeps.enabled) {
96
- for (const projectFile of projectFiles) {
97
- const projectRoot = (0, path_1.dirname)(projectFile);
98
- // Skip workspace root (already handled)
99
- if (projectRoot === '.')
100
- continue;
101
- // Check exclude patterns
102
- if (isExcluded(projectRoot, opts.circularDeps.excludePatterns)) {
103
- continue;
104
- }
105
- // Only create target if project has a src/ directory
106
- const srcDir = (0, path_1.join)(context.workspaceRoot, projectRoot, 'src');
107
- if ((0, fs_1.existsSync)(srcDir)) {
108
- const targetName = opts.circularDeps.targetName;
109
- const checkCircularDepsTarget = createCircularDepsTarget(projectRoot, targetName);
110
- const result = {
111
- projects: {
112
- [projectRoot]: {
113
- targets: {
114
- [targetName]: checkCircularDepsTarget,
115
- },
116
- },
117
- },
118
- };
119
- results.push([projectFile, result]);
120
- }
121
- }
122
- }
123
- return results;
124
- },
134
+ createNodesFunction,
125
135
  ];
126
136
  /**
127
- * Create workspace-level architecture validation targets
137
+ * Create workspace-level architecture validation targets WITHOUT prefix
138
+ * Used for virtual 'architecture' project
139
+ */
140
+ function createWorkspaceTargetsWithoutPrefix(opts) {
141
+ const targets = {};
142
+ const graphPath = opts.workspace.graphPath;
143
+ // Add help target (always available)
144
+ targets['help'] = createHelpTarget();
145
+ if (opts.workspace.features.generate) {
146
+ targets['generate'] = createGenerateTarget(graphPath);
147
+ }
148
+ if (opts.workspace.features.visualize) {
149
+ targets['visualize'] = createVisualizeTargetWithoutPrefix(graphPath);
150
+ }
151
+ if (opts.workspace.validations.noCycles) {
152
+ targets['validate-no-cycles'] = createValidateNoCyclesTarget();
153
+ }
154
+ if (opts.workspace.validations.architectureUnchanged) {
155
+ targets['validate-architecture-unchanged'] = createValidateUnchangedTarget(graphPath);
156
+ }
157
+ if (opts.workspace.validations.noSkipLevelDeps) {
158
+ targets['validate-no-skiplevel-deps'] = createValidateNoSkipLevelTarget();
159
+ }
160
+ return targets;
161
+ }
162
+ /**
163
+ * Create workspace-level architecture validation targets (DEPRECATED - keeping for backward compat)
164
+ * Used when root project.json exists (old style with '.' project)
128
165
  */
129
166
  function createWorkspaceTargets(opts) {
130
167
  const targets = {};
@@ -162,6 +199,17 @@ function createGenerateTarget(graphPath) {
162
199
  },
163
200
  };
164
201
  }
202
+ function createVisualizeTargetWithoutPrefix(graphPath) {
203
+ return {
204
+ executor: '@webpieces/dev-config:visualize',
205
+ dependsOn: ['generate'],
206
+ options: { graphPath },
207
+ metadata: {
208
+ technologies: ['nx'],
209
+ description: 'Generate visual representations of the architecture graph',
210
+ },
211
+ };
212
+ }
165
213
  function createVisualizeTarget(prefix, graphPath) {
166
214
  return {
167
215
  executor: '@webpieces/dev-config:visualize',
@@ -72,10 +72,10 @@ function addNpmScripts(tree) {
72
72
  (0, devkit_1.updateJson)(tree, 'package.json', (pkgJson) => {
73
73
  pkgJson.scripts = pkgJson.scripts ?? {};
74
74
  // Add architecture validation scripts
75
- pkgJson.scripts['arch:generate'] = 'nx run .:arch:generate';
76
- pkgJson.scripts['arch:visualize'] = 'nx run .:arch:visualize';
77
- pkgJson.scripts['arch:validate'] = 'nx run .:arch:validate-no-cycles && nx run .:arch:validate-no-skiplevel-deps';
78
- pkgJson.scripts['arch:validate-all'] = 'nx run .:arch:validate-no-cycles && nx run .:arch:validate-no-skiplevel-deps && nx run .:arch:validate-architecture-unchanged';
75
+ pkgJson.scripts['arch:generate'] = 'nx run architecture:generate';
76
+ pkgJson.scripts['arch:visualize'] = 'nx run architecture:visualize';
77
+ pkgJson.scripts['arch:validate'] = 'nx run architecture:validate-no-cycles && nx run architecture:validate-no-skiplevel-deps';
78
+ pkgJson.scripts['arch:validate-all'] = 'nx run architecture:validate-no-cycles && nx run architecture:validate-no-skiplevel-deps && nx run architecture:validate-architecture-unchanged';
79
79
  // Add circular dependency checking scripts
80
80
  pkgJson.scripts['arch:check-circular'] = 'nx run-many --target=check-circular-deps --all';
81
81
  pkgJson.scripts['arch:check-circular-affected'] = 'nx affected --target=check-circular-deps';
@@ -188,7 +188,7 @@ function createSuccessCallback(installTask) {
188
188
  console.log(` ${BOLD}npm run arch:generate${RESET} # Generate the dependency graph`);
189
189
  console.log(` ${BOLD}npm run arch:validate-complete${RESET} # Run complete validation`);
190
190
  console.log('');
191
- console.log(`💡 For full documentation, run: ${BOLD}nx run .:webpieces:help${RESET}`);
191
+ console.log(`💡 For full documentation, run: ${BOLD}nx run architecture:help${RESET}`);
192
192
  console.log('');
193
193
  };
194
194
  }