@webpieces/nx-webpieces-rules 0.3.129 ā 0.3.130
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 +5 -5
- package/src/executors/generate/executor.js +4 -4
- package/src/executors/generate/executor.js.map +1 -1
- package/src/executors/validate-architecture-unchanged/executor.js +3 -3
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/executors/validate-no-skiplevel-deps/executor.d.ts +12 -6
- package/src/executors/validate-no-skiplevel-deps/executor.js +16 -113
- package/src/executors/validate-no-skiplevel-deps/executor.js.map +1 -1
- package/src/executors/validate-nx-wiring/executor.d.ts +15 -14
- package/src/executors/validate-nx-wiring/executor.js +94 -27
- package/src/executors/validate-nx-wiring/executor.js.map +1 -1
- package/src/executors/validate-packagejson/executor.js +14 -8
- package/src/executors/validate-packagejson/executor.js.map +1 -1
- package/src/lib/graph-generator.d.ts +27 -8
- package/src/lib/graph-generator.js +48 -53
- package/src/lib/graph-generator.js.map +1 -1
- package/src/lib/package-validator.d.ts +10 -0
- package/src/lib/package-validator.js +12 -5
- package/src/lib/package-validator.js.map +1 -1
- package/src/lib/transitive-reduction.d.ts +25 -0
- package/src/lib/transitive-reduction.js +56 -0
- package/src/lib/transitive-reduction.js.map +1 -0
- package/src/plugin.js +4 -1
- package/src/plugin.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.130",
|
|
4
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",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@webpieces/ai-hook-rules": "0.3.
|
|
22
|
-
"@webpieces/code-rules": "0.3.
|
|
23
|
-
"@webpieces/eslint-rules": "0.3.
|
|
24
|
-
"@webpieces/rules-config": "0.3.
|
|
21
|
+
"@webpieces/ai-hook-rules": "0.3.130",
|
|
22
|
+
"@webpieces/code-rules": "0.3.130",
|
|
23
|
+
"@webpieces/eslint-rules": "0.3.130",
|
|
24
|
+
"@webpieces/rules-config": "0.3.130"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@nx/devkit": ">=18.0.0"
|
|
@@ -19,12 +19,12 @@ async function runExecutor(options, context) {
|
|
|
19
19
|
console.log('\nš Architecture Graph Generator\n');
|
|
20
20
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
21
21
|
try {
|
|
22
|
-
// Step 1:
|
|
23
|
-
console.log(
|
|
24
|
-
const
|
|
22
|
+
// Step 1: Build the full graph from nx, then transitively reduce it to the view
|
|
23
|
+
console.log("š Generating dependency graph from nx's project graph...");
|
|
24
|
+
const reducedGraph = await (0, graph_generator_1.generateReducedGraph)();
|
|
25
25
|
// Step 2: Topological sort (to assign levels for visualization)
|
|
26
26
|
console.log('š Computing topological layers...');
|
|
27
|
-
const enhancedGraph = (0, graph_sorter_1.sortGraphTopologically)(
|
|
27
|
+
const enhancedGraph = (0, graph_sorter_1.sortGraphTopologically)(reducedGraph);
|
|
28
28
|
// Step 3: Save the graph
|
|
29
29
|
console.log('š¾ Saving graph to architecture/dependencies.json...');
|
|
30
30
|
(0, graph_loader_1.saveGraph)(enhancedGraph, workspaceRoot, graphPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAgBH,8BAqCC;AAlDD,+
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAgBH,8BAqCC;AAlDD,+DAAiE;AACjE,yDAAgE;AAChE,yDAAmD;AACnD,2CAAwC;AAUzB,KAAK,UAAU,WAAW,CACrC,OAAgC,EAChC,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IAEnD,8DAA8D;IAC9D,IAAI,CAAC;QACD,gFAAgF;QAChF,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QACzE,MAAM,YAAY,GAAG,MAAM,IAAA,sCAAoB,GAAE,CAAC;QAElD,gEAAgE;QAChE,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,IAAA,qCAAsB,EAAC,YAAY,CAAC,CAAC;QAE3D,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,IAAA,wBAAS,EAAC,aAAa,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAE1C,gBAAgB;QAChB,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACzE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,gBAAgB,YAAY,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEpE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json.\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { saveGraph } from '../../lib/graph-loader';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\nš Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"š Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('š Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n\n // Step 3: Save the graph\n console.log('š¾ Saving graph to architecture/dependencies.json...');\n saveGraph(enhancedGraph, workspaceRoot, graphPath);\n console.log('ā
Graph saved successfully');\n\n // Print summary\n const projectCount = Object.keys(enhancedGraph).length;\n const levels = new Set(Object.values(enhancedGraph).map((e) => e.level));\n console.log(`\\nš Graph Summary:`);\n console.log(` Projects: ${projectCount}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n console.error('ā Graph generation failed:', error.message);\n return { success: false };\n }\n}\n"]}
|
|
@@ -42,12 +42,12 @@ async function runExecutor(options, context) {
|
|
|
42
42
|
console.error(' 4. Commit architecture/dependencies.json');
|
|
43
43
|
return { success: false };
|
|
44
44
|
}
|
|
45
|
-
// Step 1:
|
|
45
|
+
// Step 1: Build the full graph from nx, then transitively reduce it
|
|
46
46
|
console.log('š Generating current dependency graph...');
|
|
47
|
-
const
|
|
47
|
+
const reducedGraph = await (0, graph_generator_1.generateReducedGraph)();
|
|
48
48
|
// Step 2: Topological sort (to get enhanced graph with levels)
|
|
49
49
|
console.log('š Computing topological layers...');
|
|
50
|
-
const currentGraph = (0, graph_sorter_1.sortGraphTopologically)(
|
|
50
|
+
const currentGraph = (0, graph_sorter_1.sortGraphTopologically)(reducedGraph);
|
|
51
51
|
// Step 3: Load saved graph
|
|
52
52
|
console.log('š Loading saved graph...');
|
|
53
53
|
const savedGraph = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-architecture-unchanged/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AA8BH,8BAoEC;AA/FD,0DAAwD;AACxD,+
|
|
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 { generateReducedGraph } 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: Build the full graph from nx, then transitively reduce it\n console.log('š Generating current dependency graph...');\n const reducedGraph = await generateReducedGraph();\n\n // Step 2: Topological sort (to get enhanced graph with levels)\n console.log('š Computing topological layers...');\n const currentGraph = sortGraphTopologically(reducedGraph);\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,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Validate No Skip-Level Dependencies Executor
|
|
2
|
+
* Validate No Skip-Level Dependencies Executor ā RETIRED (no-op).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Skip-level (redundant transitive) edges used to be an error here. That is no longer
|
|
5
|
+
* meaningful: `architecture:generate` now derives the graph from nx's project graph and
|
|
6
|
+
* applies transitive reduction, so the committed architecture/dependencies.json can never
|
|
7
|
+
* contain a redundant edge. The check is superseded by auto-reduction.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
+
* Worse, its old remediation told users to remove "redundant" deps from package.json ā
|
|
10
|
+
* which is exactly the runtime-validity trap (a transitively-reachable package.json entry
|
|
11
|
+
* can still be a real runtime dependency, e.g. a peerDependency or generated client).
|
|
12
|
+
*
|
|
13
|
+
* Defaults off (workspace.validations.noSkipLevelDeps=false). Kept as a no-op for one
|
|
14
|
+
* release so any explicit enablement does not error on now-expected skip-level edges.
|
|
9
15
|
*
|
|
10
16
|
* Usage:
|
|
11
17
|
* nx run architecture:validate-no-skiplevel-deps
|
|
@@ -16,4 +22,4 @@ export interface ValidateNoSkipLevelDepsOptions {
|
|
|
16
22
|
export interface ExecutorResult {
|
|
17
23
|
success: boolean;
|
|
18
24
|
}
|
|
19
|
-
export default function runExecutor(_options: ValidateNoSkipLevelDepsOptions,
|
|
25
|
+
export default function runExecutor(_options: ValidateNoSkipLevelDepsOptions, _context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -1,125 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Validate No Skip-Level Dependencies Executor
|
|
3
|
+
* Validate No Skip-Level Dependencies Executor ā RETIRED (no-op).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Skip-level (redundant transitive) edges used to be an error here. That is no longer
|
|
6
|
+
* meaningful: `architecture:generate` now derives the graph from nx's project graph and
|
|
7
|
+
* applies transitive reduction, so the committed architecture/dependencies.json can never
|
|
8
|
+
* contain a redundant edge. The check is superseded by auto-reduction.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Worse, its old remediation told users to remove "redundant" deps from package.json ā
|
|
11
|
+
* which is exactly the runtime-validity trap (a transitively-reachable package.json entry
|
|
12
|
+
* can still be a real runtime dependency, e.g. a peerDependency or generated client).
|
|
13
|
+
*
|
|
14
|
+
* Defaults off (workspace.validations.noSkipLevelDeps=false). Kept as a no-op for one
|
|
15
|
+
* release so any explicit enablement does not error on now-expected skip-level edges.
|
|
10
16
|
*
|
|
11
17
|
* Usage:
|
|
12
18
|
* nx run architecture:validate-no-skiplevel-deps
|
|
13
19
|
*/
|
|
14
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
21
|
exports.default = runExecutor;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*/
|
|
22
|
-
function computeTransitiveDeps(project, graph, visited = new Set()) {
|
|
23
|
-
const result = new Set();
|
|
24
|
-
if (visited.has(project)) {
|
|
25
|
-
return result;
|
|
26
|
-
}
|
|
27
|
-
visited.add(project);
|
|
28
|
-
const directDeps = graph[project] || [];
|
|
29
|
-
for (const dep of directDeps) {
|
|
30
|
-
result.add(dep);
|
|
31
|
-
// Recursively get transitive deps
|
|
32
|
-
const transitive = computeTransitiveDeps(dep, graph, visited);
|
|
33
|
-
for (const t of transitive) {
|
|
34
|
-
result.add(t);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return result;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Find redundant dependencies for a project
|
|
41
|
-
*/
|
|
42
|
-
function findRedundantDeps(project, graph) {
|
|
43
|
-
const redundant = [];
|
|
44
|
-
const directDeps = graph[project] || [];
|
|
45
|
-
// For each direct dependency, compute what it transitively brings in
|
|
46
|
-
const transitiveByDep = new Map();
|
|
47
|
-
for (const dep of directDeps) {
|
|
48
|
-
transitiveByDep.set(dep, computeTransitiveDeps(dep, graph));
|
|
49
|
-
}
|
|
50
|
-
// Check if any direct dependency is already brought in by another
|
|
51
|
-
const transitiveEntries = Array.from(transitiveByDep.entries());
|
|
52
|
-
for (const dep of directDeps) {
|
|
53
|
-
for (const entry of transitiveEntries) {
|
|
54
|
-
const otherDep = entry[0];
|
|
55
|
-
const otherTransitive = entry[1];
|
|
56
|
-
if (otherDep !== dep && otherTransitive.has(dep)) {
|
|
57
|
-
redundant.push({
|
|
58
|
-
project,
|
|
59
|
-
redundantDep: dep,
|
|
60
|
-
alreadyBroughtInBy: otherDep,
|
|
61
|
-
});
|
|
62
|
-
break; // Only report once per redundant dep
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return redundant;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Write documentation file when violations are found
|
|
70
|
-
*/
|
|
71
|
-
function writeDocFile(workspaceRoot) {
|
|
72
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
73
|
-
try {
|
|
74
|
-
(0, rules_config_1.writeTemplate)(workspaceRoot, 'webpieces.transitivedeps.md');
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
const error = (0, toError_1.toError)(err);
|
|
78
|
-
console.warn('Could not write webpieces.transitivedeps.md:', error.message);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async function runExecutor(_options, context) {
|
|
82
|
-
console.log('\nš Validating No Skip-Level Dependencies\n');
|
|
83
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
84
|
-
try {
|
|
85
|
-
// Step 1: Generate current graph from project.json files
|
|
86
|
-
console.log('š Generating dependency graph from project.json files...');
|
|
87
|
-
const graph = await (0, graph_generator_1.generateGraph)();
|
|
88
|
-
// Step 2: Find all redundant dependencies
|
|
89
|
-
console.log('š Checking for redundant transitive dependencies...');
|
|
90
|
-
const allRedundant = [];
|
|
91
|
-
for (const project of Object.keys(graph)) {
|
|
92
|
-
const redundant = findRedundantDeps(project, graph);
|
|
93
|
-
allRedundant.push(...redundant);
|
|
94
|
-
}
|
|
95
|
-
if (allRedundant.length === 0) {
|
|
96
|
-
console.log('ā
No redundant transitive dependencies detected!');
|
|
97
|
-
console.log('\nš Graph is clean and minimal.');
|
|
98
|
-
return { success: true };
|
|
99
|
-
}
|
|
100
|
-
// Write documentation file
|
|
101
|
-
const workspaceRoot = context.root || process.cwd();
|
|
102
|
-
writeDocFile(workspaceRoot);
|
|
103
|
-
// Report violations
|
|
104
|
-
console.error('\nā Redundant transitive dependencies detected!\n');
|
|
105
|
-
console.error('ā ļø AI Agent: READ .webpieces/instruct-ai/webpieces.transitivedeps.md for fix instructions!\n');
|
|
106
|
-
for (const r of allRedundant) {
|
|
107
|
-
console.error(` ${r.project}:`);
|
|
108
|
-
console.error(` āā "${r.redundantDep}" is REDUNDANT`);
|
|
109
|
-
console.error(` (already brought in by "${r.alreadyBroughtInBy}")\n`);
|
|
110
|
-
}
|
|
111
|
-
console.error('\nTo fix:');
|
|
112
|
-
console.error(' 1. READ .webpieces/instruct-ai/webpieces.transitivedeps.md');
|
|
113
|
-
console.error(' 2. Remove redundant deps from project.json build.dependsOn');
|
|
114
|
-
console.error(' 3. Remove redundant deps from package.json dependencies');
|
|
115
|
-
console.error(' 4. Run: npx nx run architecture:generate');
|
|
116
|
-
console.error(' 5. Run: npm run build-all');
|
|
117
|
-
return { success: false };
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
const error = (0, toError_1.toError)(err);
|
|
121
|
-
console.error('ā Skip-level validation failed:', error.message);
|
|
122
|
-
return { success: false };
|
|
123
|
-
}
|
|
22
|
+
async function runExecutor(_options, _context) {
|
|
23
|
+
console.log('\nāļø validate-no-skiplevel-deps is retired (no-op).\n' +
|
|
24
|
+
' Superseded by auto-reduction in `architecture:generate`: the committed graph is\n' +
|
|
25
|
+
' transitively reduced, so skip-level edges cannot occur.\n');
|
|
26
|
+
return { success: true };
|
|
124
27
|
}
|
|
125
28
|
//# sourceMappingURL=executor.js.map
|
|
@@ -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":";AAAA
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-no-skiplevel-deps/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;AAYH,8BAUC;AAVc,KAAK,UAAU,WAAW,CACrC,QAAwC,EACxC,QAAyB;IAEzB,OAAO,CAAC,GAAG,CACP,wDAAwD;QACpD,sFAAsF;QACtF,8DAA8D,CACrE,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/**\n * Validate No Skip-Level Dependencies Executor ā RETIRED (no-op).\n *\n * Skip-level (redundant transitive) edges used to be an error here. That is no longer\n * meaningful: `architecture:generate` now derives the graph from nx's project graph and\n * applies transitive reduction, so the committed architecture/dependencies.json can never\n * contain a redundant edge. The check is superseded by auto-reduction.\n *\n * Worse, its old remediation told users to remove \"redundant\" deps from package.json ā\n * which is exactly the runtime-validity trap (a transitively-reachable package.json entry\n * can still be a real runtime dependency, e.g. a peerDependency or generated client).\n *\n * Defaults off (workspace.validations.noSkipLevelDeps=false). Kept as a no-op for one\n * release so any explicit enablement does not error on now-expected skip-level edges.\n *\n * Usage:\n * nx run architecture:validate-no-skiplevel-deps\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\n\nexport interface ValidateNoSkipLevelDepsOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: ValidateNoSkipLevelDepsOptions,\n _context: ExecutorContext\n): Promise<ExecutorResult> {\n console.log(\n '\\nāļø validate-no-skiplevel-deps is retired (no-op).\\n' +\n ' Superseded by auto-reduction in `architecture:generate`: the committed graph is\\n' +\n ' transitively reduced, so skip-level edges cannot occur.\\n'\n );\n return { success: true };\n}\n"]}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validate Nx Wiring Executor
|
|
3
3
|
*
|
|
4
|
-
* Enforces that the webpieces validators are actually wired into the build
|
|
5
|
-
* nx.json. The plugin auto-infers the validator targets (architecture:validate-complete,
|
|
6
|
-
* per-project validate-no-file-import-cycles), but the load-bearing connection that
|
|
7
|
-
* makes a build DEPEND on them lives in each repo's hand-edited nx.json:
|
|
4
|
+
* Enforces that the webpieces validators are actually wired into the build. Two checks:
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* 1. nx.json targetDefaults ā the compile executors (@nx/js:tsc, @angular/build:application)
|
|
7
|
+
* must carry the load-bearing dependsOn:
|
|
8
|
+
* ["architecture:validate-complete", "validate-no-file-import-cycles", "^build"]
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* 2. Per-project override guard ā a project that declares its OWN build.dependsOn OVERRIDES
|
|
11
|
+
* the targetDefaults entirely (nx does not merge dependsOn). That silently drops `^build`
|
|
12
|
+
* (ā upstream libs not built first, builds against an empty dist) AND the validator gates
|
|
13
|
+
* (ā the project builds green while validating nothing). So we check each compile project's
|
|
14
|
+
* RESOLVED build.dependsOn and fail if it is missing any required entry.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* A repo that uses neither has nothing to gate and passes.
|
|
16
|
+
* If either is stripped, validators exist but never run and the build stays green while
|
|
17
|
+
* validating nothing. This executor fails when the wiring is missing.
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Conservative by design: only REQUIRES wiring on compile executors actually in use
|
|
20
|
+
* (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.
|
|
21
|
+
*
|
|
22
|
+
* Disable via webpieces.config.json rules["nx-wiring"].mode="OFF".
|
|
22
23
|
*
|
|
23
24
|
* Usage: nx run architecture:validate-nx-wiring
|
|
24
25
|
*/
|
|
@@ -2,24 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Validate Nx Wiring Executor
|
|
4
4
|
*
|
|
5
|
-
* Enforces that the webpieces validators are actually wired into the build
|
|
6
|
-
* nx.json. The plugin auto-infers the validator targets (architecture:validate-complete,
|
|
7
|
-
* per-project validate-no-file-import-cycles), but the load-bearing connection that
|
|
8
|
-
* makes a build DEPEND on them lives in each repo's hand-edited nx.json:
|
|
5
|
+
* Enforces that the webpieces validators are actually wired into the build. Two checks:
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* 1. nx.json targetDefaults ā the compile executors (@nx/js:tsc, @angular/build:application)
|
|
8
|
+
* must carry the load-bearing dependsOn:
|
|
9
|
+
* ["architecture:validate-complete", "validate-no-file-import-cycles", "^build"]
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* 2. Per-project override guard ā a project that declares its OWN build.dependsOn OVERRIDES
|
|
12
|
+
* the targetDefaults entirely (nx does not merge dependsOn). That silently drops `^build`
|
|
13
|
+
* (ā upstream libs not built first, builds against an empty dist) AND the validator gates
|
|
14
|
+
* (ā the project builds green while validating nothing). So we check each compile project's
|
|
15
|
+
* RESOLVED build.dependsOn and fail if it is missing any required entry.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* A repo that uses neither has nothing to gate and passes.
|
|
17
|
+
* If either is stripped, validators exist but never run and the build stays green while
|
|
18
|
+
* validating nothing. This executor fails when the wiring is missing.
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* Conservative by design: only REQUIRES wiring on compile executors actually in use
|
|
21
|
+
* (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.
|
|
22
|
+
*
|
|
23
|
+
* Disable via webpieces.config.json rules["nx-wiring"].mode="OFF".
|
|
23
24
|
*
|
|
24
25
|
* Usage: nx run architecture:validate-nx-wiring
|
|
25
26
|
*/
|
|
@@ -34,6 +35,8 @@ const DEFAULT_REQUIRED_DEPS = [
|
|
|
34
35
|
'architecture:validate-complete',
|
|
35
36
|
'validate-no-file-import-cycles',
|
|
36
37
|
];
|
|
38
|
+
// Per-project build targets must ALSO keep `^build` so build order follows nx's full graph.
|
|
39
|
+
const BUILD_ORDER_DEP = '^build';
|
|
37
40
|
const DEFAULT_COMPILE_EXECUTORS = [
|
|
38
41
|
'@nx/js:tsc',
|
|
39
42
|
'@angular/build:application',
|
|
@@ -45,6 +48,14 @@ class WiringProblem {
|
|
|
45
48
|
this.found = found;
|
|
46
49
|
}
|
|
47
50
|
}
|
|
51
|
+
class ProjectGateProblem {
|
|
52
|
+
constructor(project, executorName, missing, found) {
|
|
53
|
+
this.project = project;
|
|
54
|
+
this.executorName = executorName;
|
|
55
|
+
this.missing = missing;
|
|
56
|
+
this.found = found;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
48
59
|
function readTargetDefaults(workspaceRoot) {
|
|
49
60
|
const nxJsonPath = path.join(workspaceRoot, 'nx.json');
|
|
50
61
|
if (!fs.existsSync(nxJsonPath))
|
|
@@ -62,9 +73,7 @@ function readTargetDefaults(workspaceRoot) {
|
|
|
62
73
|
return {};
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
|
-
|
|
66
|
-
const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
|
|
67
|
-
const projectsConfig = (0, devkit_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
|
|
76
|
+
function findCompileExecutorsInUse(projectsConfig, compileExecutors) {
|
|
68
77
|
const known = new Set(compileExecutors);
|
|
69
78
|
const inUse = new Set();
|
|
70
79
|
for (const cfg of Object.values(projectsConfig.projects)) {
|
|
@@ -90,8 +99,44 @@ function findProblems(relevantExecutors, targetDefaults, requiredDeps) {
|
|
|
90
99
|
}
|
|
91
100
|
return problems;
|
|
92
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Normalize an nx dependsOn entry to a comparable string. The string form ("^build",
|
|
104
|
+
* "validate-complete") passes through; the object form is rendered the same way
|
|
105
|
+
* ({target:"build", dependencies:true} ā "^build", {target:"build"} ā "build").
|
|
106
|
+
*/
|
|
107
|
+
function normalizeDeps(deps) {
|
|
108
|
+
return deps.map((dep) => {
|
|
109
|
+
if (typeof dep === 'string')
|
|
110
|
+
return dep;
|
|
111
|
+
const target = dep.target ?? '';
|
|
112
|
+
return dep.dependencies ? `^${target}` : target;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check each compile project's RESOLVED build.dependsOn (targetDefaults already merged in)
|
|
117
|
+
* for the required gates + `^build`. A project that overrode them away is flagged.
|
|
118
|
+
*/
|
|
119
|
+
function findProjectGateProblems(projectsConfig, compileExecutors, requiredPerProject) {
|
|
120
|
+
const problems = [];
|
|
121
|
+
for (const [name, cfg] of Object.entries(projectsConfig.projects)) {
|
|
122
|
+
// Tooling/foundation packages (type:tooling) BUILD the validators, so gating their own
|
|
123
|
+
// build on validate-complete would create a bootstrap cycle. They are exempt: they keep
|
|
124
|
+
// build-order ("^build") only. Application/library code is still fully gated.
|
|
125
|
+
if ((cfg.tags ?? []).includes('type:tooling'))
|
|
126
|
+
continue;
|
|
127
|
+
const build = cfg.targets?.['build'];
|
|
128
|
+
if (!build || !build.executor || !compileExecutors.has(build.executor))
|
|
129
|
+
continue;
|
|
130
|
+
const dependsOn = normalizeDeps(build.dependsOn ?? []);
|
|
131
|
+
const missing = requiredPerProject.filter((dep) => !dependsOn.includes(dep));
|
|
132
|
+
if (missing.length > 0) {
|
|
133
|
+
problems.push(new ProjectGateProblem(name, build.executor, missing, dependsOn));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return problems;
|
|
137
|
+
}
|
|
93
138
|
function reportFailure(problems, requiredDeps) {
|
|
94
|
-
console.error('\nā webpieces validators are not wired into your build.\n');
|
|
139
|
+
console.error('\nā webpieces validators are not wired into your build (nx.json).\n');
|
|
95
140
|
console.error('The validators exist but no build depends on them, so they never run.');
|
|
96
141
|
console.error('Add the missing dependsOn entries to nx.json targetDefaults:\n');
|
|
97
142
|
const depsList = requiredDeps.map((dep) => `"${dep}"`).join(', ');
|
|
@@ -102,10 +147,23 @@ function reportFailure(problems, requiredDeps) {
|
|
|
102
147
|
console.error(' }');
|
|
103
148
|
console.error(` missing: ${missingList}\n`);
|
|
104
149
|
}
|
|
105
|
-
console.error('To
|
|
106
|
-
console.error('webpieces.config.json
|
|
107
|
-
|
|
108
|
-
|
|
150
|
+
console.error('To turn this wiring check off, set rules["nx-wiring"].mode="OFF" in');
|
|
151
|
+
console.error('webpieces.config.json.\n');
|
|
152
|
+
}
|
|
153
|
+
function reportProjectGateFailure(problems) {
|
|
154
|
+
console.error('\nā Some projects override away the build gates in their project.json.\n');
|
|
155
|
+
console.error('A project-level build.dependsOn OVERRIDES nx.json targetDefaults (nx does not');
|
|
156
|
+
console.error('merge dependsOn). That silently drops "^build" (upstreams not built first ā');
|
|
157
|
+
console.error('builds against an empty dist) and/or the validators (build stays green while');
|
|
158
|
+
console.error('validating nothing).\n');
|
|
159
|
+
console.error('Fix: remove build.dependsOn from these project.json files so targetDefaults');
|
|
160
|
+
console.error('apply, OR include all required entries explicitly:\n');
|
|
161
|
+
for (const p of problems) {
|
|
162
|
+
const missingList = p.missing.map((dep) => `"${dep}"`).join(', ');
|
|
163
|
+
console.error(` ${p.project} (${p.executorName}):`);
|
|
164
|
+
console.error(` resolved build.dependsOn: ${JSON.stringify(p.found)}`);
|
|
165
|
+
console.error(` missing: ${missingList}\n`);
|
|
166
|
+
}
|
|
109
167
|
}
|
|
110
168
|
async function runExecutor(options, context) {
|
|
111
169
|
const shared = (0, rules_config_1.loadConfig)(context.root);
|
|
@@ -117,19 +175,28 @@ async function runExecutor(options, context) {
|
|
|
117
175
|
const requiredDeps = options.requiredDeps ?? DEFAULT_REQUIRED_DEPS;
|
|
118
176
|
const compileExecutors = options.compileExecutors ?? DEFAULT_COMPILE_EXECUTORS;
|
|
119
177
|
console.log('\nš Validating webpieces validators are wired into the build\n');
|
|
120
|
-
const
|
|
178
|
+
const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
|
|
179
|
+
const projectsConfig = (0, devkit_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
|
|
180
|
+
const inUse = findCompileExecutorsInUse(projectsConfig, compileExecutors);
|
|
121
181
|
const relevantExecutors = compileExecutors.filter((executorName) => inUse.has(executorName));
|
|
122
182
|
if (relevantExecutors.length === 0) {
|
|
123
183
|
console.log('ā
No known compile executors in use ā nothing to gate\n');
|
|
124
184
|
return { success: true };
|
|
125
185
|
}
|
|
126
186
|
const targetDefaults = readTargetDefaults(context.root);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
187
|
+
const wiringProblems = findProblems(relevantExecutors, targetDefaults, requiredDeps);
|
|
188
|
+
const gateProblems = findProjectGateProblems(projectsConfig, inUse, [
|
|
189
|
+
...requiredDeps,
|
|
190
|
+
BUILD_ORDER_DEP,
|
|
191
|
+
]);
|
|
192
|
+
if (wiringProblems.length === 0 && gateProblems.length === 0) {
|
|
193
|
+
console.log('ā
Validators are wired into the build (targetDefaults + every project)\n');
|
|
130
194
|
return { success: true };
|
|
131
195
|
}
|
|
132
|
-
|
|
196
|
+
if (wiringProblems.length > 0)
|
|
197
|
+
reportFailure(wiringProblems, requiredDeps);
|
|
198
|
+
if (gateProblems.length > 0)
|
|
199
|
+
reportProjectGateFailure(gateProblems);
|
|
133
200
|
return { success: false };
|
|
134
201
|
}
|
|
135
202
|
//# sourceMappingURL=executor.js.map
|