Skip to content

docs: generate reference properties from source#192

Merged
tbinna merged 1 commit intomainfrom
doc-generate-properties
Mar 25, 2026
Merged

docs: generate reference properties from source#192
tbinna merged 1 commit intomainfrom
doc-generate-properties

Conversation

@tbinna
Copy link
Copy Markdown
Member

@tbinna tbinna commented Mar 24, 2026

Closes #191

Summary by CodeRabbit

  • New Features

    • Added a docs tooling library to generate, inject, and validate reference options and a workspace docs test suite.
    • New local docs project with build, lint, and test configs.
  • Updates

    • Marked certain executors as deprecated; UI Kit packaging labeled experimental.
    • Enabled TypeScript tsc target caching and coordinated build defaults.
  • Documentation

    • Replaced verbose property sections with injected options directives and added validation markers.
  • Tests

    • Added end-to-end tests covering reference docs generation and validation.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

Adds a docs-generation tool and Vite plugin that read Nx plugin registries and JSON schemas, validate placeholder markers in reference Markdown, and inject generated option tables during the VitePress build; includes a new docs-tools library, tests, and small workspace config updates.

Changes

Cohort / File(s) Summary
Agent & Notes
AGENTS.md, CLAUDE.md
New repo-level guidance and task/maintainability notes.
VitePress site config
docs/.vitepress/config.mts
Added nx-forge-reference-docs Vite plugin to validate reference docs at buildStart and inject generated options during module load/transform.
Reference docs
docs/reference/executors.md, docs/reference/generators.md
Replaced hand-maintained Properties sections with <!-- nx-forge:options ... --> markers and adjusted deprecation notices.
Docs-tools library
tools/docs/src/index.ts, tools/docs/src/lib/reference-docs.ts, tools/docs/src/lib/reference-docs.spec.ts
New library implementing: registry/schema loading, marker parsing, options rendering, injection, validation, and comprehensive Jest tests.
Docs-tools project files
tools/docs/project.json, tools/docs/package.json, tools/docs/jest.config.ts, tools/docs/README.md, tools/docs/.eslintrc.json
Added Nx project, package manifest, Jest config, README, and ESLint config for the docs-tools library.
Docs-tools TS configs
tools/docs/tsconfig.json, tools/docs/tsconfig.lib.json, tools/docs/tsconfig.spec.json
Added TypeScript project and per-target configs for building and testing the docs-tools library.
Workspace / TS path
nx.json, tsconfig.base.json
Added targetDefaults["@nx/js:tsc"] entry and a docs-tools TS path alias.
Schema text updates
packages/nx-forge/src/executors/.../schema.json
Minor JSON-schema description edits: append “(experimental)” to uiKit2Packaging and small tsConfig wording change.

Sequence Diagram(s)

sequenceDiagram
    participant VitePress
    participant VitePlugin as nx-forge-reference-docs
    participant DocsTools as docs-tools (reference-docs)
    participant FS as Filesystem

    VitePress->>VitePlugin: buildStart()
    VitePlugin->>DocsTools: validateReferenceDocs(workspaceRoot)
    DocsTools->>FS: read registries & schemas
    DocsTools-->>VitePlugin: validation result / throw on error
    Note right of VitePlugin: During module load/transform
    VitePress->>VitePlugin: load(moduleId) / transform(code)
    VitePlugin->>FS: read markdown content (if reference file)
    VitePlugin->>DocsTools: injectReferenceOptions(markdown, path, workspaceRoot)
    DocsTools->>FS: read schema items as needed
    DocsTools-->>VitePlugin: transformed markdown module source
    VitePlugin-->>VitePress: module with injected options
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

"I nibble schemas, hop through code, 🐰
I stitch the docs where markers show.
Tables bloom from comments sown,
Tests keep the garden neatly grown,
The rabbit hops — the docs now glow!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: implementing automatic generation of reference properties from schema sources rather than maintaining them manually.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from #191: reads schemas from executors.json/generators.json, normalizes public option metadata, renders Markdown tables, validates docs via markers, and injects generated content into existing handwritten pages.
Out of Scope Changes check ✅ Passed All changes are within scope: new docs-tools library with reference-docs module, VitePress config updates to integrate injection, schema description updates, reference doc updates with markers, and supporting config files (nx.json, tsconfig, eslint).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch doc-generate-properties

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
tools/docs/.eslintrc.json (1)

