@webpieces/nx-webpieces-rules 0.4.506 → 0.4.508
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/bin/wp-design-visualize.js +27 -0
- package/executors.json +0 -5
- package/package.json +9 -8
- package/src/di-graph-targets.d.ts +0 -5
- package/src/di-graph-targets.js +12 -16
- package/src/di-graph-targets.js.map +1 -1
- package/src/executors/di-graph-generate/executor.d.ts +8 -4
- package/src/executors/di-graph-generate/executor.js +8 -4
- package/src/executors/di-graph-generate/executor.js.map +1 -1
- package/src/plugin.js +7 -4
- package/src/plugin.js.map +1 -1
- package/src/executors/validate-di-graph-unchanged/executor.d.ts +0 -25
- package/src/executors/validate-di-graph-unchanged/executor.js +0 -84
- package/src/executors/validate-di-graph-unchanged/executor.js.map +0 -1
- package/src/executors/validate-di-graph-unchanged/schema.json +0 -8
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Plain JS shim -> delegates to the compiled TypeScript entry point.
|
|
3
|
+
//
|
|
4
|
+
// Must NOT be converted to TypeScript. pnpm chmods every `bin` target while it links a
|
|
5
|
+
// package, and in THIS workspace a `workspace:*` sibling is linked from its SOURCE dir,
|
|
6
|
+
// long before any build has produced `src/scripts/wp-design-visualize.js`. Pointing `bin` straight at the
|
|
7
|
+
// compiled path therefore makes every `pnpm install` print
|
|
8
|
+
// `WARN Failed to create bin ... ENOENT ... chmod` — noise indistinguishable from a real
|
|
9
|
+
// bin-link failure. This file exists in git, so the chmod always succeeds.
|
|
10
|
+
//
|
|
11
|
+
// See setupDebugging.md (Attempt 8) and bin-targets-exist.spec.ts, which fails if any
|
|
12
|
+
// `bin` target in the workspace does not exist on disk.
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
|
|
18
|
+
const compiled = path.join(__dirname, '..', 'src', 'scripts', 'wp-design-visualize.js');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(compiled)) {
|
|
21
|
+
console.error(
|
|
22
|
+
' [nx-webpieces-rules] wp-design-visualize: package not built yet. Run the build first, or install from npm.',
|
|
23
|
+
);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
require(compiled);
|
package/executors.json
CHANGED
|
@@ -150,11 +150,6 @@
|
|
|
150
150
|
"schema": "./src/executors/di-graph-generate/schema.json",
|
|
151
151
|
"description": "Per-project: generate the Inversify DI dependency DAG into design.json + design.md at the project root"
|
|
152
152
|
},
|
|
153
|
-
"validate-di-graph-unchanged": {
|
|
154
|
-
"implementation": "./src/executors/validate-di-graph-unchanged/executor",
|
|
155
|
-
"schema": "./src/executors/validate-di-graph-unchanged/schema.json",
|
|
156
|
-
"description": "Per-project: fail when the regenerated design.json/design.md differ from the committed copies"
|
|
157
|
-
},
|
|
158
153
|
"validate-nx-wiring": {
|
|
159
154
|
"implementation": "./src/executors/validate-nx-wiring/executor",
|
|
160
155
|
"schema": "./src/executors/validate-nx-wiring/schema.json",
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.508",
|
|
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",
|
|
7
7
|
"executors": "./executors.json",
|
|
8
8
|
"bin": {
|
|
9
|
-
"wp-design-visualize": "./
|
|
9
|
+
"wp-design-visualize": "./bin/wp-design-visualize.js"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./src/index.js",
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
"src/**/*",
|
|
19
19
|
"templates/**/*",
|
|
20
20
|
"executors.json",
|
|
21
|
-
"README.md"
|
|
21
|
+
"README.md",
|
|
22
|
+
"bin/**/*"
|
|
22
23
|
],
|
|
23
24
|
"dependencies": {
|
|
24
|
-
"@webpieces/ai-hook-rules": "0.4.
|
|
25
|
-
"@webpieces/code-rules": "0.4.
|
|
26
|
-
"@webpieces/eslint-rules": "0.4.
|
|
27
|
-
"@webpieces/pr-gate": "0.4.
|
|
28
|
-
"@webpieces/rules-config": "0.4.
|
|
25
|
+
"@webpieces/ai-hook-rules": "0.4.508",
|
|
26
|
+
"@webpieces/code-rules": "0.4.508",
|
|
27
|
+
"@webpieces/eslint-rules": "0.4.508",
|
|
28
|
+
"@webpieces/pr-gate": "0.4.508",
|
|
29
|
+
"@webpieces/rules-config": "0.4.508",
|
|
29
30
|
"madge": "8.0.0"
|
|
30
31
|
},
|
|
31
32
|
"peerDependencies": {
|
|
@@ -10,8 +10,3 @@ import type { TargetConfiguration } from '@nx/devkit';
|
|
|
10
10
|
* design DAG can never silently drift from the code.
|
|
11
11
|
*/
|
|
12
12
|
export declare function createDiGraphGenerateTarget(): TargetConfiguration;
|
|
13
|
-
/**
|
|
14
|
-
* Create per-project DI graph staleness gate — regenerates first (dependsOn),
|
|
15
|
-
* then fails if the regenerated files differ from the committed copies.
|
|
16
|
-
*/
|
|
17
|
-
export declare function createValidateDiGraphUnchangedTarget(): TargetConfiguration;
|
package/src/di-graph-targets.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.createDiGraphGenerateTarget = createDiGraphGenerateTarget;
|
|
9
|
-
exports.createValidateDiGraphUnchangedTarget = createValidateDiGraphUnchangedTarget;
|
|
10
9
|
/**
|
|
11
10
|
* Create per-project DI graph generation target. cache:false because the point
|
|
12
11
|
* is to regenerate design.json/design.md on EVERY build so the committed DI
|
|
@@ -23,19 +22,16 @@ function createDiGraphGenerateTarget() {
|
|
|
23
22
|
},
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
25
|
+
// There is deliberately NO `createValidateDiGraphUnchangedTarget()` any more. That gate asked
|
|
26
|
+
// "is the working tree dirty here?" as a proxy for "is the committed design stale?" — two different
|
|
27
|
+
// questions — and it dirtied the tree as a side effect of validating. nx hashes tasks by input file
|
|
28
|
+
// CONTENTS, so one hash produced both a pass and a fail and nx labelled the task "flaky"; it was not
|
|
29
|
+
// flaky, it was stateful in a dimension nx cannot see. It also could not pass mid-3-point-merge, where
|
|
30
|
+
// merge-in-progress-guard blocks the `git commit` it demanded.
|
|
31
|
+
//
|
|
32
|
+
// The invariant now lives in ONE repo-wide check in `wp-review-upsert-pr` (@webpieces/pr-gate
|
|
33
|
+
// BuildArtifactGate), which runs right after buildCommand and requires every path the build touched to
|
|
34
|
+
// be committed OR STAGED. That covers the whole repo instead of one project's design.*, and "staged
|
|
35
|
+
// counts" is what makes it hold mid-merge with no special case. `di-graph-generate` STAYS — the design
|
|
36
|
+
// files are still generated and still committed; only the git-state-dependent gate is gone.
|
|
41
37
|
//# sourceMappingURL=di-graph-targets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"di-graph-targets.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/di-graph-targets.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AASH,kEAWC;
|
|
1
|
+
{"version":3,"file":"di-graph-targets.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/di-graph-targets.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AASH,kEAWC;AAhBD;;;;GAIG;AACH,SAAgB,2BAA2B;IACvC,OAAO;QACH,QAAQ,EAAE,iDAAiD;QAC3D,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,CAAC,2BAA2B,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;QAC9F,QAAQ,EAAE;YACN,YAAY,EAAE,CAAC,IAAI,CAAC;YACpB,WAAW,EACP,+FAA+F;SACtG;KACJ,CAAC;AACN,CAAC;AAED,8FAA8F;AAC9F,oGAAoG;AACpG,oGAAoG;AACpG,qGAAqG;AACrG,uGAAuG;AACvG,+DAA+D;AAC/D,EAAE;AACF,8FAA8F;AAC9F,uGAAuG;AACvG,oGAAoG;AACpG,uGAAuG;AACvG,4FAA4F","sourcesContent":["/**\n * Per-project DI design graph targets (design.json + design.md), attached to every\n * project.json project by the inference plugin. Split out of plugin.ts the same way\n * as runtime-targets.ts.\n */\n\nimport type { TargetConfiguration } from '@nx/devkit';\n\n/**\n * Create per-project DI graph generation target. cache:false because the point\n * is to regenerate design.json/design.md on EVERY build so the committed DI\n * design DAG can never silently drift from the code.\n */\nexport function createDiGraphGenerateTarget(): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:di-graph-generate',\n cache: false,\n outputs: ['{projectRoot}/design.json', '{projectRoot}/design.md', '{projectRoot}/design.html'],\n metadata: {\n technologies: ['nx'],\n description:\n 'Generate the Inversify DI dependency DAG into design.json + design.md + clickable design.html',\n },\n };\n}\n\n// There is deliberately NO `createValidateDiGraphUnchangedTarget()` any more. That gate asked\n// \"is the working tree dirty here?\" as a proxy for \"is the committed design stale?\" — two different\n// questions — and it dirtied the tree as a side effect of validating. nx hashes tasks by input file\n// CONTENTS, so one hash produced both a pass and a fail and nx labelled the task \"flaky\"; it was not\n// flaky, it was stateful in a dimension nx cannot see. It also could not pass mid-3-point-merge, where\n// merge-in-progress-guard blocks the `git commit` it demanded.\n//\n// The invariant now lives in ONE repo-wide check in `wp-review-upsert-pr` (@webpieces/pr-gate\n// BuildArtifactGate), which runs right after buildCommand and requires every path the build touched to\n// be committed OR STAGED. That covers the whole repo instead of one project's design.*, and \"staged\n// counts\" is what makes it hold mid-merge with no special case. `di-graph-generate` STAYS — the design\n// files are still generated and still committed; only the git-state-dependent gate is gone.\n"]}
|
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
* design.md — Mermaid diagram rendered by GitHub/IDEs in PRs
|
|
10
10
|
* design.html — clickable viz.js page (linked from architecture/dependencies.html)
|
|
11
11
|
*
|
|
12
|
-
* Runs on every build (cache:false;
|
|
13
|
-
*
|
|
14
|
-
* patterns become "unresolved" nodes rather than failing the build.
|
|
12
|
+
* Runs on every build (cache:false; `ci` dependsOn this target directly).
|
|
13
|
+
* Unrecognized DI patterns become "unresolved" nodes rather than failing the build.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
15
|
+
* That the regenerated files are actually COMMITTED is no longer checked here (the deleted
|
|
16
|
+
* validate-di-graph-unchanged target); it is one repo-wide "committed or staged" check in
|
|
17
|
+
* `wp-review-upsert-pr` — @webpieces/pr-gate BuildArtifactGate.
|
|
18
|
+
*
|
|
19
|
+
* Config (webpieces.config.json, rule key `di-graph`): mode RUN_EVERY_TIME | OFF. That key still
|
|
20
|
+
* governs THIS executor, so it stays in webpieces.config.json unchanged.
|
|
17
21
|
*
|
|
18
22
|
* Usage: nx run <project>:di-graph-generate
|
|
19
23
|
*/
|
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
* design.md — Mermaid diagram rendered by GitHub/IDEs in PRs
|
|
11
11
|
* design.html — clickable viz.js page (linked from architecture/dependencies.html)
|
|
12
12
|
*
|
|
13
|
-
* Runs on every build (cache:false;
|
|
14
|
-
*
|
|
15
|
-
* patterns become "unresolved" nodes rather than failing the build.
|
|
13
|
+
* Runs on every build (cache:false; `ci` dependsOn this target directly).
|
|
14
|
+
* Unrecognized DI patterns become "unresolved" nodes rather than failing the build.
|
|
16
15
|
*
|
|
17
|
-
*
|
|
16
|
+
* That the regenerated files are actually COMMITTED is no longer checked here (the deleted
|
|
17
|
+
* validate-di-graph-unchanged target); it is one repo-wide "committed or staged" check in
|
|
18
|
+
* `wp-review-upsert-pr` — @webpieces/pr-gate BuildArtifactGate.
|
|
19
|
+
*
|
|
20
|
+
* Config (webpieces.config.json, rule key `di-graph`): mode RUN_EVERY_TIME | OFF. That key still
|
|
21
|
+
* governs THIS executor, so it stays in webpieces.config.json unchanged.
|
|
18
22
|
*
|
|
19
23
|
* Usage: nx run <project>:di-graph-generate
|
|
20
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/di-graph-generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DI Graph Generate Executor\n *\n * Per-project: statically analyzes the project's Inversify dependency DAG\n * (constructor injection from controllers — or library top-of-DAG classes —\n * down to leaves) and writes three checked-in files at the project root:\n *\n * design.json — machine-readable graph (deterministic, sorted)\n * design.md — Mermaid diagram rendered by GitHub/IDEs in PRs\n * design.html — clickable viz.js page (linked from architecture/dependencies.html)\n *\n * Runs on every build (cache:false; the build gates on\n * validate-di-graph-unchanged which dependsOn this target). Unrecognized DI\n * patterns become \"unresolved\" nodes rather than failing the build.\n *\n * Config (webpieces.config.json, rule key `di-graph`): mode RUN_EVERY_TIME | OFF.\n *\n * Usage: nx run <project>:di-graph-generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { loadAndValidate, ResolvedConfig } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n DiAnalyzer,\n frameworkTags,\n explicitRoleTag,\n FrameworkMarkers,\n selectAnalyzer,\n} from '../../lib/di-graph/analyzer-strategy';\nimport { createProjectProgram } from '../../lib/di-graph/program';\nimport { toDesignJson } from '../../lib/di-graph/serializer';\nimport { toDesignMarkdown } from '../../lib/di-graph/mermaid';\nimport { generateDesignHTML } from '../../lib/di-graph/design-visualizer';\nimport { DiDesign, DiGraph } from '../../lib/di-graph/model';\nimport { toError } from '../../toError';\n\nexport interface DiGraphGenerateOptions {\n // No options here — config comes from webpieces.config.json at runtime.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst RULE_NAME = 'di-graph';\nconst MISSING_DESIGN_RULE_NAME = 'missing-design-annotation';\n\n// Cheap substring pre-scan: a project whose source never mentions any DI marker\n// gets an empty graph without paying for a ts.Program. Angular markers\n// (@Component/bootstrapApplication) are included so an Angular app that uses no\n// Inversify decorator isn't short-circuited to empty.\nconst DI_MARKERS = [\n '@DocumentDesign(',\n '@provideSingleton',\n '@provideFrameworkSingleton',\n '@provideTransient',\n '@injectable',\n 'new ContainerModule',\n '@inject(',\n '@Component(',\n 'bootstrapApplication',\n];\n\nconst ANGULAR_MARKERS = ['@Component(', 'bootstrapApplication'];\n// A non-Angular DI-design root. When no role tag is set, its presence steers the\n// marker-fallback toward the Inversify analyzer (server/controller mode).\nconst DESIGN_ROOT_MARKER = '@DocumentDesign(';\n\n/** Recursively read every project .ts file, folding each into `visit`. */\nfunction forEachSourceFile(dir: string, visit: (content: string) => void): void {\n if (!fs.existsSync(dir)) return;\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === 'node_modules' || entry.name === 'dist') continue;\n forEachSourceFile(full, visit);\n } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {\n visit(fs.readFileSync(full, 'utf-8'));\n }\n }\n}\n\nfunction sourceHasDiMarkers(dir: string): boolean {\n let found = false;\n forEachSourceFile(dir, (content: string) => {\n if (!found && DI_MARKERS.some((marker: string) => content.includes(marker))) found = true;\n });\n return found;\n}\n\n/** Pre-scan a project's source for the framework markers used when no tag is set. */\nfunction detectFrameworkMarkers(dir: string): FrameworkMarkers {\n let angular = false;\n let controller = false;\n forEachSourceFile(dir, (content: string) => {\n if (!angular && ANGULAR_MARKERS.some((marker: string) => content.includes(marker))) angular = true;\n if (!controller && content.includes(DESIGN_ROOT_MARKER)) controller = true;\n });\n return new FrameworkMarkers(angular, controller);\n}\n\n/**\n * Repo-relative back link from a project's committed design.html up to\n * architecture/dependencies.html, so a reader who clicked a box in the\n * architecture graph can click back out. E.g. 'packages/http/http-api' →\n * '../../../architecture/dependencies.html'.\n */\nfunction architectureBackHref(projectRoot: string): string {\n return path.posix.relative(projectRoot.replace(/\\\\/g, '/'), 'architecture/dependencies.html');\n}\n\nfunction writeDesignFiles(projectRootAbs: string, projectRoot: string, graph: DiGraph): void {\n // toDesignJson sorts the graph in place, so design.md/design.html below all\n // see the same deterministic ordering (no git churn on re-run).\n fs.writeFileSync(path.join(projectRootAbs, 'design.json'), toDesignJson(graph));\n fs.writeFileSync(path.join(projectRootAbs, 'design.md'), toDesignMarkdown(graph));\n fs.writeFileSync(\n path.join(projectRootAbs, 'design.html'),\n generateDesignHTML(graph, architectureBackHref(projectRoot)),\n );\n}\n\n/** The analyzer chosen for a project plus the role tag that drove the choice. */\nclass AnalyzerChoice {\n constructor(\n public readonly analyzer: DiAnalyzer,\n public readonly role: string | null,\n ) {}\n}\n\n/**\n * Select the analyzer by role (server & designed-lib → @DocumentDesign,\n * client→angular design, lib→skip). The explicit `role:` nx tag is the source of\n * truth; when absent we fall back to the legacy `framework:` selection + marker\n * pre-scan so designs stay identical until a project is retagged.\n */\nfunction chooseAnalyzer(tags: string[], srcDir: string): AnalyzerChoice {\n const role = explicitRoleTag(tags);\n const frameworks = frameworkTags(tags);\n const analyzer = selectAnalyzer(role, frameworks, detectFrameworkMarkers(srcDir));\n console.log(\n ` Analyzer: ${analyzer.constructor.name} ` +\n `(role tag: ${role ?? 'none'}, framework tags: ${frameworks.length > 0 ? frameworks.join(', ') : 'none'})`,\n );\n return new AnalyzerChoice(analyzer, role);\n}\n\n/**\n * A server/designed-lib project that produced no design (zero @DocumentDesign\n * roots) fails the build with role-specific guidance. Enforced under the\n * `missing-design-annotation` rule.\n */\nfunction reportMissingDesignAnnotation(projectName: string, role: string): void {\n if (role === 'server' || role === 'app') {\n console.error(\n `❌ ${projectName} is tagged role:${role} but has no @DocumentDesign class.\\n` +\n ` A role:${role} project must expose one @DocumentDesign() root class (the App the ` +\n `container resolves via container.get(XxxApp)) so its design.json / design.html get\\n` +\n ` generated and linked from architecture/dependencies.html.`,\n );\n return;\n }\n console.error(\n `❌ ${projectName} is tagged role:designed-lib but has no @DocumentDesign class.\\n` +\n ` One or more classes you want a design printed for need @DocumentDesign() ` +\n `(from @webpieces/http-routing) added —\\n` +\n ` or retag the project role:lib if it has no design.`,\n );\n}\n\n/**\n * A server/designed-lib project MUST expose at least one @DocumentDesign root,\n * else its design is empty and the role is meaningless. Returns true (and reports)\n * when the build should fail. The `missing-design-annotation` rule gates it:\n * absent (an older published config) → enforce; OFF → skip.\n */\nfunction failsMissingDesignAnnotation(\n shared: ResolvedConfig,\n role: string | null,\n graph: DiGraph,\n projectName: string,\n): boolean {\n const missingRule = shared.rules.get(MISSING_DESIGN_RULE_NAME);\n const enforce = !missingRule || !missingRule.isOff;\n if (enforce && graph.designs.length === 0 && (role === 'server' || role === 'app' || role === 'designed-lib')) {\n reportMissingDesignAnnotation(projectName, role);\n return true;\n }\n return false;\n}\n\nexport default async function runExecutor(\n _options: DiGraphGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const shared = loadAndValidate(context.root).resolved;\n const rule = shared.rules.get(RULE_NAME);\n if (rule && rule.isOff) {\n console.log(`\\n⏭️ Skipping ${RULE_NAME} generation (mode: OFF)\\n`);\n return { success: true };\n }\n\n const projectName = context.projectName ?? 'project';\n const projectConfig = context.projectsConfigurations?.projects[projectName];\n const projectRoot = projectConfig?.root ?? '.';\n const projectRootAbs = path.join(context.root, projectRoot);\n const srcDir = path.join(projectRootAbs, 'src');\n\n console.log(`\\n🧬 Generating DI design graph for ${projectName}\\n`);\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- chokepoint: a generator crash must produce an actionable failure, not a stack trace mid-build\n try {\n if (!sourceHasDiMarkers(srcDir)) {\n console.log(' No DI markers found — writing empty design graph');\n writeDesignFiles(projectRootAbs, projectRoot, new DiGraph(projectName));\n return { success: true };\n }\n\n const program = createProjectProgram(projectRootAbs);\n if (!program) {\n console.log(' No usable tsconfig/source — writing empty design graph');\n writeDesignFiles(projectRootAbs, projectRoot, new DiGraph(projectName));\n return { success: true };\n }\n\n const choice = chooseAnalyzer(projectConfig?.tags ?? [], srcDir);\n const graph = choice.analyzer.analyzeProject(program, context.root, projectRoot, projectName);\n\n if (failsMissingDesignAnnotation(shared, choice.role, graph, projectName)) {\n return { success: false };\n }\n\n writeDesignFiles(projectRootAbs, projectRoot, graph);\n\n const nodeCount = graph.designs.reduce((sum: number, d: DiDesign) => sum + d.nodes.length, 0);\n const edgeCount = graph.designs.reduce((sum: number, d: DiDesign) => sum + d.edges.length, 0);\n console.log(\n `✅ Wrote ${projectRoot}/design.json + design.md + design.html ` +\n `(${graph.designs.length} design(s), ${nodeCount} node(s), ${edgeCount} edge(s))`,\n );\n const unresolved = [...new Set(graph.designs.flatMap((d: DiDesign) => d.unresolved))];\n if (unresolved.length > 0) {\n console.warn(`⚠️ ${unresolved.length} unresolved token(s)/type(s): ${unresolved.join(', ')}`);\n }\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`❌ DI graph generation failed for ${projectName}: ${error.message}`);\n console.error(` To unblock builds, set rules[\"${RULE_NAME}\"].mode=\"OFF\" in webpieces.config.json.`);\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/di-graph-generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DI Graph Generate Executor\n *\n * Per-project: statically analyzes the project's Inversify dependency DAG\n * (constructor injection from controllers — or library top-of-DAG classes —\n * down to leaves) and writes three checked-in files at the project root:\n *\n * design.json — machine-readable graph (deterministic, sorted)\n * design.md — Mermaid diagram rendered by GitHub/IDEs in PRs\n * design.html — clickable viz.js page (linked from architecture/dependencies.html)\n *\n * Runs on every build (cache:false; `ci` dependsOn this target directly).\n * Unrecognized DI patterns become \"unresolved\" nodes rather than failing the build.\n *\n * That the regenerated files are actually COMMITTED is no longer checked here (the deleted\n * validate-di-graph-unchanged target); it is one repo-wide \"committed or staged\" check in\n * `wp-review-upsert-pr` — @webpieces/pr-gate BuildArtifactGate.\n *\n * Config (webpieces.config.json, rule key `di-graph`): mode RUN_EVERY_TIME | OFF. That key still\n * governs THIS executor, so it stays in webpieces.config.json unchanged.\n *\n * Usage: nx run <project>:di-graph-generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { loadAndValidate, ResolvedConfig } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n DiAnalyzer,\n frameworkTags,\n explicitRoleTag,\n FrameworkMarkers,\n selectAnalyzer,\n} from '../../lib/di-graph/analyzer-strategy';\nimport { createProjectProgram } from '../../lib/di-graph/program';\nimport { toDesignJson } from '../../lib/di-graph/serializer';\nimport { toDesignMarkdown } from '../../lib/di-graph/mermaid';\nimport { generateDesignHTML } from '../../lib/di-graph/design-visualizer';\nimport { DiDesign, DiGraph } from '../../lib/di-graph/model';\nimport { toError } from '../../toError';\n\nexport interface DiGraphGenerateOptions {\n // No options here — config comes from webpieces.config.json at runtime.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst RULE_NAME = 'di-graph';\nconst MISSING_DESIGN_RULE_NAME = 'missing-design-annotation';\n\n// Cheap substring pre-scan: a project whose source never mentions any DI marker\n// gets an empty graph without paying for a ts.Program. Angular markers\n// (@Component/bootstrapApplication) are included so an Angular app that uses no\n// Inversify decorator isn't short-circuited to empty.\nconst DI_MARKERS = [\n '@DocumentDesign(',\n '@provideSingleton',\n '@provideFrameworkSingleton',\n '@provideTransient',\n '@injectable',\n 'new ContainerModule',\n '@inject(',\n '@Component(',\n 'bootstrapApplication',\n];\n\nconst ANGULAR_MARKERS = ['@Component(', 'bootstrapApplication'];\n// A non-Angular DI-design root. When no role tag is set, its presence steers the\n// marker-fallback toward the Inversify analyzer (server/controller mode).\nconst DESIGN_ROOT_MARKER = '@DocumentDesign(';\n\n/** Recursively read every project .ts file, folding each into `visit`. */\nfunction forEachSourceFile(dir: string, visit: (content: string) => void): void {\n if (!fs.existsSync(dir)) return;\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === 'node_modules' || entry.name === 'dist') continue;\n forEachSourceFile(full, visit);\n } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {\n visit(fs.readFileSync(full, 'utf-8'));\n }\n }\n}\n\nfunction sourceHasDiMarkers(dir: string): boolean {\n let found = false;\n forEachSourceFile(dir, (content: string) => {\n if (!found && DI_MARKERS.some((marker: string) => content.includes(marker))) found = true;\n });\n return found;\n}\n\n/** Pre-scan a project's source for the framework markers used when no tag is set. */\nfunction detectFrameworkMarkers(dir: string): FrameworkMarkers {\n let angular = false;\n let controller = false;\n forEachSourceFile(dir, (content: string) => {\n if (!angular && ANGULAR_MARKERS.some((marker: string) => content.includes(marker))) angular = true;\n if (!controller && content.includes(DESIGN_ROOT_MARKER)) controller = true;\n });\n return new FrameworkMarkers(angular, controller);\n}\n\n/**\n * Repo-relative back link from a project's committed design.html up to\n * architecture/dependencies.html, so a reader who clicked a box in the\n * architecture graph can click back out. E.g. 'packages/http/http-api' →\n * '../../../architecture/dependencies.html'.\n */\nfunction architectureBackHref(projectRoot: string): string {\n return path.posix.relative(projectRoot.replace(/\\\\/g, '/'), 'architecture/dependencies.html');\n}\n\nfunction writeDesignFiles(projectRootAbs: string, projectRoot: string, graph: DiGraph): void {\n // toDesignJson sorts the graph in place, so design.md/design.html below all\n // see the same deterministic ordering (no git churn on re-run).\n fs.writeFileSync(path.join(projectRootAbs, 'design.json'), toDesignJson(graph));\n fs.writeFileSync(path.join(projectRootAbs, 'design.md'), toDesignMarkdown(graph));\n fs.writeFileSync(\n path.join(projectRootAbs, 'design.html'),\n generateDesignHTML(graph, architectureBackHref(projectRoot)),\n );\n}\n\n/** The analyzer chosen for a project plus the role tag that drove the choice. */\nclass AnalyzerChoice {\n constructor(\n public readonly analyzer: DiAnalyzer,\n public readonly role: string | null,\n ) {}\n}\n\n/**\n * Select the analyzer by role (server & designed-lib → @DocumentDesign,\n * client→angular design, lib→skip). The explicit `role:` nx tag is the source of\n * truth; when absent we fall back to the legacy `framework:` selection + marker\n * pre-scan so designs stay identical until a project is retagged.\n */\nfunction chooseAnalyzer(tags: string[], srcDir: string): AnalyzerChoice {\n const role = explicitRoleTag(tags);\n const frameworks = frameworkTags(tags);\n const analyzer = selectAnalyzer(role, frameworks, detectFrameworkMarkers(srcDir));\n console.log(\n ` Analyzer: ${analyzer.constructor.name} ` +\n `(role tag: ${role ?? 'none'}, framework tags: ${frameworks.length > 0 ? frameworks.join(', ') : 'none'})`,\n );\n return new AnalyzerChoice(analyzer, role);\n}\n\n/**\n * A server/designed-lib project that produced no design (zero @DocumentDesign\n * roots) fails the build with role-specific guidance. Enforced under the\n * `missing-design-annotation` rule.\n */\nfunction reportMissingDesignAnnotation(projectName: string, role: string): void {\n if (role === 'server' || role === 'app') {\n console.error(\n `❌ ${projectName} is tagged role:${role} but has no @DocumentDesign class.\\n` +\n ` A role:${role} project must expose one @DocumentDesign() root class (the App the ` +\n `container resolves via container.get(XxxApp)) so its design.json / design.html get\\n` +\n ` generated and linked from architecture/dependencies.html.`,\n );\n return;\n }\n console.error(\n `❌ ${projectName} is tagged role:designed-lib but has no @DocumentDesign class.\\n` +\n ` One or more classes you want a design printed for need @DocumentDesign() ` +\n `(from @webpieces/http-routing) added —\\n` +\n ` or retag the project role:lib if it has no design.`,\n );\n}\n\n/**\n * A server/designed-lib project MUST expose at least one @DocumentDesign root,\n * else its design is empty and the role is meaningless. Returns true (and reports)\n * when the build should fail. The `missing-design-annotation` rule gates it:\n * absent (an older published config) → enforce; OFF → skip.\n */\nfunction failsMissingDesignAnnotation(\n shared: ResolvedConfig,\n role: string | null,\n graph: DiGraph,\n projectName: string,\n): boolean {\n const missingRule = shared.rules.get(MISSING_DESIGN_RULE_NAME);\n const enforce = !missingRule || !missingRule.isOff;\n if (enforce && graph.designs.length === 0 && (role === 'server' || role === 'app' || role === 'designed-lib')) {\n reportMissingDesignAnnotation(projectName, role);\n return true;\n }\n return false;\n}\n\nexport default async function runExecutor(\n _options: DiGraphGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const shared = loadAndValidate(context.root).resolved;\n const rule = shared.rules.get(RULE_NAME);\n if (rule && rule.isOff) {\n console.log(`\\n⏭️ Skipping ${RULE_NAME} generation (mode: OFF)\\n`);\n return { success: true };\n }\n\n const projectName = context.projectName ?? 'project';\n const projectConfig = context.projectsConfigurations?.projects[projectName];\n const projectRoot = projectConfig?.root ?? '.';\n const projectRootAbs = path.join(context.root, projectRoot);\n const srcDir = path.join(projectRootAbs, 'src');\n\n console.log(`\\n🧬 Generating DI design graph for ${projectName}\\n`);\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- chokepoint: a generator crash must produce an actionable failure, not a stack trace mid-build\n try {\n if (!sourceHasDiMarkers(srcDir)) {\n console.log(' No DI markers found — writing empty design graph');\n writeDesignFiles(projectRootAbs, projectRoot, new DiGraph(projectName));\n return { success: true };\n }\n\n const program = createProjectProgram(projectRootAbs);\n if (!program) {\n console.log(' No usable tsconfig/source — writing empty design graph');\n writeDesignFiles(projectRootAbs, projectRoot, new DiGraph(projectName));\n return { success: true };\n }\n\n const choice = chooseAnalyzer(projectConfig?.tags ?? [], srcDir);\n const graph = choice.analyzer.analyzeProject(program, context.root, projectRoot, projectName);\n\n if (failsMissingDesignAnnotation(shared, choice.role, graph, projectName)) {\n return { success: false };\n }\n\n writeDesignFiles(projectRootAbs, projectRoot, graph);\n\n const nodeCount = graph.designs.reduce((sum: number, d: DiDesign) => sum + d.nodes.length, 0);\n const edgeCount = graph.designs.reduce((sum: number, d: DiDesign) => sum + d.edges.length, 0);\n console.log(\n `✅ Wrote ${projectRoot}/design.json + design.md + design.html ` +\n `(${graph.designs.length} design(s), ${nodeCount} node(s), ${edgeCount} edge(s))`,\n );\n const unresolved = [...new Set(graph.designs.flatMap((d: DiDesign) => d.unresolved))];\n if (unresolved.length > 0) {\n console.warn(`⚠️ ${unresolved.length} unresolved token(s)/type(s): ${unresolved.join(', ')}`);\n }\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`❌ DI graph generation failed for ${projectName}: ${error.message}`);\n console.error(` To unblock builds, set rules[\"${RULE_NAME}\"].mode=\"OFF\" in webpieces.config.json.`);\n return { success: false };\n }\n}\n"]}
|
package/src/plugin.js
CHANGED
|
@@ -191,12 +191,15 @@ function buildPerProjectTargets(isProjectJson, projectRoot, opts, architectureEn
|
|
|
191
191
|
validationTargets.push(targetName);
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
|
-
// Per-project DI design DAG: regenerate design.json/design.md on every build
|
|
195
|
-
//
|
|
194
|
+
// Per-project DI design DAG: regenerate design.json/design.md/design.html on every build.
|
|
195
|
+
//
|
|
196
|
+
// `di-graph-generate` is itself pushed onto validationTargets so `ci` still RUNS it — it used to ride
|
|
197
|
+
// in only as the `dependsOn` of the deleted `validate-di-graph-unchanged` gate. There is no staleness
|
|
198
|
+
// gate here any more: "the regenerated design files are committed" is now one repo-wide check in
|
|
199
|
+
// `wp-review-upsert-pr` (see di-graph-targets.ts for why the per-project gate could not work).
|
|
196
200
|
if (isProjectJson && opts.workspace.validations.diGraph) {
|
|
197
201
|
targets['di-graph-generate'] = (0, di_graph_targets_1.createDiGraphGenerateTarget)();
|
|
198
|
-
|
|
199
|
-
validationTargets.push('validate-di-graph-unchanged');
|
|
202
|
+
validationTargets.push('di-graph-generate');
|
|
200
203
|
}
|
|
201
204
|
// Add ci target to ALL projects (both project.json and package.json). ci aggregates
|
|
202
205
|
// lint + build + test + the validation gates that formerly rode on the compile
|
package/src/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unified Nx Inference Plugin for @webpieces/nx-webpieces-rules\n *\n * This plugin automatically creates targets for:\n * 1. Workspace-level architecture validation (generate, visualize, validate-*)\n * 2. Per-project circular dependency checking\n *\n * Install with: nx add @webpieces/nx-webpieces-rules\n *\n * Usage:\n * Add to nx.json plugins array:\n * {\n * \"plugins\": [\"@webpieces/nx-webpieces-rules\"]\n * }\n *\n * Then all targets appear automatically without manual project.json configuration.\n */\n\nimport { dirname, join } from 'path';\nimport { existsSync } from 'fs';\nimport type {\n CreateNodesV2,\n CreateNodesContextV2,\n CreateNodesResultV2,\n CreateNodesResult,\n TargetConfiguration,\n} from '@nx/devkit';\nimport {\n createVisualizeRuntimeTarget,\n createValidateRuntimeArchitectureTarget,\n} from './runtime-targets';\nimport { ValidationTargets } from './validation-targets';\nimport {\n createDiGraphGenerateTarget,\n createValidateDiGraphUnchangedTarget,\n} from './di-graph-targets';\n\n/**\n * Circular dependency checking options\n */\nexport interface CircularDepsOptions {\n enabled?: boolean;\n targetName?: string;\n excludePatterns?: string[];\n}\n\n/**\n * Validation options for architecture checks\n */\nexport interface ValidationOptions {\n noCycles?: boolean;\n architectureUnchanged?: boolean;\n validatePackageJson?: boolean;\n validateNewMethods?: boolean;\n validateModifiedMethods?: boolean;\n validateModifiedFiles?: boolean;\n validateVersionsLocked?: boolean;\n validateTsInSrc?: boolean;\n validateNxWiring?: boolean;\n runtimeArchitecture?: boolean;\n validateApiRelations?: boolean;\n validateApiLibTag?: boolean;\n diGraph?: boolean;\n newMethodsMaxLines?: number;\n modifiedAndNewMethodsMaxLines?: number;\n modifiedFilesMaxLines?: number;\n /**\n * Validation mode for method/file size limits:\n * - STRICT: All limits enforced, disable comments ignored\n * - NORMAL: Limits enforced, disable comments with dates work\n * - OFF: Skip size validations entirely (for fast iteration)\n */\n validationMode?: 'STRICT' | 'NORMAL' | 'OFF';\n}\n\n/**\n * Feature flags for workspace targets\n */\nexport interface FeatureOptions {\n generate?: boolean;\n visualize?: boolean;\n visualizeRuntime?: boolean;\n}\n\n/**\n * Workspace-level configuration options\n */\nexport interface WorkspaceOptions {\n enabled?: boolean;\n targetPrefix?: string;\n graphPath?: string;\n validations?: ValidationOptions;\n features?: FeatureOptions;\n}\n\n/**\n * Configuration for @webpieces/nx-webpieces-rules Nx plugin\n */\nexport interface ArchitecturePluginOptions {\n circularDeps?: CircularDepsOptions;\n workspace?: WorkspaceOptions;\n}\n\nconst DEFAULT_OPTIONS: Required<ArchitecturePluginOptions> = {\n circularDeps: {\n enabled: true,\n targetName: 'validate-no-file-import-cycles',\n excludePatterns: [],\n },\n workspace: {\n enabled: true,\n targetPrefix: 'arch:',\n graphPath: 'architecture/dependencies.json',\n validations: {\n noCycles: true,\n architectureUnchanged: true,\n validatePackageJson: true,\n validateNewMethods: true,\n validateModifiedMethods: true,\n validateModifiedFiles: true,\n validateVersionsLocked: true,\n validateTsInSrc: true,\n validateNxWiring: true,\n runtimeArchitecture: true,\n validateApiRelations: true,\n validateApiLibTag: true,\n diGraph: true,\n newMethodsMaxLines: 30,\n modifiedAndNewMethodsMaxLines: 80,\n modifiedFilesMaxLines: 900,\n validationMode: 'NORMAL',\n },\n features: {\n generate: true,\n visualize: true,\n visualizeRuntime: true,\n },\n },\n};\n\nfunction normalizeOptions(\n options: ArchitecturePluginOptions | undefined,\n): Required<ArchitecturePluginOptions> {\n const circularDeps = {\n ...DEFAULT_OPTIONS.circularDeps,\n ...options?.circularDeps,\n };\n\n const workspace = {\n ...DEFAULT_OPTIONS.workspace,\n ...options?.workspace,\n validations: {\n ...DEFAULT_OPTIONS.workspace.validations,\n ...options?.workspace?.validations,\n },\n features: {\n ...DEFAULT_OPTIONS.workspace.features,\n ...options?.workspace?.features,\n },\n };\n\n return {\n circularDeps,\n workspace,\n } as Required<ArchitecturePluginOptions>;\n}\n\nasync function createNodesFunction(\n projectFiles: readonly string[],\n options: ArchitecturePluginOptions | undefined,\n context: CreateNodesContextV2,\n): Promise<CreateNodesResultV2> {\n const opts = normalizeOptions(options);\n const results: CreateNodesResultV2 = [];\n\n // Add workspace-level architecture targets\n addArchitectureProject(results, projectFiles, opts, context);\n\n // Add per-project targets (circular-deps, ci, di-graph)\n addPerProjectTargets(results, projectFiles, opts, context);\n\n return results;\n}\n\nfunction addArchitectureProject(\n results: CreateNodesResultV2,\n projectFiles: readonly string[],\n opts: Required<ArchitecturePluginOptions>,\n context: CreateNodesContextV2,\n): void {\n if (!opts.workspace.enabled) return;\n\n const archDirPath = join(context.workspaceRoot, 'architecture');\n if (!existsSync(archDirPath)) return;\n\n const workspaceTargets = createWorkspaceTargetsWithoutPrefix(opts);\n if (Object.keys(workspaceTargets).length === 0) return;\n\n const result: CreateNodesResult = {\n projects: {\n architecture: {\n name: 'architecture',\n root: 'architecture',\n targets: workspaceTargets,\n },\n },\n };\n\n const firstProjectFile = projectFiles[0];\n if (firstProjectFile) {\n results.push([firstProjectFile, result] as const);\n }\n}\n\n\n// A project sits inside a NESTED git repo (e.g. a clone under repositories/) when any of its\n// ancestor dirs — up to but NOT including the workspace root — contains a `.git`. Such projects are\n// separate repos, not part of THIS workspace's graph, so they must not get inferred targets (that is\n// what drags foreign clones into `nx affected`). Same repo-boundary signal the AI guards use.\nexport function isInsideNestedGitRepo(workspaceRoot: string, projectRoot: string): boolean {\n let dir = projectRoot;\n while (dir && dir !== '.' && dir !== '/') {\n if (existsSync(join(workspaceRoot, dir, '.git'))) return true;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return false;\n}\n\nfunction addPerProjectTargets(\n results: CreateNodesResultV2,\n projectFiles: readonly string[],\n opts: Required<ArchitecturePluginOptions>,\n context: CreateNodesContextV2,\n): void {\n // Track processed project roots to avoid duplicates when both files exist\n const processedRoots = new Set<string>();\n\n for (const projectFile of projectFiles) {\n const isProjectJson = projectFile.endsWith('project.json');\n const isPackageJson = projectFile.endsWith('package.json');\n\n if (!isProjectJson && !isPackageJson) continue;\n\n const projectRoot = dirname(projectFile);\n\n // Skip root (workspace manifest, not a project)\n if (projectRoot === '.') continue;\n\n // Skip projects inside a nested git repo (vendored clones under repositories/): they are\n // separate repos and must not be swept into this workspace's `nx affected` graph.\n if (isInsideNestedGitRepo(context.workspaceRoot, projectRoot)) continue;\n\n // Skip if we've already processed this project root\n if (processedRoots.has(projectRoot)) continue;\n\n // For package.json, skip if project.json also exists in same directory\n // (prefer project.json - it will be processed separately)\n if (isPackageJson) {\n const projectJsonPath = join(context.workspaceRoot, projectRoot, 'project.json');\n if (existsSync(projectJsonPath)) continue;\n }\n\n processedRoots.add(projectRoot);\n\n const architectureEnabled =\n opts.workspace.enabled === true &&\n existsSync(join(context.workspaceRoot, 'architecture'));\n const targets = buildPerProjectTargets(\n isProjectJson,\n projectRoot,\n opts,\n architectureEnabled,\n );\n\n if (Object.keys(targets).length === 0) continue;\n\n const result: CreateNodesResult = {\n projects: {\n [projectRoot]: {\n targets,\n },\n },\n };\n\n results.push([projectFile, result] as const);\n }\n}\n\n/**\n * Build the target map for one project. Most targets are project.json-only\n * (package.json-only projects may not have TypeScript source); `ci` goes on all.\n */\nfunction buildPerProjectTargets(\n isProjectJson: boolean,\n projectRoot: string,\n opts: Required<ArchitecturePluginOptions>,\n architectureEnabled: boolean,\n): Record<string, TargetConfiguration> {\n const targets: Record<string, TargetConfiguration> = {};\n\n // Per-project validation gates that `ci` must depend on. Collected as they are added\n // so `ci` references only gates that actually exist on THIS project (a package.json-only\n // project has none).\n const validationTargets: string[] = [];\n\n // Add circular-deps target ONLY for project.json projects\n if (isProjectJson && opts.circularDeps.enabled) {\n if (!isExcluded(projectRoot, opts.circularDeps.excludePatterns!)) {\n const targetName = opts.circularDeps.targetName!;\n targets[targetName] = createCircularDepsTarget(projectRoot, targetName);\n validationTargets.push(targetName);\n }\n }\n\n // Per-project DI design DAG: regenerate design.json/design.md on every build,\n // then gate CI on the committed copies being current.\n if (isProjectJson && opts.workspace.validations!.diGraph) {\n targets['di-graph-generate'] = createDiGraphGenerateTarget();\n targets['validate-di-graph-unchanged'] = createValidateDiGraphUnchangedTarget();\n validationTargets.push('validate-di-graph-unchanged');\n }\n\n // Add ci target to ALL projects (both project.json and package.json). ci aggregates\n // lint + build + test + the validation gates that formerly rode on the compile\n // executor's targetDefaults, so a bare `build` stays a fast compile-only step.\n targets['ci'] = createCiTarget(validationTargets, architectureEnabled);\n\n return targets;\n}\n\n/**\n * Nx V2 Inference Plugin\n * Matches project.json and package.json files to create targets\n */\nexport const createNodesV2: CreateNodesV2<ArchitecturePluginOptions> = [\n // Pattern to match project.json and package.json files\n '**/{project,package}.json',\n\n // Inference function\n createNodesFunction,\n];\n\n/**\n * Build list of enabled validation target names for validate-complete dependency chain\n */\nfunction buildValidationTargetsList(\n validations: Required<ArchitecturePluginOptions>['workspace']['validations'],\n): string[] {\n const targets: string[] = [];\n if (validations!.noCycles) targets.push('validate-no-architecture-cycles');\n if (validations!.architectureUnchanged) targets.push('validate-architecture-unchanged');\n if (validations!.validatePackageJson) targets.push('validate-packagejson');\n // Use combined validate-code instead of 3 separate targets\n if (\n validations!.validateNewMethods ||\n validations!.validateModifiedMethods ||\n validations!.validateModifiedFiles\n ) {\n targets.push('validate-code');\n }\n if (validations!.validateVersionsLocked) targets.push('validate-versions-locked');\n if (validations!.validateTsInSrc) targets.push('validate-ts-in-src');\n if (validations!.validateNxWiring) targets.push('validate-nx-wiring');\n if (validations!.runtimeArchitecture) targets.push('validate-runtime-architecture');\n if (validations!.validateApiRelations) targets.push('validate-api-relations');\n if (validations!.validateApiLibTag) targets.push('validate-api-lib-tag');\n return targets;\n}\n\n/**\n * Create workspace-level architecture validation targets WITHOUT prefix\n * Used for virtual 'architecture' project\n */\nfunction createWorkspaceTargetsWithoutPrefix(\n opts: Required<ArchitecturePluginOptions>,\n): Record<string, TargetConfiguration> {\n const targets: Record<string, TargetConfiguration> = {};\n const graphPath = opts.workspace.graphPath!;\n const validations = opts.workspace.validations!;\n // One ValidationTargets instance, injected below to build each target config.\n const targetFactory = new ValidationTargets();\n\n // Add help target (always available)\n targets['help'] = createHelpTarget();\n\n if (opts.workspace.features!.generate) {\n targets['generate'] = createGenerateTarget(graphPath);\n }\n if (opts.workspace.features!.visualize) {\n targets['visualize'] = createVisualizeTargetWithoutPrefix(graphPath);\n }\n if (opts.workspace.features!.visualizeRuntime) {\n targets['visualize-runtime'] = createVisualizeRuntimeTarget();\n }\n if (validations.noCycles) {\n targets['validate-no-architecture-cycles'] = targetFactory.noCycles();\n }\n if (validations.architectureUnchanged) {\n targets['validate-architecture-unchanged'] = createValidateUnchangedTarget(graphPath);\n }\n if (validations.validatePackageJson) {\n targets['validate-packagejson'] = targetFactory.packageJson();\n }\n // Always available (cheap; success when a repo has no checklists). Kept out of the validate-complete /\n // ci aggregate so it does not change existing gate behavior — consumers point a required check at it.\n targets['validate-checklist-docs'] = targetFactory.checklistDocs();\n // Use combined validate-code instead of 3 separate targets\n // Options come from webpieces.config.json at the workspace root\n // (loaded via @webpieces/rules-config; same source of truth as @webpieces/ai-hook-rules)\n if (\n validations.validateNewMethods ||\n validations.validateModifiedMethods ||\n validations.validateModifiedFiles\n ) {\n targets['validate-code'] = targetFactory.code();\n }\n if (validations.validateVersionsLocked) {\n targets['validate-versions-locked'] = targetFactory.versionsLocked();\n }\n if (validations.validateTsInSrc) {\n targets['validate-ts-in-src'] = targetFactory.tsInSrc();\n }\n if (validations.validateNxWiring) {\n targets['validate-nx-wiring'] = targetFactory.nxWiring();\n }\n if (validations.runtimeArchitecture) {\n targets['validate-runtime-architecture'] = createValidateRuntimeArchitectureTarget();\n }\n if (validations.validateApiRelations) {\n targets['validate-api-relations'] = targetFactory.apiRelations();\n }\n if (validations.validateApiLibTag) {\n targets['validate-api-lib-tag'] = targetFactory.apiLibTag();\n }\n\n // Add validate-complete target that runs all enabled validations\n const validationTargets = buildValidationTargetsList(validations);\n if (validationTargets.length > 0) {\n targets['validate-complete'] = createValidateCompleteTarget(validationTargets);\n }\n\n return targets;\n}\n\nfunction createGenerateTarget(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:generate',\n cache: false,\n outputs: [\n '{workspaceRoot}/architecture/dependencies.json',\n '{workspaceRoot}/architecture/dependencies.html',\n '{workspaceRoot}/architecture/runtime-dependencies.json',\n ],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description:\n 'Generate the architecture dependency graph (+ clickable dependencies.html) and the runtime microservice graph',\n },\n };\n}\n\nfunction createVisualizeTargetWithoutPrefix(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:visualize',\n dependsOn: ['generate'],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description: 'Generate visual representations of the architecture graph',\n },\n };\n}\n\nfunction createValidateUnchangedTarget(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-architecture-unchanged',\n cache: false,\n inputs: ['default', '{workspaceRoot}/architecture/dependencies.json'],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description: 'Validate the architecture matches the saved blessed graph',\n },\n };\n}\n\nfunction createValidateNewMethodsTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-new-methods',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate new methods do not exceed ${maxLines} lines (only runs in affected mode)`,\n },\n };\n}\n\nfunction createValidateModifiedMethodsTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-modified-methods',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate new and modified methods do not exceed ${maxLines} lines (encourages gradual cleanup)`,\n },\n };\n}\n\nfunction createValidateModifiedFilesTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-modified-files',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate modified files do not exceed ${maxLines} lines (encourages keeping files small)`,\n },\n };\n}\n\nfunction createValidateCompleteTarget(validationTargets: string[]): TargetConfiguration {\n return {\n executor: 'nx:noop',\n cache: true,\n dependsOn: validationTargets,\n metadata: {\n technologies: ['nx'],\n description: 'Run all architecture validations (cycles, unchanged, package.json, code rules)',\n },\n };\n}\n\n/**\n * Create per-project ci target - Gradle-style composite target\n * Composite CI target: runs lint, build, test, and every validation gate.\n *\n * Validation (architecture graph completeness, per-project file-import cycles, DI-graph\n * unchanged) lives HERE, not on the build/compile target. That keeps `nx build` a fast\n * compile-only step for local iteration while `nx ci` is the full gate the PR check runs.\n *\n * The per-project gates are passed in because they exist only on project.json projects;\n * `architecture:validate-complete` is a cross-project dep added only when the workspace\n * `architecture` project exists.\n *\n * NOTE: Type checking is done by the build target (@nx/js:tsc) during compilation.\n */\n// webpieces-disable no-function-outside-class -- Nx inference plugin: createNodes invokes these as module-scope target factories; the entire plugin is intentionally functional (matching the surrounding 18 factories), a DI class is not how the Nx plugin API is called.\nexport function createCiTarget(\n perProjectValidation: string[],\n architectureEnabled: boolean,\n): TargetConfiguration {\n const dependsOn: string[] = ['lint', 'build', 'test', ...perProjectValidation];\n if (architectureEnabled) {\n dependsOn.push('architecture:validate-complete');\n }\n return {\n executor: 'nx:noop',\n cache: true,\n dependsOn,\n metadata: {\n technologies: ['nx'],\n description:\n 'Run all CI checks: lint, build, test, and validation (Gradle-style composite target)',\n },\n };\n}\n\nfunction createHelpTarget(): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:help',\n cache: false, // Never cache - always show help output\n metadata: {\n technologies: ['nx'],\n description: 'Display help for @webpieces/nx-webpieces-rules commands and targets',\n },\n };\n}\n\n/**\n * Create per-project circular dependency checking target.\n *\n * Uses the `validate-no-file-import-cycles` executor (which bundles madge as a\n * dependency) rather than a runtime `npx madge` fetch. The executor reads\n * webpieces.config.json so the gate can be turned on/off (`mode`) and\n * time-boxed (`ignoreModifiedUntilEpoch`) like every other webpieces rule.\n *\n * Note `projectRoot` is intentionally unused now — the executor derives the\n * project root from the Nx context — but the param is kept for call-site\n * symmetry with the rest of the per-project target factories.\n */\nfunction createCircularDepsTarget(_projectRoot: string, _targetName: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-no-file-import-cycles',\n cache: true,\n inputs: ['default'],\n outputs: [] as string[],\n metadata: {\n technologies: ['madge'],\n description: 'Check for circular file-import dependencies using madge',\n },\n };\n}\n\n/**\n * Check if a project should be excluded based on patterns\n */\nfunction isExcluded(projectRoot: string, excludePatterns: string[]): boolean {\n if (excludePatterns.length === 0) {\n return false;\n }\n\n // Simple glob matching (could be enhanced with minimatch if needed)\n return excludePatterns.some((pattern) => {\n // Convert glob pattern to regex\n const regexPattern = pattern\n .replace(/\\*\\*/g, '.*') // ** matches any path\n .replace(/\\*/g, '[^/]*'); // * matches any string except /\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(projectRoot);\n });\n}\n\n/**\n * Export plugin as default for Nx\n */\nexport default { createNodesV2 };\n"]}
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unified Nx Inference Plugin for @webpieces/nx-webpieces-rules\n *\n * This plugin automatically creates targets for:\n * 1. Workspace-level architecture validation (generate, visualize, validate-*)\n * 2. Per-project circular dependency checking\n *\n * Install with: nx add @webpieces/nx-webpieces-rules\n *\n * Usage:\n * Add to nx.json plugins array:\n * {\n * \"plugins\": [\"@webpieces/nx-webpieces-rules\"]\n * }\n *\n * Then all targets appear automatically without manual project.json configuration.\n */\n\nimport { dirname, join } from 'path';\nimport { existsSync } from 'fs';\nimport type {\n CreateNodesV2,\n CreateNodesContextV2,\n CreateNodesResultV2,\n CreateNodesResult,\n TargetConfiguration,\n} from '@nx/devkit';\nimport {\n createVisualizeRuntimeTarget,\n createValidateRuntimeArchitectureTarget,\n} from './runtime-targets';\nimport { ValidationTargets } from './validation-targets';\nimport { createDiGraphGenerateTarget } from './di-graph-targets';\n\n/**\n * Circular dependency checking options\n */\nexport interface CircularDepsOptions {\n enabled?: boolean;\n targetName?: string;\n excludePatterns?: string[];\n}\n\n/**\n * Validation options for architecture checks\n */\nexport interface ValidationOptions {\n noCycles?: boolean;\n architectureUnchanged?: boolean;\n validatePackageJson?: boolean;\n validateNewMethods?: boolean;\n validateModifiedMethods?: boolean;\n validateModifiedFiles?: boolean;\n validateVersionsLocked?: boolean;\n validateTsInSrc?: boolean;\n validateNxWiring?: boolean;\n runtimeArchitecture?: boolean;\n validateApiRelations?: boolean;\n validateApiLibTag?: boolean;\n diGraph?: boolean;\n newMethodsMaxLines?: number;\n modifiedAndNewMethodsMaxLines?: number;\n modifiedFilesMaxLines?: number;\n /**\n * Validation mode for method/file size limits:\n * - STRICT: All limits enforced, disable comments ignored\n * - NORMAL: Limits enforced, disable comments with dates work\n * - OFF: Skip size validations entirely (for fast iteration)\n */\n validationMode?: 'STRICT' | 'NORMAL' | 'OFF';\n}\n\n/**\n * Feature flags for workspace targets\n */\nexport interface FeatureOptions {\n generate?: boolean;\n visualize?: boolean;\n visualizeRuntime?: boolean;\n}\n\n/**\n * Workspace-level configuration options\n */\nexport interface WorkspaceOptions {\n enabled?: boolean;\n targetPrefix?: string;\n graphPath?: string;\n validations?: ValidationOptions;\n features?: FeatureOptions;\n}\n\n/**\n * Configuration for @webpieces/nx-webpieces-rules Nx plugin\n */\nexport interface ArchitecturePluginOptions {\n circularDeps?: CircularDepsOptions;\n workspace?: WorkspaceOptions;\n}\n\nconst DEFAULT_OPTIONS: Required<ArchitecturePluginOptions> = {\n circularDeps: {\n enabled: true,\n targetName: 'validate-no-file-import-cycles',\n excludePatterns: [],\n },\n workspace: {\n enabled: true,\n targetPrefix: 'arch:',\n graphPath: 'architecture/dependencies.json',\n validations: {\n noCycles: true,\n architectureUnchanged: true,\n validatePackageJson: true,\n validateNewMethods: true,\n validateModifiedMethods: true,\n validateModifiedFiles: true,\n validateVersionsLocked: true,\n validateTsInSrc: true,\n validateNxWiring: true,\n runtimeArchitecture: true,\n validateApiRelations: true,\n validateApiLibTag: true,\n diGraph: true,\n newMethodsMaxLines: 30,\n modifiedAndNewMethodsMaxLines: 80,\n modifiedFilesMaxLines: 900,\n validationMode: 'NORMAL',\n },\n features: {\n generate: true,\n visualize: true,\n visualizeRuntime: true,\n },\n },\n};\n\nfunction normalizeOptions(\n options: ArchitecturePluginOptions | undefined,\n): Required<ArchitecturePluginOptions> {\n const circularDeps = {\n ...DEFAULT_OPTIONS.circularDeps,\n ...options?.circularDeps,\n };\n\n const workspace = {\n ...DEFAULT_OPTIONS.workspace,\n ...options?.workspace,\n validations: {\n ...DEFAULT_OPTIONS.workspace.validations,\n ...options?.workspace?.validations,\n },\n features: {\n ...DEFAULT_OPTIONS.workspace.features,\n ...options?.workspace?.features,\n },\n };\n\n return {\n circularDeps,\n workspace,\n } as Required<ArchitecturePluginOptions>;\n}\n\nasync function createNodesFunction(\n projectFiles: readonly string[],\n options: ArchitecturePluginOptions | undefined,\n context: CreateNodesContextV2,\n): Promise<CreateNodesResultV2> {\n const opts = normalizeOptions(options);\n const results: CreateNodesResultV2 = [];\n\n // Add workspace-level architecture targets\n addArchitectureProject(results, projectFiles, opts, context);\n\n // Add per-project targets (circular-deps, ci, di-graph)\n addPerProjectTargets(results, projectFiles, opts, context);\n\n return results;\n}\n\nfunction addArchitectureProject(\n results: CreateNodesResultV2,\n projectFiles: readonly string[],\n opts: Required<ArchitecturePluginOptions>,\n context: CreateNodesContextV2,\n): void {\n if (!opts.workspace.enabled) return;\n\n const archDirPath = join(context.workspaceRoot, 'architecture');\n if (!existsSync(archDirPath)) return;\n\n const workspaceTargets = createWorkspaceTargetsWithoutPrefix(opts);\n if (Object.keys(workspaceTargets).length === 0) return;\n\n const result: CreateNodesResult = {\n projects: {\n architecture: {\n name: 'architecture',\n root: 'architecture',\n targets: workspaceTargets,\n },\n },\n };\n\n const firstProjectFile = projectFiles[0];\n if (firstProjectFile) {\n results.push([firstProjectFile, result] as const);\n }\n}\n\n\n// A project sits inside a NESTED git repo (e.g. a clone under repositories/) when any of its\n// ancestor dirs — up to but NOT including the workspace root — contains a `.git`. Such projects are\n// separate repos, not part of THIS workspace's graph, so they must not get inferred targets (that is\n// what drags foreign clones into `nx affected`). Same repo-boundary signal the AI guards use.\nexport function isInsideNestedGitRepo(workspaceRoot: string, projectRoot: string): boolean {\n let dir = projectRoot;\n while (dir && dir !== '.' && dir !== '/') {\n if (existsSync(join(workspaceRoot, dir, '.git'))) return true;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return false;\n}\n\nfunction addPerProjectTargets(\n results: CreateNodesResultV2,\n projectFiles: readonly string[],\n opts: Required<ArchitecturePluginOptions>,\n context: CreateNodesContextV2,\n): void {\n // Track processed project roots to avoid duplicates when both files exist\n const processedRoots = new Set<string>();\n\n for (const projectFile of projectFiles) {\n const isProjectJson = projectFile.endsWith('project.json');\n const isPackageJson = projectFile.endsWith('package.json');\n\n if (!isProjectJson && !isPackageJson) continue;\n\n const projectRoot = dirname(projectFile);\n\n // Skip root (workspace manifest, not a project)\n if (projectRoot === '.') continue;\n\n // Skip projects inside a nested git repo (vendored clones under repositories/): they are\n // separate repos and must not be swept into this workspace's `nx affected` graph.\n if (isInsideNestedGitRepo(context.workspaceRoot, projectRoot)) continue;\n\n // Skip if we've already processed this project root\n if (processedRoots.has(projectRoot)) continue;\n\n // For package.json, skip if project.json also exists in same directory\n // (prefer project.json - it will be processed separately)\n if (isPackageJson) {\n const projectJsonPath = join(context.workspaceRoot, projectRoot, 'project.json');\n if (existsSync(projectJsonPath)) continue;\n }\n\n processedRoots.add(projectRoot);\n\n const architectureEnabled =\n opts.workspace.enabled === true &&\n existsSync(join(context.workspaceRoot, 'architecture'));\n const targets = buildPerProjectTargets(\n isProjectJson,\n projectRoot,\n opts,\n architectureEnabled,\n );\n\n if (Object.keys(targets).length === 0) continue;\n\n const result: CreateNodesResult = {\n projects: {\n [projectRoot]: {\n targets,\n },\n },\n };\n\n results.push([projectFile, result] as const);\n }\n}\n\n/**\n * Build the target map for one project. Most targets are project.json-only\n * (package.json-only projects may not have TypeScript source); `ci` goes on all.\n */\nfunction buildPerProjectTargets(\n isProjectJson: boolean,\n projectRoot: string,\n opts: Required<ArchitecturePluginOptions>,\n architectureEnabled: boolean,\n): Record<string, TargetConfiguration> {\n const targets: Record<string, TargetConfiguration> = {};\n\n // Per-project validation gates that `ci` must depend on. Collected as they are added\n // so `ci` references only gates that actually exist on THIS project (a package.json-only\n // project has none).\n const validationTargets: string[] = [];\n\n // Add circular-deps target ONLY for project.json projects\n if (isProjectJson && opts.circularDeps.enabled) {\n if (!isExcluded(projectRoot, opts.circularDeps.excludePatterns!)) {\n const targetName = opts.circularDeps.targetName!;\n targets[targetName] = createCircularDepsTarget(projectRoot, targetName);\n validationTargets.push(targetName);\n }\n }\n\n // Per-project DI design DAG: regenerate design.json/design.md/design.html on every build.\n //\n // `di-graph-generate` is itself pushed onto validationTargets so `ci` still RUNS it — it used to ride\n // in only as the `dependsOn` of the deleted `validate-di-graph-unchanged` gate. There is no staleness\n // gate here any more: \"the regenerated design files are committed\" is now one repo-wide check in\n // `wp-review-upsert-pr` (see di-graph-targets.ts for why the per-project gate could not work).\n if (isProjectJson && opts.workspace.validations!.diGraph) {\n targets['di-graph-generate'] = createDiGraphGenerateTarget();\n validationTargets.push('di-graph-generate');\n }\n\n // Add ci target to ALL projects (both project.json and package.json). ci aggregates\n // lint + build + test + the validation gates that formerly rode on the compile\n // executor's targetDefaults, so a bare `build` stays a fast compile-only step.\n targets['ci'] = createCiTarget(validationTargets, architectureEnabled);\n\n return targets;\n}\n\n/**\n * Nx V2 Inference Plugin\n * Matches project.json and package.json files to create targets\n */\nexport const createNodesV2: CreateNodesV2<ArchitecturePluginOptions> = [\n // Pattern to match project.json and package.json files\n '**/{project,package}.json',\n\n // Inference function\n createNodesFunction,\n];\n\n/**\n * Build list of enabled validation target names for validate-complete dependency chain\n */\nfunction buildValidationTargetsList(\n validations: Required<ArchitecturePluginOptions>['workspace']['validations'],\n): string[] {\n const targets: string[] = [];\n if (validations!.noCycles) targets.push('validate-no-architecture-cycles');\n if (validations!.architectureUnchanged) targets.push('validate-architecture-unchanged');\n if (validations!.validatePackageJson) targets.push('validate-packagejson');\n // Use combined validate-code instead of 3 separate targets\n if (\n validations!.validateNewMethods ||\n validations!.validateModifiedMethods ||\n validations!.validateModifiedFiles\n ) {\n targets.push('validate-code');\n }\n if (validations!.validateVersionsLocked) targets.push('validate-versions-locked');\n if (validations!.validateTsInSrc) targets.push('validate-ts-in-src');\n if (validations!.validateNxWiring) targets.push('validate-nx-wiring');\n if (validations!.runtimeArchitecture) targets.push('validate-runtime-architecture');\n if (validations!.validateApiRelations) targets.push('validate-api-relations');\n if (validations!.validateApiLibTag) targets.push('validate-api-lib-tag');\n return targets;\n}\n\n/**\n * Create workspace-level architecture validation targets WITHOUT prefix\n * Used for virtual 'architecture' project\n */\nfunction createWorkspaceTargetsWithoutPrefix(\n opts: Required<ArchitecturePluginOptions>,\n): Record<string, TargetConfiguration> {\n const targets: Record<string, TargetConfiguration> = {};\n const graphPath = opts.workspace.graphPath!;\n const validations = opts.workspace.validations!;\n // One ValidationTargets instance, injected below to build each target config.\n const targetFactory = new ValidationTargets();\n\n // Add help target (always available)\n targets['help'] = createHelpTarget();\n\n if (opts.workspace.features!.generate) {\n targets['generate'] = createGenerateTarget(graphPath);\n }\n if (opts.workspace.features!.visualize) {\n targets['visualize'] = createVisualizeTargetWithoutPrefix(graphPath);\n }\n if (opts.workspace.features!.visualizeRuntime) {\n targets['visualize-runtime'] = createVisualizeRuntimeTarget();\n }\n if (validations.noCycles) {\n targets['validate-no-architecture-cycles'] = targetFactory.noCycles();\n }\n if (validations.architectureUnchanged) {\n targets['validate-architecture-unchanged'] = createValidateUnchangedTarget(graphPath);\n }\n if (validations.validatePackageJson) {\n targets['validate-packagejson'] = targetFactory.packageJson();\n }\n // Always available (cheap; success when a repo has no checklists). Kept out of the validate-complete /\n // ci aggregate so it does not change existing gate behavior — consumers point a required check at it.\n targets['validate-checklist-docs'] = targetFactory.checklistDocs();\n // Use combined validate-code instead of 3 separate targets\n // Options come from webpieces.config.json at the workspace root\n // (loaded via @webpieces/rules-config; same source of truth as @webpieces/ai-hook-rules)\n if (\n validations.validateNewMethods ||\n validations.validateModifiedMethods ||\n validations.validateModifiedFiles\n ) {\n targets['validate-code'] = targetFactory.code();\n }\n if (validations.validateVersionsLocked) {\n targets['validate-versions-locked'] = targetFactory.versionsLocked();\n }\n if (validations.validateTsInSrc) {\n targets['validate-ts-in-src'] = targetFactory.tsInSrc();\n }\n if (validations.validateNxWiring) {\n targets['validate-nx-wiring'] = targetFactory.nxWiring();\n }\n if (validations.runtimeArchitecture) {\n targets['validate-runtime-architecture'] = createValidateRuntimeArchitectureTarget();\n }\n if (validations.validateApiRelations) {\n targets['validate-api-relations'] = targetFactory.apiRelations();\n }\n if (validations.validateApiLibTag) {\n targets['validate-api-lib-tag'] = targetFactory.apiLibTag();\n }\n\n // Add validate-complete target that runs all enabled validations\n const validationTargets = buildValidationTargetsList(validations);\n if (validationTargets.length > 0) {\n targets['validate-complete'] = createValidateCompleteTarget(validationTargets);\n }\n\n return targets;\n}\n\nfunction createGenerateTarget(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:generate',\n cache: false,\n outputs: [\n '{workspaceRoot}/architecture/dependencies.json',\n '{workspaceRoot}/architecture/dependencies.html',\n '{workspaceRoot}/architecture/runtime-dependencies.json',\n ],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description:\n 'Generate the architecture dependency graph (+ clickable dependencies.html) and the runtime microservice graph',\n },\n };\n}\n\nfunction createVisualizeTargetWithoutPrefix(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:visualize',\n dependsOn: ['generate'],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description: 'Generate visual representations of the architecture graph',\n },\n };\n}\n\nfunction createValidateUnchangedTarget(graphPath: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-architecture-unchanged',\n cache: false,\n inputs: ['default', '{workspaceRoot}/architecture/dependencies.json'],\n options: { graphPath },\n metadata: {\n technologies: ['nx'],\n description: 'Validate the architecture matches the saved blessed graph',\n },\n };\n}\n\nfunction createValidateNewMethodsTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-new-methods',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate new methods do not exceed ${maxLines} lines (only runs in affected mode)`,\n },\n };\n}\n\nfunction createValidateModifiedMethodsTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-modified-methods',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate new and modified methods do not exceed ${maxLines} lines (encourages gradual cleanup)`,\n },\n };\n}\n\nfunction createValidateModifiedFilesTarget(\n maxLines: number,\n mode: 'STRICT' | 'NORMAL' | 'OFF',\n): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-modified-files',\n cache: false, // Don't cache - depends on git state\n inputs: ['default'],\n options: { max: maxLines, mode },\n metadata: {\n technologies: ['nx'],\n description: `Validate modified files do not exceed ${maxLines} lines (encourages keeping files small)`,\n },\n };\n}\n\nfunction createValidateCompleteTarget(validationTargets: string[]): TargetConfiguration {\n return {\n executor: 'nx:noop',\n cache: true,\n dependsOn: validationTargets,\n metadata: {\n technologies: ['nx'],\n description: 'Run all architecture validations (cycles, unchanged, package.json, code rules)',\n },\n };\n}\n\n/**\n * Create per-project ci target - Gradle-style composite target\n * Composite CI target: runs lint, build, test, and every validation gate.\n *\n * Validation (architecture graph completeness, per-project file-import cycles, DI-graph\n * unchanged) lives HERE, not on the build/compile target. That keeps `nx build` a fast\n * compile-only step for local iteration while `nx ci` is the full gate the PR check runs.\n *\n * The per-project gates are passed in because they exist only on project.json projects;\n * `architecture:validate-complete` is a cross-project dep added only when the workspace\n * `architecture` project exists.\n *\n * NOTE: Type checking is done by the build target (@nx/js:tsc) during compilation.\n */\n// webpieces-disable no-function-outside-class -- Nx inference plugin: createNodes invokes these as module-scope target factories; the entire plugin is intentionally functional (matching the surrounding 18 factories), a DI class is not how the Nx plugin API is called.\nexport function createCiTarget(\n perProjectValidation: string[],\n architectureEnabled: boolean,\n): TargetConfiguration {\n const dependsOn: string[] = ['lint', 'build', 'test', ...perProjectValidation];\n if (architectureEnabled) {\n dependsOn.push('architecture:validate-complete');\n }\n return {\n executor: 'nx:noop',\n cache: true,\n dependsOn,\n metadata: {\n technologies: ['nx'],\n description:\n 'Run all CI checks: lint, build, test, and validation (Gradle-style composite target)',\n },\n };\n}\n\nfunction createHelpTarget(): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:help',\n cache: false, // Never cache - always show help output\n metadata: {\n technologies: ['nx'],\n description: 'Display help for @webpieces/nx-webpieces-rules commands and targets',\n },\n };\n}\n\n/**\n * Create per-project circular dependency checking target.\n *\n * Uses the `validate-no-file-import-cycles` executor (which bundles madge as a\n * dependency) rather than a runtime `npx madge` fetch. The executor reads\n * webpieces.config.json so the gate can be turned on/off (`mode`) and\n * time-boxed (`ignoreModifiedUntilEpoch`) like every other webpieces rule.\n *\n * Note `projectRoot` is intentionally unused now — the executor derives the\n * project root from the Nx context — but the param is kept for call-site\n * symmetry with the rest of the per-project target factories.\n */\nfunction createCircularDepsTarget(_projectRoot: string, _targetName: string): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:validate-no-file-import-cycles',\n cache: true,\n inputs: ['default'],\n outputs: [] as string[],\n metadata: {\n technologies: ['madge'],\n description: 'Check for circular file-import dependencies using madge',\n },\n };\n}\n\n/**\n * Check if a project should be excluded based on patterns\n */\nfunction isExcluded(projectRoot: string, excludePatterns: string[]): boolean {\n if (excludePatterns.length === 0) {\n return false;\n }\n\n // Simple glob matching (could be enhanced with minimatch if needed)\n return excludePatterns.some((pattern) => {\n // Convert glob pattern to regex\n const regexPattern = pattern\n .replace(/\\*\\*/g, '.*') // ** matches any path\n .replace(/\\*/g, '[^/]*'); // * matches any string except /\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(projectRoot);\n });\n}\n\n/**\n * Export plugin as default for Nx\n */\nexport default { createNodesV2 };\n"]}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validate DI Graph Unchanged Executor
|
|
3
|
-
*
|
|
4
|
-
* Per-project gate that runs AFTER di-graph-generate (via target dependsOn):
|
|
5
|
-
* once the generator has rewritten design.json/design.md/design.html, this
|
|
6
|
-
* executor asks git whether the files differ from the committed copies. Any difference —
|
|
7
|
-
* modified OR brand-new/untracked — fails the build with a "commit the
|
|
8
|
-
* regenerated files" remediation, so the checked-in DI design DAG can never
|
|
9
|
-
* go stale on main.
|
|
10
|
-
*
|
|
11
|
-
* Uses `git status --porcelain` (not `git diff --quiet`) so first-time
|
|
12
|
-
* untracked design files also fail CI instead of slipping through.
|
|
13
|
-
*
|
|
14
|
-
* Config (webpieces.config.json, rule key `di-graph`): mode OFF disables;
|
|
15
|
-
* ignoreModifiedUntilEpoch / ignoreRuleWhileOnBranch report but pass.
|
|
16
|
-
*
|
|
17
|
-
* Usage: nx run <project>:validate-di-graph-unchanged
|
|
18
|
-
*/
|
|
19
|
-
import type { ExecutorContext } from '@nx/devkit';
|
|
20
|
-
export interface ValidateDiGraphUnchangedOptions {
|
|
21
|
-
}
|
|
22
|
-
export interface ExecutorResult {
|
|
23
|
-
success: boolean;
|
|
24
|
-
}
|
|
25
|
-
export default function runExecutor(_options: ValidateDiGraphUnchangedOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Validate DI Graph Unchanged Executor
|
|
4
|
-
*
|
|
5
|
-
* Per-project gate that runs AFTER di-graph-generate (via target dependsOn):
|
|
6
|
-
* once the generator has rewritten design.json/design.md/design.html, this
|
|
7
|
-
* executor asks git whether the files differ from the committed copies. Any difference —
|
|
8
|
-
* modified OR brand-new/untracked — fails the build with a "commit the
|
|
9
|
-
* regenerated files" remediation, so the checked-in DI design DAG can never
|
|
10
|
-
* go stale on main.
|
|
11
|
-
*
|
|
12
|
-
* Uses `git status --porcelain` (not `git diff --quiet`) so first-time
|
|
13
|
-
* untracked design files also fail CI instead of slipping through.
|
|
14
|
-
*
|
|
15
|
-
* Config (webpieces.config.json, rule key `di-graph`): mode OFF disables;
|
|
16
|
-
* ignoreModifiedUntilEpoch / ignoreRuleWhileOnBranch report but pass.
|
|
17
|
-
*
|
|
18
|
-
* Usage: nx run <project>:validate-di-graph-unchanged
|
|
19
|
-
*/
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.default = runExecutor;
|
|
22
|
-
const tslib_1 = require("tslib");
|
|
23
|
-
const rules_config_1 = require("@webpieces/rules-config");
|
|
24
|
-
const child_process_1 = require("child_process");
|
|
25
|
-
const path = tslib_1.__importStar(require("path"));
|
|
26
|
-
const toError_1 = require("../../toError");
|
|
27
|
-
const RULE_NAME = 'di-graph';
|
|
28
|
-
const DESIGN_FILES = ['design.json', 'design.md', 'design.html'];
|
|
29
|
-
function gitStatusPorcelain(workspaceRoot, projectRoot) {
|
|
30
|
-
const paths = DESIGN_FILES.map((file) => path.posix.join(projectRoot, file));
|
|
31
|
-
const output = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--', ...paths], {
|
|
32
|
-
cwd: workspaceRoot,
|
|
33
|
-
encoding: 'utf-8',
|
|
34
|
-
});
|
|
35
|
-
return output.trim();
|
|
36
|
-
}
|
|
37
|
-
function reportStale(projectName, projectRoot, status) {
|
|
38
|
-
console.error(`\n❌ DI design graph is stale for ${projectName}!`);
|
|
39
|
-
console.error('\nThe regenerated design files differ from the committed copies:');
|
|
40
|
-
for (const line of status.split('\n')) {
|
|
41
|
-
console.error(` ${line}`);
|
|
42
|
-
}
|
|
43
|
-
console.error('\nThe DI dependency DAG changed (a constructor/binding was added, removed,');
|
|
44
|
-
console.error('or rewired). To fix:');
|
|
45
|
-
console.error(` 1. Review the diff: git diff -- ${projectRoot}/design.json ${projectRoot}/design.md ${projectRoot}/design.html`);
|
|
46
|
-
console.error(' 2. If intentional, commit the regenerated design.json + design.md + design.html');
|
|
47
|
-
console.error(` 3. To turn this gate off, set rules["${RULE_NAME}"].mode="OFF" in webpieces.config.json\n`);
|
|
48
|
-
}
|
|
49
|
-
async function runExecutor(_options, context) {
|
|
50
|
-
const shared = (0, rules_config_1.loadAndValidate)(context.root).resolved;
|
|
51
|
-
const rule = shared.rules.get(RULE_NAME);
|
|
52
|
-
if (rule && rule.isOff) {
|
|
53
|
-
console.log(`\n⏭️ Skipping validate-di-graph-unchanged (mode: OFF)\n`);
|
|
54
|
-
return { success: true };
|
|
55
|
-
}
|
|
56
|
-
const projectName = context.projectName ?? 'project';
|
|
57
|
-
const projectConfig = context.projectsConfigurations?.projects[projectName];
|
|
58
|
-
const projectRoot = projectConfig?.root ?? '.';
|
|
59
|
-
console.log(`\n🔍 Validating DI design graph unchanged for ${projectName}\n`);
|
|
60
|
-
let status;
|
|
61
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- chokepoint: outside a git repo the gate warns and passes instead of crashing the build
|
|
62
|
-
try {
|
|
63
|
-
status = gitStatusPorcelain(context.root, projectRoot);
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
const error = (0, toError_1.toError)(err);
|
|
67
|
-
console.warn(`⚠️ Could not check git status (${error.message}) — skipping gate`);
|
|
68
|
-
return { success: true };
|
|
69
|
-
}
|
|
70
|
-
if (status === '') {
|
|
71
|
-
console.log('✅ DI design graph matches the committed design.json/design.md\n');
|
|
72
|
-
return { success: true };
|
|
73
|
-
}
|
|
74
|
-
reportStale(projectName, projectRoot, status);
|
|
75
|
-
const epoch = rule?.options['ignoreModifiedUntilEpoch'];
|
|
76
|
-
const branch = rule?.options['ignoreRuleWhileOnBranch'];
|
|
77
|
-
const skip = (0, rules_config_1.shouldSkipRule)(epoch, branch);
|
|
78
|
-
if (skip.skip) {
|
|
79
|
-
console.log(`⏳ ${RULE_NAME}: ${skip.reason}. Staleness reported but NOT failing the build.\n`);
|
|
80
|
-
return { success: true };
|
|
81
|
-
}
|
|
82
|
-
return { success: false };
|
|
83
|
-
}
|
|
84
|
-
//# sourceMappingURL=executor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-di-graph-unchanged/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate DI Graph Unchanged Executor\n *\n * Per-project gate that runs AFTER di-graph-generate (via target dependsOn):\n * once the generator has rewritten design.json/design.md/design.html, this\n * executor asks git whether the files differ from the committed copies. Any difference —\n * modified OR brand-new/untracked — fails the build with a \"commit the\n * regenerated files\" remediation, so the checked-in DI design DAG can never\n * go stale on main.\n *\n * Uses `git status --porcelain` (not `git diff --quiet`) so first-time\n * untracked design files also fail CI instead of slipping through.\n *\n * Config (webpieces.config.json, rule key `di-graph`): mode OFF disables;\n * ignoreModifiedUntilEpoch / ignoreRuleWhileOnBranch report but pass.\n *\n * Usage: nx run <project>:validate-di-graph-unchanged\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { loadAndValidate, shouldSkipRule } from '@webpieces/rules-config';\nimport { execFileSync } from 'child_process';\nimport * as path from 'path';\nimport { toError } from '../../toError';\n\nexport interface ValidateDiGraphUnchangedOptions {\n // No options here — config comes from webpieces.config.json at runtime.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst RULE_NAME = 'di-graph';\nconst DESIGN_FILES = ['design.json', 'design.md', 'design.html'];\n\nfunction gitStatusPorcelain(workspaceRoot: string, projectRoot: string): string {\n const paths = DESIGN_FILES.map((file: string) => path.posix.join(projectRoot, file));\n const output = execFileSync('git', ['status', '--porcelain', '--', ...paths], {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n return output.trim();\n}\n\nfunction reportStale(projectName: string, projectRoot: string, status: string): void {\n console.error(`\\n❌ DI design graph is stale for ${projectName}!`);\n console.error('\\nThe regenerated design files differ from the committed copies:');\n for (const line of status.split('\\n')) {\n console.error(` ${line}`);\n }\n console.error('\\nThe DI dependency DAG changed (a constructor/binding was added, removed,');\n console.error('or rewired). To fix:');\n console.error(` 1. Review the diff: git diff -- ${projectRoot}/design.json ${projectRoot}/design.md ${projectRoot}/design.html`);\n console.error(' 2. If intentional, commit the regenerated design.json + design.md + design.html');\n console.error(` 3. To turn this gate off, set rules[\"${RULE_NAME}\"].mode=\"OFF\" in webpieces.config.json\\n`);\n}\n\nexport default async function runExecutor(\n _options: ValidateDiGraphUnchangedOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const shared = loadAndValidate(context.root).resolved;\n const rule = shared.rules.get(RULE_NAME);\n if (rule && rule.isOff) {\n console.log(`\\n⏭️ Skipping validate-di-graph-unchanged (mode: OFF)\\n`);\n return { success: true };\n }\n\n const projectName = context.projectName ?? 'project';\n const projectConfig = context.projectsConfigurations?.projects[projectName];\n const projectRoot = projectConfig?.root ?? '.';\n\n console.log(`\\n🔍 Validating DI design graph unchanged for ${projectName}\\n`);\n\n let status: string;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- chokepoint: outside a git repo the gate warns and passes instead of crashing the build\n try {\n status = gitStatusPorcelain(context.root, projectRoot);\n } catch (err: unknown) {\n const error = toError(err);\n console.warn(`⚠️ Could not check git status (${error.message}) — skipping gate`);\n return { success: true };\n }\n\n if (status === '') {\n console.log('✅ DI design graph matches the committed design.json/design.md\\n');\n return { success: true };\n }\n\n reportStale(projectName, projectRoot, status);\n\n const epoch = rule?.options['ignoreModifiedUntilEpoch'] as number | undefined;\n const branch = rule?.options['ignoreRuleWhileOnBranch'] as string | undefined;\n const skip = shouldSkipRule(epoch, branch);\n if (skip.skip) {\n console.log(`⏳ ${RULE_NAME}: ${skip.reason}. Staleness reported but NOT failing the build.\\n`);\n return { success: true };\n }\n return { success: false };\n}\n"]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/schema",
|
|
3
|
-
"title": "Validate DI Graph Unchanged Executor",
|
|
4
|
-
"description": "Fails when the regenerated design.json/design.md differ from the committed copies (runs after di-graph-generate). On/off and grace window come from webpieces.config.json (rule: di-graph).",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {},
|
|
7
|
-
"required": []
|
|
8
|
-
}
|