@webpieces/nx-webpieces-rules 0.4.830 → 0.4.831

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.
Files changed (31) hide show
  1. package/executors.json +5 -0
  2. package/package.json +8 -8
  3. package/src/executors/docs-generate/executor.js +1 -1
  4. package/src/executors/docs-generate/executor.js.map +1 -1
  5. package/src/executors/openapi-components-generate/executor.d.ts +32 -0
  6. package/src/executors/openapi-components-generate/executor.js +40 -0
  7. package/src/executors/openapi-components-generate/executor.js.map +1 -0
  8. package/src/executors/openapi-components-generate/schema.json +18 -0
  9. package/src/executors/openapi-generate/executor.d.ts +25 -1
  10. package/src/executors/openapi-generate/executor.js +50 -19
  11. package/src/executors/openapi-generate/executor.js.map +1 -1
  12. package/src/executors/validate-nx-wiring/executor.d.ts +5 -0
  13. package/src/executors/validate-nx-wiring/executor.js +11 -2
  14. package/src/executors/validate-nx-wiring/executor.js.map +1 -1
  15. package/src/generate-targets.d.ts +9 -2
  16. package/src/generate-targets.js +46 -6
  17. package/src/generate-targets.js.map +1 -1
  18. package/src/lib/api-docs/components-wiring.d.ts +33 -0
  19. package/src/lib/api-docs/components-wiring.js +115 -0
  20. package/src/lib/api-docs/components-wiring.js.map +1 -0
  21. package/src/lib/api-docs/generate-wiring.js +9 -3
  22. package/src/lib/api-docs/generate-wiring.js.map +1 -1
  23. package/src/lib/api-docs/generator-package.d.ts +6 -0
  24. package/src/lib/api-docs/generator-package.js +7 -1
  25. package/src/lib/api-docs/generator-package.js.map +1 -1
  26. package/src/lib/api-docs/generator-target.d.ts +7 -4
  27. package/src/lib/api-docs/generator-target.js +8 -5
  28. package/src/lib/api-docs/generator-target.js.map +1 -1
  29. package/src/plugin.d.ts +3 -1
  30. package/src/plugin.js +3 -1
  31. package/src/plugin.js.map +1 -1
@@ -5,6 +5,7 @@
5
5
  * | tag | inferred target(s) |
6
6
  * |---|---|
7
7
  * | `generate:openapi` | `openapi-generate` |
8
+ * | `generate:openapi-components` | `openapi-components-generate` — a DTO library's `components.openapi.json` (#1058) |
8
9
  * | `generate:docs-site` | `openapi-generate` + `docs-generate` (a site is rendered from a document) |
9
10
  *
10
11
  * The plugin owns the executor, `cache`, `inputs` and `outputs` — the parts that are the same for every
@@ -12,8 +13,11 @@
12
13
  * restores a package WITHOUT its documents). The consumer states only what is theirs to decide, in its
13
14
  * project.json under the SAME target name, which nx merges over what is inferred here:
14
15
  *
15
- * - `openapi-generate`: `dependsOn` (`["build"]` — the tsc target whose outputPath it writes into) and
16
- * `options.manifest` / `options.format`;
16
+ * - `openapi-generate`: `dependsOn` (`["build"]` — the tsc target whose outputPath it writes into —
17
+ * plus `"^openapi-components-generate"` when a DTO library it references publishes a components
18
+ * document) and `options.manifest` / `options.format`;
19
+ * - `openapi-components-generate`: the same two, with `"^openapi-components-generate"` when the DTO
20
+ * library itself references another one — a chain of any depth;
17
21
  * - `docs-generate`: `options.document` / `options.siteDir` (and optionally `options.prose`).
18
22
  *
19
23
  * None of those has a default (`.claude/rules/no-rule-defaults.md`): a missing one fails the run naming
@@ -27,12 +31,22 @@
27
31
  * workspace down rather than naming one project's fix.
28
32
  */
29
33
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.GenerateTargets = exports.RawProjectJsonReader = exports.RawProjectJson = exports.GENERATE_DOCS_SITE_TAG = exports.GENERATE_OPENAPI_TAG = void 0;
34
+ exports.GenerateTargets = exports.RawProjectJsonReader = exports.RawProjectJson = exports.GENERATE_OPENAPI_COMPONENTS_TAG = exports.GENERATE_DOCS_SITE_TAG = exports.GENERATE_OPENAPI_TAG = void 0;
31
35
  const core_util_1 = require("@webpieces/core-util");
32
36
  const fs_1 = require("fs");
33
37
  const path_1 = require("path");
34
38
  exports.GENERATE_OPENAPI_TAG = 'generate:openapi';
35
39
  exports.GENERATE_DOCS_SITE_TAG = 'generate:docs-site';
40
+ exports.GENERATE_OPENAPI_COMPONENTS_TAG = core_util_1.GeneratedApiDocsLayout.tagOf(core_util_1.GeneratedApiDocsLayout.COMPONENTS_TARGET);
41
+ /**
42
+ * The upstream components documents, as an nx input: a contract or DTO library's document is rendered
43
+ * FROM them (it `$ref`s their schemas and refuses a stale one), so a change to a DTO library must
44
+ * invalidate every document downstream of it, transitively (#1058).
45
+ */
46
+ const UPSTREAM_COMPONENTS_INPUT = {
47
+ dependentTasksOutputFiles: `**/${core_util_1.GeneratedApiDocsLayout.COMPONENTS_FILE}`,
48
+ transitive: true,
49
+ };
36
50
  /** The two fields of the raw project.json the inference reads. Data-only. */
