@webpieces/nx-webpieces-rules 0.4.970 → 0.4.972
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/README.md +8 -8
- package/executors.json +1 -6
- package/package.json +7 -7
- package/src/executors/help/executor.js +1 -3
- package/src/executors/help/executor.js.map +1 -1
- package/src/executors/visualize-runtime/executor.d.ts +22 -3
- package/src/executors/visualize-runtime/executor.js +55 -8
- package/src/executors/visualize-runtime/executor.js.map +1 -1
- package/src/executors/visualize-runtime/schema.json +7 -2
- package/src/lib/graph-color-modes.d.ts +10 -0
- package/src/lib/graph-color-modes.js +16 -1
- package/src/lib/graph-color-modes.js.map +1 -1
- package/src/lib/graph-mode-state.client.d.ts +6 -1
- package/src/lib/graph-mode-state.client.js +9 -1
- package/src/lib/graph-mode-state.client.js.map +1 -1
- package/src/lib/graph-page-shell.d.ts +10 -2
- package/src/lib/graph-page-shell.js +20 -8
- package/src/lib/graph-page-shell.js.map +1 -1
- package/src/lib/graph-render-model.d.ts +10 -0
- package/src/lib/graph-render-model.js +7 -0
- package/src/lib/graph-render-model.js.map +1 -1
- package/src/lib/graph-visualizer.client.js +2 -2
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/runtime-edge-merge.d.ts +18 -0
- package/src/lib/runtime-edge-merge.js +38 -0
- package/src/lib/runtime-edge-merge.js.map +1 -0
- package/src/lib/runtime-goal-comparison.js +1 -1
- package/src/lib/runtime-goal-comparison.js.map +1 -1
- package/src/lib/runtime-goal-file.d.ts +6 -11
- package/src/lib/runtime-goal-file.js +10 -23
- package/src/lib/runtime-goal-file.js.map +1 -1
- package/src/lib/runtime-goal-validator.d.ts +4 -4
- package/src/lib/runtime-goal-validator.js +4 -4
- package/src/lib/runtime-goal-validator.js.map +1 -1
- package/src/lib/runtime-goal-view.d.ts +18 -11
- package/src/lib/runtime-goal-view.js +27 -27
- package/src/lib/runtime-goal-view.js.map +1 -1
- package/src/lib/runtime-html-page.d.ts +2 -0
- package/src/lib/runtime-html-page.js +8 -2
- package/src/lib/runtime-html-page.js.map +1 -1
- package/src/lib/runtime-union-graph.d.ts +0 -2
- package/src/lib/runtime-union-graph.js +3 -19
- package/src/lib/runtime-union-graph.js.map +1 -1
- package/src/lib/runtime-visualizer.d.ts +2 -3
- package/src/lib/runtime-visualizer.js +3 -4
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/runtime-viz-options.d.ts +3 -5
- package/src/lib/runtime-viz-options.js +3 -7
- package/src/lib/runtime-viz-options.js.map +1 -1
- package/src/plugin.js +0 -1
- package/src/plugin.js.map +1 -1
- package/src/runtime-targets.d.ts +0 -6
- package/src/runtime-targets.js +1 -18
- package/src/runtime-targets.js.map +1 -1
- package/src/executors/visualize-runtime-goal/executor.d.ts +0 -27
- package/src/executors/visualize-runtime-goal/executor.js +0 -99
- package/src/executors/visualize-runtime-goal/executor.js.map +0 -1
- package/src/executors/visualize-runtime-goal/schema.json +0 -13
package/README.md
CHANGED
|
@@ -107,14 +107,14 @@ their existing checks independently of visualization.
|
|
|
107
107
|
|
|
108
108
|
### Runtime goals (proposals)
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
`architecture
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`cp architecture/runtime-dependencies.json architecture/runtime-dependencies-goal.json
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
When the hand-authored `architecture/runtime-dependencies-goal.json` exists,
|
|
111
|
+
`pnpm nx run architecture:visualize-runtime` adds Color by → **Goal vs Current**, a
|
|
112
|
+
semantic diff of the two saved files. Without one, that menu item is dimmed and reads
|
|
113
|
+
"runtime-dependencies-goal.json does not exist" (its tooltip says how to start one:
|
|
114
|
+
`cp architecture/runtime-dependencies.json architecture/runtime-dependencies-goal.json`).
|
|
115
|
+
Viewing never writes either JSON file; `--prepare-goal` explicitly recomputes and saves
|
|
116
|
+
the goal's derived levels first. Details, comparison policy and the edit workflow:
|
|
117
|
+
`docs/architecture/runtime-goal.md`.
|
|
118
118
|
|
|
119
119
|
### Color modes and the page shell
|
|
120
120
|
|
package/executors.json
CHANGED
|
@@ -13,12 +13,7 @@
|
|
|
13
13
|
"visualize-runtime": {
|
|
14
14
|
"implementation": "./src/executors/visualize-runtime/executor",
|
|
15
15
|
"schema": "./src/executors/visualize-runtime/schema.json",
|
|
16
|
-
"description": "View saved runtime architecture without refreshing; refresh: pnpm nx run architecture:generate"
|
|
17
|
-
},
|
|
18
|
-
"visualize-runtime-goal": {
|
|
19
|
-
"implementation": "./src/executors/visualize-runtime-goal/executor",
|
|
20
|
-
"schema": "./src/executors/visualize-runtime-goal/schema.json",
|
|
21
|
-
"description": "View the hand-authored runtime goal (architecture/runtime-dependencies-goal.json) with Goal vs Current; --prepare recomputes its derived levels"
|
|
16
|
+
"description": "View saved runtime architecture (with Goal vs Current when architecture/runtime-dependencies-goal.json exists) without refreshing; refresh: pnpm nx run architecture:generate. --prepare-goal recomputes and saves the goal's derived levels first"
|
|
22
17
|
},
|
|
23
18
|
"validate-runtime-architecture": {
|
|
24
19
|
"implementation": "./src/executors/validate-runtime-architecture/executor",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.972",
|
|
4
4
|
"description": "Nx-specific webpieces validation rules, graph tooling, executors, and inference plugin.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@webpieces/api-doc-model": "0.4.
|
|
24
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
-
"@webpieces/eslint-rules": "0.4.
|
|
26
|
-
"@webpieces/rules-config": "0.4.
|
|
23
|
+
"@webpieces/api-doc-model": "0.4.972",
|
|
24
|
+
"@webpieces/core-util": "0.4.972",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.972",
|
|
26
|
+
"@webpieces/rules-config": "0.4.972",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.972"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@nx/devkit": ">=18.0.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@webpieces/tooling-testkit": "0.4.
|
|
44
|
+
"@webpieces/tooling-testkit": "0.4.972"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -15,7 +15,6 @@ async function helpExecutor(options, context) {
|
|
|
15
15
|
console.log(' npm run arch:generate # Explicitly refresh architecture/API/runtime facts');
|
|
16
16
|
console.log(' npm run arch:visualize # View saved snapshot (does not refresh)');
|
|
17
17
|
console.log(' npm run arch:visualize-runtime # View saved runtime snapshot (does not refresh)');
|
|
18
|
-
console.log(' npm run arch:visualize-runtime-goal # View the runtime goal proposal + Goal vs Current (never generates)');
|
|
19
18
|
console.log('');
|
|
20
19
|
console.log(' Validation:');
|
|
21
20
|
console.log(' npm run arch:validate # Quick validation (no-cycles)');
|
|
@@ -29,8 +28,7 @@ async function helpExecutor(options, context) {
|
|
|
29
28
|
console.log(' Workspace-level architecture validation:');
|
|
30
29
|
console.log(' nx run architecture:generate # Explicitly refresh architecture/API/runtime facts');
|
|
31
30
|
console.log(' nx run architecture:visualize # View saved snapshot (does not refresh)');
|
|
32
|
-
console.log(' nx run architecture:visualize-runtime # View saved runtime snapshot (
|
|
33
|
-
console.log(' nx run architecture:visualize-runtime-goal # View the runtime goal proposal (--prepare recomputes its levels)');
|
|
31
|
+
console.log(' nx run architecture:visualize-runtime # View saved runtime snapshot (+ Goal vs Current; --prepare-goal recomputes the goal levels)');
|
|
34
32
|
console.log(' nx run architecture:validate-no-architecture-cycles # Check for circular project dependencies');
|
|
35
33
|
console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');
|
|
36
34
|
console.log('');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/help/executor.ts"],"names":[],"mappings":";;AAMA,+
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/help/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExecutorContext } from '@nx/devkit';\n\nexport interface HelpExecutorOptions {}\n\ntype HelpExecutorResult = { success: true };\n\nexport default async function helpExecutor(\n options: HelpExecutorOptions,\n context: ExecutorContext\n): Promise<HelpExecutorResult> {\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/nx-webpieces-rules - 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 # Explicitly refresh architecture/API/runtime facts');\n console.log(' npm run arch:visualize # View saved snapshot (does not refresh)');\n console.log(' npm run arch:visualize-runtime # View saved runtime snapshot (does not refresh)');\n console.log('');\n console.log(' Validation:');\n console.log(' npm run arch:validate # Quick validation (no-cycles)');\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 # Explicitly refresh architecture/API/runtime facts');\n console.log(' nx run architecture:visualize # View saved snapshot (does not refresh)');\n console.log(' nx run architecture:visualize-runtime # View saved runtime snapshot (+ Goal vs Current; --prepare-goal recomputes the goal levels)');\n console.log(' nx run architecture:validate-no-architecture-cycles # Check for circular project dependencies');\n console.log(' nx run architecture:validate-architecture-unchanged # Validate against blessed graph');\n console.log('');\n console.log(' Per-project file import cycle checking:');\n console.log(' nx run <project>:validate-no-file-import-cycles # Check project for file import cycles');\n console.log(' nx affected --target=validate-no-file-import-cycles # Check all affected projects');\n console.log(' nx run-many --target=validate-no-file-import-cycles --all # Check all projects');\n console.log('');\n console.log(' Per-project CI target (lint + build + test):');\n console.log(' nx run <project>:ci # Run lint, build, test together');\n console.log(' nx run-many --target=ci --all # Run ci for all projects');\n console.log('');\n console.log(' Execution order (test waits for build via targetDefaults):');\n console.log(' ci (nx:noop)');\n console.log(' ├── lint ─────────────────┐');\n console.log(' ├── build ────────────────┼── run in parallel');\n console.log(' └── test ─────────────────┘');\n console.log(' └── depends on build (waits)');\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"]}
|
|
@@ -4,14 +4,33 @@
|
|
|
4
4
|
* Renders the runtime microservice graph (committed
|
|
5
5
|
* architecture/runtime-dependencies.json) to DOT + HTML and opens it. When a runtime goal
|
|
6
6
|
* (architecture/runtime-dependencies-goal.json, #1239) is present, the page also carries the
|
|
7
|
-
* Goal vs Current comparison; when it is absent or unusable,
|
|
7
|
+
* Goal vs Current comparison; when it is absent or unusable, that Color-by mode is dimmed and says
|
|
8
|
+
* why instead (#1373).
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Viewing never writes either JSON file. When the goal's saved derived facts disagree with its
|
|
11
|
+
* relationships, viewing warns and compares the goal as saved. `--prepare-goal` is the one explicit,
|
|
12
|
+
* WRITING step: it recomputes the goal's derived levels, self-queue flags, contract queues and
|
|
13
|
+
* maxLevel with the generator's own level machinery, saves the goal, and then views.
|
|
14
|
+
*
|
|
15
|
+
* Usage: nx run architecture:visualize-runtime [--prepare-goal]
|
|
10
16
|
*/
|
|
11
17
|
import type { ExecutorContext } from '@nx/devkit';
|
|
18
|
+
import { RuntimeGoalFile } from '../../lib/runtime-goal-file';
|
|
19
|
+
import type { RuntimeGraph } from '../../lib/runtime-graph-model';
|
|
12
20
|
export interface VisualizeRuntimeOptions {
|
|
21
|
+
/** Recompute and SAVE the runtime goal's derived facts before viewing. Viewing alone never writes. */
|
|
22
|
+
prepareGoal?: boolean;
|
|
13
23
|
}
|
|
14
24
|
export interface ExecutorResult {
|
|
15
25
|
success: boolean;
|
|
16
26
|
}
|
|
17
|
-
|
|
27
|
+
/** The goal half of the runtime view: the explicit `--prepare-goal` write, and the read-only drift warning. */
|
|
28
|
+
export declare class RuntimeGoalStep {
|
|
29
|
+
private readonly goalFile;
|
|
30
|
+
constructor(goalFile?: RuntimeGoalFile);
|
|
31
|
+
/** Recompute and save the goal's derived facts; a missing or broken goal throws naming the goal path. */
|
|
32
|
+
prepare(workspaceRoot: string): RuntimeGraph;
|
|
33
|
+
/** Warn, never write, when the saved derived facts disagree with the goal's relationships. */
|
|
34
|
+
reportDrift(goal: RuntimeGraph): void;
|
|
35
|
+
}
|
|
36
|
+
export default function runExecutor(options: VisualizeRuntimeOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -5,11 +5,18 @@
|
|
|
5
5
|
* Renders the runtime microservice graph (committed
|
|
6
6
|
* architecture/runtime-dependencies.json) to DOT + HTML and opens it. When a runtime goal
|
|
7
7
|
* (architecture/runtime-dependencies-goal.json, #1239) is present, the page also carries the
|
|
8
|
-
* Goal vs Current comparison; when it is absent or unusable,
|
|
8
|
+
* Goal vs Current comparison; when it is absent or unusable, that Color-by mode is dimmed and says
|
|
9
|
+
* why instead (#1373).
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
+
* Viewing never writes either JSON file. When the goal's saved derived facts disagree with its
|
|
12
|
+
* relationships, viewing warns and compares the goal as saved. `--prepare-goal` is the one explicit,
|
|
13
|
+
* WRITING step: it recomputes the goal's derived levels, self-queue flags, contract queues and
|
|
14
|
+
* maxLevel with the generator's own level machinery, saves the goal, and then views.
|
|
15
|
+
*
|
|
16
|
+
* Usage: nx run architecture:visualize-runtime [--prepare-goal]
|
|
11
17
|
*/
|
|
12
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.RuntimeGoalStep = void 0;
|
|
13
20
|
exports.default = runExecutor;
|
|
14
21
|
const tslib_1 = require("tslib");
|
|
15
22
|
const fs = tslib_1.__importStar(require("fs"));
|
|
@@ -23,22 +30,62 @@ const runtime_visualizer_1 = require("../../lib/runtime-visualizer");
|
|
|
23
30
|
const runtime_config_1 = require("../../lib/runtime-config");
|
|
24
31
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
25
32
|
const runtime_goal_view_1 = require("../../lib/runtime-goal-view");
|
|
33
|
+
const runtime_goal_file_1 = require("../../lib/runtime-goal-file");
|
|
26
34
|
const toError_1 = require("../../toError");
|
|
27
|
-
|
|
35
|
+
/** The goal half of the runtime view: the explicit `--prepare-goal` write, and the read-only drift warning. */
|
|
36
|
+
class RuntimeGoalStep {
|
|
37
|
+
goalFile;
|
|
38
|
+
constructor(goalFile = new runtime_goal_file_1.RuntimeGoalFile()) {
|
|
39
|
+
this.goalFile = goalFile;
|
|
40
|
+
}
|
|
41
|
+
/** Recompute and save the goal's derived facts; a missing or broken goal throws naming the goal path. */
|
|
42
|
+
prepare(workspaceRoot) {
|
|
43
|
+
const preparation = this.goalFile.prepare(workspaceRoot);
|
|
44
|
+
const goalPath = this.goalFile.goalPath;
|
|
45
|
+
if (!preparation.written)
|
|
46
|
+
console.log(`✅ ${goalPath} derived facts already consistent; nothing written.`);
|
|
47
|
+
else if (preparation.changes.length === 0)
|
|
48
|
+
console.log(`✍️ Prepared ${goalPath}: rewrote explanatory levelBecause text only.`);
|
|
49
|
+
else
|
|
50
|
+
console.log(`✍️ Prepared ${goalPath}:\n ${preparation.changes.join('\n ')}`);
|
|
51
|
+
return preparation.graph;
|
|
52
|
+
}
|
|
53
|
+
/** Warn, never write, when the saved derived facts disagree with the goal's relationships. */
|
|
54
|
+
reportDrift(goal) {
|
|
55
|
+
const drift = this.goalFile.drift(goal);
|
|
56
|
+
if (drift.cycle !== null) {
|
|
57
|
+
console.warn(`⚠️ ${drift.cycle}\n Compared with its saved levels. ${this.goalFile.cycleOption().text}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (drift.differences.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
console.warn(`⚠️ ${this.goalFile.goalPath} saves derived facts that disagree with its relationships (compared as saved):\n ` +
|
|
63
|
+
`${drift.differences.join('\n ')}\n Recompute them explicitly: ${runtime_goal_file_1.RUNTIME_GOAL_PREPARE_COMMAND}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.RuntimeGoalStep = RuntimeGoalStep;
|
|
67
|
+
// webpieces-disable no-function-outside-class -- Nx executor entry point: Nx requires a default-exported function
|
|
68
|
+
async function runExecutor(options, context) {
|
|
28
69
|
const workspaceRoot = context.root;
|
|
29
70
|
const snapshot = new saved_snapshot_1.SavedSnapshot();
|
|
71
|
+
const goalStep = new RuntimeGoalStep();
|
|
30
72
|
console.log('\n🎨 Runtime Microservice Visualization\n');
|
|
31
73
|
console.log(snapshot.message());
|
|
32
74
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx
|
|
33
75
|
try {
|
|
76
|
+
// The explicit write runs first, so a goal that cannot be prepared fails the command and changes no file.
|
|
77
|
+
const prepared = options.prepareGoal === true ? goalStep.prepare(workspaceRoot) : null;
|
|
34
78
|
const graph = snapshot.loadRuntime(workspaceRoot);
|
|
35
79
|
const config = (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot);
|
|
36
80
|
const projects = fs.existsSync(path.join(workspaceRoot, graph_loader_1.DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, graph_loader_1.DEFAULT_GRAPH_PATH).projects : null;
|
|
37
|
-
const
|
|
38
|
-
const inputs = new runtime_goal_view_1.RuntimeComparisonInputs(
|
|
39
|
-
const
|
|
81
|
+
const vizOptions = new runtime_visualizer_1.RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot, runtime_visualizer_1.RuntimePageKind.CURRENT);
|
|
82
|
+
const inputs = new runtime_goal_view_1.RuntimeComparisonInputs();
|
|
83
|
+
const goal = prepared ?? inputs.goal(workspaceRoot);
|
|
84
|
+
if (!(goal instanceof runtime_goal_view_1.RuntimeComparisonGap))
|
|
85
|
+
goalStep.reportDrift(goal);
|
|
86
|
+
const comparison = inputs.view(graph, goal, vizOptions);
|
|
40
87
|
console.log(comparison.available ? `Goal vs Current: ${comparison.summary}` : `Goal vs Current unavailable: ${comparison.reason}`);
|
|
41
|
-
const vizPaths = (0, runtime_visualizer_1.writeRuntimeVisualization)(graph, workspaceRoot, 'WebPieces Runtime Architecture',
|
|
88
|
+
const vizPaths = (0, runtime_visualizer_1.writeRuntimeVisualization)(graph, workspaceRoot, 'WebPieces Runtime Architecture', vizOptions, comparison);
|
|
42
89
|
console.log(`✅ Generated: ${vizPaths.dotPath}`);
|
|
43
90
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
44
91
|
if (projects !== null)
|
|
@@ -59,7 +106,7 @@ async function runExecutor(_options, context) {
|
|
|
59
106
|
catch (err) {
|
|
60
107
|
const error = (0, toError_1.toError)(err);
|
|
61
108
|
const rendered = error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message;
|
|
62
|
-
console.error('❌ Runtime visualization failed
|
|
109
|
+
console.error('❌ Runtime visualization failed:', rendered);
|
|
63
110
|
return { success: false };
|
|
64
111
|
}
|
|
65
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Visualize Runtime Executor\n *\n * Renders the runtime microservice graph (committed\n * architecture/runtime-dependencies.json) to DOT + HTML and opens it. When a runtime goal\n * (architecture/runtime-dependencies-goal.json, #1239) is present, the page also carries the\n * Goal vs Current comparison; when it is absent or unusable, the page says why instead.\n *\n * Usage: nx run architecture:visualize-runtime\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ImpactRefresh, ImpactSidecar, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport { LiveGraphSource } from '../../lib/graph-edge-diff';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { writeRuntimeVisualization, RuntimeVizOptions, RuntimePageKind } from '../../lib/runtime-visualizer';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { RuntimeComparisonInputs } from '../../lib/runtime-goal-view';\nimport { toError } from '../../toError';\n\nexport interface VisualizeRuntimeOptions {\n // No options.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: VisualizeRuntimeOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n\n console.log('\\n🎨 Runtime Microservice Visualization\\n');\n console.log(snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n const graph = snapshot.loadRuntime(workspaceRoot);\n const config = loadRuntimeConfig(workspaceRoot);\n const projects = fs.existsSync(path.join(workspaceRoot, DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, DEFAULT_GRAPH_PATH).projects : null;\n const options = new RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot, RuntimePageKind.CURRENT);\n const inputs = new RuntimeComparisonInputs(snapshot);\n const comparison = inputs.view(graph, inputs.goal(workspaceRoot), options);\n console.log(comparison.available ? `Goal vs Current: ${comparison.summary}` : `Goal vs Current unavailable: ${comparison.reason}`);\n const vizPaths = writeRuntimeVisualization(graph, workspaceRoot, 'WebPieces Runtime Architecture', options, comparison);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n if (projects !== null) console.log(await new ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, VISUALIZE_IMPACT_KINDS, new LiveGraphSource()));\n else { new ImpactSidecar().remove(path.dirname(vizPaths.htmlPath)); console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.'); }\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (new GraphVisualizer().openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Runtime visualization failed for architecture/runtime-dependencies.json:', rendered);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Visualize Runtime Executor\n *\n * Renders the runtime microservice graph (committed\n * architecture/runtime-dependencies.json) to DOT + HTML and opens it. When a runtime goal\n * (architecture/runtime-dependencies-goal.json, #1239) is present, the page also carries the\n * Goal vs Current comparison; when it is absent or unusable, that Color-by mode is dimmed and says\n * why instead (#1373).\n *\n * Viewing never writes either JSON file. When the goal's saved derived facts disagree with its\n * relationships, viewing warns and compares the goal as saved. `--prepare-goal` is the one explicit,\n * WRITING step: it recomputes the goal's derived levels, self-queue flags, contract queues and\n * maxLevel with the generator's own level machinery, saves the goal, and then views.\n *\n * Usage: nx run architecture:visualize-runtime [--prepare-goal]\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ImpactRefresh, ImpactSidecar, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport { LiveGraphSource } from '../../lib/graph-edge-diff';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { writeRuntimeVisualization, RuntimeVizOptions, RuntimePageKind } from '../../lib/runtime-visualizer';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { RuntimeComparisonGap, RuntimeComparisonInputs } from '../../lib/runtime-goal-view';\nimport { RuntimeGoalFile, RUNTIME_GOAL_PREPARE_COMMAND } from '../../lib/runtime-goal-file';\nimport type { RuntimeGraph } from '../../lib/runtime-graph-model';\nimport { toError } from '../../toError';\n\nexport interface VisualizeRuntimeOptions {\n /** Recompute and SAVE the runtime goal's derived facts before viewing. Viewing alone never writes. */\n prepareGoal?: boolean;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/** The goal half of the runtime view: the explicit `--prepare-goal` write, and the read-only drift warning. */\nexport class RuntimeGoalStep {\n constructor(private readonly goalFile: RuntimeGoalFile = new RuntimeGoalFile()) {}\n\n /** Recompute and save the goal's derived facts; a missing or broken goal throws naming the goal path. */\n prepare(workspaceRoot: string): RuntimeGraph {\n const preparation = this.goalFile.prepare(workspaceRoot);\n const goalPath = this.goalFile.goalPath;\n if (!preparation.written) console.log(`✅ ${goalPath} derived facts already consistent; nothing written.`);\n else if (preparation.changes.length === 0) console.log(`✍️ Prepared ${goalPath}: rewrote explanatory levelBecause text only.`);\n else console.log(`✍️ Prepared ${goalPath}:\\n ${preparation.changes.join('\\n ')}`);\n return preparation.graph;\n }\n\n /** Warn, never write, when the saved derived facts disagree with the goal's relationships. */\n reportDrift(goal: RuntimeGraph): void {\n const drift = this.goalFile.drift(goal);\n if (drift.cycle !== null) {\n console.warn(`⚠️ ${drift.cycle}\\n Compared with its saved levels. ${this.goalFile.cycleOption().text}`);\n return;\n }\n if (drift.differences.length === 0) return;\n console.warn(\n `⚠️ ${this.goalFile.goalPath} saves derived facts that disagree with its relationships (compared as saved):\\n ` +\n `${drift.differences.join('\\n ')}\\n Recompute them explicitly: ${RUNTIME_GOAL_PREPARE_COMMAND}`,\n );\n }\n}\n\n// webpieces-disable no-function-outside-class -- Nx executor entry point: Nx requires a default-exported function\nexport default async function runExecutor(\n options: VisualizeRuntimeOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n const goalStep = new RuntimeGoalStep();\n\n console.log('\\n🎨 Runtime Microservice Visualization\\n');\n console.log(snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n // The explicit write runs first, so a goal that cannot be prepared fails the command and changes no file.\n const prepared = options.prepareGoal === true ? goalStep.prepare(workspaceRoot) : null;\n const graph = snapshot.loadRuntime(workspaceRoot);\n const config = loadRuntimeConfig(workspaceRoot);\n const projects = fs.existsSync(path.join(workspaceRoot, DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, DEFAULT_GRAPH_PATH).projects : null;\n const vizOptions = new RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot, RuntimePageKind.CURRENT);\n const inputs = new RuntimeComparisonInputs();\n const goal = prepared ?? inputs.goal(workspaceRoot);\n if (!(goal instanceof RuntimeComparisonGap)) goalStep.reportDrift(goal);\n const comparison = inputs.view(graph, goal, vizOptions);\n console.log(comparison.available ? `Goal vs Current: ${comparison.summary}` : `Goal vs Current unavailable: ${comparison.reason}`);\n const vizPaths = writeRuntimeVisualization(graph, workspaceRoot, 'WebPieces Runtime Architecture', vizOptions, comparison);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n if (projects !== null) console.log(await new ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, VISUALIZE_IMPACT_KINDS, new LiveGraphSource()));\n else { new ImpactSidecar().remove(path.dirname(vizPaths.htmlPath)); console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.'); }\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (new GraphVisualizer().openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Runtime visualization failed:', rendered);\n return { success: false };\n }\n}\n"]}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/schema",
|
|
3
3
|
"title": "Visualize Runtime Executor",
|
|
4
|
-
"description": "View the saved runtime snapshot as DOT + HTML without refreshing. Refresh: pnpm nx run architecture:generate.",
|
|
4
|
+
"description": "View the saved runtime snapshot as DOT + HTML without refreshing, with Goal vs Current when architecture/runtime-dependencies-goal.json exists. Refresh: pnpm nx run architecture:generate. Viewing never writes; --prepare-goal explicitly recomputes the goal's derived levels first.",
|
|
5
5
|
"type": "object",
|
|
6
|
-
"properties": {
|
|
6
|
+
"properties": {
|
|
7
|
+
"prepareGoal": {
|
|
8
|
+
"type": "boolean",
|
|
9
|
+
"description": "Recompute and save architecture/runtime-dependencies-goal.json's derived levels, self-queue flags, contract queues and maxLevel before viewing."
|
|
10
|
+
}
|
|
11
|
+
},
|
|
7
12
|
"required": []
|
|
8
13
|
}
|
|
@@ -54,6 +54,16 @@ export declare class ModeInfo {
|
|
|
54
54
|
readonly subtitle: string;
|
|
55
55
|
constructor(mode: GraphMode, name: string, subtitle: string);
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* A mode the page must render DIMMED and unselectable (#1373): `reason` is the item's tooltip and
|
|
59
|
+
* `note` replaces its grey subtitle, e.g. "runtime-dependencies-goal.json does not exist".
|
|
60
|
+
*/
|
|
61
|
+
export declare class ModeUnavailable {
|
|
62
|
+
readonly mode: GraphMode;
|
|
63
|
+
readonly reason: string;
|
|
64
|
+
readonly note: string;
|
|
65
|
+
constructor(mode: GraphMode, reason: string, note: string);
|
|
66
|
+
}
|
|
57
67
|
/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */
|
|
58
68
|
export declare const GRAPH_MODES: readonly ModeInfo[];
|
|
59
69
|
/**
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* other products' boxes; this mode keeps every box and makes ownership visible.
|
|
39
39
|
*/
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
-
exports.NodeModeStyler = exports.LegendFacts = exports.ProductCount = exports.NodeModeDots = exports.NodeFacts = exports.IMPACT_UNTOUCHED = exports.IMPACT_BUILD_INPUT_BORDER = exports.IMPACT_BUILD_INPUT = exports.IMPACT_UNEXPLAINED_BORDER = exports.IMPACT_ROOT_FILE_BORDER = exports.IMPACT_ROOT_FILE = exports.IMPACT_IMPLICIT_BORDER = exports.IMPACT_IMPLICIT_GAP = exports.IMPACT_IMPLICIT = exports.IMPACT_DEPENDENT = exports.IMPACT_TOUCHED = exports.ROLE_STYLES = exports.RoleStyle = exports.STRIPED_CLASS = exports.NO_RUNTIME_COLOR = exports.FRAMEWORK_ORDER = exports.BASE_ORDER = exports.FRAMEWORK_STYLES = exports.FrameworkStyle = exports.LIGHT_TEXT = exports.DARK_TEXT = exports.RUNTIME_GRAPH_MODES = exports.GRAPH_MODES = exports.ModeInfo = exports.GraphMode = void 0;
|
|
41
|
+
exports.NodeModeStyler = exports.LegendFacts = exports.ProductCount = exports.NodeModeDots = exports.NodeFacts = exports.IMPACT_UNTOUCHED = exports.IMPACT_BUILD_INPUT_BORDER = exports.IMPACT_BUILD_INPUT = exports.IMPACT_UNEXPLAINED_BORDER = exports.IMPACT_ROOT_FILE_BORDER = exports.IMPACT_ROOT_FILE = exports.IMPACT_IMPLICIT_BORDER = exports.IMPACT_IMPLICIT_GAP = exports.IMPACT_IMPLICIT = exports.IMPACT_DEPENDENT = exports.IMPACT_TOUCHED = exports.ROLE_STYLES = exports.RoleStyle = exports.STRIPED_CLASS = exports.NO_RUNTIME_COLOR = exports.FRAMEWORK_ORDER = exports.BASE_ORDER = exports.FRAMEWORK_STYLES = exports.FrameworkStyle = exports.LIGHT_TEXT = exports.DARK_TEXT = exports.RUNTIME_GRAPH_MODES = exports.GRAPH_MODES = exports.ModeUnavailable = exports.ModeInfo = exports.GraphMode = void 0;
|
|
42
42
|
const rules_sdk_1 = require("@webpieces/rules-sdk");
|
|
43
43
|
const dot_syntax_1 = require("./dot-syntax");
|
|
44
44
|
const graph_products_1 = require("./graph-products");
|
|
@@ -63,6 +63,21 @@ class ModeInfo {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
exports.ModeInfo = ModeInfo;
|
|
66
|
+
/**
|
|
67
|
+
* A mode the page must render DIMMED and unselectable (#1373): `reason` is the item's tooltip and
|
|
68
|
+
* `note` replaces its grey subtitle, e.g. "runtime-dependencies-goal.json does not exist".
|
|
69
|
+
*/
|
|
70
|
+
class ModeUnavailable {
|
|
71
|
+
mode;
|
|
72
|
+
reason;
|
|
73
|
+
note;
|
|
74
|
+
constructor(mode, reason, note) {
|
|
75
|
+
this.mode = mode;
|
|
76
|
+
this.reason = reason;
|
|
77
|
+
this.note = note;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.ModeUnavailable = ModeUnavailable;
|
|
66
81
|
/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */
|
|
67
82
|
exports.GRAPH_MODES = [
|
|
68
83
|
new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * The affected set is split by WHY nx included it (#1264, graph-impact-reasons.ts):\n * owning a changed file is solid amber (\"changed\"); reached over dependency edges\n * from one is light amber with an amber border (\"dependent\"); a `{workspaceRoot}`\n * input glob match is violet hatching (\"implicitly touched\"); a lockfile /\n * package.json / tsconfig.base.json / nx.json change is teal (\"root-file touched\");\n * anything nothing could explain is light amber with a dashed grey border\n * (\"unexplained\" — never drawn as a dependent). Every transitive dependency of the\n * affected set that is not itself affected is light slate with a dashed border\n * (\"build input\": compiled or restored from cache, unchanged); everything else is\n * grey. Which box is which is only known per checkout, so every box carries every\n * variant and the page picks one from the sidecar (graph-impact.ts). The state is\n * never written on the box: color + legend + the \"why\" panel a click opens.\n * - PRODUCT \"which products it belongs to\" (#1179, graph-products.ts). Each product's legend\n * row is a toggle; the first two (sorted-name order) are ON at every load, and the\n * page repaints in the browser from the ON set (#1319, graph-product-paint.client.ts):\n * exactly one ON product is a solid fill in that product's fixed palette color; two\n * or more ON are the same vertical stripes the multi-runtime box uses, one per ON\n * product; a box carrying only OFF products is light grey; a box in no product is\n * white with a dashed border. The neutral \"shared by every product\" fill applies\n * only while EVERY product is ON and the box carries them all. The Filter HIDES\n * other products' boxes; this mode keeps every box and makes ownership visible.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\nimport { ProductPalette, ProductTemplate, ProductTemplateKind } from './graph-products';\nimport type { ProductDependencyEntry } from '../configs/tag-truth-configs';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n PRODUCT = 'product',\n /** Runtime pages only (#1239): the saved goal compared with the saved current graph. */\n GOAL = 'goal',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),\n];\n\n/**\n * The runtime page's Color-by menu: the four shared modes plus Goal vs Current (#1239), key `5`. The\n * build-dependency and DI viewers never list it.\n */\nexport const RUNTIME_GRAPH_MODES: readonly ModeInfo[] = [\n ...GRAPH_MODES,\n new ModeInfo(GraphMode.GOAL, 'Goal vs Current', 'Current → Goal proposal'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_DEPENDENT = '#fde3b0';\n/** Implicitly touched (#1264): violet HATCHING — thin alternating stripes — with a violet border. */\nexport const IMPACT_IMPLICIT = '#cdb8f4';\nexport const IMPACT_IMPLICIT_GAP = '#f5f0fd';\nexport const IMPACT_IMPLICIT_BORDER = '#7e57c2';\n/** Root-file touched (#1264): teal, with a teal border. */\nexport const IMPACT_ROOT_FILE = '#bfe6e0';\nexport const IMPACT_ROOT_FILE_BORDER = '#1a8f86';\n/** Unexplained (#1264): the dependent fill with a dashed grey border, so it never reads as a dependent. */\nexport const IMPACT_UNEXPLAINED_BORDER = '#8a8f9c';\n/** How many hatch stripes an implicitly-touched box gets. */\nconst HATCH_STRIPES = 12;\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */\n public readonly products: string[],\n public readonly summary: string = '',\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly dependent: string,\n public readonly implicit: string,\n public readonly rootFile: string,\n public readonly unexplained: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n /** Product mode's statement depends on which products are ON, so the page writes it (#1319). */\n public readonly product: ProductTemplate,\n ) {}\n}\n\n/** How many drawn boxes one product holds, and how many of them no other product does. */\nexport class ProductCount {\n constructor(\n public readonly product: string,\n public total: number,\n public only: number,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */\n products: ProductCount[] = [];\n /**\n * The ONE palette every box was painted with (#1244), built over the full declared product set.\n * The legend, the Filter's product chips and the crossing edges read their colors from it, never\n * from a palette of their own: collision resolution depends on the set, so a second palette built\n * from a subset could disagree with the boxes.\n */\n palette: ProductPalette = new ProductPalette([]);\n /** Boxes shared by every product (two or more declared). */\n sharedByAll = 0;\n /** Boxes in no product. */\n noProduct = 0;\n /** Some box belongs to two or more products, but not to every one: it is striped. */\n multiProduct = false;\n /** Dependency edges crossing into another product (#1204), drawn dashed in the Product mode. */\n crossProductEdges = 0;\n /**\n * The `product-tags` rule's matrix (#1204), listed in the Product legend; null when the rule is\n * OFF or the page was drawn without it.\n */\n productDependencies: ProductDependencyEntry[] | null = null;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_DEPENDENT, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.hatched(facts),\n this.plain(facts, IMPACT_ROOT_FILE, DARK_TEXT, `, color=\"${IMPACT_ROOT_FILE_BORDER}\", penwidth=2`),\n this.plain(facts, IMPACT_DEPENDENT, DARK_TEXT, `, color=\"${IMPACT_UNEXPLAINED_BORDER}\", penwidth=1.5`, 'filled,dashed'),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n this.product(facts),\n );\n }\n\n /** Implicitly touched: thin alternating violet stripes (a hatch), so it never reads as amber. */\n private hatched(facts: NodeFacts): string {\n const colors = Array.from({ length: HATCH_STRIPES }, (_: undefined, index: number): string =>\n index % 2 === 0 ? IMPACT_IMPLICIT : IMPACT_IMPLICIT_GAP,\n );\n return this.statement(\n facts,\n `${this.stripes(colors)}, fontcolor=\"${DARK_TEXT}\", color=\"${IMPACT_IMPLICIT_BORDER}\", penwidth=2`,\n this.labelText(facts, DARK_TEXT),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n this.recordProducts(legend, facts, palette);\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */\n private recordProducts(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n legend.palette = palette;\n if (legend.products.length === 0 && palette.colors.length > 0) {\n legend.products = palette.names().map((product: string): ProductCount => new ProductCount(product, 0, 0));\n }\n if (facts.products.length === 0) legend.noProduct++;\n if (palette.sharedByAll(facts.products)) legend.sharedByAll++;\n else if (facts.products.length > 1) legend.multiProduct = true;\n for (const count of legend.products) {\n if (!facts.products.includes(count.product)) continue;\n count.total++;\n if (facts.products.length === 1) count.only++;\n }\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>  <B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}` +\n (facts.summary === '' ? '' : `<BR/>${htmlLabelText(facts.summary)}`)\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n return this.stripes(bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color));\n }\n\n /**\n * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product\n * box share. A single color is a plain fill.\n */\n stripes(colors: string[]): string {\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Product mode (#1179): the statement's head and its label in both text colors. Which fill a box\n * gets depends on the products switched ON in the legend, so the page decides it (#1319).\n */\n private product(facts: NodeFacts): ProductTemplate {\n return new ProductTemplate(\n ProductTemplateKind.BOX,\n ` \"${dotValue(facts.nodeId)}\" [`,\n this.labelText(facts, DARK_TEXT),\n this.labelText(facts, LIGHT_TEXT),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * The affected set is split by WHY nx included it (#1264, graph-impact-reasons.ts):\n * owning a changed file is solid amber (\"changed\"); reached over dependency edges\n * from one is light amber with an amber border (\"dependent\"); a `{workspaceRoot}`\n * input glob match is violet hatching (\"implicitly touched\"); a lockfile /\n * package.json / tsconfig.base.json / nx.json change is teal (\"root-file touched\");\n * anything nothing could explain is light amber with a dashed grey border\n * (\"unexplained\" — never drawn as a dependent). Every transitive dependency of the\n * affected set that is not itself affected is light slate with a dashed border\n * (\"build input\": compiled or restored from cache, unchanged); everything else is\n * grey. Which box is which is only known per checkout, so every box carries every\n * variant and the page picks one from the sidecar (graph-impact.ts). The state is\n * never written on the box: color + legend + the \"why\" panel a click opens.\n * - PRODUCT \"which products it belongs to\" (#1179, graph-products.ts). Each product's legend\n * row is a toggle; the first two (sorted-name order) are ON at every load, and the\n * page repaints in the browser from the ON set (#1319, graph-product-paint.client.ts):\n * exactly one ON product is a solid fill in that product's fixed palette color; two\n * or more ON are the same vertical stripes the multi-runtime box uses, one per ON\n * product; a box carrying only OFF products is light grey; a box in no product is\n * white with a dashed border. The neutral \"shared by every product\" fill applies\n * only while EVERY product is ON and the box carries them all. The Filter HIDES\n * other products' boxes; this mode keeps every box and makes ownership visible.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\nimport { ProductPalette, ProductTemplate, ProductTemplateKind } from './graph-products';\nimport type { ProductDependencyEntry } from '../configs/tag-truth-configs';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n PRODUCT = 'product',\n /** Runtime pages only (#1239): the saved goal compared with the saved current graph. */\n GOAL = 'goal',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/**\n * A mode the page must render DIMMED and unselectable (#1373): `reason` is the item's tooltip and\n * `note` replaces its grey subtitle, e.g. \"runtime-dependencies-goal.json does not exist\".\n */\nexport class ModeUnavailable {\n constructor(\n public readonly mode: GraphMode,\n public readonly reason: string,\n public readonly note: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),\n];\n\n/**\n * The runtime page's Color-by menu: the four shared modes plus Goal vs Current (#1239), key `5`. The\n * build-dependency and DI viewers never list it.\n */\nexport const RUNTIME_GRAPH_MODES: readonly ModeInfo[] = [\n ...GRAPH_MODES,\n new ModeInfo(GraphMode.GOAL, 'Goal vs Current', 'Current → Goal proposal'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_DEPENDENT = '#fde3b0';\n/** Implicitly touched (#1264): violet HATCHING — thin alternating stripes — with a violet border. */\nexport const IMPACT_IMPLICIT = '#cdb8f4';\nexport const IMPACT_IMPLICIT_GAP = '#f5f0fd';\nexport const IMPACT_IMPLICIT_BORDER = '#7e57c2';\n/** Root-file touched (#1264): teal, with a teal border. */\nexport const IMPACT_ROOT_FILE = '#bfe6e0';\nexport const IMPACT_ROOT_FILE_BORDER = '#1a8f86';\n/** Unexplained (#1264): the dependent fill with a dashed grey border, so it never reads as a dependent. */\nexport const IMPACT_UNEXPLAINED_BORDER = '#8a8f9c';\n/** How many hatch stripes an implicitly-touched box gets. */\nconst HATCH_STRIPES = 12;\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */\n public readonly products: string[],\n public readonly summary: string = '',\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly dependent: string,\n public readonly implicit: string,\n public readonly rootFile: string,\n public readonly unexplained: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n /** Product mode's statement depends on which products are ON, so the page writes it (#1319). */\n public readonly product: ProductTemplate,\n ) {}\n}\n\n/** How many drawn boxes one product holds, and how many of them no other product does. */\nexport class ProductCount {\n constructor(\n public readonly product: string,\n public total: number,\n public only: number,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */\n products: ProductCount[] = [];\n /**\n * The ONE palette every box was painted with (#1244), built over the full declared product set.\n * The legend, the Filter's product chips and the crossing edges read their colors from it, never\n * from a palette of their own: collision resolution depends on the set, so a second palette built\n * from a subset could disagree with the boxes.\n */\n palette: ProductPalette = new ProductPalette([]);\n /** Boxes shared by every product (two or more declared). */\n sharedByAll = 0;\n /** Boxes in no product. */\n noProduct = 0;\n /** Some box belongs to two or more products, but not to every one: it is striped. */\n multiProduct = false;\n /** Dependency edges crossing into another product (#1204), drawn dashed in the Product mode. */\n crossProductEdges = 0;\n /**\n * The `product-tags` rule's matrix (#1204), listed in the Product legend; null when the rule is\n * OFF or the page was drawn without it.\n */\n productDependencies: ProductDependencyEntry[] | null = null;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_DEPENDENT, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.hatched(facts),\n this.plain(facts, IMPACT_ROOT_FILE, DARK_TEXT, `, color=\"${IMPACT_ROOT_FILE_BORDER}\", penwidth=2`),\n this.plain(facts, IMPACT_DEPENDENT, DARK_TEXT, `, color=\"${IMPACT_UNEXPLAINED_BORDER}\", penwidth=1.5`, 'filled,dashed'),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n this.product(facts),\n );\n }\n\n /** Implicitly touched: thin alternating violet stripes (a hatch), so it never reads as amber. */\n private hatched(facts: NodeFacts): string {\n const colors = Array.from({ length: HATCH_STRIPES }, (_: undefined, index: number): string =>\n index % 2 === 0 ? IMPACT_IMPLICIT : IMPACT_IMPLICIT_GAP,\n );\n return this.statement(\n facts,\n `${this.stripes(colors)}, fontcolor=\"${DARK_TEXT}\", color=\"${IMPACT_IMPLICIT_BORDER}\", penwidth=2`,\n this.labelText(facts, DARK_TEXT),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n this.recordProducts(legend, facts, palette);\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */\n private recordProducts(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n legend.palette = palette;\n if (legend.products.length === 0 && palette.colors.length > 0) {\n legend.products = palette.names().map((product: string): ProductCount => new ProductCount(product, 0, 0));\n }\n if (facts.products.length === 0) legend.noProduct++;\n if (palette.sharedByAll(facts.products)) legend.sharedByAll++;\n else if (facts.products.length > 1) legend.multiProduct = true;\n for (const count of legend.products) {\n if (!facts.products.includes(count.product)) continue;\n count.total++;\n if (facts.products.length === 1) count.only++;\n }\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>  <B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}` +\n (facts.summary === '' ? '' : `<BR/>${htmlLabelText(facts.summary)}`)\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n return this.stripes(bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color));\n }\n\n /**\n * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product\n * box share. A single color is a plain fill.\n */\n stripes(colors: string[]): string {\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Product mode (#1179): the statement's head and its label in both text colors. Which fill a box\n * gets depends on the products switched ON in the legend, so the page decides it (#1319).\n */\n private product(facts: NodeFacts): ProductTemplate {\n return new ProductTemplate(\n ProductTemplateKind.BOX,\n ` \"${dotValue(facts.nodeId)}\" [`,\n this.labelText(facts, DARK_TEXT),\n this.labelText(facts, LIGHT_TEXT),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
|
|
@@ -2,6 +2,7 @@ declare class WpModeState {
|
|
|
2
2
|
private readonly hasProducts;
|
|
3
3
|
private readonly viewer;
|
|
4
4
|
private readonly goalUnavailable;
|
|
5
|
+
private readonly goalNote;
|
|
5
6
|
/** `goal` (Goal vs Current, #1239) exists on runtime pages only; the shell renders it nowhere else. */
|
|
6
7
|
static readonly MODES: string[];
|
|
7
8
|
static readonly NAMES: string[];
|
|
@@ -12,13 +13,17 @@ declare class WpModeState {
|
|
|
12
13
|
/**
|
|
13
14
|
* @param hasProducts some box belongs to a product, so the Product mode has something to show
|
|
14
15
|
* @param goalUnavailable '' when Goal vs Current can be shown, else why not (#1239)
|
|
16
|
+
* @param goalNote the line shown under the dimmed Goal vs Current item, e.g.
|
|
17
|
+
* "runtime-dependencies-goal.json does not exist" (#1373); '' when it can be shown
|
|
15
18
|
*/
|
|
16
|
-
constructor(hasProducts: boolean, viewer: string, goalUnavailable: string);
|
|
19
|
+
constructor(hasProducts: boolean, viewer: string, goalUnavailable: string, goalNote?: string);
|
|
17
20
|
private storageKey;
|
|
18
21
|
/** '' when Product can be shown, else why not. */
|
|
19
22
|
productReason(): string;
|
|
20
23
|
/** '' when Goal vs Current can be shown, else why not (a missing or unusable input, never "no differences"). */
|
|
21
24
|
goalReason(): string;
|
|
25
|
+
/** The Goal vs Current item's second line: its subtitle, or the short reason it is dimmed (#1373). */
|
|
26
|
+
goalSubtitle(): string;
|
|
22
27
|
/** The active scan; null when the page has no Impact data. */
|
|
23
28
|
get impact(): ImpactJson | null;
|
|
24
29
|
/** '' when Impact can be shown, else the one line the drawer and the Filter popover say instead. */
|