5-16: Remove redundant no-op override blocks.

Line 5-16 defines overlapping overrides with empty rules; they don’t add behavior and can be collapsed for readability.

♻️ Optional cleanup
   "overrides": [
     {
       "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
       "rules": {}
     },
-    {
-      "files": ["*.ts", "*.tsx"],
-      "rules": {}
-    },
-    {
-      "files": ["*.js", "*.jsx"],
-      "rules": {}
-    },
     {
       "files": ["*.json"],
       "parser": "jsonc-eslint-parser",
       "rules": {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/.eslintrc.json` around lines 5 - 16, Remove the redundant no-op
override blocks in the ESLint config: collapse the three overlapping override
objects that each contain empty "rules" and identical "files" patterns into a
single override (or remove them entirely) so only one entry handles "*.ts,
*.tsx, *.js, *.jsx" where needed; update the JSON array that currently contains
the duplicate objects to a single object to improve readability and avoid
redundant overrides.
docs/.vitepress/config.mts (1)

48-66: Remove the load hook to eliminate duplicate reference item map rebuilds.

Both the load and transform hooks call injectReferenceProperties, which rebuilds the reference items map on each invocation. Since Vite's default file loading provides the same content as the load hook's readFileSync, consolidating to transform alone avoids this redundant overhead without losing functionality.

♻️ Proposed simplification (single transform path)
-import { readFileSync } from 'fs';
 import { fileURLToPath } from 'url';
 import { defineConfig } from 'vitepress';
@@
         buildStart() {
           validateReferenceDocs(workspaceRoot);
         },
-        load(id) {
-          if (!isReferenceMarkdownFile(id)) {
-            return null;
-          }
-
-          const filePath = normalizeViteId(id);
-          const markdown = readFileSync(filePath, 'utf8');
-
-          return injectReferenceProperties(markdown, filePath, workspaceRoot);
-        },
         transform(code, id) {
           if (!isReferenceMarkdownFile(id)) {
             return null;
           }
 
           const filePath = normalizeViteId(id);
 
           return injectReferenceProperties(code, filePath, workspaceRoot);
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/config.mts` around lines 48 - 66, Remove the redundant load
hook that calls injectReferenceProperties to avoid rebuilding the reference
items map twice; keep the transform hook as the single path. Specifically,
delete the load(...) block that checks isReferenceMarkdownFile(id), reads the
file via normalizeViteId/readFileSync and calls
injectReferenceProperties(markdown, filePath, workspaceRoot), leaving
transform(code, id) intact to call injectReferenceProperties(code, filePath,
workspaceRoot) after the isReferenceMarkdownFile(id) and normalizeViteId(id)
checks. Ensure no other code paths rely on the load hook and that imports
(readFileSync) are removed if no longer used.
tools/docs/src/lib/reference-docs.ts (1)

76-82: Make reference-doc discovery recursive so validation matches injection scope.

validateReferenceDocs() only inspects direct children of docs/reference, while the VitePress integration is set up to transform docs/reference/**/*.md. If a nested reference page gets added later, its markers will be injected but never validated.

♻️ Suggested refactor
 export function getReferenceMarkdownFilePaths(workspaceRoot: string): string[] {
   const referenceDocsDir = join(workspaceRoot, REFERENCE_DOCS_DIR);

-  return readdirSync(referenceDocsDir)
-    .filter((entry) => entry.endsWith('.md'))
-    .sort()
-    .map((entry) => join(referenceDocsDir, entry));
+  const walk = (dir: string): string[] =>
+    readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
+      const entryPath = join(dir, entry.name);
+      if (entry.isDirectory()) {
+        return walk(entryPath);
+      }
+      return entry.name.endsWith('.md') ? [entryPath] : [];
+    });
+
+  return walk(referenceDocsDir).sort();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/src/lib/reference-docs.ts` around lines 76 - 82, The
getReferenceMarkdownFilePaths function currently only lists direct children of
REFERENCE_DOCS_DIR, causing nested markdown under docs/reference to be ignored
by validateReferenceDocs; update getReferenceMarkdownFilePaths to recursively
traverse referenceDocsDir (e.g., using a recursive readdir/walk or a glob like
**/*.md) and return all .md file paths under that tree, preserving a stable
sort; ensure the logic around join(referenceDocsDir, entry) is updated to return
full recursive paths so validateReferenceDocs sees the same set of files that
the VitePress injection (docs/reference/**/*.md) will process.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/reference/executors.md`:
- Around line 102-104: The deprecation warning text contains a duplicated
article ("in favor of a the") in the line referencing the `install` executor and
the `forge` executor; update the warning so it reads "in favor of the [`forge`
executor](`#forge`)" (remove the extra "a") in the warning block that mentions the
`install` executor and `forge` executor.

In `@tools/docs/src/lib/reference-docs.ts`:
- Line 39: ReferenceProperty currently defines alias?: string which drops
secondary aliases and forces single-dash formatting; change the type to preserve
all aliases (aliases?: string[] on ReferenceProperty), update the normalization
logic (the normalization step that currently copies property.alias) to
copy/normalize the full aliases array (e.g., property.aliases =
Array.from(property.aliases || [])) instead of a single string, and update the
rendering code (the render/format function that currently emits '-' + alias) to
iterate over all aliases and render each using '-' for single-character aliases
and '--' for multi-character aliases so both short and long forms are
documented.

---

Nitpick comments:
In `@docs/.vitepress/config.mts`:
- Around line 48-66: Remove the redundant load hook that calls
injectReferenceProperties to avoid rebuilding the reference items map twice;
keep the transform hook as the single path. Specifically, delete the load(...)
block that checks isReferenceMarkdownFile(id), reads the file via
normalizeViteId/readFileSync and calls injectReferenceProperties(markdown,
filePath, workspaceRoot), leaving transform(code, id) intact to call
injectReferenceProperties(code, filePath, workspaceRoot) after the
isReferenceMarkdownFile(id) and normalizeViteId(id) checks. Ensure no other code
paths rely on the load hook and that imports (readFileSync) are removed if no
longer used.

In `@tools/docs/.eslintrc.json`:
- Around line 5-16: Remove the redundant no-op override blocks in the ESLint
config: collapse the three overlapping override objects that each contain empty
"rules" and identical "files" patterns into a single override (or remove them
entirely) so only one entry handles "*.ts, *.tsx, *.js, *.jsx" where needed;
update the JSON array that currently contains the duplicate objects to a single
object to improve readability and avoid redundant overrides.

In `@tools/docs/src/lib/reference-docs.ts`:
- Around line 76-82: The getReferenceMarkdownFilePaths function currently only
lists direct children of REFERENCE_DOCS_DIR, causing nested markdown under
docs/reference to be ignored by validateReferenceDocs; update
getReferenceMarkdownFilePaths to recursively traverse referenceDocsDir (e.g.,
using a recursive readdir/walk or a glob like **/*.md) and return all .md file
paths under that tree, preserving a stable sort; ensure the logic around
join(referenceDocsDir, entry) is updated to return full recursive paths so
validateReferenceDocs sees the same set of files that the VitePress injection
(docs/reference/**/*.md) will process.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc19a806-b7a7-4c4a-945d-4dc9599705d9

📥 Commits

Reviewing files that changed from the base of the PR and between 4d29630 and 20b818c.

📒 Files selected for processing (20)
  • AGENTS.md
  • CLAUDE.md
  • docs/.vitepress/config.mts
  • docs/reference/executors.md
  • docs/reference/generators.md
  • nx.json
  • packages/nx-forge/src/executors/build/schema.json
  • packages/nx-forge/src/executors/package/schema.json
  • tools/docs/.eslintrc.json
  • tools/docs/README.md
  • tools/docs/jest.config.ts
  • tools/docs/package.json
  • tools/docs/project.json
  • tools/docs/src/index.ts
  • tools/docs/src/lib/reference-docs.spec.ts
  • tools/docs/src/lib/reference-docs.ts
  • tools/docs/tsconfig.json
  • tools/docs/tsconfig.lib.json
  • tools/docs/tsconfig.spec.json
  • tsconfig.base.json

@tbinna tbinna force-pushed the doc-generate-properties branch from 20b818c to bbc30e2 Compare March 25, 2026 08:47
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tools/docs/src/lib/reference-docs.ts (1)

75-82: Make reference-doc discovery recursive to match injection scope.

validateReferenceDocs currently scans only direct files under docs/reference (Line 78), while injection logic is applied to any markdown path containing /docs/reference/. If nested reference pages are added, validation can miss markers and incorrectly report missing items.

♻️ Proposed refactor
 export function getReferenceMarkdownFilePaths(workspaceRoot: string): string[] {
   const referenceDocsDir = join(workspaceRoot, REFERENCE_DOCS_DIR);
-
-  return readdirSync(referenceDocsDir)
-    .filter((entry) => entry.endsWith('.md'))
-    .sort()
-    .map((entry) => join(referenceDocsDir, entry));
+  const results: string[] = [];
+  const stack = [referenceDocsDir];
+
+  while (stack.length > 0) {
+    const currentDir = stack.pop()!;
+    for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
+      const fullPath = join(currentDir, entry.name);
+      if (entry.isDirectory()) {
+        stack.push(fullPath);
+      } else if (entry.isFile() && entry.name.endsWith('.md')) {
+        results.push(fullPath);
+      }
+    }
+  }
+
+  return results.sort();
 }

