@webpieces/nx-webpieces-rules 0.4.829 → 0.4.830
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/package.json +8 -8
- package/src/generate-targets.js +1 -1
- package/src/generate-targets.js.map +1 -1
- package/src/lib/api-docs/generate-wiring.js +2 -1
- package/src/lib/api-docs/generate-wiring.js.map +1 -1
- package/src/lib/api-docs/generator-target.js +2 -1
- package/src/lib/api-docs/generator-target.js.map +1 -1
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.830",
|
|
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.830",
|
|
22
|
+
"@webpieces/api-doc-model": "0.4.830",
|
|
23
|
+
"@webpieces/code-rules": "0.4.830",
|
|
24
|
+
"@webpieces/core-util": "0.4.830",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.830",
|
|
26
|
+
"@webpieces/pr-gate": "0.4.830",
|
|
27
|
+
"@webpieces/rules-config": "0.4.830",
|
|
28
28
|
"madge": "8.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
package/src/generate-targets.js
CHANGED
|
@@ -81,7 +81,7 @@ class GenerateTargets {
|
|
|
81
81
|
*/
|
|
82
82
|
openApiGenerate() {
|
|
83
83
|
const found = new core_util_1.GeneratedApiDocsLayout(this.projectRoot, this.project.name, this.project.targets)
|
|
84
|
-
.outputTarget().found;
|
|
84
|
+
.outputTarget(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET).found;
|
|
85
85
|
const outputs = found === undefined
|
|
86
86
|
? []
|
|
87
87
|
: [
|
|
@@ -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,
|
|
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: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"]}
|
|
@@ -209,7 +209,8 @@ class GenerateWiring {
|
|
|
209
209
|
shapeOf(name) {
|
|
210
210
|
const project = this.projects[name];
|
|
211
211
|
const where = `${project.root}/project.json`;
|
|
212
|
-
const lookup = new core_util_1.GeneratedApiDocsLayout(project.root, name, project.targets ?? {})
|
|
212
|
+
const lookup = new core_util_1.GeneratedApiDocsLayout(project.root, name, project.targets ?? {})
|
|
213
|
+
.outputTarget(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET);
|
|
213
214
|
if (lookup.found === undefined) {
|
|
214
215
|
return [new GenerateWiringProblem(name, lookup.problem.problem, `${name}: ${lookup.problem.cure}`)];
|
|
215
216
|
}
|
|
@@ -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 ?? {}).outputTarget();\n if (lookup.found === undefined) {\n return [new GenerateWiringProblem(name, lookup.problem!.problem, `${name}: ${lookup.problem!.cure}`)];\n }\n if (lookup.found.targetName === BUILD_TARGET) return [];\n const other = lookup.found.targetName;\n return [new GenerateWiringProblem(\n name,\n `${name}:${GeneratedApiDocsLayout.OPENAPI_TARGET} dependsOn \"${other}\", and must dependsOn \"${BUILD_TARGET}\" — ` +\n `the api library's @nx/js:tsc target keeps the name build. @nx/js:tsc treats a dependency as buildable ` +\n `only when it has a target of the SAME name as the one running, so a tsc target renamed \"${other}\" ` +\n 'compiles every build-only dependency from SOURCE and fails with TS6059 (nrwl/nx#18257).',\n `${name}: in ${where}, name the @nx/js:tsc target \"${BUILD_TARGET}\" again (delete any nx:noop \"build\" that ` +\n `wrapped it) and set targets.${GeneratedApiDocsLayout.OPENAPI_TARGET}.dependsOn to [\"${BUILD_TARGET}\"]. ` +\n `Dependents pull generation in with \"${UPSTREAM_GENERATE}\" in nx.json targetDefaults.`,\n )];\n }\n\n /**\n * (b) Every project that depends — transitively — on a generating library has `^openapi-generate`\n * in the EFFECTIVE dependsOn of its `build` and `test`. One problem per EDIT, listing every project\n * that edit fixes: an nx.json targetDefaults key usually covers the whole repo at once.\n */\n private dependentsOf(generating: ReadonlySet<string>): GenerateWiringProblem[] {\n const byEdit = new Map<string, MissingGenerate[]>();\n for (const name of Object.keys(this.projects).sort()) {\n const upstream = this.generatingUpstreamOf(name, generating);\n if (upstream.length === 0) continue;\n for (const targetName of DEPENDENT_TARGETS) {\n const target = this.projects[name]!.targets?.[targetName];\n if (target === undefined || this.names(target, UPSTREAM_GENERATE)) continue;\n const missing = this.missingFor(name, targetName, target, upstream);\n const group = byEdit.get(missing.where) ?? [];\n group.push(missing);\n byEdit.set(missing.where, group);\n }\n }\n const problems: GenerateWiringProblem[] = [];\n for (const group of byEdit.values()) {\n const first = group[0]!;\n const who = group.map((each: MissingGenerate) => `${each.project}:${each.target}`).join(', ');\n const libraries = [...new Set(group.flatMap((each: MissingGenerate) => [...each.upstream]))].sort();\n problems.push(new GenerateWiringProblem(\n who,\n `depends on ${libraries.join(', ')}, which generate${libraries.length === 1 ? 's' : ''} API documents and MCP ` +\n `tool catalogs into ${libraries.length === 1 ? 'its' : 'their'} build output, and the effective dependsOn ` +\n `does not name \"${UPSTREAM_GENERATE}\" — so nothing generates them before this runs (a spec that boots ` +\n 'the MCP server would read catalogs that were never written, or stale ones).',\n first.cure,\n ));\n }\n return problems;\n }\n\n /** The generating libraries `name` reaches through the dependency graph, itself excluded. */\n private generatingUpstreamOf(name: string, generating: ReadonlySet<string>): string[] {\n const found = new Set<string>();\n const seen = new Set<string>([name]);\n const queue: string[] = [name];\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const dependency of this.dependencies[current] ?? []) {\n if (seen.has(dependency.target)) continue;\n seen.add(dependency.target);\n if (generating.has(dependency.target)) found.add(dependency.target);\n queue.push(dependency.target);\n }\n }\n return [...found].sort();\n }\n\n /** Where the missing `^openapi-generate` goes: the project.json that overrides, or the nx.json key. */\n private missingFor(\n name: string,\n targetName: string,\n target: TargetConfiguration,\n upstream: readonly string[],\n ): MissingGenerate {\n const effective = (target.dependsOn ?? []) as DependsOnEntry[];\n if (this.declared.declares(name, targetName)) {\n const where = `${this.projects[name]!.root}/project.json`;\n return new MissingGenerate(\n name, targetName, upstream, `${where}#${targetName}`,\n `In ${where}, targets.${targetName}.dependsOn REPLACES nx.json's (nx does not merge dependsOn), so add ` +\n `\"${UPSTREAM_GENERATE}\" there: \"dependsOn\": ${this.render([...effective, UPSTREAM_GENERATE])}`,\n );\n }\n const key = this.defaultKeyFor(targetName, target.executor);\n const existing = key === undefined ? [] : [...(this.targetDefaults[key]!.dependsOn ?? [])];\n const editKey = key ?? targetName;\n const line = `\"${editKey}\": { \"dependsOn\": ${this.render([...existing, UPSTREAM_GENERATE])} }`;\n return new MissingGenerate(\n name, targetName, upstream, `nx.json#${editKey}`,\n key === undefined\n ? `In nx.json, add the targetDefaults entry ${line} — no targetDefaults key governs ${targetName} yet.`\n : `In nx.json, set targetDefaults[\"${editKey}\"].dependsOn so the entry reads ${line} ` +\n '(keep its other fields).',\n );\n }\n\n /**\n * The targetDefaults key nx reads for this target — nx's own precedence\n * (`readTargetDefaultsForTarget`): the EXECUTOR key when one exists, else the target NAME, else the\n * longest glob key matching the name.\n */\n private defaultKeyFor(targetName: string, executor: string | undefined): string | undefined {\n if (executor !== undefined && this.targetDefaults[executor] !== undefined) return executor;\n if (this.targetDefaults[targetName] !== undefined) return targetName;\n let best: string | undefined;\n for (const key of Object.keys(this.targetDefaults)) {\n if (!/[*?[\\]{}!]/.test(key) || !matchesAnyGlob(targetName, [key])) continue;\n if (best === undefined || key.length > best.length) best = key;\n }\n return best;\n }\n\n private render(entries: readonly DependsOnEntry[]): string {\n return `[${entries.map((entry: DependsOnEntry) => JSON.stringify(entry)).join(', ')}]`;\n }\n\n /** Whether `target` dependsOn `wanted` — `^x` for the upstream form, `x` for a sibling. */\n private names(target: TargetConfiguration | undefined, wanted: string): boolean {\n const upstream = wanted.startsWith('^');\n const name = upstream ? wanted.slice(1) : wanted;\n return (target?.dependsOn ?? []).some((entry: DependsOnEntry) => {\n if (typeof entry === 'string') return entry === wanted;\n return entry.target === name && (entry.dependencies === true) === upstream;\n });\n }\n}\n"]}
|
|
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"]}
|
|
@@ -63,7 +63,8 @@ class GeneratorTarget {
|
|
|
63
63
|
* `openapi-generate` dependsOn — the directory the package is packed from.
|
|
64
64
|
*/
|
|
65
65
|
documentsDir() {
|
|
66
|
-
const lookup = new core_util_1.GeneratedApiDocsLayout(this.projectRoot, this.projectName, this.targets)
|
|
66
|
+
const lookup = new core_util_1.GeneratedApiDocsLayout(this.projectRoot, this.projectName, this.targets)
|
|
67
|
+
.outputTarget(core_util_1.GeneratedApiDocsLayout.OPENAPI_TARGET);
|
|
67
68
|
if (lookup.found === undefined) {
|
|
68
69
|
throw new rules_config_1.RuleFailError(this.ruleName, `${lookup.problem.problem} The documents go into the build's own output directory — the ` +
|
|
69
70
|
'one the package is packed from — and are never assumed to be ./dist.', undefined, undefined, [new rules_config_1.Option(lookup.problem.cure, true)]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator-target.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-docs/generator-target.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The shared half of the `openapi-generate` and `docs-generate` executors: WHERE a generated document\n * goes, and the proof that the target is wired so that nx both orders and caches it correctly.\n *\n * Both halves read the project's own declarations and never supply a value of their own:\n *\n * - the documents' directory is the `outputPath` of the target `openapi-generate` dependsOn — the api\n * library's `build` (tsc) step, which writes the directory the package is packed from, so the\n * documents ship INSIDE the published package. It is ASKED of nx through `GeneratedApiDocsLayout`\n * (`@webpieces/core-util`), the same lookup `McpToolCatalog.fromPackages` reads with, and never\n * assumed: this repo builds into a workspace-root `dist/apps/...`, another consumer builds into a\n * project-local `<project>/dist`, and hardcoding either one — or the target's NAME — generates the\n * document somewhere the package is not packed from;\n * - the ordering and the cache are the target's own `dependsOn` and `outputs`, which the executor\n * checks rather than trusts, because both failures are silent: without the `dependsOn` edge, tsc's\n * clean of `outputPath` races the write (the document vanishes and a dependent test dies on ENOENT,\n * intermittently, only in CI), and `outputs` that miss a written file make every cache hit restore a\n * package without that file.\n */\n\nimport type { ExecutorContext, TargetConfiguration, TargetDependencyConfig } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { Option, RuleFailError, matchesAnyGlob } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n/** What `dependsOn` may name: a sibling target by name, or nx's object form of the same. */\ntype DependsOnEntry = string | TargetDependencyConfig;\n\nexport class GeneratorTarget {\n constructor(\n /** The executor's name, which is what every refusal is reported under. */\n readonly ruleName: string,\n readonly workspaceRoot: string,\n readonly projectName: string,\n /** Workspace-relative. */\n readonly projectRoot: string,\n readonly targetName: string,\n readonly target: TargetConfiguration,\n readonly targets: Record<string, TargetConfiguration>,\n ) {}\n\n // webpieces-disable no-function-outside-class -- static factory of this class\n static of(ruleName: string, context: ExecutorContext): GeneratorTarget {\n const projectName = context.projectName ?? '';\n const project = context.projectsConfigurations?.projects[projectName];\n const targetName = context.targetName ?? '';\n const target = project?.targets?.[targetName];\n if (project === undefined || target === undefined) {\n // nx hands every executor its own project and target; their absence is our bug, not the consumer's.\n throw new Error(`${ruleName}: nx did not describe project '${projectName}' target '${targetName}'`);\n }\n return new GeneratorTarget(\n ruleName, context.root, projectName, project.root, targetName, target, project.targets ?? {});\n }\n\n /**\n * Absolute path to the directory the documents live in: the `outputPath` of the target\n * `openapi-generate` dependsOn — the directory the package is packed from.\n */\n documentsDir(): string {\n const lookup = new GeneratedApiDocsLayout(this.projectRoot, this.projectName, this.targets).outputTarget();\n if (lookup.found === undefined) {\n throw new RuleFailError(\n this.ruleName,\n `${lookup.problem!.problem} The documents go into the build's own output directory — the ` +\n 'one the package is packed from — and are never assumed to be ./dist.',\n undefined,\n undefined,\n [new Option(lookup.problem!.cure, true)],\n );\n }\n return path.resolve(this.workspaceRoot, lookup.found.outputPath);\n }\n\n /** Absolute `<projectRoot>/<dir>`, refusing anything that is not strictly inside the project. */\n insideProject(dir: string, optionName: string): string {\n const projectAbs = path.resolve(this.workspaceRoot, this.projectRoot);\n const resolved = path.resolve(projectAbs, dir);\n const relative = path.relative(projectAbs, resolved);\n if (relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)) return resolved;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} options.${optionName} is '${dir}', which is not a directory ` +\n `strictly inside ${this.projectRoot}. It is emptied and rewritten on every run, so it may only ` +\n 'name a directory of the project\\'s own.',\n undefined,\n undefined,\n [new Option(`Set options.${optionName} to a project-relative directory, e.g. \"generated-docs\"`, true)],\n );\n }\n\n /** Refuse unless this target `dependsOn` the sibling `required` target. */\n assertDependsOn(required: string): void {\n const entries: readonly DependsOnEntry[] = this.target.dependsOn ?? [];\n if (entries.some((entry: DependsOnEntry) => this.namesSibling(entry, required))) return;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} does not declare dependsOn \"${required}\". It reads what ` +\n `${required} writes, so without that edge nx may run it before or alongside ${required} and ` +\n `render a stale or missing document. That fails intermittently and mostly in CI, which is ` +\n `why it is refused here instead.`,\n undefined,\n undefined,\n [new Option(`Add \"dependsOn\": [\"${required}\"] to the ${this.targetName} target in ${this.projectRoot}/project.json`, true)],\n );\n }\n\n /** Refuse unless every written file is covered by the target's declared `outputs`, so a cache hit restores it. */\n assertOutputsCover(writtenAbs: readonly string[]): void {\n const patterns = (this.target.outputs ?? []).map((output: string) => this.interpolate(output));\n const uncovered = writtenAbs\n .map((file: string) => path.relative(this.workspaceRoot, file).split(path.sep).join('/'))\n .filter((file: string) => !matchesAnyGlob(file, patterns));\n if (uncovered.length === 0) return;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} wrote files its declared outputs do not cover, so a ` +\n `cache hit would restore a package WITHOUT them:\\n` +\n uncovered.map((file: string) => ` ${file}`).join('\\n'),\n undefined,\n undefined,\n [new Option(\n `Cover them in the ${this.targetName} target's \"outputs\" in ${this.projectRoot}/project.json, ` +\n `e.g. \"{workspaceRoot}/${path.posix.dirname(uncovered[0]!)}/<the files>\"`,\n true,\n )],\n );\n }\n\n /** A workspace-relative path the consumer stated, from `options`, required and never defaulted. */\n requiredOption(value: string | undefined, name: string): string {\n if (typeof value === 'string' && value.trim() !== '') return value;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} has no options.${name}. It is required and has no default.`,\n undefined,\n undefined,\n [new Option(`Set options.${name} on the ${this.targetName} target in ${this.projectRoot}/project.json`, true)],\n );\n }\n\n private namesSibling(entry: DependsOnEntry, required: string): boolean {\n if (typeof entry === 'string') return entry === required;\n const projects = entry.projects;\n const self = projects === undefined || projects === 'self' ||\n (Array.isArray(projects) && projects.length === 1 && projects[0] === this.projectName);\n return entry.target === required && entry.dependencies !== true && self;\n }\n\n /** nx's own tokens, so a path written the way nx accepts it resolves the way nx resolves it. */\n private interpolate(text: string): string {\n return text\n .replace(/\\{workspaceRoot\\}\\/?/g, '')\n .replace(/\\{projectRoot\\}/g, this.projectRoot)\n .replace(/\\{projectName\\}/g, this.projectName)\n .replace(/\\{options\\.([A-Za-z0-9_]+)\\}/g, (whole: string, key: string): string => {\n const value = this.target.options?.[key];\n return typeof value === 'string' ? value : whole;\n });\n }\n}\n\n/**\n * Generate into a STAGING directory, then publish into the output directory — so the executor knows\n * exactly which files the run produced (for {@link GeneratorTarget.assertOutputsCover}) without\n * parsing a generator's console output, and a failed run leaves the output directory untouched.\n */\nexport class StagedOutput {\n publish(stagingDir: string, destDir: string): string[] {\n const written: string[] = [];\n const copy = (from: string, to: string): void => {\n fs.mkdirSync(to, { recursive: true });\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const source = path.join(from, entry.name);\n const target = path.join(to, entry.name);\n if (entry.isDirectory()) {\n copy(source, target);\n } else {\n fs.copyFileSync(source, target);\n written.push(target);\n }\n }\n };\n copy(stagingDir, destDir);\n return written.sort();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"generator-target.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-docs/generator-target.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;;AAGH,oDAA8D;AAC9D,0DAAgF;AAChF,+CAAyB;AACzB,mDAA6B;AAK7B,MAAa,eAAe;IAGX;IACA;IACA;IAEA;IACA;IACA;IACA;IATb;IACI,0EAA0E;IACjE,QAAgB,EAChB,aAAqB,EACrB,WAAmB;IAC5B,0BAA0B;IACjB,WAAmB,EACnB,UAAkB,EAClB,MAA2B,EAC3B,OAA4C;QAP5C,aAAQ,GAAR,QAAQ,CAAQ;QAChB,kBAAa,GAAb,aAAa,CAAQ;QACrB,gBAAW,GAAX,WAAW,CAAQ;QAEnB,gBAAW,GAAX,WAAW,CAAQ;QACnB,eAAU,GAAV,UAAU,CAAQ;QAClB,WAAM,GAAN,MAAM,CAAqB;QAC3B,YAAO,GAAP,OAAO,CAAqC;IACtD,CAAC;IAEJ,8EAA8E;IAC9E,MAAM,CAAC,EAAE,CAAC,QAAgB,EAAE,OAAwB;QAChD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,sBAAsB,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAChD,oGAAoG;YACpG,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,kCAAkC,WAAW,aAAa,UAAU,GAAG,CAAC,CAAC;QACxG,CAAC;QACD,OAAO,IAAI,eAAe,CACtB,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACtG,CAAC;IAED;;;OAGG;IACH,YAAY;QACR,MAAM,MAAM,GAAG,IAAI,kCAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC;aACtF,YAAY,CAAC,kCAAsB,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,4BAAa,CACnB,IAAI,CAAC,QAAQ,EACb,GAAG,MAAM,CAAC,OAAQ,CAAC,OAAO,gEAAgE;gBACtF,sEAAsE,EAC1E,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CAAC,MAAM,CAAC,OAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAC3C,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrE,CAAC;IAED,iGAAiG;IACjG,aAAa,CAAC,GAAW,EAAE,UAAkB;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACrD,IAAI,QAAQ,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;QACjG,MAAM,IAAI,4BAAa,CACnB,IAAI,CAAC,QAAQ,EACb,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,YAAY,UAAU,QAAQ,GAAG,8BAA8B;YACjG,mBAAmB,IAAI,CAAC,WAAW,6DAA6D;YAChG,yCAAyC,EAC7C,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CAAC,eAAe,UAAU,yDAAyD,EAAE,IAAI,CAAC,CAAC,CACzG,CAAC;IACN,CAAC;IAED,2EAA2E;IAC3E,eAAe,CAAC,QAAgB;QAC5B,MAAM,OAAO,GAA8B,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QACvE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,KAAqB,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAAE,OAAO;QACxF,MAAM,IAAI,4BAAa,CACnB,IAAI,CAAC,QAAQ,EACb,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,gCAAgC,QAAQ,mBAAmB;YAC7F,GAAG,QAAQ,mEAAmE,QAAQ,OAAO;YAC7F,2FAA2F;YAC3F,iCAAiC,EACrC,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CAAC,sBAAsB,QAAQ,aAAa,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,WAAW,eAAe,EAAE,IAAI,CAAC,CAAC,CAC9H,CAAC;IACN,CAAC;IAED,kHAAkH;IAClH,kBAAkB,CAAC,UAA6B;QAC5C,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/F,MAAM,SAAS,GAAG,UAAU;aACvB,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aACxF,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAA,6BAAc,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACnC,MAAM,IAAI,4BAAa,CACnB,IAAI,CAAC,QAAQ,EACb,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,uDAAuD;YACzF,mDAAmD;YACnD,SAAS,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAC3D,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CACP,qBAAqB,IAAI,CAAC,UAAU,0BAA0B,IAAI,CAAC,WAAW,iBAAiB;gBAC3F,yBAAyB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,eAAe,EAC7E,IAAI,CACP,CAAC,CACL,CAAC;IACN,CAAC;IAED,mGAAmG;IACnG,cAAc,CAAC,KAAyB,EAAE,IAAY;QAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC;QACnE,MAAM,IAAI,4BAAa,CACnB,IAAI,CAAC,QAAQ,EACb,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,mBAAmB,IAAI,sCAAsC,EACnG,SAAS,EACT,SAAS,EACT,CAAC,IAAI,qBAAM,CAAC,eAAe,IAAI,WAAW,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,WAAW,eAAe,EAAE,IAAI,CAAC,CAAC,CACjH,CAAC;IACN,CAAC;IAEO,YAAY,CAAC,KAAqB,EAAE,QAAgB;QACxD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,KAAK,QAAQ,CAAC;QACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,MAAM,IAAI,GAAG,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,MAAM;YACtD,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3F,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,IAAI,IAAI,CAAC;IAC5E,CAAC;IAED,gGAAgG;IACxF,WAAW,CAAC,IAAY;QAC5B,OAAO,IAAI;aACN,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC;aACpC,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC;aAC7C,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC;aAC7C,OAAO,CAAC,+BAA+B,EAAE,CAAC,KAAa,EAAE,GAAW,EAAU,EAAE;YAC7E,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QACrD,CAAC,CAAC,CAAC;IACX,CAAC;CACJ;AArID,0CAqIC;AAED;;;;GAIG;AACH,MAAa,YAAY;IACrB,OAAO,CAAC,UAAkB,EAAE,OAAe;QACvC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAU,EAAQ,EAAE;YAC5C,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACzC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACtB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACJ,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBAChC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACzB,CAAC;YACL,CAAC;QACL,CAAC,CAAC;QACF,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC1B,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACJ;AAnBD,oCAmBC","sourcesContent":["/**\n * The shared half of the `openapi-generate` and `docs-generate` executors: WHERE a generated document\n * goes, and the proof that the target is wired so that nx both orders and caches it correctly.\n *\n * Both halves read the project's own declarations and never supply a value of their own:\n *\n * - the documents' directory is the `outputPath` of the target `openapi-generate` dependsOn — the api\n * library's `build` (tsc) step, which writes the directory the package is packed from, so the\n * documents ship INSIDE the published package. It is ASKED of nx through `GeneratedApiDocsLayout`\n * (`@webpieces/core-util`), the same lookup `McpToolCatalog.fromPackages` reads with, and never\n * assumed: this repo builds into a workspace-root `dist/apps/...`, another consumer builds into a\n * project-local `<project>/dist`, and hardcoding either one — or the target's NAME — generates the\n * document somewhere the package is not packed from;\n * - the ordering and the cache are the target's own `dependsOn` and `outputs`, which the executor\n * checks rather than trusts, because both failures are silent: without the `dependsOn` edge, tsc's\n * clean of `outputPath` races the write (the document vanishes and a dependent test dies on ENOENT,\n * intermittently, only in CI), and `outputs` that miss a written file make every cache hit restore a\n * package without that file.\n */\n\nimport type { ExecutorContext, TargetConfiguration, TargetDependencyConfig } from '@nx/devkit';\nimport { GeneratedApiDocsLayout } from '@webpieces/core-util';\nimport { Option, RuleFailError, matchesAnyGlob } from '@webpieces/rules-config';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n/** What `dependsOn` may name: a sibling target by name, or nx's object form of the same. */\ntype DependsOnEntry = string | TargetDependencyConfig;\n\nexport class GeneratorTarget {\n constructor(\n /** The executor's name, which is what every refusal is reported under. */\n readonly ruleName: string,\n readonly workspaceRoot: string,\n readonly projectName: string,\n /** Workspace-relative. */\n readonly projectRoot: string,\n readonly targetName: string,\n readonly target: TargetConfiguration,\n readonly targets: Record<string, TargetConfiguration>,\n ) {}\n\n // webpieces-disable no-function-outside-class -- static factory of this class\n static of(ruleName: string, context: ExecutorContext): GeneratorTarget {\n const projectName = context.projectName ?? '';\n const project = context.projectsConfigurations?.projects[projectName];\n const targetName = context.targetName ?? '';\n const target = project?.targets?.[targetName];\n if (project === undefined || target === undefined) {\n // nx hands every executor its own project and target; their absence is our bug, not the consumer's.\n throw new Error(`${ruleName}: nx did not describe project '${projectName}' target '${targetName}'`);\n }\n return new GeneratorTarget(\n ruleName, context.root, projectName, project.root, targetName, target, project.targets ?? {});\n }\n\n /**\n * Absolute path to the directory the documents live in: the `outputPath` of the target\n * `openapi-generate` dependsOn — the directory the package is packed from.\n */\n documentsDir(): string {\n const lookup = new GeneratedApiDocsLayout(this.projectRoot, this.projectName, this.targets)\n .outputTarget(GeneratedApiDocsLayout.OPENAPI_TARGET);\n if (lookup.found === undefined) {\n throw new RuleFailError(\n this.ruleName,\n `${lookup.problem!.problem} The documents go into the build's own output directory — the ` +\n 'one the package is packed from — and are never assumed to be ./dist.',\n undefined,\n undefined,\n [new Option(lookup.problem!.cure, true)],\n );\n }\n return path.resolve(this.workspaceRoot, lookup.found.outputPath);\n }\n\n /** Absolute `<projectRoot>/<dir>`, refusing anything that is not strictly inside the project. */\n insideProject(dir: string, optionName: string): string {\n const projectAbs = path.resolve(this.workspaceRoot, this.projectRoot);\n const resolved = path.resolve(projectAbs, dir);\n const relative = path.relative(projectAbs, resolved);\n if (relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)) return resolved;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} options.${optionName} is '${dir}', which is not a directory ` +\n `strictly inside ${this.projectRoot}. It is emptied and rewritten on every run, so it may only ` +\n 'name a directory of the project\\'s own.',\n undefined,\n undefined,\n [new Option(`Set options.${optionName} to a project-relative directory, e.g. \"generated-docs\"`, true)],\n );\n }\n\n /** Refuse unless this target `dependsOn` the sibling `required` target. */\n assertDependsOn(required: string): void {\n const entries: readonly DependsOnEntry[] = this.target.dependsOn ?? [];\n if (entries.some((entry: DependsOnEntry) => this.namesSibling(entry, required))) return;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} does not declare dependsOn \"${required}\". It reads what ` +\n `${required} writes, so without that edge nx may run it before or alongside ${required} and ` +\n `render a stale or missing document. That fails intermittently and mostly in CI, which is ` +\n `why it is refused here instead.`,\n undefined,\n undefined,\n [new Option(`Add \"dependsOn\": [\"${required}\"] to the ${this.targetName} target in ${this.projectRoot}/project.json`, true)],\n );\n }\n\n /** Refuse unless every written file is covered by the target's declared `outputs`, so a cache hit restores it. */\n assertOutputsCover(writtenAbs: readonly string[]): void {\n const patterns = (this.target.outputs ?? []).map((output: string) => this.interpolate(output));\n const uncovered = writtenAbs\n .map((file: string) => path.relative(this.workspaceRoot, file).split(path.sep).join('/'))\n .filter((file: string) => !matchesAnyGlob(file, patterns));\n if (uncovered.length === 0) return;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} wrote files its declared outputs do not cover, so a ` +\n `cache hit would restore a package WITHOUT them:\\n` +\n uncovered.map((file: string) => ` ${file}`).join('\\n'),\n undefined,\n undefined,\n [new Option(\n `Cover them in the ${this.targetName} target's \"outputs\" in ${this.projectRoot}/project.json, ` +\n `e.g. \"{workspaceRoot}/${path.posix.dirname(uncovered[0]!)}/<the files>\"`,\n true,\n )],\n );\n }\n\n /** A workspace-relative path the consumer stated, from `options`, required and never defaulted. */\n requiredOption(value: string | undefined, name: string): string {\n if (typeof value === 'string' && value.trim() !== '') return value;\n throw new RuleFailError(\n this.ruleName,\n `${this.projectName}:${this.targetName} has no options.${name}. It is required and has no default.`,\n undefined,\n undefined,\n [new Option(`Set options.${name} on the ${this.targetName} target in ${this.projectRoot}/project.json`, true)],\n );\n }\n\n private namesSibling(entry: DependsOnEntry, required: string): boolean {\n if (typeof entry === 'string') return entry === required;\n const projects = entry.projects;\n const self = projects === undefined || projects === 'self' ||\n (Array.isArray(projects) && projects.length === 1 && projects[0] === this.projectName);\n return entry.target === required && entry.dependencies !== true && self;\n }\n\n /** nx's own tokens, so a path written the way nx accepts it resolves the way nx resolves it. */\n private interpolate(text: string): string {\n return text\n .replace(/\\{workspaceRoot\\}\\/?/g, '')\n .replace(/\\{projectRoot\\}/g, this.projectRoot)\n .replace(/\\{projectName\\}/g, this.projectName)\n .replace(/\\{options\\.([A-Za-z0-9_]+)\\}/g, (whole: string, key: string): string => {\n const value = this.target.options?.[key];\n return typeof value === 'string' ? value : whole;\n });\n }\n}\n\n/**\n * Generate into a STAGING directory, then publish into the output directory — so the executor knows\n * exactly which files the run produced (for {@link GeneratorTarget.assertOutputsCover}) without\n * parsing a generator's console output, and a failed run leaves the output directory untouched.\n */\nexport class StagedOutput {\n publish(stagingDir: string, destDir: string): string[] {\n const written: string[] = [];\n const copy = (from: string, to: string): void => {\n fs.mkdirSync(to, { recursive: true });\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const source = path.join(from, entry.name);\n const target = path.join(to, entry.name);\n if (entry.isDirectory()) {\n copy(source, target);\n } else {\n fs.copyFileSync(source, target);\n written.push(target);\n }\n }\n };\n copy(stagingDir, destDir);\n return written.sort();\n }\n}\n"]}
|