37
51
  class RawProjectJson {
38
52
  name;
@@ -66,14 +80,38 @@ class GenerateTargets {
66
80
  infer() {
67
81
  const tags = this.project.tags;
68
82
  const docs = tags.includes(exports.GENERATE_DOCS_SITE_TAG);
69
- if (!docs && !tags.includes(exports.GENERATE_OPENAPI_TAG))
70
- return {};
71
83
  const targets = {};
84
+ if (tags.includes(exports.GENERATE_OPENAPI_COMPONENTS_TAG)) {
85
+ targets[core_util_1.GeneratedApiDocsLayout.COMPONENTS_TARGET] = this.componentsGenerate();
86
+ }
87
+ if (!docs && !tags.includes(exports.GENERATE_OPENAPI_TAG))
88
+ return targets;
72
89
  targets[core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET] = this.openApiGenerate();
73
90
  if (docs)
74
91
  targets[core_util_1.GeneratedApiDocsLayout.DOCS_TARGET] = this.docsGenerate();
75
92
  return targets;
76
93
  }
94
+ /** A DTO library's `components.openapi.json`, into its build outputPath — see {@link openApiGenerate}. */
95
+ componentsGenerate() {
96
+ const found = new core_util_1.GeneratedApiDocsLayout(this.projectRoot, this.project.name, this.project.targets)
97
+ .outputTarget(core_util_1.GeneratedApiDocsLayout.COMPONENTS_TARGET).found;
98
+ const outputs = found === undefined
99
+ ? []
100
+ : [
101
+ `{workspaceRoot}/${found.outputPath}/components.openapi.json`,
102
+ `{workspaceRoot}/${found.outputPath}/components.openapi.yaml`,
103
+ ];
104
+ return {
105
+ executor: '@webpieces/nx-webpieces-rules:openapi-components-generate',
106
+ cache: true,
107
+ inputs: ['default', '^default', UPSTREAM_COMPONENTS_INPUT],
108
+ outputs,
109
+ metadata: {
110
+ technologies: ['nx'],
111
+ description: "Generate the DTO library's components-only components.openapi.json into the build outputPath (tag: generate:openapi-components)",
112
+ },
113
+ };
114
+ }
77
115
  /**
78
116
  * `outputs` point into the build target's outputPath, read here from the SAME lookup the
79
117
  * executor writes with. When the consumer has not stated `dependsOn` yet there is no outputPath to
@@ -87,12 +125,14 @@ class GenerateTargets {
87
125
  : [
88
126
  `{workspaceRoot}/${found.outputPath}/*openapi.json`,
89
127
  `{workspaceRoot}/${found.outputPath}/*openapi.yaml`,
128
+ `{workspaceRoot}/${found.outputPath}/*openapi.bundled.json`,
129
+ `{workspaceRoot}/${found.outputPath}/*openapi.bundled.yaml`,
90
130
  `{workspaceRoot}/${found.outputPath}/mcp-*-tools.json`,
91
131
  ];
92
132
  return {
93
133
  executor: '@webpieces/nx-webpieces-rules:openapi-generate',
94
134
  cache: true,
95
- inputs: ['default', '^default'],
135
+ inputs: ['default', '^default', UPSTREAM_COMPONENTS_INPUT],
96
136
  outputs,
97
137
  metadata: {
98
138
  technologies: ['nx'],
@@ -1 +1 @@
1
- {"version":3,"file":"generate-targets.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/generate-targets.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAGH,oDAA6E;AAC7E,2BAAkC;AAClC,+BAA4B;AAEf,QAAA,oBAAoB,GAAG,kBAAkB,CAAC;AAC1C,QAAA,sBAAsB,GAAG,oBAAoB,CAAC;AAE3D,6EAA6E;AAC7E,MAAa,cAAc;IAEV;IACA;IACA;IAHb,YACa,IAAY,EACZ,IAAuB,EACvB,OAAsB;QAFtB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAmB;QACvB,YAAO,GAAP,OAAO,CAAe;IAChC,CAAC;CACP;AAND,wCAMC;AAKD,mGAAmG;AACnG,MAAa,oBAAoB;IAC7B,IAAI,CAAC,aAAqB,EAAE,WAAmB,EAAE,WAAmB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAsB,CAAC;QACpG,OAAO,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAC1F,CAAC;CACJ;AALD,oDAKC;AAED,MAAa,eAAe;IAGH;IACA;IAHrB;IACI,uCAAuC;IACtB,WAAmB,EACnB,OAAuB;QADvB,gBAAW,GAAX,WAAW,CAAQ;QACnB,YAAO,GAAP,OAAO,CAAgB;IACzC,CAAC;IAEJ,iFAAiF;IACjF,KAAK;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,8BAAsB,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,4BAAoB,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAwC,EAAE,CAAC;QACxD,OAAO,CAAC,kCAAsB,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACxE,IAAI,IAAI;YAAE,OAAO,CAAC,kCAAsB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAC5E,OAAO,OAAO,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACK,eAAe;QACnB,MAAM,KAAK,GAAG,IAAI,kCAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;aAC9F,YAAY,CAAC,kCAAsB,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;QAC/D,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS;YAC/B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,mBAAmB,KAAK,CAAC,UAAU,gBAAgB;gBACnD,mBAAmB,KAAK,CAAC,UAAU,gBAAgB;gBACnD,mBAAmB,KAAK,CAAC,UAAU,mBAAmB;aACzD,CAAC;QACN,OAAO;YACH,QAAQ,EAAE,gDAAgD;YAC1D,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;YAC/B,OAAO;YACP,QAAQ,EAAE;gBACN,YAAY,EAAE,CAAC,IAAI,CAAC;gBACpB,WAAW,EACP,wIAAwI;aAC/I;SACJ,CAAC;IACN,CAAC;IAEO,YAAY;QAChB,OAAO;YACH,QAAQ,EAAE,6CAA6C;YACvD,SAAS,EAAE,CAAC,kCAAsB,CAAC,cAAc,CAAC;YAClD,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;YAC/B,OAAO,EAAE,CAAC,iCAAiC,CAAC;YAC5C,QAAQ,EAAE;gBACN,YAAY,EAAE,CAAC,IAAI,CAAC;gBACpB,WAAW,EACP,yGAAyG;aAChH;SACJ,CAAC;IACN,CAAC;CACJ;AA5DD,0CA4DC","sourcesContent":["/**\n * The API-document targets the inference plugin adds to a project that OPTS IN with an nx tag:\n *\n * | tag | inferred target(s) |\n * |---|---|\n * | `generate:openapi` | `openapi-generate` |\n * | `generate:docs-site` | `openapi-generate` + `docs-generate` (a site is rendered from a document) |\n *\n * The plugin owns the executor, `cache`, `inputs` and `outputs` — the parts that are the same for every\n * consumer and that a hand-written target gets wrong silently (a missed output is a cache hit that\n * restores a package WITHOUT its documents). The consumer states only what is theirs to decide, in its\n * project.json under the SAME target name, which nx merges over what is inferred here:\n *\n * - `openapi-generate`: `dependsOn` (`[\"build\"]` — the tsc target whose outputPath it writes into) and\n * `options.manifest` / `options.format`;\n * - `docs-generate`: `options.document` / `options.siteDir` (and optionally `options.prose`).\n *\n * None of those has a default (`.claude/rules/no-rule-defaults.md`): a missing one fails the run naming\n * the key. An untagged project costs one in-memory tag check — no source scan, no `@ApiType` search.\n * A tag the INSTALLED plugin does not know yet is ignored by it, whereas a project.json naming an\n * executor the installed plugin lacks breaks the graph load — which is why opting in is a tag and not\n * a hand-written executor target.\n *\n * The wiring (`openapi-generate` dependsOn `build`, and `^openapi-generate` in every dependent's\n * `build` / `test`) is enforced by `validate-nx-wiring`, not here: this runs while nx builds the graph, where a throw takes the whole\n * workspace down rather than naming one project's fix.\n */\n\nimport type { TargetConfiguration } from '@nx/devkit';\nimport { GeneratedApiDocsLayout, LayoutTargets } from '@webpieces/core-util';\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\n\nexport const GENERATE_OPENAPI_TAG = 'generate:openapi';\nexport const GENERATE_DOCS_SITE_TAG = 'generate:docs-site';\n\n/** The two fields of the raw project.json the inference reads. Data-only. */\nexport class RawProjectJson {\n constructor(\n readonly name: string,\n readonly tags: readonly string[],\n readonly targets: LayoutTargets,\n ) {}\n}\n\n/** The fields of a project.json that tag-driven inference reads, as they sit in the file. */\ntype ProjectJsonFields = { name?: string; tags?: string[]; targets?: LayoutTargets };\n\n/** Reads the raw project.json — the tags and the consumer's own target options — at graph time. */\nexport class RawProjectJsonReader {\n read(workspaceRoot: string, projectFile: string, projectRoot: string): RawProjectJson {\n const raw = JSON.parse(readFileSync(join(workspaceRoot, projectFile), 'utf8')) as ProjectJsonFields;\n return new RawProjectJson(raw.name ?? projectRoot, raw.tags ?? [], raw.targets ?? {});\n }\n}\n\nexport class GenerateTargets {\n constructor(\n /** Workspace-relative project root. */\n private readonly projectRoot: string,\n private readonly project: RawProjectJson,\n ) {}\n\n /** The targets the project's tags opt it into — `{}` for an untagged project. */\n infer(): Record<string, TargetConfiguration> {\n const tags = this.project.tags;\n const docs = tags.includes(GENERATE_DOCS_SITE_TAG);\n if (!docs && !tags.includes(GENERATE_OPENAPI_TAG)) return {};\n const targets: Record<string, TargetConfiguration> = {};\n targets[GeneratedApiDocsLayout.OPENAPI_TARGET] = this.openApiGenerate();\n if (docs) targets[GeneratedApiDocsLayout.DOCS_TARGET] = this.docsGenerate();\n return targets;\n }\n\n /**\n * `outputs` point into the build target's outputPath, read here from the SAME lookup the\n * executor writes with. When the consumer has not stated `dependsOn` yet there is no outputPath to\n * point at, so no outputs are inferred — and the executor's refusal names the missing `dependsOn`.\n */\n private openApiGenerate(): TargetConfiguration {\n const found = new GeneratedApiDocsLayout(this.projectRoot, this.project.name, this.project.targets)\n .outputTarget(GeneratedApiDocsLayout.OPENAPI_TARGET).found;\n const outputs = found === undefined\n ? []\n : [\n `{workspaceRoot}/${found.outputPath}/*openapi.json`,\n `{workspaceRoot}/${found.outputPath}/*openapi.yaml`,\n `{workspaceRoot}/${found.outputPath}/mcp-*-tools.json`,\n ];\n return {\n executor: '@webpieces/nx-webpieces-rules:openapi-generate',\n cache: true,\n inputs: ['default', '^default'],\n outputs,\n metadata: {\n technologies: ['nx'],\n description:\n 'Generate the OpenAPI documents + one mcp-<ContractClass>-tools.json per MCP contract into the build outputPath (tag: generate:openapi)',\n },\n };\n }\n\n private docsGenerate(): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:docs-generate',\n dependsOn: [GeneratedApiDocsLayout.OPENAPI_TARGET],\n cache: true,\n inputs: ['default', '^default'],\n outputs: ['{projectRoot}/{options.siteDir}'],\n metadata: {\n technologies: ['nx'],\n description:\n 'Render the static API reference site into <projectRoot>/<siteDir> for hosting (tag: generate:docs-site)',\n },\n };\n }\n}\n"]}
1
+ {"version":3,"file":"generate-targets.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/generate-targets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The API-document targets the inference plugin adds to a project that OPTS IN with an nx tag:\n *\n * | tag | inferred target(s) |\n * |---|---|\n * | `generate:openapi` | `openapi-generate` |\n * | `generate:openapi-components` | `openapi-components-generate` — a DTO library's `components.openapi.json` (#1058) |\n * | `generate:docs-site` | `openapi-generate` + `docs-generate` (a site is rendered from a document) |\n *\n * The plugin owns the executor, `cache`, `inputs` and `outputs` — the parts that are the same for every\n * consumer and that a hand-written target gets wrong silently (a missed output is a cache hit that\n * restores a package WITHOUT its documents). The consumer states only what is theirs to decide, in its\n * project.json under the SAME target name, which nx merges over what is inferred here:\n *\n * - `openapi-generate`: `dependsOn` (`[\"build\"]` — the tsc target whose outputPath it writes into —\n * plus `\"^openapi-components-generate\"` when a DTO library it references publishes a components\n * document) and `options.manifest` / `options.format`;\n * - `openapi-components-generate`: the same two, with `\"^openapi-components-generate\"` when the DTO\n * library itself references another one — a chain of any depth;\n * - `docs-generate`: `options.document` / `options.siteDir` (and optionally `options.prose`).\n *\n * None of those has a default (`.claude/rules/no-rule-defaults.md`): a missing one fails the run naming\n * the key. An untagged project costs one in-memory tag check — no source scan, no `@ApiType` search.\n * A tag the INSTALLED plugin does not know yet is ignored by it, whereas a project.json naming an\n * executor the installed plugin lacks breaks the graph load — which is why opting in is a tag and not\n * a hand-written executor target.\n *\n * The wiring (`openapi-generate` dependsOn `build`, and `^openapi-generate` in every dependent's\n * `build` / `test`) is enforced by `validate-nx-wiring`, not here: this runs while nx builds the graph, where a throw takes the whole\n * workspace down rather than naming one project's fix.\n */\n\nimport type { TargetConfiguration } from '@nx/devkit';\nimport { GeneratedApiDocsLayout, LayoutTargets } from '@webpieces/core-util';\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\n\nexport const GENERATE_OPENAPI_TAG = 'generate:openapi';\nexport const GENERATE_DOCS_SITE_TAG = 'generate:docs-site';\nexport const GENERATE_OPENAPI_COMPONENTS_TAG = GeneratedApiDocsLayout.tagOf(GeneratedApiDocsLayout.COMPONENTS_TARGET);\n\n/**\n * The upstream components documents, as an nx input: a contract or DTO library's document is rendered\n * FROM them (it `$ref`s their schemas and refuses a stale one), so a change to a DTO library must\n * invalidate every document downstream of it, transitively (#1058).\n */\nconst UPSTREAM_COMPONENTS_INPUT = {\n dependentTasksOutputFiles: `**/${GeneratedApiDocsLayout.COMPONENTS_FILE}`,\n transitive: true,\n};\n\n/** The two fields of the raw project.json the inference reads. Data-only. */\nexport class RawProjectJson {\n constructor(\n readonly name: string,\n readonly tags: readonly string[],\n readonly targets: LayoutTargets,\n ) {}\n}\n\n/** The fields of a project.json that tag-driven inference reads, as they sit in the file. */\ntype ProjectJsonFields = { name?: string; tags?: string[]; targets?: LayoutTargets };\n\n/** Reads the raw project.json — the tags and the consumer's own target options — at graph time. */\nexport class RawProjectJsonReader {\n read(workspaceRoot: string, projectFile: string, projectRoot: string): RawProjectJson {\n const raw = JSON.parse(readFileSync(join(workspaceRoot, projectFile), 'utf8')) as ProjectJsonFields;\n return new RawProjectJson(raw.name ?? projectRoot, raw.tags ?? [], raw.targets ?? {});\n }\n}\n\nexport class GenerateTargets {\n constructor(\n /** Workspace-relative project root. */\n private readonly projectRoot: string,\n private readonly project: RawProjectJson,\n ) {}\n\n /** The targets the project's tags opt it into — `{}` for an untagged project. */\n infer(): Record<string, TargetConfiguration> {\n const tags = this.project.tags;\n const docs = tags.includes(GENERATE_DOCS_SITE_TAG);\n const targets: Record<string, TargetConfiguration> = {};\n if (tags.includes(GENERATE_OPENAPI_COMPONENTS_TAG)) {\n targets[GeneratedApiDocsLayout.COMPONENTS_TARGET] = this.componentsGenerate();\n }\n if (!docs && !tags.includes(GENERATE_OPENAPI_TAG)) return targets;\n targets[GeneratedApiDocsLayout.OPENAPI_TARGET] = this.openApiGenerate();\n if (docs) targets[GeneratedApiDocsLayout.DOCS_TARGET] = this.docsGenerate();\n return targets;\n }\n\n /** A DTO library's `components.openapi.json`, into its build outputPath — see {@link openApiGenerate}. */\n private componentsGenerate(): TargetConfiguration {\n const found = new GeneratedApiDocsLayout(this.projectRoot, this.project.name, this.project.targets)\n .outputTarget(GeneratedApiDocsLayout.COMPONENTS_TARGET).found;\n const outputs = found === undefined\n ? []\n : [\n `{workspaceRoot}/${found.outputPath}/components.openapi.json`,\n `{workspaceRoot}/${found.outputPath}/components.openapi.yaml`,\n ];\n return {\n executor: '@webpieces/nx-webpieces-rules:openapi-components-generate',\n cache: true,\n inputs: ['default', '^default', UPSTREAM_COMPONENTS_INPUT],\n outputs,\n metadata: {\n technologies: ['nx'],\n description:\n \"Generate the DTO library's components-only components.openapi.json into the build outputPath (tag: generate:openapi-components)\",\n },\n };\n }\n\n /**\n * `outputs` point into the build target's outputPath, read here from the SAME lookup the\n * executor writes with. When the consumer has not stated `dependsOn` yet there is no outputPath to\n * point at, so no outputs are inferred — and the executor's refusal names the missing `dependsOn`.\n */\n private openApiGenerate(): TargetConfiguration {\n const found = new GeneratedApiDocsLayout(this.projectRoot, this.project.name, this.project.targets)\n .outputTarget(GeneratedApiDocsLayout.OPENAPI_TARGET).found;\n const outputs = found === undefined\n ? []\n : [\n `{workspaceRoot}/${found.outputPath}/*openapi.json`,\n `{workspaceRoot}/${found.outputPath}/*openapi.yaml`,\n `{workspaceRoot}/${found.outputPath}/*openapi.bundled.json`,\n `{workspaceRoot}/${found.outputPath}/*openapi.bundled.yaml`,\n `{workspaceRoot}/${found.outputPath}/mcp-*-tools.json`,\n ];\n return {\n executor: '@webpieces/nx-webpieces-rules:openapi-generate',\n cache: true,\n inputs: ['default', '^default', UPSTREAM_COMPONENTS_INPUT],\n outputs,\n metadata: {\n technologies: ['nx'],\n description:\n 'Generate the OpenAPI documents + one mcp-<ContractClass>-tools.json per MCP contract into the build outputPath (tag: generate:openapi)',\n },\n };\n }\n\n private docsGenerate(): TargetConfiguration {\n return {\n executor: '@webpieces/nx-webpieces-rules:docs-generate',\n dependsOn: [GeneratedApiDocsLayout.OPENAPI_TARGET],\n cache: true,\n inputs: ['default', '^default'],\n outputs: ['{projectRoot}/{options.siteDir}'],\n metadata: {\n technologies: ['nx'],\n description:\n 'Render the static API reference site into <projectRoot>/<siteDir> for hosting (tag: generate:docs-site)',\n },\n };\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The CHAINED half of `validate-nx-wiring` (#1058): DTO libraries tagged `generate:openapi-components`
3
+ * publish `components.openapi.json`, and every document that `$ref`s one must be generated AFTER it.
4
+ *
5
+ * ```
6
+ * <dto-lib>:openapi-components-generate dependsOn ["build", "^openapi-components-generate"]
7
+ * <api-lib>:openapi-generate dependsOn ["build", "^openapi-components-generate"]
8
+ * ```
9
+ *
10
+ * The `^` edge is what orders a chain of any depth — contract lib → DTO lib A → DTO lib B — because
11
+ * nx skips a dependency without the target and keeps walking ITS dependencies. Without it nx may render
12
+ * a contract document before the library document it references exists, and the generator's
13
+ * fail-closed refusal ("never generated") fires intermittently, mostly in CI. So it is refused here,
14
+ * on the resolved graph, with the exact `dependsOn` to write.
15
+ */
16
+ import type { ProjectConfiguration } from '@nx/devkit';
17
+ import { DeclaredDependsOn, GenerateWiringProblem, ProjectDependency } from './generate-wiring';
18
+ export declare class ComponentsWiring {
19
+ private readonly projects;
20
+ private readonly dependencies;
21
+ private readonly declared;
22
+ constructor(projects: Readonly<Record<string, ProjectConfiguration>>, dependencies: Readonly<Record<string, readonly ProjectDependency[]>>, declared: DeclaredDependsOn);
23
+ /** The DTO libraries tagged to publish a components document. */
24
+ libraries(): string[];
25
+ problems(): GenerateWiringProblem[];
26
+ /** `openapi-components-generate` dependsOn exactly the library's own `build`, the tsc target. */
27
+ private shapeOf;
28
+ private missingEdge;
29
+ /** The components-publishing libraries `name` reaches through the dependency graph, itself excluded. */
30
+ private librariesUpstreamOf;
31
+ /** Whether `target` dependsOn `^openapi-components-generate`, in either of nx's spellings. */
32
+ private namesUpstream;
33
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * The CHAINED half of `validate-nx-wiring` (#1058): DTO libraries tagged `generate:openapi-components`
4
+ * publish `components.openapi.json`, and every document that `$ref`s one must be generated AFTER it.
5
+ *
6
+ * ```
7
+ * <dto-lib>:openapi-components-generate dependsOn ["build", "^openapi-components-generate"]
8
+ * <api-lib>:openapi-generate dependsOn ["build", "^openapi-components-generate"]
9
+ * ```
10
+ *
11
+ * The `^` edge is what orders a chain of any depth — contract lib → DTO lib A → DTO lib B — because
12
+ * nx skips a dependency without the target and keeps walking ITS dependencies. Without it nx may render
13
+ * a contract document before the library document it references exists, and the generator's
14
+ * fail-closed refusal ("never generated") fires intermittently, mostly in CI. So it is refused here,
15
+ * on the resolved graph, with the exact `dependsOn` to write.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ComponentsWiring = void 0;
19
+ const core_util_1 = require("@webpieces/core-util");
20
+ const generate_targets_1 = require("../../generate-targets");
21
+ const generate_wiring_1 = require("./generate-wiring");
22
+ /** The tsc target a library keeps, and its generating target dependsOn. */
23
+ const BUILD_TARGET = 'build';
24
+ const COMPONENTS_TARGET = core_util_1.GeneratedApiDocsLayout.COMPONENTS_TARGET;
25
+ const UPSTREAM_COMPONENTS = `^${COMPONENTS_TARGET}`;
26
+ /** The targets that READ upstream components documents, and so must run after them. */
27
+ const READING_TARGETS = [COMPONENTS_TARGET, core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET];
28
+ class ComponentsWiring {
29
+ projects;
30
+ dependencies;
31
+ declared;
32
+ constructor(projects, dependencies, declared) {
33
+ this.projects = projects;
34
+ this.dependencies = dependencies;
35
+ this.declared = declared;
36
+ }
37
+ /** The DTO libraries tagged to publish a components document. */
38
+ libraries() {
39
+ return Object.keys(this.projects)
40
+ .filter((name) => (this.projects[name].tags ?? []).includes(generate_targets_1.GENERATE_OPENAPI_COMPONENTS_TAG))
41
+ .sort();
42
+ }
43
+ problems() {
44
+ const libraries = new Set(this.libraries());
45
+ const problems = [];
46
+ for (const name of this.libraries())
47
+ problems.push(...this.shapeOf(name));
48
+ for (const name of Object.keys(this.projects).sort()) {
49
+ const upstream = this.librariesUpstreamOf(name, libraries);
50
+ if (upstream.length === 0)
51
+ continue;
52
+ for (const targetName of READING_TARGETS) {
53
+ const target = this.projects[name].targets?.[targetName];
54
+ if (target === undefined || this.namesUpstream(target))
55
+ continue;
56
+ problems.push(this.missingEdge(name, targetName, target, upstream));
57
+ }
58
+ }
59
+ return problems;
60
+ }
61
+ /** `openapi-components-generate` dependsOn exactly the library's own `build`, the tsc target. */
62
+ shapeOf(name) {
63
+ const project = this.projects[name];
64
+ const lookup = new core_util_1.GeneratedApiDocsLayout(project.root, name, project.targets ?? {})
65
+ .outputTarget(COMPONENTS_TARGET);
66
+ if (lookup.found === undefined) {
67
+ return [new generate_wiring_1.GenerateWiringProblem(name, lookup.problem.problem, `${name}: ${lookup.problem.cure}`)];
68
+ }
69
+ if (lookup.found.targetName === BUILD_TARGET)
70
+ return [];
71
+ return [new generate_wiring_1.GenerateWiringProblem(name, `${name}:${COMPONENTS_TARGET} dependsOn "${lookup.found.targetName}", and must dependsOn "${BUILD_TARGET}" — ` +
72
+ 'the library\'s @nx/js:tsc target keeps the name build (TS6059, nrwl/nx#18257).', `${name}: in ${project.root}/project.json, name the @nx/js:tsc target "${BUILD_TARGET}" and set ` +
73
+ `targets.${COMPONENTS_TARGET}.dependsOn to ["${BUILD_TARGET}", "${UPSTREAM_COMPONENTS}"].`)];
74
+ }
75
+ missingEdge(name, targetName, target, upstream) {
76
+ const where = `${this.projects[name].root}/project.json`;
77
+ const wanted = [...(target.dependsOn ?? []), UPSTREAM_COMPONENTS];
78
+ const line = `[${wanted.map((entry) => JSON.stringify(entry)).join(', ')}]`;
79
+ const replaces = this.declared.declares(name, targetName)
80
+ ? ''
81
+ : ' (a project.json dependsOn replaces nx.json targetDefaults, which is why it lists every entry)';
82
+ return new generate_wiring_1.GenerateWiringProblem(`${name}:${targetName}`, `references the components document${upstream.length === 1 ? '' : 's'} of ${upstream.join(', ')}, and its ` +
83
+ `effective dependsOn does not name "${UPSTREAM_COMPONENTS}" — so nx may render it before ` +
84
+ `${upstream.length === 1 ? 'that document exists' : 'those documents exist'}, and generation fails closed ` +
85
+ 'intermittently, mostly in CI.', `In ${where}, set targets.${targetName}.dependsOn to ${line}${replaces}.`);
86
+ }
87
+ /** The components-publishing libraries `name` reaches through the dependency graph, itself excluded. */
88
+ librariesUpstreamOf(name, libraries) {
89
+ const found = new Set();
90
+ const seen = new Set([name]);
91
+ const queue = [name];
92
+ while (queue.length > 0) {
93
+ const current = queue.shift();
94
+ for (const dependency of this.dependencies[current] ?? []) {
95
+ if (seen.has(dependency.target))
96
+ continue;
97
+ seen.add(dependency.target);
98
+ if (libraries.has(dependency.target))
99
+ found.add(dependency.target);
100
+ queue.push(dependency.target);
101
+ }
102
+ }
103
+ return [...found].sort();
104
+ }
105
+ /** Whether `target` dependsOn `^openapi-components-generate`, in either of nx's spellings. */
106
+ namesUpstream(target) {
107
+ return (target.dependsOn ?? []).some((entry) => {
108
+ if (typeof entry === 'string')
109
+ return entry === UPSTREAM_COMPONENTS;
110
+ return entry.target === COMPONENTS_TARGET && entry.dependencies === true;
111
+ });
112
+ }
113
+ }
114
+ exports.ComponentsWiring = ComponentsWiring;
115
+ //# sourceMappingURL=components-wiring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components-wiring.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-docs/components-wiring.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The CHAINED half of `validate-nx-wiring` (#1058): DTO libraries tagged `generate:openapi-components`\n * publish `components.openapi.json`, and every document that `$ref`s one must be generated AFTER it.\n *\n * ```\n * <dto-lib>:openapi-components-generate dependsOn [\"build\", \"^openapi-components-generate\"]\n * <api-lib>:openapi-generate dependsOn [\"build\", \"^openapi-components-generate\"]\n * ```\n *\n * The `^` edge is what orders a chain of any depth — contract lib → DTO lib A → DTO lib B — because\n * nx skips a dependency without the target and keeps walking ITS dependencies. Without it nx may render\n * a contract document before the library document it references exists, and the generator's\n * fail-closed refusal (\"never generated\") fires intermittently, mostly in CI. So it is refused here,\n * on the resolved graph, with the exact `dependsOn` to write.\n */\n\nimport type { ProjectConfiguration, TargetConfiguration, TargetDependencyConfig } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { GENERATE_OPENAPI_COMPONENTS_TAG } from '../../generate-targets';\nimport { DeclaredDependsOn, GenerateWiringProblem, ProjectDependency } from './generate-wiring';\n\ntype DependsOnEntry = string | TargetDependencyConfig;\n\n/** The tsc target a library keeps, and its generating target dependsOn. */\nconst BUILD_TARGET = 'build';\nconst COMPONENTS_TARGET = GeneratedApiDocsLayout.COMPONENTS_TARGET;\nconst UPSTREAM_COMPONENTS = `^${COMPONENTS_TARGET}`;\n\n/** The targets that READ upstream components documents, and so must run after them. */\nconst READING_TARGETS: readonly string[] = [COMPONENTS_TARGET, GeneratedApiDocsLayout.OPENAPI_TARGET];\n\nexport class ComponentsWiring {\n constructor(\n private readonly projects: Readonly<Record<string, ProjectConfiguration>>,\n private readonly dependencies: Readonly<Record<string, readonly ProjectDependency[]>>,\n private readonly declared: DeclaredDependsOn,\n ) {}\n\n /** The DTO libraries tagged to publish a components document. */\n libraries(): string[] {\n return Object.keys(this.projects)\n .filter((name: string) => (this.projects[name]!.tags ?? []).includes(GENERATE_OPENAPI_COMPONENTS_TAG))\n .sort();\n }\n\n problems(): GenerateWiringProblem[] {\n const libraries = new Set(this.libraries());\n const problems: GenerateWiringProblem[] = [];\n for (const name of this.libraries()) problems.push(...this.shapeOf(name));\n for (const name of Object.keys(this.projects).sort()) {\n const upstream = this.librariesUpstreamOf(name, libraries);\n if (upstream.length === 0) continue;\n for (const targetName of READING_TARGETS) {\n const target = this.projects[name]!.targets?.[targetName];\n if (target === undefined || this.namesUpstream(target)) continue;\n problems.push(this.missingEdge(name, targetName, target, upstream));\n }\n }\n return problems;\n }\n\n /** `openapi-components-generate` dependsOn exactly the library's own `build`, the tsc target. */\n private shapeOf(name: string): GenerateWiringProblem[] {\n const project = this.projects[name]!;\n const lookup = new GeneratedApiDocsLayout(project.root, name, project.targets ?? {})\n .outputTarget(COMPONENTS_TARGET);\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 return [new GenerateWiringProblem(\n name,\n `${name}:${COMPONENTS_TARGET} dependsOn \"${lookup.found.targetName}\", and must dependsOn \"${BUILD_TARGET}\" — ` +\n 'the library\\'s @nx/js:tsc target keeps the name build (TS6059, nrwl/nx#18257).',\n `${name}: in ${project.root}/project.json, name the @nx/js:tsc target \"${BUILD_TARGET}\" and set ` +\n `targets.${COMPONENTS_TARGET}.dependsOn to [\"${BUILD_TARGET}\", \"${UPSTREAM_COMPONENTS}\"].`,\n )];\n }\n\n private missingEdge(\n name: string,\n targetName: string,\n target: TargetConfiguration,\n upstream: readonly string[],\n ): GenerateWiringProblem {\n const where = `${this.projects[name]!.root}/project.json`;\n const wanted = [...((target.dependsOn ?? []) as DependsOnEntry[]), UPSTREAM_COMPONENTS];\n const line = `[${wanted.map((entry: DependsOnEntry) => JSON.stringify(entry)).join(', ')}]`;\n const replaces = this.declared.declares(name, targetName)\n ? ''\n : ' (a project.json dependsOn replaces nx.json targetDefaults, which is why it lists every entry)';\n return new GenerateWiringProblem(\n `${name}:${targetName}`,\n `references the components document${upstream.length === 1 ? '' : 's'} of ${upstream.join(', ')}, and its ` +\n `effective dependsOn does not name \"${UPSTREAM_COMPONENTS}\" — so nx may render it before ` +\n `${upstream.length === 1 ? 'that document exists' : 'those documents exist'}, and generation fails closed ` +\n 'intermittently, mostly in CI.',\n `In ${where}, set targets.${targetName}.dependsOn to ${line}${replaces}.`,\n );\n }\n\n /** The components-publishing libraries `name` reaches through the dependency graph, itself excluded. */\n private librariesUpstreamOf(name: string, libraries: 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 (libraries.has(dependency.target)) found.add(dependency.target);\n queue.push(dependency.target);\n }\n }\n return [...found].sort();\n }\n\n /** Whether `target` dependsOn `^openapi-components-generate`, in either of nx's spellings. */\n private namesUpstream(target: TargetConfiguration): boolean {\n return (target.dependsOn ?? []).some((entry: DependsOnEntry) => {\n if (typeof entry === 'string') return entry === UPSTREAM_COMPONENTS;\n return entry.target === COMPONENTS_TARGET && entry.dependencies === true;\n });\n }\n}\n"]}
@@ -115,6 +115,7 @@ exports.WiringSourceReader = WiringSourceReader;
115
115
  /** The executors the plugin infers from a tag — a project.json naming one by hand is a second opt-in. */
116
116
  const INFERRED_EXECUTORS = [
117
117
  '@webpieces/nx-webpieces-rules:openapi-generate',
118
+ '@webpieces/nx-webpieces-rules:openapi-components-generate',
118
119
  '@webpieces/nx-webpieces-rules:docs-generate',
119
120
  ];
120
121
  /** The tsc target an api library keeps, and `openapi-generate` dependsOn. */
@@ -178,7 +179,12 @@ class GenerateWiring {
178
179
  }
179
180
  problems() {
180
181
  const generating = this.generating();
181
- const problems = this.handWritten(new Set(generating));
182
+ const tagged = new Set(generating);
183
+ for (const name of Object.keys(this.projects)) {
184
+ if ((this.projects[name].tags ?? []).includes(generate_targets_1.GENERATE_OPENAPI_COMPONENTS_TAG))
185
+ tagged.add(name);
186
+ }
187
+ const problems = this.handWritten(tagged);
182
188
  for (const name of generating)
183
189
  problems.push(...this.shapeOf(name));
184
190
  problems.push(...this.dependentsOf(new Set(generating)));
@@ -198,8 +204,8 @@ class GenerateWiring {
198
204
  if (target.executor === undefined || !INFERRED_EXECUTORS.includes(target.executor))
199
205
  continue;
200
206
  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}" ` +
207
+ `"${generate_targets_1.GENERATE_OPENAPI_TAG}" / "${generate_targets_1.GENERATE_OPENAPI_COMPONENTS_TAG}" / "${generate_targets_1.GENERATE_DOCS_SITE_TAG}" tag. ` +
208
+ 'Opting in is the tag; the plugin infers the executor.', `${name}: add "${targetName === core_util_1.GeneratedApiDocsLayout.DOCS_TARGET ? generate_targets_1.GENERATE_DOCS_SITE_TAG : core_util_1.GeneratedApiDocsLayout.tagOf(targetName)}" ` +
203
209
  `to "tags" in ${project.root}/project.json and delete the "executor" line of targets.${targetName}.`));
204
210
  }
205
211
  }
@@ -1 +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 ?? {})\n .outputTarget(GeneratedApiDocsLayout.OPENAPI_TARGET);\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"]}
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 {\n GENERATE_DOCS_SITE_TAG,\n GENERATE_OPENAPI_COMPONENTS_TAG,\n GENERATE_OPENAPI_TAG,\n} 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:openapi-components-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 tagged = new Set(generating);\n for (const name of Object.keys(this.projects)) {\n if ((this.projects[name]!.tags ?? []).includes(GENERATE_OPENAPI_COMPONENTS_TAG)) tagged.add(name);\n }\n const problems: GenerateWiringProblem[] = this.handWritten(tagged);\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_OPENAPI_COMPONENTS_TAG}\" / \"${GENERATE_DOCS_SITE_TAG}\" tag. ` +\n 'Opting in is the tag; the plugin infers the executor.',\n `${name}: add \"${targetName === GeneratedApiDocsLayout.DOCS_TARGET ? GENERATE_DOCS_SITE_TAG : GeneratedApiDocsLayout.tagOf(targetName)}\" ` +\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 ?? {})\n .outputTarget(GeneratedApiDocsLayout.OPENAPI_TARGET);\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"]}
@@ -19,5 +19,11 @@ export declare class GeneratorPackage {
19
19
  * older generator writes the single `mcp-tools.json` no server of this release reads.
20
20
  */
21
21
  export declare const OPENAPI_GENERATOR: GeneratorPackage;
22
+ /**
23
+ * `wp-openapi` reading a `"kind": "components"` manifest into a DTO library's
24
+ * `components.openapi.json` (#1058). 0.4.830 is the first release carrying it: an older generator
25
+ * refuses the manifest's missing `apis`.
26
+ */
27
+ export declare const OPENAPI_COMPONENTS_GENERATOR: GeneratorPackage;
22
28
  /** `wp-docs-site --spec/--prose/--out`: 0.4.807 is the first release carrying the docs site (#1007). */
23
29
  export declare const DOCS_SITE: GeneratorPackage;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DOCS_SITE = exports.OPENAPI_GENERATOR = exports.GeneratorPackage = void 0;
3
+ exports.DOCS_SITE = exports.OPENAPI_COMPONENTS_GENERATOR = exports.OPENAPI_GENERATOR = exports.GeneratorPackage = void 0;
4
4
  /**
5
5
  * A generator the `openapi-generate` / `docs-generate` executors RUN but never bundle, and the oldest
6
6
  * release of it that has every capability the executor relies on. Data-only.
@@ -27,6 +27,12 @@ exports.GeneratorPackage = GeneratorPackage;
27
27
  * older generator writes the single `mcp-tools.json` no server of this release reads.
28
28
  */
29
29
  exports.OPENAPI_GENERATOR = new GeneratorPackage('@webpieces/openapi-generator', 'wp-openapi', '0.4.812');
30
+ /**
31
+ * `wp-openapi` reading a `"kind": "components"` manifest into a DTO library's
32
+ * `components.openapi.json` (#1058). 0.4.830 is the first release carrying it: an older generator
33
+ * refuses the manifest's missing `apis`.
34
+ */
35
+ exports.OPENAPI_COMPONENTS_GENERATOR = new GeneratorPackage('@webpieces/openapi-generator', 'wp-openapi', '0.4.830');
30
36
  /** `wp-docs-site --spec/--prose/--out`: 0.4.807 is the first release carrying the docs site (#1007). */
31
37
  exports.DOCS_SITE = new GeneratorPackage('@webpieces/docs-site', 'wp-docs-site', '0.4.807');
32
38
  //# sourceMappingURL=generator-package.js.map
@@ -1 +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"]}
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;;;;GAIG;AACU,QAAA,4BAA4B,GAAG,IAAI,gBAAgB,CAAC,8BAA8B,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAE1H,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/**\n * `wp-openapi` reading a `\"kind\": \"components\"` manifest into a DTO library's\n * `components.openapi.json` (#1058). 0.4.830 is the first release carrying it: an older generator\n * refuses the manifest's missing `apis`.\n */\nexport const OPENAPI_COMPONENTS_GENERATOR = new GeneratorPackage('@webpieces/openapi-generator', 'wp-openapi', '0.4.830');\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"]}
@@ -1,10 +1,12 @@
1
1
  /**
2
- * The shared half of the `openapi-generate` and `docs-generate` executors: WHERE a generated document
2
+ * The shared half of the `openapi-generate`, `openapi-components-generate` and `docs-generate`
3
+ * executors: WHERE a generated document
3
4
  * goes, and the proof that the target is wired so that nx both orders and caches it correctly.
4
5
  *
5
6
  * Both halves read the project's own declarations and never supply a value of their own:
6
7
  *
7
- * - the documents' directory is the `outputPath` of the target `openapi-generate` dependsOn — the api
8
+ * - the documents' directory is the `outputPath` of the target the generating target
9
+ * (`openapi-generate`, or a DTO library's `openapi-components-generate`) dependsOn — the api
8
10
  * library's `build` (tsc) step, which writes the directory the package is packed from, so the
9
11
  * documents ship INSIDE the published package. It is ASKED of nx through `GeneratedApiDocsLayout`
10
12
  * (`@webpieces/core-util`), the same lookup `McpToolCatalog.fromPackages` reads with, and never
@@ -36,9 +38,10 @@ export declare class GeneratorTarget {
36
38
  static of(ruleName: string, context: ExecutorContext): GeneratorTarget;
37
39
  /**
38
40
  * Absolute path to the directory the documents live in: the `outputPath` of the target
39
- * `openapi-generate` dependsOn — the directory the package is packed from.
41
+ * `generateTarget` dependsOn — the directory the package is packed from. `generateTarget` is
42
+ * `openapi-generate` for a contract library and `openapi-components-generate` for a DTO library.
40
43
  */
41
- documentsDir(): string;
44
+ documentsDir(generateTarget: string): string;
42
45
  /** Absolute `<projectRoot>/<dir>`, refusing anything that is not strictly inside the project. */
43
46
  insideProject(dir: string, optionName: string): string;
44
47
  /** Refuse unless this target `dependsOn` the sibling `required` target. */
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  /**
3
- * The shared half of the `openapi-generate` and `docs-generate` executors: WHERE a generated document
3
+ * The shared half of the `openapi-generate`, `openapi-components-generate` and `docs-generate`
4
+ * executors: WHERE a generated document
4
5
  * goes, and the proof that the target is wired so that nx both orders and caches it correctly.
5
6
  *
6
7
  * Both halves read the project's own declarations and never supply a value of their own:
7
8
  *
8
- * - the documents' directory is the `outputPath` of the target `openapi-generate` dependsOn — the api
9
+ * - the documents' directory is the `outputPath` of the target the generating target
10
+ * (`openapi-generate`, or a DTO library's `openapi-components-generate`) dependsOn — the api
9
11
  * library's `build` (tsc) step, which writes the directory the package is packed from, so the
10
12
  * documents ship INSIDE the published package. It is ASKED of nx through `GeneratedApiDocsLayout`
11
13
  * (`@webpieces/core-util`), the same lookup `McpToolCatalog.fromPackages` reads with, and never
@@ -60,11 +62,12 @@ class GeneratorTarget {
60
62
  }
61
63
  /**
62
64
  * Absolute path to the directory the documents live in: the `outputPath` of the target
63
- * `openapi-generate` dependsOn — the directory the package is packed from.
65
+ * `generateTarget` dependsOn — the directory the package is packed from. `generateTarget` is
66
+ * `openapi-generate` for a contract library and `openapi-components-generate` for a DTO library.
64
67
  */
65
- documentsDir() {
68
+ documentsDir(generateTarget) {
66
69
  const lookup = new core_util_1.GeneratedApiDocsLayout(this.projectRoot, this.projectName, this.targets)
67
- .outputTarget(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET);
70
+ .outputTarget(generateTarget);
68
71
  if (lookup.found === undefined) {
69
72
  throw new rules_config_1.RuleFailError(this.ruleName, `${lookup.problem.problem} The documents go into the build's own output directory — the ` +
70
73
  'one the package is packed from — and are never assumed to be ./dist.', undefined, undefined, [new rules_config_1.Option(lookup.problem.cure, true)]);