Also applies to: 106-108

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/src/lib/reference-docs.ts` around lines 75 - 82,
getReferenceMarkdownFilePaths currently only lists top-level .md files under
REFERENCE_DOCS_DIR causing nested docs to be missed; change it to recursively
discover all .md files under referenceDocsDir (e.g., use readdirSync with Dirent
and recursion or a glob) so any file whose path contains /docs/reference/ is
returned; apply the same recursive approach to the companion function used
around lines 106-108 (the other reference-doc path helper) so
validateReferenceDocs sees nested reference pages too and returns full paths for
all .md files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tools/docs/src/lib/reference-docs.ts`:
- Around line 75-82: getReferenceMarkdownFilePaths currently only lists
top-level .md files under REFERENCE_DOCS_DIR causing nested docs to be missed;
change it to recursively discover all .md files under referenceDocsDir (e.g.,
use readdirSync with Dirent and recursion or a glob) so any file whose path
contains /docs/reference/ is returned; apply the same recursive approach to the
companion function used around lines 106-108 (the other reference-doc path
helper) so validateReferenceDocs sees nested reference pages too and returns
full paths for all .md files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 551571b0-39ad-4e95-8fb7-9a0cd2377489

📥 Commits

Reviewing files that changed from the base of the PR and between 20b818c and bbc30e2.

