@webpieces/nx-webpieces-rules 0.4.830 → 0.4.832
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 +5 -0
- package/package.json +8 -8
- package/src/executors/docs-generate/executor.js +1 -1
- package/src/executors/docs-generate/executor.js.map +1 -1
- package/src/executors/openapi-components-generate/executor.d.ts +32 -0
- package/src/executors/openapi-components-generate/executor.js +40 -0
- package/src/executors/openapi-components-generate/executor.js.map +1 -0
- package/src/executors/openapi-components-generate/schema.json +18 -0
- package/src/executors/openapi-generate/executor.d.ts +25 -1
- package/src/executors/openapi-generate/executor.js +50 -19
- package/src/executors/openapi-generate/executor.js.map +1 -1
- package/src/executors/validate-nx-wiring/executor.d.ts +5 -0
- package/src/executors/validate-nx-wiring/executor.js +11 -2
- package/src/executors/validate-nx-wiring/executor.js.map +1 -1
- package/src/generate-targets.d.ts +9 -2
- package/src/generate-targets.js +46 -6
- package/src/generate-targets.js.map +1 -1
- package/src/lib/api-docs/components-wiring.d.ts +33 -0
- package/src/lib/api-docs/components-wiring.js +115 -0
- package/src/lib/api-docs/components-wiring.js.map +1 -0
- package/src/lib/api-docs/generate-wiring.js +9 -3
- package/src/lib/api-docs/generate-wiring.js.map +1 -1
- package/src/lib/api-docs/generator-package.d.ts +6 -0
- package/src/lib/api-docs/generator-package.js +7 -1
- package/src/lib/api-docs/generator-package.js.map +1 -1
- package/src/lib/api-docs/generator-target.d.ts +7 -4
- package/src/lib/api-docs/generator-target.js +8 -5
- package/src/lib/api-docs/generator-target.js.map +1 -1
- package/src/plugin.d.ts +3 -1
- package/src/plugin.js +3 -1
- package/src/plugin.js.map +1 -1
package/executors.json
CHANGED
|
@@ -155,6 +155,11 @@
|
|
|
155
155
|
"schema": "./src/executors/openapi-generate/schema.json",
|
|
156
156
|
"description": "Per-project, inferred from the generate:openapi tag: render the contracts to OpenAPI + one mcp-<ContractClass>-tools.json per MCP contract into the build (tsc) target outputPath, with the consumer's own @webpieces/openapi-generator"
|
|
157
157
|
},
|
|
158
|
+
"openapi-components-generate": {
|
|
159
|
+
"implementation": "./src/executors/openapi-components-generate/executor",
|
|
160
|
+
"schema": "./src/executors/openapi-components-generate/schema.json",
|
|
161
|
+
"description": "Inferred on generate:openapi-components: write a DTO library's components.openapi.json into its build outputPath"
|
|
162
|
+
},
|
|
158
163
|
"docs-generate": {
|
|
159
164
|
"implementation": "./src/executors/docs-generate/executor",
|
|
160
165
|
"schema": "./src/executors/docs-generate/schema.json",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.832",
|
|
4
4
|
"description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@webpieces/ai-hook-rules": "0.4.
|
|
22
|
-
"@webpieces/api-doc-model": "0.4.
|
|
23
|
-
"@webpieces/code-rules": "0.4.
|
|
24
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
-
"@webpieces/eslint-rules": "0.4.
|
|
26
|
-
"@webpieces/pr-gate": "0.4.
|
|
27
|
-
"@webpieces/rules-config": "0.4.
|
|
21
|
+
"@webpieces/ai-hook-rules": "0.4.832",
|
|
22
|
+
"@webpieces/api-doc-model": "0.4.832",
|
|
23
|
+
"@webpieces/code-rules": "0.4.832",
|
|
24
|
+
"@webpieces/core-util": "0.4.832",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.832",
|
|
26
|
+
"@webpieces/pr-gate": "0.4.832",
|
|
27
|
+
"@webpieces/rules-config": "0.4.832",
|
|
28
28
|
"madge": "8.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
@@ -55,7 +55,7 @@ class DocsGenerate {
|
|
|
55
55
|
target.assertDependsOn(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET);
|
|
56
56
|
const document = target.requiredOption(options.document, 'document');
|
|
57
57
|
const siteOut = target.insideProject(target.requiredOption(options.siteDir, 'siteDir'), 'siteDir');
|
|
58
|
-
const documentsDir = target.documentsDir();
|
|
58
|
+
const documentsDir = target.documentsDir(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET);
|
|
59
59
|
const spec = path.join(documentsDir, document);
|
|
60
60
|
if (!fs.existsSync(spec)) {
|
|
61
61
|
throw new rules_config_1.RuleFailError(RULE_NAME, `${path.relative(context.root, spec)} does not exist, so there is nothing to render. ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/docs-generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AA8EH,8BAcC;;AAzFD,oDAA8D;AAC9D,0DAAyG;AACzG,+CAAyB;AACzB,mDAA6B;AAC7B,2DAAuD;AACvD,oFAAmG;AACnG,4EAAiE;AACjE,0EAAsE;AACtE,0EAAoF;AACpF,2CAAwC;AAWxC,MAAM,SAAS,GAAG,kCAAsB,CAAC,WAAW,CAAC;AAErD,iGAAiG;AACjG,MAAa,YAAY;IAEA;IACA;IACA;IAHrB,YACqB,WAAgC,IAAI,2CAAmB,EAAE,EACzD,SAA0B,IAAI,kCAAe,EAAE,EAC/C,UAA2B,IAAI,8BAAe,EAAE;QAFhD,aAAQ,GAAR,QAAQ,CAAiD;QACzD,WAAM,GAAN,MAAM,CAAyC;QAC/C,YAAO,GAAP,OAAO,CAAyC;IAClE,CAAC;IAEJ,mFAAmF;IACnF,GAAG,CAAC,OAA4B,EAAE,OAAwB;QACtD,MAAM,MAAM,GAAG,kCAAe,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,CAAC,eAAe,CAAC,kCAAsB,CAAC,cAAc,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QACnG,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/docs-generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AA8EH,8BAcC;;AAzFD,oDAA8D;AAC9D,0DAAyG;AACzG,+CAAyB;AACzB,mDAA6B;AAC7B,2DAAuD;AACvD,oFAAmG;AACnG,4EAAiE;AACjE,0EAAsE;AACtE,0EAAoF;AACpF,2CAAwC;AAWxC,MAAM,SAAS,GAAG,kCAAsB,CAAC,WAAW,CAAC;AAErD,iGAAiG;AACjG,MAAa,YAAY;IAEA;IACA;IACA;IAHrB,YACqB,WAAgC,IAAI,2CAAmB,EAAE,EACzD,SAA0B,IAAI,kCAAe,EAAE,EAC/C,UAA2B,IAAI,8BAAe,EAAE;QAFhD,aAAQ,GAAR,QAAQ,CAAiD;QACzD,WAAM,GAAN,MAAM,CAAyC;QAC/C,YAAO,GAAP,OAAO,CAAyC;IAClE,CAAC;IAEJ,mFAAmF;IACnF,GAAG,CAAC,OAA4B,EAAE,OAAwB;QACtD,MAAM,MAAM,GAAG,kCAAe,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,CAAC,eAAe,CAAC,kCAAsB,CAAC,cAAc,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QACnG,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,kCAAsB,CAAC,cAAc,CAAC,CAAC;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,4BAAa,CACnB,SAAS,EACT,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,kDAAkD;gBAClF,kFAAkF,EACtF,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CAAC,+DAA+D,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CACjI,CAAC;QACN,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,0CAAkB,CACpD,SAAS,EAAE,6BAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAExF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QACrE,gGAAgG;QAChG,0FAA0F;QAC1F,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YACjG,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACV,MAAM,IAAI,4BAAa,CAAC,SAAS,EAAE,GAAG,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,OAAO,YAAY,QAAQ,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAChH,CAAC;YACD,6FAA6F;YAC7F,iCAAiC;YACjC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,MAAM,OAAO,GAAG,IAAI,+BAAY,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO,OAAO,CAAC;QACnB,CAAC;gBAAS,CAAC;YACP,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;CACJ;AAjDD,oCAiDC;AAED,gHAAgH;AACjG,KAAK,UAAU,WAAW,CACrC,OAA4B,EAC5B,OAAwB;IAExB,iHAAiH;IACjH,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,MAAM,iCAAiC,OAAO,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;QACjG,OAAO,IAAI,gCAAc,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,SAAS,KAAK,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACnH,OAAO,IAAI,gCAAc,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;AACL,CAAC","sourcesContent":["/**\n * docs-generate Executor\n *\n * Renders the static API reference site from a document `openapi-generate` already wrote into the api\n * library's build output, and writes the site into `<projectRoot>/<siteDir>` — a gitignored directory\n * of the project, for HOSTING. It is deliberately not written into the package: a docs site is\n * something you deploy, not something an npm consumer installs.\n *\n * Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project\n * tagged `generate:docs-site` (which implies `generate:openapi`), with `dependsOn: [\"openapi-generate\"]`;\n * project.json states only the options, under the same target name:\n *\n * \"docs-generate\": {\n * \"options\": { \"document\": \"public-openapi.json\", \"siteDir\": \"generated-docs\", \"prose\": \"<project>/docs\" }\n * }\n *\n * and the repo's .gitignore carries `generated-docs/`.\n *\n * `document` should be the PARTNER-facing document: a site built from `full-private-openapi.json`\n * publishes exactly the operations somebody decided not to publish. `prose` is optional because a site\n * with no prose pages is a legal site; `document` and `siteDir` are required and have no defaults.\n *\n * The renderer is the CONSUMER's `@webpieces/docs-site`, resolved from its node_modules and\n * version-checked; this plugin bundles no copy of it.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { Option, RepoScratchDirs, RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ExecutorResult } from '../../executor-result';\nimport { ConsumerBinRequest, ConsumerBinResolver } from '../../lib/api-docs/consumer-bin-resolver';\nimport { DOCS_SITE } from '../../lib/api-docs/generator-package';\nimport { GeneratorRunner } from '../../lib/api-docs/generator-runner';\nimport { GeneratorTarget, StagedOutput } from '../../lib/api-docs/generator-target';\nimport { toError } from '../../toError';\n\nexport interface DocsGenerateOptions {\n /** A document name inside the api library's build output, e.g. `public-openapi.json`. Required. */\n document?: string;\n /** The site's directory, relative to the project root, e.g. `generated-docs`. Required. */\n siteDir?: string;\n /** Workspace-relative directory holding `docs.manifest.json` and its markdown. Optional. */\n prose?: string;\n}\n\nconst RULE_NAME = GeneratedApiDocsLayout.DOCS_TARGET;\n\n/** Everything except the process-facing reporting, so the suite drives it exactly as nx does. */\nexport class DocsGenerate {\n constructor(\n private readonly resolver: ConsumerBinResolver = new ConsumerBinResolver(),\n private readonly runner: GeneratorRunner = new GeneratorRunner(),\n private readonly scratch: RepoScratchDirs = new RepoScratchDirs(),\n ) {}\n\n /** @returns the files written, absolute. Throws RuleFailError on every refusal. */\n run(options: DocsGenerateOptions, context: ExecutorContext): string[] {\n const target = GeneratorTarget.of(RULE_NAME, context);\n target.assertDependsOn(GeneratedApiDocsLayout.OPENAPI_TARGET);\n const document = target.requiredOption(options.document, 'document');\n const siteOut = target.insideProject(target.requiredOption(options.siteDir, 'siteDir'), 'siteDir');\n const documentsDir = target.documentsDir(GeneratedApiDocsLayout.OPENAPI_TARGET);\n const spec = path.join(documentsDir, document);\n if (!fs.existsSync(spec)) {\n throw new RuleFailError(\n RULE_NAME,\n `${path.relative(context.root, spec)} does not exist, so there is nothing to render. ` +\n `openapi-generate writes only the documents the contracts' @ApiType(...) ask for.`,\n undefined,\n undefined,\n [new Option(`Set options.document to a file openapi-generate writes into ${path.relative(context.root, documentsDir)}`, true)],\n );\n }\n const bin = this.resolver.resolve(new ConsumerBinRequest(\n RULE_NAME, DOCS_SITE, [path.join(context.root, target.projectRoot), context.root]));\n\n const staging = this.scratch.make(context.root, 'wp-docs-generate-');\n // webpieces-disable no-unmanaged-exceptions -- try/FINALLY only, nothing is caught: the staging\n // directory is removed however this ends, and every throw still reaches runExecutor below\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const args = ['--spec', spec, '--out', staging];\n if (options.prose !== undefined) args.push('--prose', path.resolve(context.root, options.prose));\n const run = this.runner.run(bin, args, context.root);\n if (!run.ok) {\n throw new RuleFailError(RULE_NAME, `${bin.packageName} ${bin.version} refused ${document}:\\n${run.output}`);\n }\n // The site REPLACES the previous one: a page for an operation that no longer exists must not\n // survive into what gets hosted.\n fs.rmSync(siteOut, { recursive: true, force: true });\n const written = new StagedOutput().publish(staging, siteOut);\n target.assertOutputsCover(written);\n return written;\n } finally {\n fs.rmSync(staging, { recursive: true, force: true });\n }\n }\n}\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: DocsGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- this IS the executor's single top-level handler\n try {\n const written = new DocsGenerate().run(options, context);\n console.log(`wrote ${written.length} file(s) of the docs site for ${context.projectName ?? ''}`);\n return new ExecutorResult(true);\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`❌ ${RULE_NAME}: ${error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message}`);\n return new ExecutorResult(false);\n }\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openapi-components-generate Executor (#1058)
|
|
3
|
+
*
|
|
4
|
+
* Renders a DTO library's components-only OpenAPI document, `components.openapi.json` — every type
|
|
5
|
+
* the library exports and declares, `info.version` = the package version — INTO the outputPath of the
|
|
6
|
+
* target it dependsOn (the library's `build`), so it ships inside the library's npm package exactly as
|
|
7
|
+
* a contract library's documents do. Contract documents `$ref` its schemas instead of copying them.
|
|
8
|
+
*
|
|
9
|
+
* Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project
|
|
10
|
+
* tagged `generate:openapi-components`; project.json states only the consumer's values:
|
|
11
|
+
*
|
|
12
|
+
* "tags": ["generate:openapi-components"],
|
|
13
|
+
* "targets": {
|
|
14
|
+
* "openapi-components-generate": {
|
|
15
|
+
* "dependsOn": ["build", "^openapi-components-generate"],
|
|
16
|
+
* "options": { "manifest": "<project>/openapi.manifest.json", "format": "json" }
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* where the manifest declares `"kind": "components"` and names the library's entry files. The
|
|
21
|
+
* `^openapi-components-generate` edge orders a chain of DTO libraries; a contract library's
|
|
22
|
+
* `openapi-generate` carries the same edge. validate-nx-wiring enforces both (ComponentsWiring).
|
|
23
|
+
*
|
|
24
|
+
* It is the openapi-generate executor body with its own target name and generator minimum: `wp-openapi`
|
|
25
|
+
* reads the manifest's `kind` and decides what to render.
|
|
26
|
+
*/
|
|
27
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
28
|
+
import { ExecutorResult } from '../../executor-result';
|
|
29
|
+
import { GenerateSpec, OpenApiGenerateOptions } from '../openapi-generate/executor';
|
|
30
|
+
/** `openapi-components-generate`: a DTO library's `components.openapi.json`. */
|
|
31
|
+
export declare const COMPONENTS_DOCUMENT: GenerateSpec;
|
|
32
|
+
export default function runExecutor(options: OpenApiGenerateOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* openapi-components-generate Executor (#1058)
|
|
4
|
+
*
|
|
5
|
+
* Renders a DTO library's components-only OpenAPI document, `components.openapi.json` — every type
|
|
6
|
+
* the library exports and declares, `info.version` = the package version — INTO the outputPath of the
|
|
7
|
+
* target it dependsOn (the library's `build`), so it ships inside the library's npm package exactly as
|
|
8
|
+
* a contract library's documents do. Contract documents `$ref` its schemas instead of copying them.
|
|
9
|
+
*
|
|
10
|
+
* Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project
|
|
11
|
+
* tagged `generate:openapi-components`; project.json states only the consumer's values:
|
|
12
|
+
*
|
|
13
|
+
* "tags": ["generate:openapi-components"],
|
|
14
|
+
* "targets": {
|
|
15
|
+
* "openapi-components-generate": {
|
|
16
|
+
* "dependsOn": ["build", "^openapi-components-generate"],
|
|
17
|
+
* "options": { "manifest": "<project>/openapi.manifest.json", "format": "json" }
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* where the manifest declares `"kind": "components"` and names the library's entry files. The
|
|
22
|
+
* `^openapi-components-generate` edge orders a chain of DTO libraries; a contract library's
|
|
23
|
+
* `openapi-generate` carries the same edge. validate-nx-wiring enforces both (ComponentsWiring).
|
|
24
|
+
*
|
|
25
|
+
* It is the openapi-generate executor body with its own target name and generator minimum: `wp-openapi`
|
|
26
|
+
* reads the manifest's `kind` and decides what to render.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.COMPONENTS_DOCUMENT = void 0;
|
|
30
|
+
exports.default = runExecutor;
|
|
31
|
+
const core_util_1 = require("@webpieces/core-util");
|
|
32
|
+
const generator_package_1 = require("../../lib/api-docs/generator-package");
|
|
33
|
+
const executor_1 = require("../openapi-generate/executor");
|
|
34
|
+
/** `openapi-components-generate`: a DTO library's `components.openapi.json`. */
|
|
35
|
+
exports.COMPONENTS_DOCUMENT = new executor_1.GenerateSpec(core_util_1.GeneratedApiDocsLayout.COMPONENTS_TARGET, generator_package_1.OPENAPI_COMPONENTS_GENERATOR);
|
|
36
|
+
// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here
|
|
37
|
+
async function runExecutor(options, context) {
|
|
38
|
+
return executor_1.GenerateExecutorMain.run(exports.COMPONENTS_DOCUMENT, options, context);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/openapi-components-generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;AAmBH,8BAKC;AArBD,oDAA8D;AAE9D,4EAAoF;AACpF,2DAIsC;AAEtC,gFAAgF;AACnE,QAAA,mBAAmB,GAAG,IAAI,uBAAY,CAC/C,kCAAsB,CAAC,iBAAiB,EACxC,gDAA4B,CAC/B,CAAC;AAEF,gHAAgH;AACjG,KAAK,UAAU,WAAW,CACrC,OAA+B,EAC/B,OAAwB;IAExB,OAAO,+BAAoB,CAAC,GAAG,CAAC,2BAAmB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["/**\n * openapi-components-generate Executor (#1058)\n *\n * Renders a DTO library's components-only OpenAPI document, `components.openapi.json` — every type\n * the library exports and declares, `info.version` = the package version — INTO the outputPath of the\n * target it dependsOn (the library's `build`), so it ships inside the library's npm package exactly as\n * a contract library's documents do. Contract documents `$ref` its schemas instead of copying them.\n *\n * Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project\n * tagged `generate:openapi-components`; project.json states only the consumer's values:\n *\n * \"tags\": [\"generate:openapi-components\"],\n * \"targets\": {\n * \"openapi-components-generate\": {\n * \"dependsOn\": [\"build\", \"^openapi-components-generate\"],\n * \"options\": { \"manifest\": \"<project>/openapi.manifest.json\", \"format\": \"json\" }\n * }\n * }\n *\n * where the manifest declares `\"kind\": \"components\"` and names the library's entry files. The\n * `^openapi-components-generate` edge orders a chain of DTO libraries; a contract library's\n * `openapi-generate` carries the same edge. validate-nx-wiring enforces both (ComponentsWiring).\n *\n * It is the openapi-generate executor body with its own target name and generator minimum: `wp-openapi`\n * reads the manifest's `kind` and decides what to render.\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { ExecutorResult } from '../../executor-result';\nimport { OPENAPI_COMPONENTS_GENERATOR } from '../../lib/api-docs/generator-package';\nimport {\n GenerateExecutorMain,\n GenerateSpec,\n OpenApiGenerateOptions,\n} from '../openapi-generate/executor';\n\n/** `openapi-components-generate`: a DTO library's `components.openapi.json`. */\nexport const COMPONENTS_DOCUMENT = new GenerateSpec(\n GeneratedApiDocsLayout.COMPONENTS_TARGET,\n OPENAPI_COMPONENTS_GENERATOR,\n);\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: OpenApiGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n return GenerateExecutorMain.run(COMPONENTS_DOCUMENT, options, context);\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"title": "OpenAPI Components Generate Executor",
|
|
4
|
+
"description": "Inferred on a project tagged generate:openapi-components. Render the DTO library's components-only components.openapi.json (every exported type it declares, info.version = package version) with the consumer's own @webpieces/openapi-generator, into the outputPath of the ONE target this target dependsOn (the library's build).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"manifest": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Workspace-relative path to the library's \"kind\": \"components\" openapi.manifest.json. No default."
|
|
10
|
+
},
|
|
11
|
+
"format": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"enum": ["json", "both"],
|
|
14
|
+
"description": "Which serializations to write. components.openapi.json is what contract documents $ref, so JSON is always written. No default."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"required": ["manifest", "format"]
|
|
18
|
+
}
|
|
@@ -34,6 +34,7 @@ import type { ExecutorContext } from '@nx/devkit';
|
|
|
34
34
|
import { RepoScratchDirs } from '@webpieces/rules-config';
|
|
35
35
|
import { ExecutorResult } from '../../executor-result';
|
|
36
36
|
import { ConsumerBinResolver } from '../../lib/api-docs/consumer-bin-resolver';
|
|
37
|
+
import { GeneratorPackage } from '../../lib/api-docs/generator-package';
|
|
37
38
|
import { GeneratorRunner } from '../../lib/api-docs/generator-runner';
|
|
38
39
|
export interface OpenApiGenerateOptions {
|
|
39
40
|
/** Workspace-relative path to the project's `openapi.manifest.json`. Required; no default. */
|
|
@@ -41,13 +42,36 @@ export interface OpenApiGenerateOptions {
|
|
|
41
42
|
/** `json`, `yaml` or `both`. Required; no default — it decides what the package ships. */
|
|
42
43
|
format?: string;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* WHICH generating target an {@link OpenApiGenerate} runs as: the target name (what its refusals are
|
|
47
|
+
* reported under, and whose dependsOn names the output directory) and the oldest generator release
|
|
48
|
+
* carrying what that target needs. Data-only.
|
|
49
|
+
*
|
|
50
|
+
* `wp-openapi` decides what to render from the manifest's own `kind`, so the contract documents and a
|
|
51
|
+
* DTO library's components document (#1058) are one executor body with two of these.
|
|
52
|
+
*/
|
|
53
|
+
export declare class GenerateSpec {
|
|
54
|
+
readonly targetName: string;
|
|
55
|
+
readonly generator: GeneratorPackage;
|
|
56
|
+
constructor(targetName: string, generator: GeneratorPackage);
|
|
57
|
+
}
|
|
58
|
+
/** `openapi-generate`: a contract library's documents and MCP tool catalogs. */
|
|
59
|
+
export declare const CONTRACT_DOCUMENTS: GenerateSpec;
|
|
44
60
|
/** Everything except the process-facing reporting, so the suite drives it exactly as nx does. */
|
|
45
61
|
export declare class OpenApiGenerate {
|
|
62
|
+
/** Which generating target this run is — stated by every caller, never implied. */
|
|
63
|
+
private readonly spec;
|
|
46
64
|
private readonly resolver;
|
|
47
65
|
private readonly runner;
|
|
48
66
|
private readonly scratch;
|
|
49
|
-
constructor(
|
|
67
|
+
constructor(
|
|
68
|
+
/** Which generating target this run is — stated by every caller, never implied. */
|
|
69
|
+
spec: GenerateSpec, resolver?: ConsumerBinResolver, runner?: GeneratorRunner, scratch?: RepoScratchDirs);
|
|
50
70
|
/** @returns the files written, absolute. Throws RuleFailError on every refusal. */
|
|
51
71
|
run(options: OpenApiGenerateOptions, context: ExecutorContext): string[];
|
|
52
72
|
}
|
|
73
|
+
/** The single top-level handler both generating executors report through. */
|
|
74
|
+
export declare class GenerateExecutorMain {
|
|
75
|
+
static run(spec: GenerateSpec, options: OpenApiGenerateOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
76
|
+
}
|
|
53
77
|
export default function runExecutor(options: OpenApiGenerateOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* version-checked; this plugin bundles no copy of it (see ConsumerBinResolver).
|
|
33
33
|
*/
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.OpenApiGenerate = void 0;
|
|
35
|
+
exports.GenerateExecutorMain = exports.OpenApiGenerate = exports.CONTRACT_DOCUMENTS = exports.GenerateSpec = void 0;
|
|
36
36
|
exports.default = runExecutor;
|
|
37
37
|
const tslib_1 = require("tslib");
|
|
38
38
|
const core_util_1 = require("@webpieces/core-util");
|
|
@@ -45,24 +45,47 @@ const generator_package_1 = require("../../lib/api-docs/generator-package");
|
|
|
45
45
|
const generator_runner_1 = require("../../lib/api-docs/generator-runner");
|
|
46
46
|
const generator_target_1 = require("../../lib/api-docs/generator-target");
|
|
47
47
|
const toError_1 = require("../../toError");
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* WHICH generating target an {@link OpenApiGenerate} runs as: the target name (what its refusals are
|
|
50
|
+
* reported under, and whose dependsOn names the output directory) and the oldest generator release
|
|
51
|
+
* carrying what that target needs. Data-only.
|
|
52
|
+
*
|
|
53
|
+
* `wp-openapi` decides what to render from the manifest's own `kind`, so the contract documents and a
|
|
54
|
+
* DTO library's components document (#1058) are one executor body with two of these.
|
|
55
|
+
*/
|
|
56
|
+
class GenerateSpec {
|
|
57
|
+
targetName;
|
|
58
|
+
generator;
|
|
59
|
+
constructor(targetName, generator) {
|
|
60
|
+
this.targetName = targetName;
|
|
61
|
+
this.generator = generator;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.GenerateSpec = GenerateSpec;
|
|
65
|
+
/** `openapi-generate`: a contract library's documents and MCP tool catalogs. */
|
|
66
|
+
exports.CONTRACT_DOCUMENTS = new GenerateSpec(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET, generator_package_1.OPENAPI_GENERATOR);
|
|
49
67
|
/** Everything except the process-facing reporting, so the suite drives it exactly as nx does. */
|
|
50
68
|
class OpenApiGenerate {
|
|
69
|
+
spec;
|
|
51
70
|
resolver;
|
|
52
71
|
runner;
|
|
53
72
|
scratch;
|
|
54
|
-
constructor(
|
|
73
|
+
constructor(
|
|
74
|
+
/** Which generating target this run is — stated by every caller, never implied. */
|
|
75
|
+
spec, resolver = new consumer_bin_resolver_1.ConsumerBinResolver(), runner = new generator_runner_1.GeneratorRunner(), scratch = new rules_config_1.RepoScratchDirs()) {
|
|
76
|
+
this.spec = spec;
|
|
55
77
|
this.resolver = resolver;
|
|
56
78
|
this.runner = runner;
|
|
57
79
|
this.scratch = scratch;
|
|
58
80
|
}
|
|
59
81
|
/** @returns the files written, absolute. Throws RuleFailError on every refusal. */
|
|
60
82
|
run(options, context) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
83
|
+
const ruleName = this.spec.targetName;
|
|
84
|
+
const target = generator_target_1.GeneratorTarget.of(ruleName, context);
|
|
85
|
+
const outDir = target.documentsDir(ruleName);
|
|
63
86
|
const manifest = target.requiredOption(options.manifest, 'manifest');
|
|
64
87
|
const format = target.requiredOption(options.format, 'format');
|
|
65
|
-
const bin = this.resolver.resolve(new consumer_bin_resolver_1.ConsumerBinRequest(
|
|
88
|
+
const bin = this.resolver.resolve(new consumer_bin_resolver_1.ConsumerBinRequest(ruleName, this.spec.generator, [path.join(context.root, target.projectRoot), context.root]));
|
|
66
89
|
const staging = this.scratch.make(context.root, 'wp-openapi-generate-');
|
|
67
90
|
// webpieces-disable no-unmanaged-exceptions -- try/FINALLY only, nothing is caught: the staging
|
|
68
91
|
// directory is removed however this ends, and every throw still reaches runExecutor below
|
|
@@ -72,7 +95,7 @@ class OpenApiGenerate {
|
|
|
72
95
|
'--manifest', path.resolve(context.root, manifest), '--out', staging, '--format', format,
|
|
73
96
|
], context.root);
|
|
74
97
|
if (!run.ok) {
|
|
75
|
-
throw new rules_config_1.RuleFailError(
|
|
98
|
+
throw new rules_config_1.RuleFailError(ruleName, `${bin.packageName} ${bin.version} refused ${manifest}:\n${run.output}`);
|
|
76
99
|
}
|
|
77
100
|
const written = new generator_target_1.StagedOutput().publish(staging, outDir);
|
|
78
101
|
target.assertOutputsCover(written);
|
|
@@ -84,19 +107,27 @@ class OpenApiGenerate {
|
|
|
84
107
|
}
|
|
85
108
|
}
|
|
86
109
|
exports.OpenApiGenerate = OpenApiGenerate;
|
|
110
|
+
/** The single top-level handler both generating executors report through. */
|
|
111
|
+
class GenerateExecutorMain {
|
|
112
|
+
// webpieces-disable no-function-outside-class -- static entry point of this class
|
|
113
|
+
static async run(spec, options, context) {
|
|
114
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- this IS the executor's single top-level handler
|
|
115
|
+
try {
|
|
116
|
+
const written = new OpenApiGenerate(spec).run(options, context);
|
|
117
|
+
for (const file of written)
|
|
118
|
+
console.log(`wrote ${path.relative(context.root, file)}`);
|
|
119
|
+
return new executor_result_1.ExecutorResult(true);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
const error = (0, toError_1.toError)(err);
|
|
123
|
+
console.error(`❌ ${spec.targetName}: ${error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message}`);
|
|
124
|
+
return new executor_result_1.ExecutorResult(false);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.GenerateExecutorMain = GenerateExecutorMain;
|
|
87
129
|
// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here
|
|
88
130
|
async function runExecutor(options, context) {
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const written = new OpenApiGenerate().run(options, context);
|
|
92
|
-
for (const file of written)
|
|
93
|
-
console.log(`wrote ${path.relative(context.root, file)}`);
|
|
94
|
-
return new executor_result_1.ExecutorResult(true);
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
const error = (0, toError_1.toError)(err);
|
|
98
|
-
console.error(`❌ ${RULE_NAME}: ${error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message}`);
|
|
99
|
-
return new executor_result_1.ExecutorResult(false);
|
|
100
|
-
}
|
|
131
|
+
return GenerateExecutorMain.run(exports.CONTRACT_DOCUMENTS, options, context);
|
|
101
132
|
}
|
|
102
133
|
//# sourceMappingURL=executor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/openapi-generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * openapi-generate Executor\n *\n * Renders a project's contracts to OpenAPI, plus ONE MCP tool catalog per contract\n * (`mcp-<ContractClass>-tools.json`), INTO the outputPath of the target it dependsOn — the api\n * library's `build` (its @nx/js:tsc step) — so the documents are packed and published inside the api library's npm\n * package: a consumer installs the library and has the contract. They are build output, never\n * committed.\n *\n * Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project\n * tagged `generate:openapi` (executor, cache, inputs, outputs); project.json states only what is the\n * consumer's to decide, under the same target name:\n *\n * \"tags\": [\"generate:openapi\"],\n * \"targets\": {\n * \"build\": { \"executor\": \"@nx/js:tsc\", \"outputs\": [\"{options.outputPath}\"],\n * \"options\": { \"outputPath\": \"dist/<project>\", ... } },\n * \"openapi-generate\": {\n * \"dependsOn\": [\"build\"],\n * \"options\": { \"manifest\": \"<project>/openapi.manifest.json\", \"format\": \"both\" }\n * }\n * }\n *\n * and dependents pull it in through nx.json: `\"^openapi-generate\"` in every targetDefaults entry\n * governing a `build` or `test` (#1023 — see GenerateWiring).\n *\n * The output directory is the outputPath of the ONE target `dependsOn` names (GeneratedApiDocsLayout\n * in @webpieces/core-util — the same lookup the MCP server reads with), never a hardcoded target name\n * and never an assumed dist/. `outputs` are CHECKED against what a run wrote — see GeneratorTarget. The\n * generator itself is the CONSUMER's `@webpieces/openapi-generator`, resolved from its node_modules and\n * version-checked; this plugin bundles no copy of it (see ConsumerBinResolver).\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { RepoScratchDirs, RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ExecutorResult } from '../../executor-result';\nimport { ConsumerBinRequest, ConsumerBinResolver } from '../../lib/api-docs/consumer-bin-resolver';\nimport { OPENAPI_GENERATOR } from '../../lib/api-docs/generator-package';\nimport { GeneratorRunner } from '../../lib/api-docs/generator-runner';\nimport { GeneratorTarget, StagedOutput } from '../../lib/api-docs/generator-target';\nimport { toError } from '../../toError';\n\nexport interface OpenApiGenerateOptions {\n /** Workspace-relative path to the project's `openapi.manifest.json`. Required; no default. */\n manifest?: string;\n /** `json`, `yaml` or `both`. Required; no default — it decides what the package ships. */\n format?: string;\n}\n\nconst RULE_NAME = GeneratedApiDocsLayout.OPENAPI_TARGET;\n\n/** Everything except the process-facing reporting, so the suite drives it exactly as nx does. */\nexport class OpenApiGenerate {\n constructor(\n private readonly resolver: ConsumerBinResolver = new ConsumerBinResolver(),\n private readonly runner: GeneratorRunner = new GeneratorRunner(),\n private readonly scratch: RepoScratchDirs = new RepoScratchDirs(),\n ) {}\n\n /** @returns the files written, absolute. Throws RuleFailError on every refusal. */\n run(options: OpenApiGenerateOptions, context: ExecutorContext): string[] {\n const target = GeneratorTarget.of(RULE_NAME, context);\n const outDir = target.documentsDir();\n const manifest = target.requiredOption(options.manifest, 'manifest');\n const format = target.requiredOption(options.format, 'format');\n const bin = this.resolver.resolve(new ConsumerBinRequest(\n RULE_NAME, OPENAPI_GENERATOR, [path.join(context.root, target.projectRoot), context.root]));\n\n const staging = this.scratch.make(context.root, 'wp-openapi-generate-');\n // webpieces-disable no-unmanaged-exceptions -- try/FINALLY only, nothing is caught: the staging\n // directory is removed however this ends, and every throw still reaches runExecutor below\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const run = this.runner.run(bin, [\n '--manifest', path.resolve(context.root, manifest), '--out', staging, '--format', format,\n ], context.root);\n if (!run.ok) {\n throw new RuleFailError(\n RULE_NAME,\n `${bin.packageName} ${bin.version} refused ${manifest}:\\n${run.output}`,\n );\n }\n const written = new StagedOutput().publish(staging, outDir);\n target.assertOutputsCover(written);\n return written;\n } finally {\n fs.rmSync(staging, { recursive: true, force: true });\n }\n }\n}\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: OpenApiGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- this IS the executor's single top-level handler\n try {\n const written = new OpenApiGenerate().run(options, context);\n for (const file of written) console.log(`wrote ${path.relative(context.root, file)}`);\n return new ExecutorResult(true);\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`❌ ${RULE_NAME}: ${error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message}`);\n return new ExecutorResult(false);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/openapi-generate/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;;;AAoGH,8BAKC;;AAtGD,oDAA8D;AAC9D,0DAAiG;AACjG,+CAAyB;AACzB,mDAA6B;AAC7B,2DAAuD;AACvD,oFAAmG;AACnG,4EAA2F;AAC3F,0EAAsE;AACtE,0EAAoF;AACpF,2CAAwC;AASxC;;;;;;;GAOG;AACH,MAAa,YAAY;IAER;IACA;IAFb,YACa,UAAkB,EAClB,SAA2B;QAD3B,eAAU,GAAV,UAAU,CAAQ;QAClB,cAAS,GAAT,SAAS,CAAkB;IACrC,CAAC;CACP;AALD,oCAKC;AAED,gFAAgF;AACnE,QAAA,kBAAkB,GAAG,IAAI,YAAY,CAAC,kCAAsB,CAAC,cAAc,EAAE,qCAAiB,CAAC,CAAC;AAE7G,iGAAiG;AACjG,MAAa,eAAe;IAGH;IACA;IACA;IACA;IALrB;IACI,mFAAmF;IAClE,IAAkB,EAClB,WAAgC,IAAI,2CAAmB,EAAE,EACzD,SAA0B,IAAI,kCAAe,EAAE,EAC/C,UAA2B,IAAI,8BAAe,EAAE;QAHhD,SAAI,GAAJ,IAAI,CAAc;QAClB,aAAQ,GAAR,QAAQ,CAAiD;QACzD,WAAM,GAAN,MAAM,CAAyC;QAC/C,YAAO,GAAP,OAAO,CAAyC;IAClE,CAAC;IAEJ,mFAAmF;IACnF,GAAG,CAAC,OAA+B,EAAE,OAAwB;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;QACtC,MAAM,MAAM,GAAG,kCAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,0CAAkB,CACpD,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEjG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;QACxE,gGAAgG;QAChG,0FAA0F;QAC1F,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;gBAC7B,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM;aAC3F,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACV,MAAM,IAAI,4BAAa,CACnB,QAAQ,EACR,GAAG,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,OAAO,YAAY,QAAQ,MAAM,GAAG,CAAC,MAAM,EAAE,CAC1E,CAAC;YACN,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,+BAAY,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5D,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO,OAAO,CAAC;QACnB,CAAC;gBAAS,CAAC;YACP,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;CACJ;AAxCD,0CAwCC;AAED,6EAA6E;AAC7E,MAAa,oBAAoB;IAC7B,kFAAkF;IAClF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAkB,EAAE,OAA+B,EAAE,OAAwB;QAC1F,iHAAiH;QACjH,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAChE,KAAK,MAAM,IAAI,IAAI,OAAO;gBAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YACtF,OAAO,IAAI,gCAAc,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,UAAU,KAAK,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzH,OAAO,IAAI,gCAAc,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;CACJ;AAdD,oDAcC;AAED,gHAAgH;AACjG,KAAK,UAAU,WAAW,CACrC,OAA+B,EAC/B,OAAwB;IAExB,OAAO,oBAAoB,CAAC,GAAG,CAAC,0BAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["/**\n * openapi-generate Executor\n *\n * Renders a project's contracts to OpenAPI, plus ONE MCP tool catalog per contract\n * (`mcp-<ContractClass>-tools.json`), INTO the outputPath of the target it dependsOn — the api\n * library's `build` (its @nx/js:tsc step) — so the documents are packed and published inside the api library's npm\n * package: a consumer installs the library and has the contract. They are build output, never\n * committed.\n *\n * Consumers do not declare this executor. The nx-webpieces-rules plugin INFERS the target on a project\n * tagged `generate:openapi` (executor, cache, inputs, outputs); project.json states only what is the\n * consumer's to decide, under the same target name:\n *\n * \"tags\": [\"generate:openapi\"],\n * \"targets\": {\n * \"build\": { \"executor\": \"@nx/js:tsc\", \"outputs\": [\"{options.outputPath}\"],\n * \"options\": { \"outputPath\": \"dist/<project>\", ... } },\n * \"openapi-generate\": {\n * \"dependsOn\": [\"build\"],\n * \"options\": { \"manifest\": \"<project>/openapi.manifest.json\", \"format\": \"both\" }\n * }\n * }\n *\n * and dependents pull it in through nx.json: `\"^openapi-generate\"` in every targetDefaults entry\n * governing a `build` or `test` (#1023 — see GenerateWiring).\n *\n * The output directory is the outputPath of the ONE target `dependsOn` names (GeneratedApiDocsLayout\n * in @webpieces/core-util — the same lookup the MCP server reads with), never a hardcoded target name\n * and never an assumed dist/. `outputs` are CHECKED against what a run wrote — see GeneratorTarget. The\n * generator itself is the CONSUMER's `@webpieces/openapi-generator`, resolved from its node_modules and\n * version-checked; this plugin bundles no copy of it (see ConsumerBinResolver).\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { RepoScratchDirs, RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { ExecutorResult } from '../../executor-result';\nimport { ConsumerBinRequest, ConsumerBinResolver } from '../../lib/api-docs/consumer-bin-resolver';\nimport { GeneratorPackage, OPENAPI_GENERATOR } from '../../lib/api-docs/generator-package';\nimport { GeneratorRunner } from '../../lib/api-docs/generator-runner';\nimport { GeneratorTarget, StagedOutput } from '../../lib/api-docs/generator-target';\nimport { toError } from '../../toError';\n\nexport interface OpenApiGenerateOptions {\n /** Workspace-relative path to the project's `openapi.manifest.json`. Required; no default. */\n manifest?: string;\n /** `json`, `yaml` or `both`. Required; no default — it decides what the package ships. */\n format?: string;\n}\n\n/**\n * WHICH generating target an {@link OpenApiGenerate} runs as: the target name (what its refusals are\n * reported under, and whose dependsOn names the output directory) and the oldest generator release\n * carrying what that target needs. Data-only.\n *\n * `wp-openapi` decides what to render from the manifest's own `kind`, so the contract documents and a\n * DTO library's components document (#1058) are one executor body with two of these.\n */\nexport class GenerateSpec {\n constructor(\n readonly targetName: string,\n readonly generator: GeneratorPackage,\n ) {}\n}\n\n/** `openapi-generate`: a contract library's documents and MCP tool catalogs. */\nexport const CONTRACT_DOCUMENTS = new GenerateSpec(GeneratedApiDocsLayout.OPENAPI_TARGET, OPENAPI_GENERATOR);\n\n/** Everything except the process-facing reporting, so the suite drives it exactly as nx does. */\nexport class OpenApiGenerate {\n constructor(\n /** Which generating target this run is — stated by every caller, never implied. */\n private readonly spec: GenerateSpec,\n private readonly resolver: ConsumerBinResolver = new ConsumerBinResolver(),\n private readonly runner: GeneratorRunner = new GeneratorRunner(),\n private readonly scratch: RepoScratchDirs = new RepoScratchDirs(),\n ) {}\n\n /** @returns the files written, absolute. Throws RuleFailError on every refusal. */\n run(options: OpenApiGenerateOptions, context: ExecutorContext): string[] {\n const ruleName = this.spec.targetName;\n const target = GeneratorTarget.of(ruleName, context);\n const outDir = target.documentsDir(ruleName);\n const manifest = target.requiredOption(options.manifest, 'manifest');\n const format = target.requiredOption(options.format, 'format');\n const bin = this.resolver.resolve(new ConsumerBinRequest(\n ruleName, this.spec.generator, [path.join(context.root, target.projectRoot), context.root]));\n\n const staging = this.scratch.make(context.root, 'wp-openapi-generate-');\n // webpieces-disable no-unmanaged-exceptions -- try/FINALLY only, nothing is caught: the staging\n // directory is removed however this ends, and every throw still reaches runExecutor below\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const run = this.runner.run(bin, [\n '--manifest', path.resolve(context.root, manifest), '--out', staging, '--format', format,\n ], context.root);\n if (!run.ok) {\n throw new RuleFailError(\n ruleName,\n `${bin.packageName} ${bin.version} refused ${manifest}:\\n${run.output}`,\n );\n }\n const written = new StagedOutput().publish(staging, outDir);\n target.assertOutputsCover(written);\n return written;\n } finally {\n fs.rmSync(staging, { recursive: true, force: true });\n }\n }\n}\n\n/** The single top-level handler both generating executors report through. */\nexport class GenerateExecutorMain {\n // webpieces-disable no-function-outside-class -- static entry point of this class\n static async run(spec: GenerateSpec, options: OpenApiGenerateOptions, context: ExecutorContext): Promise<ExecutorResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- this IS the executor's single top-level handler\n try {\n const written = new OpenApiGenerate(spec).run(options, context);\n for (const file of written) console.log(`wrote ${path.relative(context.root, file)}`);\n return new ExecutorResult(true);\n } catch (err: unknown) {\n const error = toError(err);\n console.error(`❌ ${spec.targetName}: ${error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message}`);\n return new ExecutorResult(false);\n }\n }\n}\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: OpenApiGenerateOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n return GenerateExecutorMain.run(CONTRACT_DOCUMENTS, options, context);\n}\n"]}
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
* `^openapi-generate` in the effective dependsOn of its `build` and `test`. A failure names the
|
|
25
25
|
* nx.json targetDefaults key to edit and the exact line. See GenerateWiring for why.
|
|
26
26
|
*
|
|
27
|
+
* 4. Chained API documents (#1058): a DTO library tagged `generate:openapi-components` has
|
|
28
|
+
* `openapi-components-generate` dependsOn its own `build`, and every `openapi-generate` /
|
|
29
|
+
* `openapi-components-generate` reaching such a library — transitively — names
|
|
30
|
+
* `^openapi-components-generate`, so a chain of any depth renders in order. See ComponentsWiring.
|
|
31
|
+
*
|
|
27
32
|
* Conservative by design: only REQUIRES wiring on compile executors actually in use
|
|
28
33
|
* (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.
|
|
29
34
|
*
|
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
* `^openapi-generate` in the effective dependsOn of its `build` and `test`. A failure names the
|
|
26
26
|
* nx.json targetDefaults key to edit and the exact line. See GenerateWiring for why.
|
|
27
27
|
*
|
|
28
|
+
* 4. Chained API documents (#1058): a DTO library tagged `generate:openapi-components` has
|
|
29
|
+
* `openapi-components-generate` dependsOn its own `build`, and every `openapi-generate` /
|
|
30
|
+
* `openapi-components-generate` reaching such a library — transitively — names
|
|
31
|
+
* `^openapi-components-generate`, so a chain of any depth renders in order. See ComponentsWiring.
|
|
32
|
+
*
|
|
28
33
|
* Conservative by design: only REQUIRES wiring on compile executors actually in use
|
|
29
34
|
* (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.
|
|
30
35
|
*
|
|
@@ -37,6 +42,7 @@ exports.default = runExecutor;
|
|
|
37
42
|
const tslib_1 = require("tslib");
|
|
38
43
|
const devkit_1 = require("@nx/devkit");
|
|
39
44
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
45
|
+
const components_wiring_1 = require("../../lib/api-docs/components-wiring");
|
|
40
46
|
const generate_wiring_1 = require("../../lib/api-docs/generate-wiring");
|
|
41
47
|
const fs = tslib_1.__importStar(require("fs"));
|
|
42
48
|
const path = tslib_1.__importStar(require("path"));
|
|
@@ -191,8 +197,11 @@ async function runExecutor(options, context) {
|
|
|
191
197
|
const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
|
|
192
198
|
const projectsConfig = (0, devkit_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
|
|
193
199
|
const sources = new generate_wiring_1.WiringSourceReader(context.root);
|
|
194
|
-
const
|
|
195
|
-
const
|
|
200
|
+
const declared = sources.declaredDependsOn(projectsConfig.projects);
|
|
201
|
+
const generateWiring = new generate_wiring_1.GenerateWiring(projectsConfig.projects, projectGraph.dependencies, sources.targetDefaults(), declared);
|
|
202
|
+
// The chained half (#1058): a document that $refs a DTO library's components document runs after it.
|
|
203
|
+
const componentsWiring = new components_wiring_1.ComponentsWiring(projectsConfig.projects, projectGraph.dependencies, declared);
|
|
204
|
+
const generateProblems = [...generateWiring.problems(), ...componentsWiring.problems()];
|
|
196
205
|
const inUse = findCompileExecutorsInUse(projectsConfig, compileExecutors);
|
|
197
206
|
const relevantExecutors = compileExecutors.filter((executorName) => inUse.has(executorName));
|
|
198
207
|
if (relevantExecutors.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-nx-wiring/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Nx Wiring Executor\n *\n * Enforces that the build graph is wired for correct build ORDER. Two checks:\n *\n * 1. nx.json targetDefaults — the compile executors (@nx/js:tsc, @angular/build:application)\n * must carry the load-bearing dependsOn:\n * [\"^build\"]\n *\n * 2. Per-project override guard — a project that declares its OWN build.dependsOn OVERRIDES\n * the targetDefaults entirely (nx does not merge dependsOn). That silently drops `^build`\n * (→ upstream libs not built first, builds against an empty dist). So we check each compile\n * project's RESOLVED build.dependsOn and fail if `^build` is missing.\n *\n * NOTE: the webpieces validators (architecture:validate-complete, per-project file-import\n * cycles, DI-graph unchanged) NO LONGER ride on the compile target. They live on the `ci`\n * composite target (synthesized by this plugin's createCiTarget), so `build`/compile is a\n * fast compile-only step while `nx ci` runs the full gate. This executor therefore only\n * guards build ORDER (`^build`), not the validators — those are guaranteed by the plugin.\n *\n * 3. Generated API documents (#1023) — on the RESOLVED graph: a project tagged `generate:openapi` /\n * `generate:docs-site` has `openapi-generate` dependsOn exactly its own `build` (the @nx/js:tsc\n * target, which keeps that name), and every project depending on one — transitively — has\n * `^openapi-generate` in the effective dependsOn of its `build` and `test`. A failure names the\n * nx.json targetDefaults key to edit and the exact line. See GenerateWiring for why.\n *\n * Conservative by design: only REQUIRES wiring on compile executors actually in use\n * (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.\n *\n * Disable via webpieces.config.json rules[\"nx-wiring\"].mode=\"OFF\".\n *\n * Usage: nx run architecture:validate-nx-wiring\n */\n\nimport type {\n ExecutorContext,\n ProjectsConfigurations,\n TargetDependencyConfig,\n} from '@nx/devkit';\nimport { createProjectGraphAsync, readProjectsConfigurationFromProjectGraph } from '@nx/devkit';\nimport { loadAndValidate } from '@webpieces/rules-config';\nimport { GenerateWiring, WiringSourceReader } from '../../lib/api-docs/generate-wiring';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateNxWiringOptions {\n requiredDeps?: string[];\n compileExecutors?: string[];\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// Compile executors (and any per-project build override) must keep `^build` so build order\n// follows nx's full graph — upstreams are built into dist before dependents compile.\n// Validators no longer belong here; they ride on the `ci` composite target instead.\nconst DEFAULT_REQUIRED_DEPS: string[] = ['^build'];\n\nconst DEFAULT_COMPILE_EXECUTORS: string[] = [\n '@nx/js:tsc',\n '@angular/build:application',\n];\n\nclass WiringProblem {\n executorName: string;\n missing: string[];\n found: string[];\n\n constructor(executorName: string, missing: string[], found: string[]) {\n this.executorName = executorName;\n this.missing = missing;\n this.found = found;\n }\n}\n\nclass ProjectGateProblem {\n project: string;\n executorName: string;\n missing: string[];\n found: string[];\n\n constructor(project: string, executorName: string, missing: string[], found: string[]) {\n this.project = project;\n this.executorName = executorName;\n this.missing = missing;\n this.found = found;\n }\n}\n\ninterface TargetDefaultEntry {\n dependsOn?: string[];\n}\n\ninterface RawNxJson {\n targetDefaults?: Record<string, TargetDefaultEntry>;\n}\n\nfunction readTargetDefaults(workspaceRoot: string): Record<string, TargetDefaultEntry> {\n const nxJsonPath = path.join(workspaceRoot, 'nx.json');\n if (!fs.existsSync(nxJsonPath)) return {};\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const raw = fs.readFileSync(nxJsonPath, 'utf8');\n const parsed = JSON.parse(raw) as RawNxJson;\n return parsed.targetDefaults ?? {};\n // webpieces-disable catch-error-pattern -- malformed nx.json fails open so the check does not crash the build\n } catch (err: unknown) {\n //const error = toError(err); -- malformed nx.json fails open\n void err;\n return {};\n }\n}\n\nfunction findCompileExecutorsInUse(\n projectsConfig: ProjectsConfigurations,\n compileExecutors: string[],\n): Set<string> {\n const known = new Set(compileExecutors);\n const inUse = new Set<string>();\n for (const cfg of Object.values(projectsConfig.projects)) {\n const targets = cfg.targets ?? {};\n for (const target of Object.values(targets)) {\n const executorName = target.executor;\n if (executorName && known.has(executorName)) {\n inUse.add(executorName);\n }\n }\n }\n return inUse;\n}\n\nfunction findProblems(\n relevantExecutors: string[],\n targetDefaults: Record<string, TargetDefaultEntry>,\n requiredDeps: string[],\n): WiringProblem[] {\n const problems: WiringProblem[] = [];\n for (const executorName of relevantExecutors) {\n const entry = targetDefaults[executorName];\n const dependsOn = entry?.dependsOn ?? [];\n const missing = requiredDeps.filter((dep: string) => !dependsOn.includes(dep));\n if (missing.length > 0) {\n problems.push(new WiringProblem(executorName, missing, dependsOn));\n }\n }\n return problems;\n}\n\n/**\n * Normalize an nx dependsOn entry to a comparable string. The string form (\"^build\",\n * \"validate-complete\") passes through; the object form is rendered the same way\n * ({target:\"build\", dependencies:true} → \"^build\", {target:\"build\"} → \"build\").\n */\nfunction normalizeDeps(deps: (string | TargetDependencyConfig)[]): string[] {\n return deps.map((dep) => {\n if (typeof dep === 'string') return dep;\n const target = dep.target ?? '';\n return dep.dependencies ? `^${target}` : target;\n });\n}\n\n/**\n * Check each compile project's RESOLVED build.dependsOn (targetDefaults already merged in)\n * for `^build`. A project that overrode it away is flagged.\n */\nfunction findProjectGateProblems(\n projectsConfig: ProjectsConfigurations,\n compileExecutors: Set<string>,\n requiredPerProject: string[],\n): ProjectGateProblem[] {\n const problems: ProjectGateProblem[] = [];\n for (const [name, cfg] of Object.entries(projectsConfig.projects)) {\n // Tooling/foundation packages (type:tooling) BUILD the validators, so gating their own\n // build on validate-complete would create a bootstrap cycle. They are exempt: they keep\n // build-order (\"^build\") only. Application/library code is still fully gated.\n if ((cfg.tags ?? []).includes('type:tooling')) continue;\n const build = cfg.targets?.['build'];\n if (!build || !build.executor || !compileExecutors.has(build.executor)) continue;\n const dependsOn = normalizeDeps(build.dependsOn ?? []);\n const missing = requiredPerProject.filter((dep) => !dependsOn.includes(dep));\n if (missing.length > 0) {\n problems.push(new ProjectGateProblem(name, build.executor, missing, dependsOn));\n }\n }\n return problems;\n}\n\nfunction reportFailure(problems: WiringProblem[], requiredDeps: string[]): void {\n console.error('\\n❌ Build order is not wired in nx.json targetDefaults.\\n');\n console.error('Compile executors must keep \"^build\" so upstream libs build into dist first.');\n console.error('Add the missing dependsOn entries to nx.json targetDefaults:\\n');\n const depsList = requiredDeps.map((dep: string) => `\"${dep}\"`).join(', ');\n for (const problem of problems) {\n const missingList = problem.missing.map((dep: string) => `\"${dep}\"`).join(', ');\n console.error(` \"${problem.executorName}\": {`);\n console.error(` \"dependsOn\": [${depsList}]`);\n console.error(' }');\n console.error(` missing: ${missingList}\\n`);\n }\n console.error('To turn this wiring check off, set rules[\"nx-wiring\"].mode=\"OFF\" in');\n console.error('webpieces.config.json.\\n');\n}\n\nfunction reportProjectGateFailure(problems: ProjectGateProblem[]): void {\n console.error('\\n❌ Some projects override away the build order in their project.json.\\n');\n console.error('A project-level build.dependsOn OVERRIDES nx.json targetDefaults (nx does not');\n console.error('merge dependsOn). That silently drops \"^build\" (upstreams not built first →');\n console.error('builds against an empty dist).\\n');\n console.error('Fix: remove build.dependsOn from these project.json files so targetDefaults');\n console.error('apply, OR include \"^build\" explicitly:\\n');\n for (const p of problems) {\n const missingList = p.missing.map((dep) => `\"${dep}\"`).join(', ');\n console.error(` ${p.project} (${p.executorName}):`);\n console.error(` resolved build.dependsOn: ${JSON.stringify(p.found)}`);\n console.error(` missing: ${missingList}\\n`);\n }\n}\n\nexport default async function runExecutor(\n options: ValidateNxWiringOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const shared = loadAndValidate(context.root).resolved;\n const rule = shared.rules.get('nx-wiring');\n if (rule && rule.isOff) {\n console.log('\\n⏭️ Skipping validate-nx-wiring (mode: OFF)\\n');\n return { success: true };\n }\n\n const requiredDeps = options.requiredDeps ?? DEFAULT_REQUIRED_DEPS;\n const compileExecutors = options.compileExecutors ?? DEFAULT_COMPILE_EXECUTORS;\n\n console.log('\\n🔌 Validating webpieces validators are wired into the build\\n');\n\n const projectGraph = await createProjectGraphAsync();\n const projectsConfig = readProjectsConfigurationFromProjectGraph(projectGraph);\n\n const sources = new WiringSourceReader(context.root);\n const generateWiring = new GenerateWiring(\n projectsConfig.projects,\n projectGraph.dependencies,\n sources.targetDefaults(),\n sources.declaredDependsOn(projectsConfig.projects),\n );\n const generateProblems = generateWiring.problems();\n\n const inUse = findCompileExecutorsInUse(projectsConfig, compileExecutors);\n const relevantExecutors = compileExecutors.filter((executorName: string) =>\n inUse.has(executorName),\n );\n\n if (relevantExecutors.length === 0) {\n if (generateProblems.length > 0) {\n generateWiring.report(generateProblems);\n return { success: false };\n }\n console.log('✅ No known compile executors in use — nothing to gate\\n');\n return { success: true };\n }\n\n const targetDefaults = readTargetDefaults(context.root);\n const wiringProblems = findProblems(relevantExecutors, targetDefaults, requiredDeps);\n const gateProblems = findProjectGateProblems(projectsConfig, inUse, requiredDeps);\n\n if (wiringProblems.length === 0 && gateProblems.length === 0 && generateProblems.length === 0) {\n console.log('✅ Validators are wired into the build (targetDefaults + every project)\\n');\n return { success: true };\n }\n\n if (wiringProblems.length > 0) reportFailure(wiringProblems, requiredDeps);\n if (gateProblems.length > 0) reportProjectGateFailure(gateProblems);\n if (generateProblems.length > 0) generateWiring.report(generateProblems);\n return { success: false };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-nx-wiring/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Nx Wiring Executor\n *\n * Enforces that the build graph is wired for correct build ORDER. Two checks:\n *\n * 1. nx.json targetDefaults — the compile executors (@nx/js:tsc, @angular/build:application)\n * must carry the load-bearing dependsOn:\n * [\"^build\"]\n *\n * 2. Per-project override guard — a project that declares its OWN build.dependsOn OVERRIDES\n * the targetDefaults entirely (nx does not merge dependsOn). That silently drops `^build`\n * (→ upstream libs not built first, builds against an empty dist). So we check each compile\n * project's RESOLVED build.dependsOn and fail if `^build` is missing.\n *\n * NOTE: the webpieces validators (architecture:validate-complete, per-project file-import\n * cycles, DI-graph unchanged) NO LONGER ride on the compile target. They live on the `ci`\n * composite target (synthesized by this plugin's createCiTarget), so `build`/compile is a\n * fast compile-only step while `nx ci` runs the full gate. This executor therefore only\n * guards build ORDER (`^build`), not the validators — those are guaranteed by the plugin.\n *\n * 3. Generated API documents (#1023) — on the RESOLVED graph: a project tagged `generate:openapi` /\n * `generate:docs-site` has `openapi-generate` dependsOn exactly its own `build` (the @nx/js:tsc\n * target, which keeps that name), and every project depending on one — transitively — has\n * `^openapi-generate` in the effective dependsOn of its `build` and `test`. A failure names the\n * nx.json targetDefaults key to edit and the exact line. See GenerateWiring for why.\n *\n * 4. Chained API documents (#1058): a DTO library tagged `generate:openapi-components` has\n * `openapi-components-generate` dependsOn its own `build`, and every `openapi-generate` /\n * `openapi-components-generate` reaching such a library — transitively — names\n * `^openapi-components-generate`, so a chain of any depth renders in order. See ComponentsWiring.\n *\n * Conservative by design: only REQUIRES wiring on compile executors actually in use\n * (@nx/js:tsc, @angular/build:application). A repo that uses neither passes.\n *\n * Disable via webpieces.config.json rules[\"nx-wiring\"].mode=\"OFF\".\n *\n * Usage: nx run architecture:validate-nx-wiring\n */\n\nimport type {\n ExecutorContext,\n ProjectsConfigurations,\n TargetDependencyConfig,\n} from '@nx/devkit';\nimport { createProjectGraphAsync, readProjectsConfigurationFromProjectGraph } from '@nx/devkit';\nimport { loadAndValidate } from '@webpieces/rules-config';\nimport { ComponentsWiring } from '../../lib/api-docs/components-wiring';\nimport { GenerateWiring, WiringSourceReader } from '../../lib/api-docs/generate-wiring';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface ValidateNxWiringOptions {\n requiredDeps?: string[];\n compileExecutors?: string[];\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// Compile executors (and any per-project build override) must keep `^build` so build order\n// follows nx's full graph — upstreams are built into dist before dependents compile.\n// Validators no longer belong here; they ride on the `ci` composite target instead.\nconst DEFAULT_REQUIRED_DEPS: string[] = ['^build'];\n\nconst DEFAULT_COMPILE_EXECUTORS: string[] = [\n '@nx/js:tsc',\n '@angular/build:application',\n];\n\nclass WiringProblem {\n executorName: string;\n missing: string[];\n found: string[];\n\n constructor(executorName: string, missing: string[], found: string[]) {\n this.executorName = executorName;\n this.missing = missing;\n this.found = found;\n }\n}\n\nclass ProjectGateProblem {\n project: string;\n executorName: string;\n missing: string[];\n found: string[];\n\n constructor(project: string, executorName: string, missing: string[], found: string[]) {\n this.project = project;\n this.executorName = executorName;\n this.missing = missing;\n this.found = found;\n }\n}\n\ninterface TargetDefaultEntry {\n dependsOn?: string[];\n}\n\ninterface RawNxJson {\n targetDefaults?: Record<string, TargetDefaultEntry>;\n}\n\nfunction readTargetDefaults(workspaceRoot: string): Record<string, TargetDefaultEntry> {\n const nxJsonPath = path.join(workspaceRoot, 'nx.json');\n if (!fs.existsSync(nxJsonPath)) return {};\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const raw = fs.readFileSync(nxJsonPath, 'utf8');\n const parsed = JSON.parse(raw) as RawNxJson;\n return parsed.targetDefaults ?? {};\n // webpieces-disable catch-error-pattern -- malformed nx.json fails open so the check does not crash the build\n } catch (err: unknown) {\n //const error = toError(err); -- malformed nx.json fails open\n void err;\n return {};\n }\n}\n\nfunction findCompileExecutorsInUse(\n projectsConfig: ProjectsConfigurations,\n compileExecutors: string[],\n): Set<string> {\n const known = new Set(compileExecutors);\n const inUse = new Set<string>();\n for (const cfg of Object.values(projectsConfig.projects)) {\n const targets = cfg.targets ?? {};\n for (const target of Object.values(targets)) {\n const executorName = target.executor;\n if (executorName && known.has(executorName)) {\n inUse.add(executorName);\n }\n }\n }\n return inUse;\n}\n\nfunction findProblems(\n relevantExecutors: string[],\n targetDefaults: Record<string, TargetDefaultEntry>,\n requiredDeps: string[],\n): WiringProblem[] {\n const problems: WiringProblem[] = [];\n for (const executorName of relevantExecutors) {\n const entry = targetDefaults[executorName];\n const dependsOn = entry?.dependsOn ?? [];\n const missing = requiredDeps.filter((dep: string) => !dependsOn.includes(dep));\n if (missing.length > 0) {\n problems.push(new WiringProblem(executorName, missing, dependsOn));\n }\n }\n return problems;\n}\n\n/**\n * Normalize an nx dependsOn entry to a comparable string. The string form (\"^build\",\n * \"validate-complete\") passes through; the object form is rendered the same way\n * ({target:\"build\", dependencies:true} → \"^build\", {target:\"build\"} → \"build\").\n */\nfunction normalizeDeps(deps: (string | TargetDependencyConfig)[]): string[] {\n return deps.map((dep) => {\n if (typeof dep === 'string') return dep;\n const target = dep.target ?? '';\n return dep.dependencies ? `^${target}` : target;\n });\n}\n\n/**\n * Check each compile project's RESOLVED build.dependsOn (targetDefaults already merged in)\n * for `^build`. A project that overrode it away is flagged.\n */\nfunction findProjectGateProblems(\n projectsConfig: ProjectsConfigurations,\n compileExecutors: Set<string>,\n requiredPerProject: string[],\n): ProjectGateProblem[] {\n const problems: ProjectGateProblem[] = [];\n for (const [name, cfg] of Object.entries(projectsConfig.projects)) {\n // Tooling/foundation packages (type:tooling) BUILD the validators, so gating their own\n // build on validate-complete would create a bootstrap cycle. They are exempt: they keep\n // build-order (\"^build\") only. Application/library code is still fully gated.\n if ((cfg.tags ?? []).includes('type:tooling')) continue;\n const build = cfg.targets?.['build'];\n if (!build || !build.executor || !compileExecutors.has(build.executor)) continue;\n const dependsOn = normalizeDeps(build.dependsOn ?? []);\n const missing = requiredPerProject.filter((dep) => !dependsOn.includes(dep));\n if (missing.length > 0) {\n problems.push(new ProjectGateProblem(name, build.executor, missing, dependsOn));\n }\n }\n return problems;\n}\n\nfunction reportFailure(problems: WiringProblem[], requiredDeps: string[]): void {\n console.error('\\n❌ Build order is not wired in nx.json targetDefaults.\\n');\n console.error('Compile executors must keep \"^build\" so upstream libs build into dist first.');\n console.error('Add the missing dependsOn entries to nx.json targetDefaults:\\n');\n const depsList = requiredDeps.map((dep: string) => `\"${dep}\"`).join(', ');\n for (const problem of problems) {\n const missingList = problem.missing.map((dep: string) => `\"${dep}\"`).join(', ');\n console.error(` \"${problem.executorName}\": {`);\n console.error(` \"dependsOn\": [${depsList}]`);\n console.error(' }');\n console.error(` missing: ${missingList}\\n`);\n }\n console.error('To turn this wiring check off, set rules[\"nx-wiring\"].mode=\"OFF\" in');\n console.error('webpieces.config.json.\\n');\n}\n\nfunction reportProjectGateFailure(problems: ProjectGateProblem[]): void {\n console.error('\\n❌ Some projects override away the build order in their project.json.\\n');\n console.error('A project-level build.dependsOn OVERRIDES nx.json targetDefaults (nx does not');\n console.error('merge dependsOn). That silently drops \"^build\" (upstreams not built first →');\n console.error('builds against an empty dist).\\n');\n console.error('Fix: remove build.dependsOn from these project.json files so targetDefaults');\n console.error('apply, OR include \"^build\" explicitly:\\n');\n for (const p of problems) {\n const missingList = p.missing.map((dep) => `\"${dep}\"`).join(', ');\n console.error(` ${p.project} (${p.executorName}):`);\n console.error(` resolved build.dependsOn: ${JSON.stringify(p.found)}`);\n console.error(` missing: ${missingList}\\n`);\n }\n}\n\nexport default async function runExecutor(\n options: ValidateNxWiringOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const shared = loadAndValidate(context.root).resolved;\n const rule = shared.rules.get('nx-wiring');\n if (rule && rule.isOff) {\n console.log('\\n⏭️ Skipping validate-nx-wiring (mode: OFF)\\n');\n return { success: true };\n }\n\n const requiredDeps = options.requiredDeps ?? DEFAULT_REQUIRED_DEPS;\n const compileExecutors = options.compileExecutors ?? DEFAULT_COMPILE_EXECUTORS;\n\n console.log('\\n🔌 Validating webpieces validators are wired into the build\\n');\n\n const projectGraph = await createProjectGraphAsync();\n const projectsConfig = readProjectsConfigurationFromProjectGraph(projectGraph);\n\n const sources = new WiringSourceReader(context.root);\n const declared = sources.declaredDependsOn(projectsConfig.projects);\n const generateWiring = new GenerateWiring(\n projectsConfig.projects,\n projectGraph.dependencies,\n sources.targetDefaults(),\n declared,\n );\n // The chained half (#1058): a document that $refs a DTO library's components document runs after it.\n const componentsWiring = new ComponentsWiring(projectsConfig.projects, projectGraph.dependencies, declared);\n const generateProblems = [...generateWiring.problems(), ...componentsWiring.problems()];\n\n const inUse = findCompileExecutorsInUse(projectsConfig, compileExecutors);\n const relevantExecutors = compileExecutors.filter((executorName: string) =>\n inUse.has(executorName),\n );\n\n if (relevantExecutors.length === 0) {\n if (generateProblems.length > 0) {\n generateWiring.report(generateProblems);\n return { success: false };\n }\n console.log('✅ No known compile executors in use — nothing to gate\\n');\n return { success: true };\n }\n\n const targetDefaults = readTargetDefaults(context.root);\n const wiringProblems = findProblems(relevantExecutors, targetDefaults, requiredDeps);\n const gateProblems = findProjectGateProblems(projectsConfig, inUse, requiredDeps);\n\n if (wiringProblems.length === 0 && gateProblems.length === 0 && generateProblems.length === 0) {\n console.log('✅ Validators are wired into the build (targetDefaults + every project)\\n');\n return { success: true };\n }\n\n if (wiringProblems.length > 0) reportFailure(wiringProblems, requiredDeps);\n if (gateProblems.length > 0) reportProjectGateFailure(gateProblems);\n if (generateProblems.length > 0) generateWiring.report(generateProblems);\n return { success: false };\n}\n"]}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* | tag | inferred target(s) |
|
|
5
5
|
* |---|---|
|
|
6
6
|
* | `generate:openapi` | `openapi-generate` |
|
|
7
|
+
* | `generate:openapi-components` | `openapi-components-generate` — a DTO library's `components.openapi.json` (#1058) |
|
|
7
8
|
* | `generate:docs-site` | `openapi-generate` + `docs-generate` (a site is rendered from a document) |
|
|
8
9
|
*
|
|
9
10
|
* The plugin owns the executor, `cache`, `inputs` and `outputs` — the parts that are the same for every
|
|
@@ -11,8 +12,11 @@
|
|
|
11
12
|
* restores a package WITHOUT its documents). The consumer states only what is theirs to decide, in its
|
|
12
13
|
* project.json under the SAME target name, which nx merges over what is inferred here:
|
|
13
14
|
*
|
|
14
|
-
* - `openapi-generate`: `dependsOn` (`["build"]` — the tsc target whose outputPath it writes into
|
|
15
|
-
* `
|
|
15
|
+
* - `openapi-generate`: `dependsOn` (`["build"]` — the tsc target whose outputPath it writes into —
|
|
16
|
+
* plus `"^openapi-components-generate"` when a DTO library it references publishes a components
|
|
17
|
+
* document) and `options.manifest` / `options.format`;
|
|
18
|
+
* - `openapi-components-generate`: the same two, with `"^openapi-components-generate"` when the DTO
|
|
19
|
+
* library itself references another one — a chain of any depth;
|
|
16
20
|
* - `docs-generate`: `options.document` / `options.siteDir` (and optionally `options.prose`).
|
|
17
21
|
*
|
|
18
22
|
* None of those has a default (`.claude/rules/no-rule-defaults.md`): a missing one fails the run naming
|
|
@@ -29,6 +33,7 @@ import type { TargetConfiguration } from '@nx/devkit';
|
|
|
29
33
|
import { LayoutTargets } from '@webpieces/core-util';
|
|
30
34
|
export declare const GENERATE_OPENAPI_TAG = "generate:openapi";
|
|
31
35
|
export declare const GENERATE_DOCS_SITE_TAG = "generate:docs-site";
|
|
36
|
+
export declare const GENERATE_OPENAPI_COMPONENTS_TAG: string;
|
|
32
37
|
/** The two fields of the raw project.json the inference reads. Data-only. */
|
|
33
38
|
export declare class RawProjectJson {
|
|
34
39
|
readonly name: string;
|
|
@@ -49,6 +54,8 @@ export declare class GenerateTargets {
|
|
|
49
54
|
projectRoot: string, project: RawProjectJson);
|
|
50
55
|
/** The targets the project's tags opt it into — `{}` for an untagged project. */
|
|
51
56
|
infer(): Record<string, TargetConfiguration>;
|
|
57
|
+
/** A DTO library's `components.openapi.json`, into its build outputPath — see {@link openApiGenerate}. */
|
|
58
|
+
private componentsGenerate;
|
|
52
59
|
/**
|
|
53
60
|
* `outputs` point into the build target's outputPath, read here from the SAME lookup the
|
|
54
61
|
* executor writes with. When the consumer has not stated `dependsOn` yet there is no outputPath to
|