@webpieces/nx-webpieces-rules 0.4.811 → 0.4.813
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/executors.json +2 -2
- package/package.json +8 -7
- package/src/executors/docs-generate/executor.d.ts +14 -13
- package/src/executors/docs-generate/executor.js +24 -22
- package/src/executors/docs-generate/executor.js.map +1 -1
- package/src/executors/docs-generate/schema.json +3 -3
- package/src/executors/openapi-generate/executor.d.ts +25 -18
- package/src/executors/openapi-generate/executor.js +32 -24
- package/src/executors/openapi-generate/executor.js.map +1 -1
- package/src/executors/openapi-generate/schema.json +2 -2
- package/src/executors/validate-nx-wiring/executor.d.ts +6 -0
- package/src/executors/validate-nx-wiring/executor.js +17 -1
- package/src/executors/validate-nx-wiring/executor.js.map +1 -1
- package/src/generate-targets.d.ts +59 -0
- package/src/generate-targets.js +118 -0
- package/src/generate-targets.js.map +1 -0
- package/src/lib/api-docs/consumer-bin-resolver.d.ts +62 -0
- package/src/lib/api-docs/consumer-bin-resolver.js +148 -0
- package/src/lib/api-docs/consumer-bin-resolver.js.map +1 -0
- package/src/lib/api-docs/generate-wiring.d.ts +124 -0
- package/src/lib/api-docs/generate-wiring.js +328 -0
- package/src/lib/api-docs/generate-wiring.js.map +1 -0
- package/src/lib/api-docs/generator-package.d.ts +23 -0
- package/src/lib/api-docs/generator-package.js +32 -0
- package/src/lib/api-docs/generator-package.js.map +1 -0
- package/src/lib/api-docs/generator-runner.d.ts +21 -0
- package/src/lib/api-docs/generator-runner.js +38 -0
- package/src/lib/api-docs/generator-runner.js.map +1 -0
- package/src/lib/{generated-docs → api-docs}/generator-target.d.ts +19 -12
- package/src/lib/{generated-docs → api-docs}/generator-target.js +40 -24
- package/src/lib/api-docs/generator-target.js.map +1 -0
- package/src/lib/api-docs/json-value.d.ts +6 -0
- package/src/lib/api-docs/json-value.js +3 -0
- package/src/lib/api-docs/json-value.js.map +1 -0
- package/src/plugin.d.ts +2 -0
- package/src/plugin.js +15 -2
- package/src/plugin.js.map +1 -1
- package/src/lib/generated-docs/generator-target.js.map +0 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wiring a project opting into generated API documents needs, checked by `validate-nx-wiring`
|
|
3
|
+
* on nx's RESOLVED project graph (#1023, correcting #1021). It is nx's own documented codegen shape
|
|
4
|
+
* (https://nx.dev/blog/dotnet-openapi-type-safety):
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* <api-lib>:build @nx/js:tsc → its outputPath (unchanged — still named `build`)
|
|
8
|
+
* <api-lib>:openapi-generate dependsOn ["build"] → writes into build's outputPath
|
|
9
|
+
* nx.json targetDefaults every build / test default a dependent runs: dependsOn "^openapi-generate"
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Why the api library's tsc target must stay `build`: `@nx/js:tsc` decides whether each dependency is
|
|
13
|
+
* BUILDABLE by looking for a target with the SAME NAME as the one running (nx 22,
|
|
14
|
+
* `buildable-libs-utils.js` `calculateProjectDependencies`). A library whose tsc target is renamed
|
|
15
|
+
* `compile` sees every `build`-only dependency as non-buildable, pulls its SOURCE into the compile,
|
|
16
|
+
* and tsc fails with TS6059 "rootDir is expected to contain all source files" (nrwl/nx#18257, closed
|
|
17
|
+
* as outdated, never fixed). #1021's `compile` → `openapi-generate` → noop-`build` split hit exactly
|
|
18
|
+
* that in the first real consumer, so it is no longer the design.
|
|
19
|
+
*
|
|
20
|
+
* Why the dependents carry `^openapi-generate`: nx `dependsOn` only points BACKWARD, so generation
|
|
21
|
+
* (which must run AFTER tsc, whose cleanup wipes the outputPath) cannot be hung off the library's own
|
|
22
|
+
* `build`. ONE repo-wide `nx.json` line per targetDefaults entry pulls it in instead — nx skips a
|
|
23
|
+
* dependency that has no `openapi-generate` and keeps walking its dependencies, so a project with no
|
|
24
|
+
* generating library upstream is unaffected, and a transitive dependent is reached through the
|
|
25
|
+
* projects between. A server spec that boots the MCP server reads the library's generated tool
|
|
26
|
+
* catalogs out of its build output, which is why `test` needs it as much as `build` does.
|
|
27
|
+
*/
|
|
28
|
+
import type { ProjectConfiguration, TargetDependencyConfig } from '@nx/devkit';
|
|
29
|
+
import { RuleFailError } from '@webpieces/rules-config';
|
|
30
|
+
/** One wiring defect, and the exact edit that fixes it. Data-only. */
|
|
31
|
+
export declare class GenerateWiringProblem {
|
|
32
|
+
/** The project — or, for an nx.json edit, every project that edit fixes. */
|
|
33
|
+
readonly project: string;
|
|
34
|
+
readonly problem: string;
|
|
35
|
+
readonly cure: string;
|
|
36
|
+
constructor(
|
|
37
|
+
/** The project — or, for an nx.json edit, every project that edit fixes. */
|
|
38
|
+
project: string, problem: string, cure: string);
|
|
39
|
+
}
|
|
40
|
+
/** The one field of an nx graph dependency this check reads. Data-only. */
|
|
41
|
+
export declare class ProjectDependency {
|
|
42
|
+
readonly target: string;
|
|
43
|
+
constructor(target: string);
|
|
44
|
+
}
|
|
45
|
+
/** One `nx.json` targetDefaults entry — only the field this check reads. Data-only. */
|
|
46
|
+
export declare class TargetDefault {
|
|
47
|
+
readonly dependsOn: readonly DependsOnEntry[] | undefined;
|
|
48
|
+
constructor(dependsOn: readonly DependsOnEntry[] | undefined);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Which targets each project's OWN project.json declares a `dependsOn` for. nx does not merge
|
|
52
|
+
* `dependsOn` — a project-level one REPLACES the targetDefaults one — so this decides whether the
|
|
53
|
+
* cure is an nx.json edit or a project.json edit. Data-only.
|
|
54
|
+
*/
|
|
55
|
+
export declare class DeclaredDependsOn {
|
|
56
|
+
readonly byProject: Readonly<Record<string, readonly string[]>>;
|
|
57
|
+
constructor(byProject: Readonly<Record<string, readonly string[]>>);
|
|
58
|
+
declares(project: string, target: string): boolean;
|
|
59
|
+
}
|
|
60
|
+
type DependsOnEntry = string | TargetDependencyConfig;
|
|
61
|
+
/** Reads the two on-disk facts the resolved graph has already merged away. */
|
|
62
|
+
export declare class WiringSourceReader {
|
|
63
|
+
private readonly workspaceRoot;
|
|
64
|
+
constructor(workspaceRoot: string);
|
|
65
|
+
/** `nx.json` targetDefaults, as written. A missing or unreadable nx.json has none. */
|
|
66
|
+
targetDefaults(): Record<string, TargetDefault>;
|
|
67
|
+
/** Which targets each project's own project.json declares `dependsOn` for. */
|
|
68
|
+
declaredDependsOn(projects: Readonly<Record<string, ProjectConfiguration>>): DeclaredDependsOn;
|
|
69
|
+
/** A JSON file's contents, or undefined when it is absent — a project inferred from package.json has no project.json. */
|
|
70
|
+
private readJson;
|
|
71
|
+
}
|
|
72
|
+
export declare class GenerateWiring {
|
|
73
|
+
/** nx's RESOLVED project configurations — targetDefaults and inferred targets merged in. */
|
|
74
|
+
private readonly projects;
|
|
75
|
+
/** The project graph's dependencies, by source project. */
|
|
76
|
+
private readonly dependencies;
|
|
77
|
+
/** `nx.json` targetDefaults, as written — to name the key a cure edits. */
|
|
78
|
+
private readonly targetDefaults;
|
|
79
|
+
/** Which targets each project.json declares its own dependsOn for. */
|
|
80
|
+
private readonly declared;
|
|
81
|
+
constructor(
|
|
82
|
+
/** nx's RESOLVED project configurations — targetDefaults and inferred targets merged in. */
|
|
83
|
+
projects: Readonly<Record<string, ProjectConfiguration>>,
|
|
84
|
+
/** The project graph's dependencies, by source project. */
|
|
85
|
+
dependencies: Readonly<Record<string, readonly ProjectDependency[]>>,
|
|
86
|
+
/** `nx.json` targetDefaults, as written — to name the key a cure edits. */
|
|
87
|
+
targetDefaults: Readonly<Record<string, TargetDefault>>,
|
|
88
|
+
/** Which targets each project.json declares its own dependsOn for. */
|
|
89
|
+
declared: DeclaredDependsOn);
|
|
90
|
+
/** The projects tagged to generate API documents. */
|
|
91
|
+
generating(): string[];
|
|
92
|
+
/** The problems as ONE structured failure: each problem in the message, each fix as an Option. */
|
|
93
|
+
failure(problems: readonly GenerateWiringProblem[]): RuleFailError;
|
|
94
|
+
/** Render {@link failure} through the one human renderer, as validate-nx-wiring's report. */
|
|
95
|
+
report(problems: readonly GenerateWiringProblem[]): void;
|
|
96
|
+
problems(): GenerateWiringProblem[];
|
|
97
|
+
/**
|
|
98
|
+
* An UNTAGGED project naming an inferred executor by hand: a second way to opt in, which also slips
|
|
99
|
+
* past the shape check (and breaks the graph load the day the installed plugin lacks the executor).
|
|
100
|
+
*/
|
|
101
|
+
private handWritten;
|
|
102
|
+
/** (a) `openapi-generate` dependsOn exactly the project's own `build`, the tsc target. */
|
|
103
|
+
private shapeOf;
|
|
104
|
+
/**
|
|
105
|
+
* (b) Every project that depends — transitively — on a generating library has `^openapi-generate`
|
|
106
|
+
* in the EFFECTIVE dependsOn of its `build` and `test`. One problem per EDIT, listing every project
|
|
107
|
+
* that edit fixes: an nx.json targetDefaults key usually covers the whole repo at once.
|
|
108
|
+
*/
|
|
109
|
+
private dependentsOf;
|
|
110
|
+
/** The generating libraries `name` reaches through the dependency graph, itself excluded. */
|
|
111
|
+
private generatingUpstreamOf;
|
|
112
|
+
/** Where the missing `^openapi-generate` goes: the project.json that overrides, or the nx.json key. */
|
|
113
|
+
private missingFor;
|
|
114
|
+
/**
|
|
115
|
+
* The targetDefaults key nx reads for this target — nx's own precedence
|
|
116
|
+
* (`readTargetDefaultsForTarget`): the EXECUTOR key when one exists, else the target NAME, else the
|
|
117
|
+
* longest glob key matching the name.
|
|
118
|
+
*/
|
|
119
|
+
private defaultKeyFor;
|
|
120
|
+
private render;
|
|
121
|
+
/** Whether `target` dependsOn `wanted` — `^x` for the upstream form, `x` for a sibling. */
|
|
122
|
+
private names;
|
|
123
|
+
}
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The wiring a project opting into generated API documents needs, checked by `validate-nx-wiring`
|
|
4
|
+
* on nx's RESOLVED project graph (#1023, correcting #1021). It is nx's own documented codegen shape
|
|
5
|
+
* (https://nx.dev/blog/dotnet-openapi-type-safety):
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* <api-lib>:build @nx/js:tsc → its outputPath (unchanged — still named `build`)
|
|
9
|
+
* <api-lib>:openapi-generate dependsOn ["build"] → writes into build's outputPath
|
|
10
|
+
* nx.json targetDefaults every build / test default a dependent runs: dependsOn "^openapi-generate"
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Why the api library's tsc target must stay `build`: `@nx/js:tsc` decides whether each dependency is
|
|
14
|
+
* BUILDABLE by looking for a target with the SAME NAME as the one running (nx 22,
|
|
15
|
+
* `buildable-libs-utils.js` `calculateProjectDependencies`). A library whose tsc target is renamed
|
|
16
|
+
* `compile` sees every `build`-only dependency as non-buildable, pulls its SOURCE into the compile,
|
|
17
|
+
* and tsc fails with TS6059 "rootDir is expected to contain all source files" (nrwl/nx#18257, closed
|
|
18
|
+
* as outdated, never fixed). #1021's `compile` → `openapi-generate` → noop-`build` split hit exactly
|
|
19
|
+
* that in the first real consumer, so it is no longer the design.
|
|
20
|
+
*
|
|
21
|
+
* Why the dependents carry `^openapi-generate`: nx `dependsOn` only points BACKWARD, so generation
|
|
22
|
+
* (which must run AFTER tsc, whose cleanup wipes the outputPath) cannot be hung off the library's own
|
|
23
|
+
* `build`. ONE repo-wide `nx.json` line per targetDefaults entry pulls it in instead — nx skips a
|
|
24
|
+
* dependency that has no `openapi-generate` and keeps walking its dependencies, so a project with no
|
|
25
|
+
* generating library upstream is unaffected, and a transitive dependent is reached through the
|
|
26
|
+
* projects between. A server spec that boots the MCP server reads the library's generated tool
|
|
27
|
+
* catalogs out of its build output, which is why `test` needs it as much as `build` does.
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.GenerateWiring = exports.WiringSourceReader = exports.DeclaredDependsOn = exports.TargetDefault = exports.ProjectDependency = exports.GenerateWiringProblem = void 0;
|
|
31
|
+
const tslib_1 = require("tslib");
|
|
32
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
33
|
+
const path = tslib_1.__importStar(require("path"));
|
|
34
|
+
const core_util_1 = require("@webpieces/core-util");
|
|
35
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
36
|
+
const generate_targets_1 = require("../../generate-targets");
|
|
37
|
+
/** One wiring defect, and the exact edit that fixes it. Data-only. */
|
|
38
|
+
class GenerateWiringProblem {
|
|
39
|
+
project;
|
|
40
|
+
problem;
|
|
41
|
+
cure;
|
|
42
|
+
constructor(
|
|
43
|
+
/** The project — or, for an nx.json edit, every project that edit fixes. */
|
|
44
|
+
project, problem, cure) {
|
|
45
|
+
this.project = project;
|
|
46
|
+
this.problem = problem;
|
|
47
|
+
this.cure = cure;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.GenerateWiringProblem = GenerateWiringProblem;
|
|
51
|
+
/** The one field of an nx graph dependency this check reads. Data-only. */
|
|
52
|
+
class ProjectDependency {
|
|
53
|
+
target;
|
|
54
|
+
constructor(target) {
|
|
55
|
+
this.target = target;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.ProjectDependency = ProjectDependency;
|
|
59
|
+
/** One `nx.json` targetDefaults entry — only the field this check reads. Data-only. */
|
|
60
|
+
class TargetDefault {
|
|
61
|
+
dependsOn;
|
|
62
|
+
constructor(dependsOn) {
|
|
63
|
+
this.dependsOn = dependsOn;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.TargetDefault = TargetDefault;
|
|
67
|
+
/**
|
|
68
|
+
* Which targets each project's OWN project.json declares a `dependsOn` for. nx does not merge
|
|
69
|
+
* `dependsOn` — a project-level one REPLACES the targetDefaults one — so this decides whether the
|
|
70
|
+
* cure is an nx.json edit or a project.json edit. Data-only.
|
|
71
|
+
*/
|
|
72
|
+
class DeclaredDependsOn {
|
|
73
|
+
byProject;
|
|
74
|
+
constructor(byProject) {
|
|
75
|
+
this.byProject = byProject;
|
|
76
|
+
}
|
|
77
|
+
declares(project, target) {
|
|
78
|
+
return (this.byProject[project] ?? []).includes(target);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.DeclaredDependsOn = DeclaredDependsOn;
|
|
82
|
+
/** Reads the two on-disk facts the resolved graph has already merged away. */
|
|
83
|
+
class WiringSourceReader {
|
|
84
|
+
workspaceRoot;
|
|
85
|
+
constructor(workspaceRoot) {
|
|
86
|
+
this.workspaceRoot = workspaceRoot;
|
|
87
|
+
}
|
|
88
|
+
/** `nx.json` targetDefaults, as written. A missing or unreadable nx.json has none. */
|
|
89
|
+
targetDefaults() {
|
|
90
|
+
const raw = this.readJson(path.join(this.workspaceRoot, 'nx.json'));
|
|
91
|
+
const defaults = {};
|
|
92
|
+
for (const [key, entry] of Object.entries(raw?.targetDefaults ?? {})) {
|
|
93
|
+
defaults[key] = new TargetDefault(entry.dependsOn);
|
|
94
|
+
}
|
|
95
|
+
return defaults;
|
|
96
|
+
}
|
|
97
|
+
/** Which targets each project's own project.json declares `dependsOn` for. */
|
|
98
|
+
declaredDependsOn(projects) {
|
|
99
|
+
const byProject = {};
|
|
100
|
+
for (const [name, project] of Object.entries(projects)) {
|
|
101
|
+
const raw = this.readJson(path.join(this.workspaceRoot, project.root, 'project.json'));
|
|
102
|
+
const targets = raw?.targets ?? {};
|
|
103
|
+
byProject[name] = Object.keys(targets).filter((target) => targets[target].dependsOn !== undefined);
|
|
104
|
+
}
|
|
105
|
+
return new DeclaredDependsOn(byProject);
|
|
106
|
+
}
|
|
107
|
+
/** A JSON file's contents, or undefined when it is absent — a project inferred from package.json has no project.json. */
|
|
108
|
+
readJson(file) {
|
|
109
|
+
if (!fs.existsSync(file))
|
|
110
|
+
return undefined;
|
|
111
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.WiringSourceReader = WiringSourceReader;
|
|
115
|
+
/** The executors the plugin infers from a tag — a project.json naming one by hand is a second opt-in. */
|
|
116
|
+
const INFERRED_EXECUTORS = [
|
|
117
|
+
'@webpieces/nx-webpieces-rules:openapi-generate',
|
|
118
|
+
'@webpieces/nx-webpieces-rules:docs-generate',
|
|
119
|
+
];
|
|
120
|
+
/** The tsc target an api library keeps, and `openapi-generate` dependsOn. */
|
|
121
|
+
const BUILD_TARGET = 'build';
|
|
122
|
+
/** The targets a dependent runs that must pull generation in. */
|
|
123
|
+
const DEPENDENT_TARGETS = ['build', 'test'];
|
|
124
|
+
const UPSTREAM_GENERATE = `^${core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET}`;
|
|
125
|
+
/** One missing `^openapi-generate`, before grouping by the edit that fixes it. Data-only. */
|
|
126
|
+
class MissingGenerate {
|
|
127
|
+
project;
|
|
128
|
+
target;
|
|
129
|
+
upstream;
|
|
130
|
+
where;
|
|
131
|
+
cure;
|
|
132
|
+
constructor(project, target, upstream,
|
|
133
|
+
/** Where the fix goes — the grouping key. */
|
|
134
|
+
where, cure) {
|
|
135
|
+
this.project = project;
|
|
136
|
+
this.target = target;
|
|
137
|
+
this.upstream = upstream;
|
|
138
|
+
this.where = where;
|
|
139
|
+
this.cure = cure;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
class GenerateWiring {
|
|
143
|
+
projects;
|
|
144
|
+
dependencies;
|
|
145
|
+
targetDefaults;
|
|
146
|
+
declared;
|
|
147
|
+
constructor(
|
|
148
|
+
/** nx's RESOLVED project configurations — targetDefaults and inferred targets merged in. */
|
|
149
|
+
projects,
|
|
150
|
+
/** The project graph's dependencies, by source project. */
|
|
151
|
+
dependencies,
|
|
152
|
+
/** `nx.json` targetDefaults, as written — to name the key a cure edits. */
|
|
153
|
+
targetDefaults,
|
|
154
|
+
/** Which targets each project.json declares its own dependsOn for. */
|
|
155
|
+
declared) {
|
|
156
|
+
this.projects = projects;
|
|
157
|
+
this.dependencies = dependencies;
|
|
158
|
+
this.targetDefaults = targetDefaults;
|
|
159
|
+
this.declared = declared;
|
|
160
|
+
}
|
|
161
|
+
/** The projects tagged to generate API documents. */
|
|
162
|
+
generating() {
|
|
163
|
+
return Object.keys(this.projects)
|
|
164
|
+
.filter((name) => {
|
|
165
|
+
const tags = this.projects[name].tags ?? [];
|
|
166
|
+
return tags.includes(generate_targets_1.GENERATE_OPENAPI_TAG) || tags.includes(generate_targets_1.GENERATE_DOCS_SITE_TAG);
|
|
167
|
+
})
|
|
168
|
+
.sort();
|
|
169
|
+
}
|
|
170
|
+
/** The problems as ONE structured failure: each problem in the message, each fix as an Option. */
|
|
171
|
+
failure(problems) {
|
|
172
|
+
return new rules_config_1.RuleFailError('nx-wiring', 'A project generating API documents, or one depending on it, is not wired so that build and test generate them:\n' +
|
|
173
|
+
problems.map((each) => ` ${each.project}: ${each.problem}`).join('\n'), undefined, undefined, problems.map((each) => new rules_config_1.Option(each.cure, true)));
|
|
174
|
+
}
|
|
175
|
+
/** Render {@link failure} through the one human renderer, as validate-nx-wiring's report. */
|
|
176
|
+
report(problems) {
|
|
177
|
+
console.error(`\n❌ ${(0, rules_config_1.renderRuleFailForHuman)(this.failure(problems))}\n`);
|
|
178
|
+
}
|
|
179
|
+
problems() {
|
|
180
|
+
const generating = this.generating();
|
|
181
|
+
const problems = this.handWritten(new Set(generating));
|
|
182
|
+
for (const name of generating)
|
|
183
|
+
problems.push(...this.shapeOf(name));
|
|
184
|
+
problems.push(...this.dependentsOf(new Set(generating)));
|
|
185
|
+
return problems;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* An UNTAGGED project naming an inferred executor by hand: a second way to opt in, which also slips
|
|
189
|
+
* past the shape check (and breaks the graph load the day the installed plugin lacks the executor).
|
|
190
|
+
*/
|
|
191
|
+
handWritten(generating) {
|
|
192
|
+
const problems = [];
|
|
193
|
+
for (const name of Object.keys(this.projects).sort()) {
|
|
194
|
+
if (generating.has(name))
|
|
195
|
+
continue;
|
|
196
|
+
const project = this.projects[name];
|
|
197
|
+
for (const [targetName, target] of Object.entries(project.targets ?? {})) {
|
|
198
|
+
if (target.executor === undefined || !INFERRED_EXECUTORS.includes(target.executor))
|
|
199
|
+
continue;
|
|
200
|
+
problems.push(new GenerateWiringProblem(name, `targets.${targetName} names the executor ${target.executor} by hand, and the project has no ` +
|
|
201
|
+
`"${generate_targets_1.GENERATE_OPENAPI_TAG}" / "${generate_targets_1.GENERATE_DOCS_SITE_TAG}" tag. Opting in is the tag; the plugin ` +
|
|
202
|
+
'infers the executor.', `${name}: add "${targetName === core_util_1.GeneratedApiDocsLayout.DOCS_TARGET ? generate_targets_1.GENERATE_DOCS_SITE_TAG : generate_targets_1.GENERATE_OPENAPI_TAG}" ` +
|
|
203
|
+
`to "tags" in ${project.root}/project.json and delete the "executor" line of targets.${targetName}.`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return problems;
|
|
207
|
+
}
|
|
208
|
+
/** (a) `openapi-generate` dependsOn exactly the project's own `build`, the tsc target. */
|
|
209
|
+
shapeOf(name) {
|
|
210
|
+
const project = this.projects[name];
|
|
211
|
+
const where = `${project.root}/project.json`;
|
|
212
|
+
const lookup = new core_util_1.GeneratedApiDocsLayout(project.root, name, project.targets ?? {}).outputTarget();
|
|
213
|
+
if (lookup.found === undefined) {
|
|
214
|
+
return [new GenerateWiringProblem(name, lookup.problem.problem, `${name}: ${lookup.problem.cure}`)];
|
|
215
|
+
}
|
|
216
|
+
if (lookup.found.targetName === BUILD_TARGET)
|
|
217
|
+
return [];
|
|
218
|
+
const other = lookup.found.targetName;
|
|
219
|
+
return [new GenerateWiringProblem(name, `${name}:${core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET} dependsOn "${other}", and must dependsOn "${BUILD_TARGET}" — ` +
|
|
220
|
+
`the api library's @nx/js:tsc target keeps the name build. @nx/js:tsc treats a dependency as buildable ` +
|
|
221
|
+
`only when it has a target of the SAME name as the one running, so a tsc target renamed "${other}" ` +
|
|
222
|
+
'compiles every build-only dependency from SOURCE and fails with TS6059 (nrwl/nx#18257).', `${name}: in ${where}, name the @nx/js:tsc target "${BUILD_TARGET}" again (delete any nx:noop "build" that ` +
|
|
223
|
+
`wrapped it) and set targets.${core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET}.dependsOn to ["${BUILD_TARGET}"]. ` +
|
|
224
|
+
`Dependents pull generation in with "${UPSTREAM_GENERATE}" in nx.json targetDefaults.`)];
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* (b) Every project that depends — transitively — on a generating library has `^openapi-generate`
|
|
228
|
+
* in the EFFECTIVE dependsOn of its `build` and `test`. One problem per EDIT, listing every project
|
|
229
|
+
* that edit fixes: an nx.json targetDefaults key usually covers the whole repo at once.
|
|
230
|
+
*/
|
|
231
|
+
dependentsOf(generating) {
|
|
232
|
+
const byEdit = new Map();
|
|
233
|
+
for (const name of Object.keys(this.projects).sort()) {
|
|
234
|
+
const upstream = this.generatingUpstreamOf(name, generating);
|
|
235
|
+
if (upstream.length === 0)
|
|
236
|
+
continue;
|
|
237
|
+
for (const targetName of DEPENDENT_TARGETS) {
|
|
238
|
+
const target = this.projects[name].targets?.[targetName];
|
|
239
|
+
if (target === undefined || this.names(target, UPSTREAM_GENERATE))
|
|
240
|
+
continue;
|
|
241
|
+
const missing = this.missingFor(name, targetName, target, upstream);
|
|
242
|
+
const group = byEdit.get(missing.where) ?? [];
|
|
243
|
+
group.push(missing);
|
|
244
|
+
byEdit.set(missing.where, group);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const problems = [];
|
|
248
|
+
for (const group of byEdit.values()) {
|
|
249
|
+
const first = group[0];
|
|
250
|
+
const who = group.map((each) => `${each.project}:${each.target}`).join(', ');
|
|
251
|
+
const libraries = [...new Set(group.flatMap((each) => [...each.upstream]))].sort();
|
|
252
|
+
problems.push(new GenerateWiringProblem(who, `depends on ${libraries.join(', ')}, which generate${libraries.length === 1 ? 's' : ''} API documents and MCP ` +
|
|
253
|
+
`tool catalogs into ${libraries.length === 1 ? 'its' : 'their'} build output, and the effective dependsOn ` +
|
|
254
|
+
`does not name "${UPSTREAM_GENERATE}" — so nothing generates them before this runs (a spec that boots ` +
|
|
255
|
+
'the MCP server would read catalogs that were never written, or stale ones).', first.cure));
|
|
256
|
+
}
|
|
257
|
+
return problems;
|
|
258
|
+
}
|
|
259
|
+
/** The generating libraries `name` reaches through the dependency graph, itself excluded. */
|
|
260
|
+
generatingUpstreamOf(name, generating) {
|
|
261
|
+
const found = new Set();
|
|
262
|
+
const seen = new Set([name]);
|
|
263
|
+
const queue = [name];
|
|
264
|
+
while (queue.length > 0) {
|
|
265
|
+
const current = queue.shift();
|
|
266
|
+
for (const dependency of this.dependencies[current] ?? []) {
|
|
267
|
+
if (seen.has(dependency.target))
|
|
268
|
+
continue;
|
|
269
|
+
seen.add(dependency.target);
|
|
270
|
+
if (generating.has(dependency.target))
|
|
271
|
+
found.add(dependency.target);
|
|
272
|
+
queue.push(dependency.target);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return [...found].sort();
|
|
276
|
+
}
|
|
277
|
+
/** Where the missing `^openapi-generate` goes: the project.json that overrides, or the nx.json key. */
|
|
278
|
+
missingFor(name, targetName, target, upstream) {
|
|
279
|
+
const effective = (target.dependsOn ?? []);
|
|
280
|
+
if (this.declared.declares(name, targetName)) {
|
|
281
|
+
const where = `${this.projects[name].root}/project.json`;
|
|
282
|
+
return new MissingGenerate(name, targetName, upstream, `${where}#${targetName}`, `In ${where}, targets.${targetName}.dependsOn REPLACES nx.json's (nx does not merge dependsOn), so add ` +
|
|
283
|
+
`"${UPSTREAM_GENERATE}" there: "dependsOn": ${this.render([...effective, UPSTREAM_GENERATE])}`);
|
|
284
|
+
}
|
|
285
|
+
const key = this.defaultKeyFor(targetName, target.executor);
|
|
286
|
+
const existing = key === undefined ? [] : [...(this.targetDefaults[key].dependsOn ?? [])];
|
|
287
|
+
const editKey = key ?? targetName;
|
|
288
|
+
const line = `"${editKey}": { "dependsOn": ${this.render([...existing, UPSTREAM_GENERATE])} }`;
|
|
289
|
+
return new MissingGenerate(name, targetName, upstream, `nx.json#${editKey}`, key === undefined
|
|
290
|
+
? `In nx.json, add the targetDefaults entry ${line} — no targetDefaults key governs ${targetName} yet.`
|
|
291
|
+
: `In nx.json, set targetDefaults["${editKey}"].dependsOn so the entry reads ${line} ` +
|
|
292
|
+
'(keep its other fields).');
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* The targetDefaults key nx reads for this target — nx's own precedence
|
|
296
|
+
* (`readTargetDefaultsForTarget`): the EXECUTOR key when one exists, else the target NAME, else the
|
|
297
|
+
* longest glob key matching the name.
|
|
298
|
+
*/
|
|
299
|
+
defaultKeyFor(targetName, executor) {
|
|
300
|
+
if (executor !== undefined && this.targetDefaults[executor] !== undefined)
|
|
301
|
+
return executor;
|
|
302
|
+
if (this.targetDefaults[targetName] !== undefined)
|
|
303
|
+
return targetName;
|
|
304
|
+
let best;
|
|
305
|
+
for (const key of Object.keys(this.targetDefaults)) {
|
|
306
|
+
if (!/[*?[\]{}!]/.test(key) || !(0, rules_config_1.matchesAnyGlob)(targetName, [key]))
|
|
307
|
+
continue;
|
|
308
|
+
if (best === undefined || key.length > best.length)
|
|
309
|
+
best = key;
|
|
310
|
+
}
|
|
311
|
+
return best;
|
|
312
|
+
}
|
|
313
|
+
render(entries) {
|
|
314
|
+
return `[${entries.map((entry) => JSON.stringify(entry)).join(', ')}]`;
|
|
315
|
+
}
|
|
316
|
+
/** Whether `target` dependsOn `wanted` — `^x` for the upstream form, `x` for a sibling. */
|
|
317
|
+
names(target, wanted) {
|
|
318
|
+
const upstream = wanted.startsWith('^');
|
|
319
|
+
const name = upstream ? wanted.slice(1) : wanted;
|
|
320
|
+
return (target?.dependsOn ?? []).some((entry) => {
|
|
321
|
+
if (typeof entry === 'string')
|
|
322
|
+
return entry === wanted;
|
|
323
|
+
return entry.target === name && (entry.dependencies === true) === upstream;
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
exports.GenerateWiring = GenerateWiring;
|
|
328
|
+
//# sourceMappingURL=generate-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-wiring.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-docs/generate-wiring.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The wiring a project opting into generated API documents needs, checked by `validate-nx-wiring`\n * on nx's RESOLVED project graph (#1023, correcting #1021). It is nx's own documented codegen shape\n * (https://nx.dev/blog/dotnet-openapi-type-safety):\n *\n * ```\n * <api-lib>:build @nx/js:tsc → its outputPath (unchanged — still named `build`)\n * <api-lib>:openapi-generate dependsOn [\"build\"] → writes into build's outputPath\n * nx.json targetDefaults every build / test default a dependent runs: dependsOn \"^openapi-generate\"\n * ```\n *\n * Why the api library's tsc target must stay `build`: `@nx/js:tsc` decides whether each dependency is\n * BUILDABLE by looking for a target with the SAME NAME as the one running (nx 22,\n * `buildable-libs-utils.js` `calculateProjectDependencies`). A library whose tsc target is renamed\n * `compile` sees every `build`-only dependency as non-buildable, pulls its SOURCE into the compile,\n * and tsc fails with TS6059 \"rootDir is expected to contain all source files\" (nrwl/nx#18257, closed\n * as outdated, never fixed). #1021's `compile` → `openapi-generate` → noop-`build` split hit exactly\n * that in the first real consumer, so it is no longer the design.\n *\n * Why the dependents carry `^openapi-generate`: nx `dependsOn` only points BACKWARD, so generation\n * (which must run AFTER tsc, whose cleanup wipes the outputPath) cannot be hung off the library's own\n * `build`. ONE repo-wide `nx.json` line per targetDefaults entry pulls it in instead — nx skips a\n * dependency that has no `openapi-generate` and keeps walking its dependencies, so a project with no\n * generating library upstream is unaffected, and a transitive dependent is reached through the\n * projects between. A server spec that boots the MCP server reads the library's generated tool\n * catalogs out of its build output, which is why `test` needs it as much as `build` does.\n */\n\nimport type { ProjectConfiguration, TargetConfiguration, TargetDependencyConfig } from '@nx/devkit';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { Option, RuleFailError, matchesAnyGlob, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { GENERATE_DOCS_SITE_TAG, GENERATE_OPENAPI_TAG } from '../../generate-targets';\n\n/** One wiring defect, and the exact edit that fixes it. Data-only. */\nexport class GenerateWiringProblem {\n constructor(\n /** The project — or, for an nx.json edit, every project that edit fixes. */\n readonly project: string,\n readonly problem: string,\n readonly cure: string,\n ) {}\n}\n\n/** The one field of an nx graph dependency this check reads. Data-only. */\nexport class ProjectDependency {\n constructor(readonly target: string) {}\n}\n\n/** One `nx.json` targetDefaults entry — only the field this check reads. Data-only. */\nexport class TargetDefault {\n constructor(readonly dependsOn: readonly DependsOnEntry[] | undefined) {}\n}\n\n/**\n * Which targets each project's OWN project.json declares a `dependsOn` for. nx does not merge\n * `dependsOn` — a project-level one REPLACES the targetDefaults one — so this decides whether the\n * cure is an nx.json edit or a project.json edit. Data-only.\n */\nexport class DeclaredDependsOn {\n constructor(readonly byProject: Readonly<Record<string, readonly string[]>>) {}\n\n declares(project: string, target: string): boolean {\n return (this.byProject[project] ?? []).includes(target);\n }\n}\n\ntype DependsOnEntry = string | TargetDependencyConfig;\n\n/** The fields of a project.json / nx.json this check reads, as they sit in the file. */\ntype RawTarget = { dependsOn?: DependsOnEntry[] };\ntype RawTargets = { targets?: Record<string, RawTarget> };\ntype RawNxJson = { targetDefaults?: Record<string, RawTarget> };\n\n/** Reads the two on-disk facts the resolved graph has already merged away. */\nexport class WiringSourceReader {\n constructor(private readonly workspaceRoot: string) {}\n\n /** `nx.json` targetDefaults, as written. A missing or unreadable nx.json has none. */\n targetDefaults(): Record<string, TargetDefault> {\n const raw = this.readJson(path.join(this.workspaceRoot, 'nx.json')) as RawNxJson | undefined;\n const defaults: Record<string, TargetDefault> = {};\n for (const [key, entry] of Object.entries(raw?.targetDefaults ?? {})) {\n defaults[key] = new TargetDefault(entry.dependsOn);\n }\n return defaults;\n }\n\n /** Which targets each project's own project.json declares `dependsOn` for. */\n declaredDependsOn(projects: Readonly<Record<string, ProjectConfiguration>>): DeclaredDependsOn {\n const byProject: Record<string, string[]> = {};\n for (const [name, project] of Object.entries(projects)) {\n const raw = this.readJson(path.join(this.workspaceRoot, project.root, 'project.json')) as RawTargets | undefined;\n const targets = raw?.targets ?? {};\n byProject[name] = Object.keys(targets).filter((target: string) => targets[target]!.dependsOn !== undefined);\n }\n return new DeclaredDependsOn(byProject);\n }\n\n /** A JSON file's contents, or undefined when it is absent — a project inferred from package.json has no project.json. */\n private readJson(file: string): RawTargets | RawNxJson | undefined {\n if (!fs.existsSync(file)) return undefined;\n return JSON.parse(fs.readFileSync(file, 'utf8')) as RawTargets | RawNxJson;\n }\n}\n\n/** The executors the plugin infers from a tag — a project.json naming one by hand is a second opt-in. */\nconst INFERRED_EXECUTORS: readonly string[] = [\n '@webpieces/nx-webpieces-rules:openapi-generate',\n '@webpieces/nx-webpieces-rules:docs-generate',\n];\n\n/** The tsc target an api library keeps, and `openapi-generate` dependsOn. */\nconst BUILD_TARGET = 'build';\n/** The targets a dependent runs that must pull generation in. */\nconst DEPENDENT_TARGETS: readonly string[] = ['build', 'test'];\nconst UPSTREAM_GENERATE = `^${GeneratedApiDocsLayout.OPENAPI_TARGET}`;\n\n/** One missing `^openapi-generate`, before grouping by the edit that fixes it. Data-only. */\nclass MissingGenerate {\n constructor(\n readonly project: string,\n readonly target: string,\n readonly upstream: readonly string[],\n /** Where the fix goes — the grouping key. */\n readonly where: string,\n readonly cure: string,\n ) {}\n}\n\nexport class GenerateWiring {\n constructor(\n /** nx's RESOLVED project configurations — targetDefaults and inferred targets merged in. */\n private readonly projects: Readonly<Record<string, ProjectConfiguration>>,\n /** The project graph's dependencies, by source project. */\n private readonly dependencies: Readonly<Record<string, readonly ProjectDependency[]>>,\n /** `nx.json` targetDefaults, as written — to name the key a cure edits. */\n private readonly targetDefaults: Readonly<Record<string, TargetDefault>>,\n /** Which targets each project.json declares its own dependsOn for. */\n private readonly declared: DeclaredDependsOn,\n ) {}\n\n /** The projects tagged to generate API documents. */\n generating(): string[] {\n return Object.keys(this.projects)\n .filter((name: string) => {\n const tags = this.projects[name]!.tags ?? [];\n return tags.includes(GENERATE_OPENAPI_TAG) || tags.includes(GENERATE_DOCS_SITE_TAG);\n })\n .sort();\n }\n\n /** The problems as ONE structured failure: each problem in the message, each fix as an Option. */\n failure(problems: readonly GenerateWiringProblem[]): RuleFailError {\n return new RuleFailError(\n 'nx-wiring',\n 'A project generating API documents, or one depending on it, is not wired so that build and test generate them:\\n' +\n problems.map((each: GenerateWiringProblem) => ` ${each.project}: ${each.problem}`).join('\\n'),\n undefined,\n undefined,\n problems.map((each: GenerateWiringProblem) => new Option(each.cure, true)),\n );\n }\n\n /** Render {@link failure} through the one human renderer, as validate-nx-wiring's report. */\n report(problems: readonly GenerateWiringProblem[]): void {\n console.error(`\\n❌ ${renderRuleFailForHuman(this.failure(problems))}\\n`);\n }\n\n problems(): GenerateWiringProblem[] {\n const generating = this.generating();\n const problems: GenerateWiringProblem[] = this.handWritten(new Set(generating));\n for (const name of generating) problems.push(...this.shapeOf(name));\n problems.push(...this.dependentsOf(new Set(generating)));\n return problems;\n }\n\n /**\n * An UNTAGGED project naming an inferred executor by hand: a second way to opt in, which also slips\n * past the shape check (and breaks the graph load the day the installed plugin lacks the executor).\n */\n private handWritten(generating: ReadonlySet<string>): GenerateWiringProblem[] {\n const problems: GenerateWiringProblem[] = [];\n for (const name of Object.keys(this.projects).sort()) {\n if (generating.has(name)) continue;\n const project = this.projects[name]!;\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n if (target.executor === undefined || !INFERRED_EXECUTORS.includes(target.executor)) continue;\n problems.push(new GenerateWiringProblem(\n name,\n `targets.${targetName} names the executor ${target.executor} by hand, and the project has no ` +\n `\"${GENERATE_OPENAPI_TAG}\" / \"${GENERATE_DOCS_SITE_TAG}\" tag. Opting in is the tag; the plugin ` +\n 'infers the executor.',\n `${name}: add \"${targetName === GeneratedApiDocsLayout.DOCS_TARGET ? GENERATE_DOCS_SITE_TAG : GENERATE_OPENAPI_TAG}\" ` +\n `to \"tags\" in ${project.root}/project.json and delete the \"executor\" line of targets.${targetName}.`,\n ));\n }\n }\n return problems;\n }\n\n /** (a) `openapi-generate` dependsOn exactly the project's own `build`, the tsc target. */\n private shapeOf(name: string): GenerateWiringProblem[] {\n const project = this.projects[name]!;\n const where = `${project.root}/project.json`;\n const lookup = new GeneratedApiDocsLayout(project.root, name, project.targets ?? {}).outputTarget();\n if (lookup.found === undefined) {\n return [new GenerateWiringProblem(name, lookup.problem!.problem, `${name}: ${lookup.problem!.cure}`)];\n }\n if (lookup.found.targetName === BUILD_TARGET) return [];\n const other = lookup.found.targetName;\n return [new GenerateWiringProblem(\n name,\n `${name}:${GeneratedApiDocsLayout.OPENAPI_TARGET} dependsOn \"${other}\", and must dependsOn \"${BUILD_TARGET}\" — ` +\n `the api library's @nx/js:tsc target keeps the name build. @nx/js:tsc treats a dependency as buildable ` +\n `only when it has a target of the SAME name as the one running, so a tsc target renamed \"${other}\" ` +\n 'compiles every build-only dependency from SOURCE and fails with TS6059 (nrwl/nx#18257).',\n `${name}: in ${where}, name the @nx/js:tsc target \"${BUILD_TARGET}\" again (delete any nx:noop \"build\" that ` +\n `wrapped it) and set targets.${GeneratedApiDocsLayout.OPENAPI_TARGET}.dependsOn to [\"${BUILD_TARGET}\"]. ` +\n `Dependents pull generation in with \"${UPSTREAM_GENERATE}\" in nx.json targetDefaults.`,\n )];\n }\n\n /**\n * (b) Every project that depends — transitively — on a generating library has `^openapi-generate`\n * in the EFFECTIVE dependsOn of its `build` and `test`. One problem per EDIT, listing every project\n * that edit fixes: an nx.json targetDefaults key usually covers the whole repo at once.\n */\n private dependentsOf(generating: ReadonlySet<string>): GenerateWiringProblem[] {\n const byEdit = new Map<string, MissingGenerate[]>();\n for (const name of Object.keys(this.projects).sort()) {\n const upstream = this.generatingUpstreamOf(name, generating);\n if (upstream.length === 0) continue;\n for (const targetName of DEPENDENT_TARGETS) {\n const target = this.projects[name]!.targets?.[targetName];\n if (target === undefined || this.names(target, UPSTREAM_GENERATE)) continue;\n const missing = this.missingFor(name, targetName, target, upstream);\n const group = byEdit.get(missing.where) ?? [];\n group.push(missing);\n byEdit.set(missing.where, group);\n }\n }\n const problems: GenerateWiringProblem[] = [];\n for (const group of byEdit.values()) {\n const first = group[0]!;\n const who = group.map((each: MissingGenerate) => `${each.project}:${each.target}`).join(', ');\n const libraries = [...new Set(group.flatMap((each: MissingGenerate) => [...each.upstream]))].sort();\n problems.push(new GenerateWiringProblem(\n who,\n `depends on ${libraries.join(', ')}, which generate${libraries.length === 1 ? 's' : ''} API documents and MCP ` +\n `tool catalogs into ${libraries.length === 1 ? 'its' : 'their'} build output, and the effective dependsOn ` +\n `does not name \"${UPSTREAM_GENERATE}\" — so nothing generates them before this runs (a spec that boots ` +\n 'the MCP server would read catalogs that were never written, or stale ones).',\n first.cure,\n ));\n }\n return problems;\n }\n\n /** The generating libraries `name` reaches through the dependency graph, itself excluded. */\n private generatingUpstreamOf(name: string, generating: ReadonlySet<string>): string[] {\n const found = new Set<string>();\n const seen = new Set<string>([name]);\n const queue: string[] = [name];\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const dependency of this.dependencies[current] ?? []) {\n if (seen.has(dependency.target)) continue;\n seen.add(dependency.target);\n if (generating.has(dependency.target)) found.add(dependency.target);\n queue.push(dependency.target);\n }\n }\n return [...found].sort();\n }\n\n /** Where the missing `^openapi-generate` goes: the project.json that overrides, or the nx.json key. */\n private missingFor(\n name: string,\n targetName: string,\n target: TargetConfiguration,\n upstream: readonly string[],\n ): MissingGenerate {\n const effective = (target.dependsOn ?? []) as DependsOnEntry[];\n if (this.declared.declares(name, targetName)) {\n const where = `${this.projects[name]!.root}/project.json`;\n return new MissingGenerate(\n name, targetName, upstream, `${where}#${targetName}`,\n `In ${where}, targets.${targetName}.dependsOn REPLACES nx.json's (nx does not merge dependsOn), so add ` +\n `\"${UPSTREAM_GENERATE}\" there: \"dependsOn\": ${this.render([...effective, UPSTREAM_GENERATE])}`,\n );\n }\n const key = this.defaultKeyFor(targetName, target.executor);\n const existing = key === undefined ? [] : [...(this.targetDefaults[key]!.dependsOn ?? [])];\n const editKey = key ?? targetName;\n const line = `\"${editKey}\": { \"dependsOn\": ${this.render([...existing, UPSTREAM_GENERATE])} }`;\n return new MissingGenerate(\n name, targetName, upstream, `nx.json#${editKey}`,\n key === undefined\n ? `In nx.json, add the targetDefaults entry ${line} — no targetDefaults key governs ${targetName} yet.`\n : `In nx.json, set targetDefaults[\"${editKey}\"].dependsOn so the entry reads ${line} ` +\n '(keep its other fields).',\n );\n }\n\n /**\n * The targetDefaults key nx reads for this target — nx's own precedence\n * (`readTargetDefaultsForTarget`): the EXECUTOR key when one exists, else the target NAME, else the\n * longest glob key matching the name.\n */\n private defaultKeyFor(targetName: string, executor: string | undefined): string | undefined {\n if (executor !== undefined && this.targetDefaults[executor] !== undefined) return executor;\n if (this.targetDefaults[targetName] !== undefined) return targetName;\n let best: string | undefined;\n for (const key of Object.keys(this.targetDefaults)) {\n if (!/[*?[\\]{}!]/.test(key) || !matchesAnyGlob(targetName, [key])) continue;\n if (best === undefined || key.length > best.length) best = key;\n }\n return best;\n }\n\n private render(entries: readonly DependsOnEntry[]): string {\n return `[${entries.map((entry: DependsOnEntry) => JSON.stringify(entry)).join(', ')}]`;\n }\n\n /** Whether `target` dependsOn `wanted` — `^x` for the upstream form, `x` for a sibling. */\n private names(target: TargetConfiguration | undefined, wanted: string): boolean {\n const upstream = wanted.startsWith('^');\n const name = upstream ? wanted.slice(1) : wanted;\n return (target?.dependsOn ?? []).some((entry: DependsOnEntry) => {\n if (typeof entry === 'string') return entry === wanted;\n return entry.target === name && (entry.dependencies === true) === upstream;\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generator the `openapi-generate` / `docs-generate` executors RUN but never bundle, and the oldest
|
|
3
|
+
* release of it that has every capability the executor relies on. Data-only.
|
|
4
|
+
*
|
|
5
|
+
* `minimumVersion` is the version handshake. A capability ships in the generator (the server stream)
|
|
6
|
+
* first; the executor that relies on it follows, and raises this number in the same change. Keep it the
|
|
7
|
+
* FIRST release carrying the capability, not the latest one — raising it further forces a consumer bump
|
|
8
|
+
* that buys nothing.
|
|
9
|
+
*/
|
|
10
|
+
export declare class GeneratorPackage {
|
|
11
|
+
readonly packageName: string;
|
|
12
|
+
readonly binName: string;
|
|
13
|
+
readonly minimumVersion: string;
|
|
14
|
+
constructor(packageName: string, binName: string, minimumVersion: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `wp-openapi` with `--manifest/--out/--format`, writing ONE MCP tool catalog PER CONTRACT
|
|
18
|
+
* (`mcp-<ContractClass>-tools.json`, #1021). 0.4.812 is the first release carrying that layout; an
|
|
19
|
+
* older generator writes the single `mcp-tools.json` no server of this release reads.
|
|
20
|
+
*/
|
|
21
|
+
export declare const OPENAPI_GENERATOR: GeneratorPackage;
|
|
22
|
+
/** `wp-docs-site --spec/--prose/--out`: 0.4.807 is the first release carrying the docs site (#1007). */
|
|
23
|
+
export declare const DOCS_SITE: GeneratorPackage;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DOCS_SITE = exports.OPENAPI_GENERATOR = exports.GeneratorPackage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A generator the `openapi-generate` / `docs-generate` executors RUN but never bundle, and the oldest
|
|
6
|
+
* release of it that has every capability the executor relies on. Data-only.
|
|
7
|
+
*
|
|
8
|
+
* `minimumVersion` is the version handshake. A capability ships in the generator (the server stream)
|
|
9
|
+
* first; the executor that relies on it follows, and raises this number in the same change. Keep it the
|
|
10
|
+
* FIRST release carrying the capability, not the latest one — raising it further forces a consumer bump
|
|
11
|
+
* that buys nothing.
|
|
12
|
+
*/
|
|
13
|
+
class GeneratorPackage {
|
|
14
|
+
packageName;
|
|
15
|
+
binName;
|
|
16
|
+
minimumVersion;
|
|
17
|
+
constructor(packageName, binName, minimumVersion) {
|
|
18
|
+
this.packageName = packageName;
|
|
19
|
+
this.binName = binName;
|
|
20
|
+
this.minimumVersion = minimumVersion;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.GeneratorPackage = GeneratorPackage;
|
|
24
|
+
/**
|
|
25
|
+
* `wp-openapi` with `--manifest/--out/--format`, writing ONE MCP tool catalog PER CONTRACT
|
|
26
|
+
* (`mcp-<ContractClass>-tools.json`, #1021). 0.4.812 is the first release carrying that layout; an
|
|
27
|
+
* older generator writes the single `mcp-tools.json` no server of this release reads.
|
|
28
|
+
*/
|
|
29
|
+
exports.OPENAPI_GENERATOR = new GeneratorPackage('@webpieces/openapi-generator', 'wp-openapi', '0.4.812');
|
|
30
|
+
/** `wp-docs-site --spec/--prose/--out`: 0.4.807 is the first release carrying the docs site (#1007). */
|
|
31
|
+
exports.DOCS_SITE = new GeneratorPackage('@webpieces/docs-site', 'wp-docs-site', '0.4.807');
|
|
32
|
+
//# sourceMappingURL=generator-package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator-package.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-docs/generator-package.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,MAAa,gBAAgB;IAEZ;IACA;IACA;IAHb,YACa,WAAmB,EACnB,OAAe,EACf,cAAsB;QAFtB,gBAAW,GAAX,WAAW,CAAQ;QACnB,YAAO,GAAP,OAAO,CAAQ;QACf,mBAAc,GAAd,cAAc,CAAQ;IAChC,CAAC;CACP;AAND,4CAMC;AAED;;;;GAIG;AACU,QAAA,iBAAiB,GAAG,IAAI,gBAAgB,CAAC,8BAA8B,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAE/G,wGAAwG;AAC3F,QAAA,SAAS,GAAG,IAAI,gBAAgB,CAAC,sBAAsB,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC","sourcesContent":["/**\n * A generator the `openapi-generate` / `docs-generate` executors RUN but never bundle, and the oldest\n * release of it that has every capability the executor relies on. Data-only.\n *\n * `minimumVersion` is the version handshake. A capability ships in the generator (the server stream)\n * first; the executor that relies on it follows, and raises this number in the same change. Keep it the\n * FIRST release carrying the capability, not the latest one — raising it further forces a consumer bump\n * that buys nothing.\n */\nexport class GeneratorPackage {\n constructor(\n readonly packageName: string,\n readonly binName: string,\n readonly minimumVersion: string,\n ) {}\n}\n\n/**\n * `wp-openapi` with `--manifest/--out/--format`, writing ONE MCP tool catalog PER CONTRACT\n * (`mcp-<ContractClass>-tools.json`, #1021). 0.4.812 is the first release carrying that layout; an\n * older generator writes the single `mcp-tools.json` no server of this release reads.\n */\nexport const OPENAPI_GENERATOR = new GeneratorPackage('@webpieces/openapi-generator', 'wp-openapi', '0.4.812');\n\n/** `wp-docs-site --spec/--prose/--out`: 0.4.807 is the first release carrying the docs site (#1007). */\nexport const DOCS_SITE = new GeneratorPackage('@webpieces/docs-site', 'wp-docs-site', '0.4.807');\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ConsumerBin } from './consumer-bin-resolver';
|
|
2
|
+
/** One finished generator process. Data-only. */
|
|
3
|
+
export declare class GeneratorRun {
|
|
4
|
+
readonly exitCode: number;
|
|
5
|
+
/** stdout and stderr, in that order — the generator reports its refusals on stdout. */
|
|
6
|
+
readonly output: string;
|
|
7
|
+
constructor(exitCode: number,
|
|
8
|
+
/** stdout and stderr, in that order — the generator reports its refusals on stdout. */
|
|
9
|
+
output: string);
|
|
10
|
+
get ok(): boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Runs a resolved {@link ConsumerBin} with the node that is running US, so the generator gets the
|
|
14
|
+
* consumer's `node_modules` (it lives there) without depending on a `.bin` shim or on `PATH`.
|
|
15
|
+
*
|
|
16
|
+
* It REPORTS the outcome rather than throwing on a non-zero exit, so the executor that ran it decides
|
|
17
|
+
* how to word the refusal (it names the manifest or the document it was rendering).
|
|
18
|
+
*/
|
|
19
|
+
export declare class GeneratorRunner {
|
|
20
|
+
run(bin: ConsumerBin, args: readonly string[], cwd: string): GeneratorRun;
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GeneratorRunner = exports.GeneratorRun = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/** One finished generator process. Data-only. */
|
|
6
|
+
class GeneratorRun {
|
|
7
|
+
exitCode;
|
|
8
|
+
output;
|
|
9
|
+
constructor(exitCode,
|
|
10
|
+
/** stdout and stderr, in that order — the generator reports its refusals on stdout. */
|
|
11
|
+
output) {
|
|
12
|
+
this.exitCode = exitCode;
|
|
13
|
+
this.output = output;
|
|
14
|
+
}
|
|
15
|
+
get ok() {
|
|
16
|
+
return this.exitCode === 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.GeneratorRun = GeneratorRun;
|
|
20
|
+
/**
|
|
21
|
+
* Runs a resolved {@link ConsumerBin} with the node that is running US, so the generator gets the
|
|
22
|
+
* consumer's `node_modules` (it lives there) without depending on a `.bin` shim or on `PATH`.
|
|
23
|
+
*
|
|
24
|
+
* It REPORTS the outcome rather than throwing on a non-zero exit, so the executor that ran it decides
|
|
25
|
+
* how to word the refusal (it names the manifest or the document it was rendering).
|
|
26
|
+
*/
|
|
27
|
+
class GeneratorRunner {
|
|
28
|
+
run(bin, args, cwd) {
|
|
29
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [bin.binPath, ...args], { cwd, encoding: 'utf8' });
|
|
30
|
+
const output = [result.stdout ?? '', result.stderr ?? '', result.error?.message ?? '']
|
|
31
|
+
.filter((part) => part.trim() !== '')
|
|
32
|
+
.join('\n')
|
|
33
|
+
.trim();
|
|
34
|
+
return new GeneratorRun(result.status ?? 1, output);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.GeneratorRunner = GeneratorRunner;
|
|
38
|
+
//# sourceMappingURL=generator-runner.js.map
|