Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@
> grounded in the actual commit diffs for that version (see the referenced
> commit messages/backlog docs) — nothing here is invented.

## [Unreleased]

### Added
- **Architecture Insight page.** `gen-docs` now builds an "Architecture" page inside the generated site, read directly off the target project's `package.json` and folder layout (no AI, nothing guessed — every line shows its own evidence):
- **Architecture-pattern signals** (`lib/project-facts.js`, `getArchitecturePatterns`) — 23 patterns detected (CLI tool, publishable library, monorepo, Layered, MVC, Hexagonal, Onion, Repository, Vertical Slice, Feature-Based, Modular Monolith, Monolith, Serverless, and more), each returned with the concrete evidence that triggered it (a `bin` entry, an `exports` field, an npm `workspaces` list, matching directory names) rather than a single confident label — real projects usually match more than one pattern at once.
- **Framework/stack detection** — React, Next.js, Angular, Vue, Express, and NestJS detected from dependencies, each marked as a dependency match or a lower-confidence file-pattern guess.
- **Folder structure map** — a collapsible directory tree with a plain-English file-count summary per directory, plus workspace package listing for monorepos.
- Wired through `bin/gen-docs.js` (`getAllFacts(process.cwd())`, computed once per live run) and the programmatic API (`lib/docs.js`'s `generateSite()` via an opt-in `rootDir` option — omitted `rootDir` keeps prior behavior unchanged, no Architecture page).
- Per ADR Decision 6: facts are never computed or shown on a historical `site-versions/` snapshot render, only on the live/current-`docs/` output.
- The page (and its sidebar link) is omitted entirely when a project has zero detectable signals, rather than rendering empty.
- Docs: new [Architecture Insight](https://imchintoo.github.io/jsdoc-scribe/docs/architecture-insight.html) page; README feature list updated.

### Fixed
- **Module pages missing `.index-content` layout wrapper.** The Architecture Insight work (above) introduced `.index-content` as the shared padding/width wrapper for the index and Architecture pages, but per-module pages were never updated to use it — leaving module pages visually inconsistent (missing the same side padding and max-width as every other page). `buildSite()` in `lib/renderer.js` now wraps each module page's body in `<div class="index-content">` to match.
- **Code-scanning: `architectureSignalSentence` and `buildArchitectureSection` over complexity threshold** (`lib/renderer.js`, GitHub Advanced Security / code-multivitals alerts blocking PR #10). Both refactored from large inline branch/concatenation chains into small dispatch/helper functions with identical output (verified byte-for-byte against the prior implementation across 21+ fixtures, plus the full `renderer.test.js` phrasing-table suite) — `architectureSignalSentence` now dispatches through a `ARCHITECTURE_SIGNAL_SENTENCES` lookup table instead of an if/else chain; `buildArchitectureSection` is split into `buildArchitectureSignalsSection`/`buildArchitecturePatternsSection`/`buildFrameworkSignalsSection`/`buildStructureSection`/`buildStructureBodyHtml`. Cyclomatic complexity 22→12, Halstead volume 1716→under threshold; total repo-wide code-scanning errors 54→52 with zero new alerts introduced.
- **`getArchitecturePatterns` dogfood test asserted patterns that don't exist in this repo's real tree** (`test/project-facts.test.js`). The test claimed `sample/express/repositories`, a `components`-shaped `sample/react`, and a `sample/nestjs/modules` directory all existed and asserted MVC/Repository Pattern/Component-Based/Feature-Based were detected — none of those directories actually exist (verified via direct `find`), so the assertion was broken from the commit that introduced it, independent of anything in this PR. Rewritten to assert what's actually true today: only Layered (N-Tier) fires, not Monolith.

## [2.4.7] - 2026-07-13

### Fixed
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ token) are the same workflows running live.
maintainability, duplicate-code, orphan-file detection) against the same files it
documents. It's an optional `peerDependency` — never installed by default, and `gen-docs`
behaves identically whether it's present or not.
- **Architecture Insight page.** Every `gen-docs` run now includes an Architecture page:
a plain-English read of your folder structure, detected framework/stack (React, Express,
NestJS, etc.), and architecture-pattern signals (CLI tool, publishable library, monorepo,
layered/MVC layout) — each shown with the actual evidence, never a bare guess. No new
flag, no AI, generated automatically alongside the rest of the site. Details:
[Architecture Insight](https://imchintoo.github.io/jsdoc-scribe/docs/architecture-insight.html).
- **Config file support.** `.jsdoc-scribe.json` for `gen-docs` output dir, title, source
URL, and ignore globs — CLI flags override it. Full reference: [Features](https://imchintoo.github.io/jsdoc-scribe/docs/features.html).

Expand Down
14 changes: 13 additions & 1 deletion bin/gen-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { extractModule } = require("../lib/extractor.js");
const { buildSite, moduleLabel, moduleHtmlPath } = require("../lib/renderer.js");
const { loadConfig, mergeConfig } = require("../lib/config.js");
const { buildImportGraph, findOrphanFiles } = require("../lib/import-graph.js");
const { getAllFacts } = require("../lib/project-facts.js");
const quality = require("../lib/quality.js");
const siteData = require("../lib/site-data.js");
const pkg = require("../package.json");
Expand Down Expand Up @@ -592,6 +593,9 @@ function writeSite(modules, outDir, projectName, projectVersion, opts, silent, q
versions: switcherVersions,
currentVersionId: currentVersionId,
isSnapshot: isSnapshot,
// task-arch-04 / ADR Decision 6: never surface facts on a historical
// snapshot render -- versionOpts is only set by renderVersionSnapshot().
facts: versionOpts ? null : (opts.facts || null),
});
fs.mkdirSync(path.join(outDir, "modules"), { recursive: true });

Expand Down Expand Up @@ -661,6 +665,14 @@ function main() {
const projectName = resolveTitle(opts.title);
const projectVersion = resolveVersion();

// task-arch-04: computed once per live/main run (ADR Decision 6). Never
// persisted to opts (config file), same "one-off flag" pattern as
// opts.data above. writeSite() is the single place that decides whether
// to actually use it -- it forces facts to null for a historical
// site-versions/ snapshot render (versionOpts set) regardless of this
// value, so no exclusion logic is needed here.
opts.facts = getAllFacts(process.cwd());

// --from-data: fast path -- skip collectAllFiles()/extractModule()/
// runQuality()/buildImportGraph() entirely, load a previously-written
// site-data.json instead (ADR Decision 4/5). Mutually exercised
Expand Down Expand Up @@ -723,7 +735,7 @@ function main() {
return;
}

// ── Watch mode ────────────────────────────────────────────────────────────
// ── Watch mode ───────────────────────────────────────────────
function stamp() { return new Date().toLocaleTimeString(); }
console.log(`[watch] Watching ${files.length} file(s). Output: ${path.relative(process.cwd(), outDir) || outDir}`);
if (opts.ignore.length) console.log(`[watch] ignoring: ${opts.ignore.join(", ")}`);
Expand Down
26 changes: 26 additions & 0 deletions docs-site/docs/09-architecture-insight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
slug: architecture-insight
title: Architecture Insight
description: gen-docs now maps your folder structure and detects your stack, right inside the generated site.
command: gen-docs src --out docs
---

## What it is

Every `gen-docs` run now looks at the project it is documenting -- not just the source files you point it at -- and builds an **Architecture** page inside the generated site. It answers the three questions a new contributor (human or AI agent) usually has to answer by hand: what kind of project is this, what is it built with, and how is it organized.

This is read straight off your `package.json` and folder layout. There is no AI involved and nothing is guessed: every line on the page says exactly what it found and where, so you can verify it yourself in seconds.

## What shows up

- **What kind of project is this** -- pattern signals like CLI tool, publishable library, monorepo, or a layered/MVC-style layout, each shown with the actual evidence (a `bin` entry, an `exports` field, an npm `workspaces` list, matching directory names) rather than a single confident label. Real projects usually match more than one pattern at once, and the page shows that plainly instead of forcing a single answer.
- **What it's built with** -- frameworks detected from your dependencies (React, Next.js, Angular, Vue, Express, NestJS), each marked as a dependency match or a lower-confidence file-pattern guess so you always know how sure the tool is.
- **How it's organized** -- a collapsible folder tree with a plain-English file-count summary per directory, plus your npm workspace packages if the repo is a monorepo.

## Why it's there

Documentation is usually written for "what does this function do." This page is for the question that comes before that: "what am I even looking at." A new team member, a contractor picking up a repo for the first time, or an AI coding agent orienting itself in an unfamiliar codebase can open one page and get an accurate, evidence-backed map instead of reading `package.json` and guessing.

## When it appears

The Architecture page is generated automatically -- no new flag needed. If your project has nothing meaningful to report (a truly empty structure with zero detectable signals), the page and its sidebar link are simply left out rather than shown empty. It also never appears on a historical version snapshot, since those represent a past `docs/` output, not the live state of your repo today.
11 changes: 9 additions & 2 deletions lib/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const { collectFiles, DEFAULT_EXTENSIONS, DEFAULT_IGNORE_DIRS } = require("./index.js");
const { extractModule } = require("./extractor.js");
const { buildSite, moduleLabel, moduleHtmlPath } = require("./renderer.js");
const { getAllFacts } = require("./project-facts.js");

/**
* Extract documentation models from an array of file paths.
Expand Down Expand Up @@ -32,7 +33,12 @@ async function extractModules(files) {
* One-shot convenience: collect files, extract docs, build HTML site.
*
* @param {string|string[]} inputPaths Source file or directory paths
* @param {object} [options] Same options as buildSite()
* @param {object} [options] Same options as buildSite(), plus an
* opt-in `rootDir` (task-arch-04): when provided, `getAllFacts(rootDir)` is
* computed and passed through as `facts` so the generated site includes the
* Architecture page. Omitted/falsy `rootDir` (every pre-task-arch-04 caller)
* leaves `facts` undefined -- `buildSite()` treats that as "no Architecture
* page", so this is fully backward-compatible.
* @returns {Promise<Array<{path:string,html:string}>>}
*/
async function generateSite(inputPaths, options) {
Expand All @@ -41,7 +47,8 @@ async function generateSite(inputPaths, options) {
const files = [].concat(...paths.map(p => collectFiles(p, opts.extensions, opts.ignoreDirs)));
const unique = [...new Set(files)];
const modules = await extractModules(unique);
return buildSite(modules, { projectName: opts.projectName, version: opts.version });
const facts = opts.rootDir ? getAllFacts(opts.rootDir) : undefined;
return buildSite(modules, { projectName: opts.projectName, version: opts.version, facts });
}

module.exports = {
Expand Down
Loading
Loading