📒 Files selected for processing (20)
  • AGENTS.md
  • CLAUDE.md
  • docs/.vitepress/config.mts
  • docs/reference/executors.md
  • docs/reference/generators.md
  • nx.json
  • packages/nx-forge/src/executors/build/schema.json
  • packages/nx-forge/src/executors/package/schema.json
  • tools/docs/.eslintrc.json
  • tools/docs/README.md
  • tools/docs/jest.config.ts
  • tools/docs/package.json
  • tools/docs/project.json
  • tools/docs/src/index.ts
  • tools/docs/src/lib/reference-docs.spec.ts
  • tools/docs/src/lib/reference-docs.ts
  • tools/docs/tsconfig.json
  • tools/docs/tsconfig.lib.json
  • tools/docs/tsconfig.spec.json
  • tsconfig.base.json
✅ Files skipped from review due to trivial changes (13)
  • CLAUDE.md
  • tools/docs/src/index.ts
  • tools/docs/tsconfig.json
  • tools/docs/tsconfig.lib.json
  • tools/docs/jest.config.ts
  • nx.json
  • tools/docs/README.md
  • tools/docs/.eslintrc.json
  • tools/docs/tsconfig.spec.json
  • tools/docs/package.json
  • packages/nx-forge/src/executors/package/schema.json
  • tools/docs/project.json
  • docs/reference/generators.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • tsconfig.base.json
  • packages/nx-forge/src/executors/build/schema.json
  • docs/reference/executors.md
  • tools/docs/src/lib/reference-docs.spec.ts

@tbinna tbinna force-pushed the doc-generate-properties branch from bbc30e2 to a3adacf Compare March 25, 2026 08:59
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tools/docs/src/lib/reference-docs.ts (1)

139-165: Consider caching reference items for repeated invocations.

loadReferenceItems reads and parses JSON files from disk on every call. Per the VitePress config, injectReferenceOptions is invoked in both load() and transform() hooks for each reference markdown file, resulting in redundant I/O.

For the current small number of files this is acceptable, but if the reference docs grow, a simple module-level cache could improve build performance.

💡 Optional caching pattern
let cachedItemsByKey: Map<string, ReferenceItem> | undefined;

function getReferenceItemsMap(workspaceRoot: string): Map<string, ReferenceItem> {
  if (!cachedItemsByKey) {
    cachedItemsByKey = createReferenceItemsMap(loadReferenceItems(workspaceRoot));
  }
  return cachedItemsByKey;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/src/lib/reference-docs.ts` around lines 139 - 165,
injectReferenceOptions currently calls loadReferenceItems each invocation
causing redundant disk I/O; introduce a module-level cache (e.g.,
cachedItemsByKey) and a small accessor like getReferenceItemsMap(workspaceRoot)
that only calls createReferenceItemsMap(loadReferenceItems(workspaceRoot)) when
the cache is empty or the workspaceRoot changes, then replace the direct call to
createReferenceItemsMap(loadReferenceItems(workspaceRoot)) in
injectReferenceOptions with a call to the new getter so repeated calls reuse the
cached Map; ensure the cache key accounts for workspaceRoot or reset when
workspaceRoot differs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tools/docs/src/lib/reference-docs.ts`:
- Around line 139-165: injectReferenceOptions currently calls loadReferenceItems
each invocation causing redundant disk I/O; introduce a module-level cache
(e.g., cachedItemsByKey) and a small accessor like
getReferenceItemsMap(workspaceRoot) that only calls
createReferenceItemsMap(loadReferenceItems(workspaceRoot)) when the cache is
empty or the workspaceRoot changes, then replace the direct call to
createReferenceItemsMap(loadReferenceItems(workspaceRoot)) in
injectReferenceOptions with a call to the new getter so repeated calls reuse the
cached Map; ensure the cache key accounts for workspaceRoot or reset when
workspaceRoot differs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7ea2ba8-c69a-4478-93c5-6001ef20ad9f

📥 Commits

Reviewing files that changed from the base of the PR and between bbc30e2 and a3adacf.

📒 Files selected for processing (20)
  • AGENTS.md
  • CLAUDE.md
  • docs/.vitepress/config.mts
  • docs/reference/executors.md
  • docs/reference/generators.md
  • nx.json
  • packages/nx-forge/src/executors/build/schema.json
  • packages/nx-forge/src/executors/package/schema.json
  • tools/docs/.eslintrc.json
  • tools/docs/README.md
  • tools/docs/jest.config.ts
  • tools/docs/package.json
  • tools/docs/project.json
  • tools/docs/src/index.ts
  • tools/docs/src/lib/reference-docs.spec.ts
  • tools/docs/src/lib/reference-docs.ts
  • tools/docs/tsconfig.json
  • tools/docs/tsconfig.lib.json
  • tools/docs/tsconfig.spec.json
  • tsconfig.base.json
✅ Files skipped from review due to trivial changes (13)
  • CLAUDE.md
  • tools/docs/README.md
  • tools/docs/tsconfig.lib.json
  • packages/nx-forge/src/executors/build/schema.json
  • tools/docs/src/index.ts
  • tools/docs/jest.config.ts
  • nx.json
  • tools/docs/tsconfig.spec.json
  • tools/docs/tsconfig.json
  • tools/docs/.eslintrc.json
  • docs/reference/generators.md
  • packages/nx-forge/src/executors/package/schema.json
  • tools/docs/package.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • tsconfig.base.json
  • tools/docs/project.json
  • tools/docs/src/lib/reference-docs.spec.ts
  • docs/.vitepress/config.mts
  • docs/reference/executors.md

@tbinna tbinna merged commit 2247e2f into main Mar 25, 2026
7 checks passed
@tbinna tbinna deleted the doc-generate-properties branch March 25, 2026 09:12
@coderabbitai coderabbitai bot mentioned this pull request Mar 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 7.0.0-beta.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 7.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generate reference properties from schema spec

1 participant