π On this page: Changelog βΎ
Documentation
Changelog
Release history from the repository CHANGELOG.md.
Release history
Every release, rendered directly from CHANGELOG.md -- nothing excerpted or hidden.
Note: the entries for2.4.1and2.4.3β2.4.7below were reconstructed from the repository's real git history on 2026-07-15, closing a gap where these versions had shipped without changelog entries. Each entry is grounded in the actual commit diffs for that version (see the referenced commit messages/backlog docs) β nothing here is invented.
[2.5.1] - 2026-07-31
Changed
- README rewritten end to end (
README.md) -- restructured around a Preview section, the--qualityCode Health dashboard preview, a real-world sample-code table (sample/express,sample/nestjs,sample/vanilla-js, top-levelsample/*.ts), a Benchmarks table, Install, "Three ways to use jsdoc-scribe" (CLI / programmatic API / GitHub Actions), Highlights, and Known limitations -- so a developer landing on the repo cold sees what's supported and how to try it before anything else. - Hand-drawn
assets/preview.svgmockup replaced with a real generated screenshot,assets/preview.png(plusassets/preview-quality.pngfor the Code Health dashboard preview);docs-site/site.js's default social-share image andscripts/build-pages-docs.js's video-block placeholder both updated to point at the new.png.
Fixed
- GitHub Pages changelog page only ever showed the newest ~8-9 releases.
scripts/build-pages-docs.js'srenderChangelog()hard-capped the rendered output to the first 220 lines ofCHANGELOG.md(.slice(0, 220)), silently dropping every older version despite the page's own copy claiming to show "the complete release history." This patch itself is the version that most visibly exposed the gap, since it shipped with no changelog entry of its own --CHANGELOG.mdhad a real release with nothing recorded for it. Both are fixed together: the 220-line cap is removed (the full file now renders), and this entry closes the gap for2.5.1itself.
[2.5.0] - 2026-07-31
Added
- SEO metadata for generated doc sites (
buildSite/gen-docs). Every pagegen-docsbuilds (index, module pages,architecture.html, Code Health detail pages) now gets a<meta name="robots" content="index, follow">tag unconditionally, plus two new opt-in options for full SEO coverage: baseUrl(new--base-url <url>CLI flag /GenerateSiteOptions.baseUrl): when given, every page gets<link rel="canonical">plus matchingog:type/og:title/og:description/og:urltags, each with that page's own correct absolute URL. Omitted by default β without it, no canonical/OG is emitted (a relative or guessed URL would be worse than none).description(new--description <text>CLI flag /GenerateSiteOptions.description): a site-wide<meta name="description">fallback. Module pages prefer their own source file's top-of-file JSDoc description (mod.description) when present, so pages with real file-level docs get a distinct, accurate meta description instead of all pages sharing one string.- Fully backward compatible: omitting both options reproduces the exact same output as before, aside from the always-on robots tag (deliberate β there's no case where a generated doc site should default to not signaling indexability).
- This project's own docs site (
scripts/build-pages-docs.js'sbuildApiDocs()) now passes both flags for its/api/section, closing a real gap found during a full-site SEO review: the~35individual API reference pages previously had no meta description, canonical, or OpenGraph tags at all (only<title>), and were missing fromsitemap.xmlentirely (onlyapi/index.htmlwas listed). Both are now fixed at the site-build level, not patched per-page. - Tests: 5 new cases in
test/renderer.test.jscovering the no-option default, site-wide description fallback, per-module description precedence, canonical/OG correctness per page, and determinism with the new options set. - Architecture Insight page.
gen-docsnow builds an "Architecture" page inside the generated site, read directly off the target project'spackage.jsonand 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 (abinentry, anexportsfield, an npmworkspaceslist, 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'sgenerateSite()via an opt-inrootDiroption β omittedrootDirkeeps 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 page; README feature list updated.
Fixed
- Module pages missing
.index-contentlayout wrapper. The Architecture Insight work (above) introduced.index-contentas 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()inlib/renderer.jsnow wraps each module page's body in<div class="index-content">to match. - Code-scanning:
architectureSignalSentenceandbuildArchitectureSectionover 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 fullrenderer.test.jsphrasing-table suite) βarchitectureSignalSentencenow dispatches through aARCHITECTURE_SIGNAL_SENTENCESlookup table instead of an if/else chain;buildArchitectureSectionis split intobuildArchitectureSignalsSection/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. getArchitecturePatternsdogfood test asserted patterns that don't exist in this repo's real tree (test/project-facts.test.js). The test claimedsample/express/repositories, acomponents-shapedsample/react, and asample/nestjs/modulesdirectory all existed and asserted MVC/Repository Pattern/Component-Based/Feature-Based were detected β none of those directories actually exist (verified via directfind), 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.--fixcould corrupt source files that had a single-line blank JSDoc block (lib/fix.js, found in a full top-to-bottom code review, 2026-07-31).lightFixGenericBlock'sno-blank-block-descriptions/require-descriptionhandling didlines.splice(1, 0, " * " + TODO_DESCRIPTION)on the raw comment's line array, which assumed at least 2 lines already existed. A genuinely single-line raw comment β e.g./** */directly above a class/interface/enum/type-alias/variable (function-like symbols are unaffected; they go through a different, full-rebuild fix path) β has only 1 element in that array, so the splice landed the TODO text after the only element, i.e. outside the closing*/. Reproduced live:/** */\nclass Widget {}run through--fix --writeproduced/** */\n * TODO: describe what this does.\nclass Widget {}β invalid JavaScript (node -cconfirmsSyntaxError: Unexpected token '*'), while the CLI itself reported "1 issue(s) fixed" with no warning.lightFixGenericBlocknow detects the single-line case and expands it into a proper multi-line block instead, preserving the file's original indentation convention and any real inline content between/**and*/(there normally isn't any β a lone@internal-style tag-only single-liner is the one case that's possible) rather than discarding it. Regression test added (test/fix.test.js).- Architecture-pattern detection false-positived on this repo's own gitignored build-output directories.
lib/project-facts.js'sIGNORE_DIR_NAMESexcluded the usualnode_modules/.git/dist/etc. but notbench/generated/(synthetic perf-fixture tree from thebench:perf-gatework, containingmodels//controllers/-named directories at many depths),docs-dashboard/, or_site/(both gen-docs's own generated site output, which always includes amodules/folder) β all three are gitignored but can be present on disk after a localnpm run bench:perf-gate/npm run dashboard/npm run docs:pages. Whichever happened to exist locally madegetArchitecturePatterns()falsely detect MVC-style/Feature-Based architecture for jsdoc-scribe's own repo, which is what broke the dogfood test referenced two entries above again, via a new cause distinct from the one it originally documented.docs/itself was deliberately left out of the new ignore list β unlike the other three it can legitimately hold real content (docs/backlog) and is intentionally walked/described elsewhere. gen-docsandbin/cli.js --coverage-badgesilently exited 0 on per-file parse failures. Same silent-failure class the TS7 compatibility fix-track (task-ts7-02) already closed for--check-drift/--lint/--fix/--check/--dry-run/write, just not applied to these two spots: both caught and logged a per-fileextractModule()/analyseFile()failure but never counted it or setprocess.exitCode, so a run where every file failed to parse (e.g. an incompatibletypescriptmajor installed despite the<7.0.0dependency pin) still reported success. Both now count failures and setprocess.exitCode = 1, matching the rest of the CLI.lib/renderer.js'sbuildFunctionHealthLookupwas O(modules Γ files). Did a fresh linear.find()scan overquality.result.fileson every call, once per module insidebuildSite()'s module loop β the same class of bug the v2.4.2 linear-scaling fix (adr-linear-scaling-fix.md) closed elsewhere in this exact file, just not caught in this function. Now builds aWeakMap-memoizedMapkeyed onquality.resultonce and reuses it, same patternbuildFileHealthLookupalready used correctly.lib/renderer.jsType Alias cards silently lost their source-file link. A typo (mod.ilePathinstead ofmod.filePath) passedundefinedas the file path when rendering the Type Aliases section, degradingsourceLink()to a bareline Nlabel instead of a real file:line link.lib/docs.d.ts'sGenerateSiteOptionswas missingrootDir, even thoughgenerateSite()'s own JS implementation and JSDoc already supported it (the Architecture page opt-in, added alongside the Architecture Insight feature above). TypeScript consumers ofrequire("jsdoc-scribe/docs")couldn't passrootDirwithout a type error. Verified against a real.tsconsumer snippet withtsc --noEmit --strictβ errored before, compiles clean now.lib/config.js'smergeConfig()doc comment and the README's config-file section both claimed "CLI flags override the config file" without qualification β true for every option exceptignore, which is deliberately additive (a project's checked-in ignore list should always apply; a one-off--ignoreflag adds an extra exclusion for that run, it doesn't silently drop the file's exclusions). The behavior was already correct; only the documentation was wrong. Corrected in both places, no behavior change.lib/fix.jsandlib/lint.jsindependently reimplemented the same "does this@tagline have trailing text" check for theempty-tagsrule, one via a straightforwardm[2].replace(...), the other via an equivalent but needlessly convoluted slice-offset calculation β same result today, but two divergent implementations that could silently drift apart on a future edit. Extracted into one sharedtagTrailingText()helper inlib/lint.js(an internal sharing detail, not added tolint.d.ts's public declarations), used by both.- Code Health dashboard's "Most-imported files" and "Orphan files" cards rendered absolute filesystem paths instead of project-relative ones, unlike the "Files needing attention" and "Duplicate code" cards on the same page (found while regenerating the README preview images against a real
gen-docs --qualityrun, where it surfaced as full local machine paths).lib/import-graph.js'sbuildImportGraph/findOrphanFilescorrectly use absolute paths internally (needed to match against the resolved file set) butlib/renderer.jsrendered those same absolute paths straight to HTML on both the embedded index-page cards and theirhealth-imports.html/health-orphans.htmldetail pages. Added a presentation-onlytoDisplayPath()helper (cwd-relative, falls back to the original absolute string if it can't be shortened) applied at all four render sites;lib/import-graph.js's own data and tests are unchanged.
Tests
test/docs.test.jsβ first direct test coverage for the publicrequire("jsdoc-scribe/docs")API (extractModules,generateSite); previously untested despite being a documentedpackage.jsonexport ("./docs"). Covers parsing, a bad-file-skips-not-throws contract, therootDirβ Architecture-page opt-in (present and absent), input dedup, and single-path-vs-array input. Required makingtest/run.js's hand-rolledcheck()runner async-aware β it previously treated anyfn()that returned a pending Promise as an unconditional pass regardless of how that Promise actually resolved, since a later rejection was never caught by its synchronoustry/catch.check()now detects a thenable return and tracks it in apendingAsyncChecksarray, awaited viaPromise.all()immediately before the final pass/fail summary and exit code are computed; every other (fully synchronous) suite is unaffected. Verified with a deliberately-failing async check to confirm a real failure is now actually caught, not silently counted as a pass.test/fix.test.jsβ regression guard for the single-line blank block corruption fix above: asserts the fixed output of a/** */-preceded class is both syntactically valid (vm.Scriptconstruction, i.e. an actual parse, not just "didn't throw") and correctly expanded into a 3-line block, and explicitly asserts the TODO text never lands outside the closing*/.
[2.4.7] - 2026-07-13
Fixed
- Docs site markdown rendering: list/bold/link gaps unified.
scripts/build-pages-docs.js's markdown parser previously had no block type for-/*/numbered lists (list lines fell through and rendered as plain paragraph text), and blockquotes/paragraphs ran through inconsistent inline-formatting passes.parsePostBlocks()now recognizes ordered/unordered list items as their own block type (rendered as real<ul>/<ol>), and every text-bearing block (paragraph, quote, list item) now runs through the sameinlineMarkdown()pass, so bold text and links render consistently everywhere instead of only in paragraphs. - A layout/design issue on the generated GitHub Pages site was corrected (
scripts/build-pages-docs.js).
Added
- New blog post: "Halving Engineering Overhead: Documentation Automation" (
docs-site/posts/), with its own preview image (assets/halving-engineering-overhead-documentation-automation.png).
Changed
README.mdsubstantially trimmed (503 β ~100 net lines) now that the full walkthrough content lives on the GitHub Pages documentation site instead of duplicated in the README.
[2.4.6] - 2026-07-12
Changed
- Expanded and corrected the GitHub Pages documentation content across every doc page (quick-start, CLI, GitHub Actions, GitHub Pages, programmatic API, ESLint plugin, features) and the corresponding rendering in
scripts/build-pages-docs.js. README.mdupdated to match the expanded docs site.
[2.4.5] - 2026-07-10
Added
- GitHub Pages documentation site.
scripts/build-pages-docs.jsbuilds a full static site from markdown sources:docs-site/docs/(quick-start, CLI, GitHub Actions, GitHub Pages, programmatic API, ESLint plugin, features, and this changelog page β which renders directly fromCHANGELOG.md, not a separately hand-maintained copy) anddocs-site/posts/(blog). Newnpm run docs:pagesscript. Ships with two launch posts: "Publish your jsdoc site with GitHub Pages" and "Why deterministic JSDoc matters."
Fixed
- CI publish reliability: pinned
npmto11.5.1in the publish workflow (.github/workflows/publish.yml) β a floatingnpm install -g npm@latesthad picked up a release whose bundledlibnpmpublishrequired the unscopedsigstorepackage while that npm release's ownpackage.jsononly shipped the newer scoped@sigstore/*packages, breakingnpm publishwithCannot find module 'sigstore'. - Perf-gate threshold raised from 4.5x to 5.0x (
bench/run-perf-gate.js) after a CI run measured 4.55x on functionally unchanged code β shared CI-runner timing noise, not a regression. Still tight enough to catch a genuine superlinear blowup.
Changed
- CI:
docs.yml/perf-gate.yml/quality.ymlworkflows' Node version bumped 20 β 24;test.yml's test matrix updated from[18, 20, 22]to[22, 24, 26].
[2.4.4] - 2026-07-09
Changed
- CI:
publish.ymlworkflow's Node version bumped 20 β 24.
[2.4.3] - 2026-07-09
Fixed
- TypeScript 7 silent-failure bug (
docs/backlog/task-ts7-01.mdβtask-ts7-04.md, reported against a real user repo). A file that failed to parse insideextractModule()was logged (<file> -> FAILED: <message>) but otherwise treated as a no-op β--check-drift,--lint,--fix,--check,--dry-run, and plain--writeruns all still exited0and could print a false "all clean" message ("No drift detected...","No lint issues found.","All symbols are documented.") even when one or more files had silently failed to parse.bin/cli.jsnow tracks a per-runfailedTotalacross all six of those code paths: a parse failure now forces a non-zero exit code and prints an explicitN file(s) failed to parse.line, and the success messages are replaced with an honest "found no issues among the files that did parse, but N failed" variant when applicable. typescriptdependency range pinned to>=5.0.0 <7.0.0(package.json) β an untested major-version bump of the parser this tool is built on was the root cause a user could hit silently, per the above..github/ISSUE_TEMPLATE/bug_report.md: fixed a broken version-check command in the template.
Tests
- New
test/cli-failure-accounting.test.js, wired intotest/run.js: asserts the failure-accounting behavior above across all six CLI modes, on all-healthy, mixed healthy/broken, and 100%-broken input sets.
[2.4.2] - 2026-07-08
Fixed
gen-docssuperlinear scaling past ~300-500 source files (docs/backlog/story-gendocs-linear-scaling.md,docs/backlog/adr-linear-scaling-fix.md). Root cause, confirmed vianode --prof(not just a code read):commonRoot(modules)inlib/renderer.jsre-walked every module's split file path on every call, reached throughmoduleLabel/moduleHtmlPathonce per sidebar-tree leaf insiderenderTreeLevel, itself invoked once per generated page (buildSidebar()) -- an O(N³) path overall, not the trie-building step (already fixed in an earlier sprint). Two changes, both additive/targeted (no restructuring of surrounding logic):commonRoot()is now memoized in aWeakMapkeyed on themodulesarray reference (deliberately not a filePath string, to avoid two independentbuildSite()calls bleeding a cached label into each other -- see the ADR's Alternatives).- The sidebar's per-page render now precomputes each tree node's default (closed, non-active) HTML once per
buildSite()call and reuses it for every page; only the active ancestor chain + active leaf are rendered fresh per page (was: the entire tree, every page). The redundant per-page__parent-linking walk was also folded into this one-time pass (same call chain, not called out separately in the original ticket list, disclosed indocs/backlog/task-ls-04-sidebar-node-caching.md's implementation notes). worker_threadsparallelization of the parse phase was evaluated and explicitly not pursued -- profiling at N=300 showed TS Compiler API parse functions at 0.0%-0.1% of JS ticks, nowhere near co-dominant with the render-phase bottleneck above.
Added
bench/generate-fixtures.js+bench/run-perf-gate.js(npm run bench:perf-gate): a deterministic (seeded-PRNG) synthetic-fixture generator and a perf-gate script assertingtime(2000 files) / time(500 files) < 4.5x..github/workflows/perf-gate.yml: runs the gate on every push/PR tomain.test/renderer-memoization.test.js: dedicated cross-call cache-isolation regression suite for the two caches above (twobuildSite()calls sharing a filePath string but a different common root must not bleed a label/href between them).test/gen-docs.test.js: a permanent full-output-tree byte-diff regression test (every generated file, not amodules/-only spot check, both with and without--quality) -- extends the existing byte-diff pattern from a one-time verification into an automated guard against futurerenderer.jsregressions.
Performance
- Wall-clock, synthetic multi-directory fixtures,
gen-docsend to end (single machine, informal numbers -- see README Benchmarks for the full table and the CI gate for the authoritative, continuously-re-measured check): 400 files went from 28.38s pre-fix to 0.90s post-fix; 500 files did not reliably complete pre-fix within a reasonable window and now completes in 2.40s; 1,000 files in 4.68s. Seedocs/backlog/task-ls-02-profiling-spike.mdfor the full profiling data anddocs/backlog/task-ls-04-sidebar-node-caching.mdfor the post-fix numbers.
Disclosed trade-offs / known follow-ups
- A pre-existing, unrelated memory-footprint characteristic surfaced during verification:
buildSite()holds every generated page's full HTML (including each page's own embedded sidebar markup) in memory until the whole site is built. This is not something this fix introduced or worsened, but it means very large sites (order of thousands of files) may hit memory limits on constrained hardware independent of the wall-clock fix above -- not yet sized against realistic CI hardware, tracked as an open follow-up rather than silently declared fine. See README's Known limitations. - The 2,000-file point of the perf-gate's own threshold check was not hand-verified in this development environment (memory-constrained sandbox, see above) -- the CI gate (
.github/workflows/perf-gate.yml) re-measures it on every push/PR going forward, which is the authoritative, continuously-current source rather than a single frozen number in this changelog.
Tests
- Test suite grew from 228 to 234 assertions (2 new full-tree byte-diff tests in
test/gen-docs.test.js, 3 new cross-call isolation tests in the newtest/renderer-memoization.test.js). All pre-existing 228 assertions pass unchanged -- verified via byte-diff againstsample/, both with and without--quality, confirming zero output/behavior change (story AC7).
[2.4.1] - 2026-07-07
Version-only republish β package.json/package-lock.json version bump with no functional code change against 2.4.0.
[2.4.0] - 2026-07-07
Added
- Doc-site version switcher (
docs/backlog/story-doc-version-switcher.md): every generated page (index, module pages, health-detail pages) now carries a "Current" control in the topnav. With history >=2 generations, it's a native<details>/<summary>dropdown listing "Current" plus every preserved prior generation, most-recent-first, each linking to a complete, self-contained rendering of the site as it looked at that generation -- not a changelog/diff summary. With no history yet, it renders as a plain, non-interactiveCurrentlabel instead of a broken empty dropdown. Zero client-side JS -- the control is native HTML disclosure, degrades to plain navigable links with JS disabled. --datanow also backfills<out>/site-versions/<version-id>/-- a full rendered snapshot for everysite-data-history/*.jsonentry that doesn't have one yet ("render-only-missing": an already-rendered, immutable snapshot is never re-rendered, keeping per-run cost O(1) regardless of history depth after a one-time backfill).<version-id>is the same ISO-timestampsite-data.jsalready uses for history filenames, so the two stay trivially correlated.- Per-function/method Code Health drill-down (
docs/backlog/story-function-level-health-drilldown.md): function and class-member (constructor/method/getter/setter) entries on module pages now show their own grade/score chip and metric dots, sourced from code-multivitals'sFileReport.functions[], matched by name + tightest-containing-range (our AST extraction only has a start line; code-multivitals'sstartLine/endLinerange is used as the match target). Falls back to a muted "no data" chip -- never a stale file-level number repeated per function -- when no match is found. - Corrected design tokens (
docs/backlog/adr-phase-n-exact-design-tokens.md): the two newly-uploaded mockups (Code Health Report.html,File Detail Report.html) shipped literal, recoverable design tokens this time (unlike Phase M's reconstruction).--coralcorrected from#FF6452to its real#FF4B2E; full corrected radius/shadow/type-scale token set;Space Grotesk/JetBrains Mononow loaded via Google Fonts CDN@import(Chintan's explicit choice over self-hosting) -- the first generated-output dependency on a third-party network resource, documented as a precedent-setting exception to this project's usual zero-external-dependency posture.
Fixed
- Caught during this batch's own live verification, not by a written test in advance: a version-switcher snapshot's own switcher menu failed to mark itself as the current selection on first render, because the version list passed to a backfill pass was computed from already-existing
site-versions/directories rather than from allsite-data-history/*.jsonentries. Fixed before this shipped; every snapshot's own page now correctly self-identifies (is-current/aria-current="page").
Disclosed trade-offs
- An older version-switcher snapshot does not link forward to snapshots rendered after it (a reader must go via "Current" first, one extra hop) -- the direct, necessary consequence of render-only-missing's cost guarantee (re-patching every prior snapshot on every run would reopen the unbounded-cost problem this design avoided). Accepted at product-owner acceptance.
- The live/current generation's own
site-versions/snapshot is deliberately never pre-rendered (a documented deviation from the original architect spec) --site-data.js's history-eviction timestamp can't be predicted before the eviction actually happens, so only already-evicted, stably-named history entries are ever backfilled.
Tests
- Test suite grew from 222 to 228 assertions: 6 new tests in
test/gen-docs.test.jscover the version switcher's full lifecycle -- fresh-repo static-span shape, 3 successive generations with genuinely differing content producing correctly-ordered history, render-only-missing immutability (mtime + byte-identical content across a later run), cross-pagearia-currentcorrectness (the test that caught the bug above), zero-JS degradation, and themax-height:320pxCSS cap. - Verified live against real, incrementally-modified fixtures across 3 successive
gen-docs --quality --dataruns: correct eviction/backfill sequencing, correct render-only-missing, correct most-recent-first ordering, and correct per-pagearia-currenton both the live index and every snapshot page. - Default (no
--data/--quality)gen-docsoutput confirmed byte-for-byte unaffected.
[2.3.0] - 2026-07-07
Changed
- Exact-design shell (
docs/backlog/adr-phase-m-exact-design-shell.md): the doc site's chrome now matchesCode Health Redesign.dc.html/File Detail Redesign.dc.htmldirectly instead of reusing the prior light theme -- dark sidebar (--sidebar-bg:#0E0E10), offwhite content background (--bg:#F5F4F0), and the mockups' purple/lime/coral/gold accents. This is a site-wide chrome change (every page), not scoped to Code Health/File Detail alone. - The project logo, version tag, and the Overview/Code Health nav buttons moved from the header into the sidebar (
.sidebar-brand,.navbtn); the header now carries only a breadcrumb-style crumb and the search pill, matching both mockups. Thetopnav-quality-linkclass is renamedsidebar-quality-linkat its new location. - Sidebar symbol markers (
symRows()) changed from colored text pills to a small colored dot + kind abbreviation, matching the mockup's member-row style;sym-fn/sym-cls/etc. class names are unchanged (now applied to the dot), so no new taxonomy was invented. - Index hero gauge grows 120px -> 168px (the mockup's literal size); the focus-area cards' "view more" link is now a filled pill button using the card's own tag color (mockup's
focus-btn), andtag-chipgets the mockup's 1.5px white outline. - Disclosed limitation: both mockups
@importahundi-design-systemtoken package that was not included in the upload (only referenced by path) -- exact hex values for most of the palette aren't literally recoverable. A few values ARE literal evidence in the mockups' own markup/JS and are used verbatim (--lime:#C6FF3Dfrom the mockup's owngradeHex,--offwhite:#F5F4F0and--accent:#5B4FE8from hover/active rgba tints, the gold/todo-badge hex codes). The rest of the palette is reconstructed to be visually consistent with those anchors; see the ADR's "Finding" section for the full disclosure.
Tests
test/renderer.test.jswas rebuilt after a second sandbox file-truncation incident (this one struck mid-edit and the git-HEAD recovery path turned out to predate the Code Health/File Detail redesigns entirely, since none of this project's work has ever been committed) -- rebuilt directly against the currentlib/renderer.jsrather than forward-patching a stale baseline. Full suite: 219 assertions passing across all suites (renderer/lint/fix/import-graph/project-facts/quality/site-data/gen-docs).- New coverage: sidebar-brand/navbtn/sidebar-quality-link presence, header crumb content and the absence of
topnav-logo,sym-dot/sym-kind-abbrmarkup (and the absence of legacysym-pill), the dark palette tokens and gauge-size CSS selectors, and a<script>-tag count assertion (zero-new-JS still holds). - Verified live against this repo's own
lib/+bin/+plugin source vianpm run dashboard: dark sidebar, breadcrumb header, 168px index gauge, and dot-based symbol markers all render correctly; default (--quality-less) output unaffected beyond chrome colors.
[2.2.0] - 2026-07-08
Added
- Per-file Code Health hero (
docs/backlog/story-file-detail-redesign.md,docs/backlog/adr-phase-l-file-detail-and-site-data.md): module pages now show a circular grade/score gauge (reusing v2.1.0'sscoreToGrade()/qColor()primitives, scoped to that one file) in place of the old flat.qstripchip row, plus a factual one-line narrative built purely from real error/warning/clone counts (deliberately not stylized "encouraging" copy). Files with no code-multivitals entry keep the same graceful muted fallback as before. - TODO-placeholder badge: any
TODO:-prefixed description (function/param/returns text written by--fix, perlib/fix.js's own literal templates) now renders with a small "todo" badge and muted italic text, in function/class descriptions, the params table's Description column, and the Returns line. Real authored text is unaffected -- this is presentation-only. lib/site-data.js+--data/--from-data:--datawrites<out>/site-data.json, one JSON capturing everythingbuildSite()needs (modules + quality/import-graph/snapshot data). It never silently overwrites a prior generation -- the previoussite-data.jsonis moved into<out>/site-data-history/first, so successive generations stay individually comparable.--from-data <path>builds the full site directly from a savedsite-data.json, skipping source parsing and quality analysis entirely (a template/CSS-only iteration no longer re-parses the tree or re-runs code-multivitals). Fully additive:--json/docs.json's existing shape and behavior are untouched.site-data.json/site-data-history/added to.gitignore(generated output, same category asdocs//snapshots/).
Tests
- Test suite grew from 209 to 231 assertions: 2 new suites (
test/site-data.test.js,test/gen-docs.test.js-- the latter isbin/gen-docs.js's first-ever dedicated test file, spawning the real CLI same astest/cli.test.jsdoes forbin/cli.js) plus renderer-level coverage forisTodoText/descText, the per-file hero's grade/narrative/fallback states, and CSS selector presence. Two pre-existing tests deliberately revised (documented inline, same category as prior sanctioned exceptions): the old flat health-strip field/fallback assertions now check the hero gauge's markup shape instead of the retired.qstrip/qChipoutput. - Verified live against this repo's own
lib/+bin/+plugin source: real per-file grades (e.g.bin/cli.jsgraded F), a real--fix-generated TODO description/param/returns rendering the todo badge (constructed fixture, since this repo's own source currently has none left to exercise),--datarun twice preserving history correctly, and--from-datareproducing byte-identicalindex.html/module pages against the original run. Confirmed default (--quality/--data/--from-data-less) output remains deterministic with no new markup beyond the always-present (page-wide) CSS selectors.
[2.1.0] - 2026-07-07
Changed
- Code Health dashboard redesign (
docs/backlog/story-code-health-redesign.md,docs/backlog/adr-phase-k-code-health-redesign.md): the embedded Code Health section's 7 flat stat cards are replaced by a hero panel -- a CSSconic-gradientgauge showing a letter grade + the existing health score (grade is a purely cosmetic finer scale layered on the existingqColor3-color bands, not a second palette), the remaining 6 stats as a compact metrics list, and a trend sparkline + delta badge sourced from--quality-trend <dir>snapshot history (renders an honest "not enough scan history yet" message below 2 snapshots -- never a fabricated line). - The 4 focus-area cards (files needing attention, duplicate code, most-imported files, orphan files) are restyled with a tag chip, colored top border, and a big headline stat; rows beyond the 5-row preview are reachable via a native
<details>/<summary>inline expand -- zero new client-side JS (reuses the same disclosure pattern already used by the sidebar's directory tree). The standalone detail pages (health-attention.htmletc.) are unchanged and still linked from every card. --quality-trend <dir>now also drives the embedded index-page sparkline when passed alongside plain--quality(previously only wired for the standalone--quality-reporter dashboardmode) -- no new CLI flag.- Environment/hardware requirement badges (cores/RAM/disk/editor/browser) from the originally-proposed mockup were explicitly deferred, not built -- none of those values are derivable from repo state, and inventing them would contradict this project's "derived from actual repo state, never hand-typed prose" principle. See the ADR's Decision 7.
Tests
- Test suite grew by 8 assertions:
scoreToGradeboundary cases at every named grade, hero gauge/metrics rendering against real data,buildHealthSparkline's 0/1/2+-snapshot branches (including correctly-signed delta direction), per-cardqcard-expandpresence/absence, a zero-<script>assertion scoped tobuildQualitySection()'s own output, and new CSS selector coverage. One pre-existing test deliberately revised (documented inline, same category as the prior "5β9 pages" exception): the index card's inline expand now embeds overflow rows in the DOM (behind a native, collapsed<details>), so the assertion now checks that exactly 5 rows are visible ahead of the expand, rather than asserting overflow rows are entirely absent fromindex.html. - Verified live against this repo's own
lib/+bin/+plugin source: real avg health 64.1, grade D, 20 errors, 204 warnings (non-placeholder). Confirmed default (--quality-less) output is deterministic and contains zero occurrences of the new markup (qhero/qcard2/code-health). Confirmed the sparkline renders a real trend line from 2 saved snapshots via--quality-snapshot.
[1.21.1] - 2026-07-06
Changed
- Code Health-only index under
--quality(same-day follow-up to v1.21.0,docs/backlog/story-code-health-drilldown.md): when--qualityis passed, the index page's Modules grid is no longer rendered β the Code Health dashboard (7 stat cards + 4 summary cards) is the only content section on the page, instead of sitting alongside the module grid. Module navigation is unaffected: the full module tree remains in the sidebar on the index page (and every page) regardless of this flag. Plaingen-docswith no--qualityis unaffected β the Modules grid renders exactly as before. lib/renderer.js'sbuildSite(): the index body now conditionally skipsbuildIndexBody(modules)whenoptions.qualityis set β a one-line change, additive/conditional only, no other index-page markup touched.README.md: added a "Code Health dashboard (--quality)" preview (assets/preview-quality.svg) and rewrote the### Quality reportingdescription to reflect the card layout, the 4 detail pages, the per-module health strip, and that the dashboard replaces (not sits alongside) the Modules grid on the index page. Also corrected the stale "152 passing tests" figure to the current 201.
Added
assets/preview-quality.svg: new hand-crafted mockup (same style/size convention asassets/preview.svg) depicting the--qualityindex page β stat cards, the 4 summary cards with "View all β" links, no Modules grid.
Tests
- Test suite grew from 195 to 201 assertions: 3 new tests cover (1) Modules grid removed from index body when
--qualityis active, (2) Modules grid still renders when--qualityis absent (regression), and (3) sidebar module navigation is unaffected either way.
[1.21.0] - 2026-07-06
Added
- Code Health card layout + drill-down detail pages (fast-follow to v1.20.1's embedded section,
docs/backlog/story-code-health-drilldown.md): the index page's 4 full inline tables (files needing attention, duplicate code, most-imported files, orphan files) are now compact summary cards (top 3-5 rows + a "View more" link), each linking to its own full, uncapped detail page at the output root:health-attention.html,health-duplicates.html,health-imports.html,health-orphans.html. The 7 stat cards above them are unchanged. - Per-module health strip: every generated module page now shows a health-metrics strip (
<aside aria-label="Code health summary">) directly below its header β Health Score, Maintainability, Functions, Errors, Warnings, Clone involvement, Worst severity, Code smells β so a reader lands on a file's health context without cross-referencing the index. Modules with no code-multivitals entry show a single muted "No code health data for this file." row instead of throwing or omitting the strip. - Both are additive to the existing
--qualityflag β no new CLI flag, and default (--quality-less)gen-docsoutput is unaffected (verified: 0 occurrences of the new markup, 0 new files, when--qualityisn't passed).
Changed
lib/renderer.js:buildQualitySection()now rendersqCard-based summary cards instead of full inline tables; newbuildHealthDetailPages(),buildFileHealthLookup(), andbuildHealthStrip(). Per-file error/warning/function counts for the strip are derived from existingFileReport.functions[].metrics[].severitydata (code-multivitals'sFileSummarydoesn't carry them directly) β no new metrics, no new dependency.- Test suite grew from 187 to 195 assertions. One existing regression test's contract deliberately changed:
buildSite()under--qualitynow returns 9 pages (5 base + 4 health detail pages), not the prior "still exactly 5" β documented inline intest/renderer.test.jsas a sanctioned exception to the single-artifact principle, not a silent regression (the 4 new pages are extensions of the doc site's pre-existing multi-page nature, same category as the one-page-per-module pages that have always existed).
Correction (caught during design review, before implementation)
- The initial architecture pass assumed the 4 new detail pages should live "in the same output directory as module pages." That's incorrect against the actual code β module pages live in a
modules/subdirectory (moduleHtmlPath()), whileindex.htmlis written at the output root. Corrected before implementation: the 4 detail pages live at the output root, sibling toindex.html, since they're extensions of the index page's Code Health section, not per-module content. Seedocs/backlog/story-code-health-drilldown.md.
[1.20.1] - 2026-07-06
Fixed
gen-docs --qualitynow embeds its findings directly intoindex.htmlinstead of writing (or, in the internal-only path, requiring) a separate file. Chintan's direct feedback after reviewing the generateddocs/index.html: the code-multivitals statistics belonged in the same page as the API docs, not off in a second artifact.--qualitywith no--quality-reporternow computes the code-multivitals analysis + the import-graph/orphan-file findings and passes them into the normal doc-site build, which renders a new "Code Health" section (stat cards, files-needing-attention table, duplicate-code pairs, most-imported files, orphan files) on the index page, with a "Code Health" link in the top nav.--quality-reporter <console|json|html|sarif|badge|dashboard>is still available and unchanged for anyone who explicitly wants a standalone report file instead (CI/export use cases) β it still skips the doc-site build, same as before.- Retired
scripts/gen-dashboard.js(and thenpm run dashboard/npm run quality:dashboardscripts) β the separate internaldocs/dashboard.html+docs/dashboard-quality.htmlpair it produced is now redundant with the embedded section above. Newnpm run docs:internalscript documents jsdoc-scribe's ownlib//bin//plugin source with--quality, producing the same one-file result contributors get from any project.npm run qualitynow explicitly passes--quality-reporter console(previously the implicit default) so it keeps its old plain-console behavior now that the flag's un-reportered default means something different.
Changed
lib/renderer.js:buildSite(modules, options)accepts an optionaloptions.quality({ result, graph, orphans }); when present,buildQualitySection()renders the Code Health section on the index page only β module pages are unaffected, and no new page is added to the site (still exactly3 assets + index.html + one page per module, verified by the existing "5 pages total" regression test)..gitignore: addeddocs-internal(the new script's generated output, never committed β same treatment asdocs).
[1.20.0] - 2026-07-06
Added
- Project dashboard (internal, repo-only):
npm run dashboardgeneratesdocs/dashboard.html(onboarding facts β Node version vs. CI-tested matrix, package manager, project structure, global dependencies, test tooling β plus a project-wide import graph and orphan-file report) anddocs/dashboard-quality.html(embedded code-multivitals dashboard). New moduleslib/project-facts.jsandlib/import-graph.js(the latter does its own lightweight import/export extraction via thetypescriptcompiler API β no new dependency, but see Correction below), new repo-onlyscripts/gen-dashboard.js(not part of the published package). Seedocs/backlog/epic-project-dashboard.md/adr-phase-j-project-dashboard.md. - Quality reporting for
gen-docs(--quality,--quality-reporter,--quality-profile,--quality-config,--quality-baseline/--quality-save-baseline,--quality-snapshot/--quality-trend): opt-in code-multivitals integration (cyclomatic/cognitive complexity, Halstead volume, maintainability index, health score, compound smells, duplicate-code detection, all 6 reporters, baseline/diff mode, snapshots/trends/hotspots).code-multivitalsis an optional peerDependency (peerDependenciesMeta.optional: true) β never installed bynpm install jsdoc-scribe, never affects defaultgen-docsbehavior, and produces a clear install-instruction error (not a crash) if used without it installed. New shared modulelib/quality.js(dynamicrequire, never a top-level import, so the optional-dependency contract holds). code-multivitalsalso added as this repo's first-everdevDependency(distinct from the peerDependency above), used by the internal dashboard. Committed.code-multivitals.json(thedefaultthreshold profile).- New npm scripts:
dashboard,quality,quality:dashboard.
Correction (caught during implementation)
- The original design (
adr-phase-j-project-dashboard.md,story-code-health-dashboard.md) assumedlib/import-graph.jscould reuselib/extractor.js's existing per-file import/export data, the same waylib/drift.js/lib/coverage.jsreuseextractModule()'s output. That data does not exist βextractModule()has never extracted imports/exports.lib/import-graph.jsdoes its own lightweight extraction instead, using thetypescriptcompiler API (the project's existing one runtime dependency) β zero new dependency, but genuinely new parsing, not zero-new-parsing as originally assumed. Corrected in the ADR/story rather than shipped against the false premise.
Changed
- Test suite grew from 152 to 181 assertions (10 new import-graph tests, 12 new project-facts tests, 9 new quality-module tests β the last including a real clean-room "package not installed" repro via a child process, not just a cache-eviction approximation).
package.json:files/binunchanged (still onlybin/cli.jsandbin/gen-docs.js) β the new internal dashboard script lives inscripts/, outside the publishedfilesglob, on purpose (verified vianpm pack --dry-run).
Design note
- Two different, deliberately separate dependency relationships to
code-multivitalsin the same release: adevDependency(internal dashboard, always present in this repo) and an optionalpeerDependency(end-usergen-docs --quality*, never forced on anyone). Conflating the two β e.g. making it a plain runtime dependency so end users get it "for free" β was considered and rejected; seeadr-phase-j-project-dashboard.mdDecision 10 and its Alternatives Considered section.
[1.19.0] - 2026-07-06
Added
--fixflag forgen-comments: rewrites existing JSDoc blocks in place to resolve--lintfindings β reorders@paramtags to match real parameter order, fills a missing@param/@returns/description with a fixed, deterministic"TODO: ..."placeholder (never invented prose), strips trailing text off a no-description tag, collapses stray asterisks, and drops an unnecessary@returnson a function that never returns a value. Implies--lint. Does not add JSDoc to undocumented symbols (that stays--write's job) and never auto-fixescheck-tag-names(an unknown/typo'd tag has no safe default β always left as a remaining issue for a human). Newlib/fix.js, zero new npm dependency.eslint-plugin-jsdoc-scribe@0.2.0: 10 of the plugin's 12 rules now ship a real ESLintfix()(fixable: "code"), using the same rebuild strategy andTODO:placeholder convention as the core CLI's--fix.require-jsdocandcheck-tag-namesremain fixer-less, for the same reasons as the core CLI.
Changed
- Internal:
lib/extractor.js'sreadJSDoc()now also returnscommentRange({ pos, end }, the exact source-text range of an existing JSDoc block) per symbol β purely additive, same threading pattern asrawComment/badCommentin v1.18.0. Verified zero behavior change: all 132 pre-existing tests pass unmodified. - Test suite grew from 132 to 152 assertions in the core package (20 new: 16
lib/fix.jsunit tests, 4 CLI-level--fixtests) and from 36 to 39 ineslint-plugin-jsdoc-scribe(3 newLinter.verifyAndFix()convergence/preservation tests, alongside 10 newRuleTesteroutputassertions on already-existing cases).
Design note
- Filling in a missing description with a placeholder is a deliberate, narrow exception to "no AI, no guessing" β every placeholder is a fixed template (
"TODO: describe ...β), never code-derived prose, and is unmistakably marked so a human immediately knows it needs attention (grep -r "TODO: describe"finds every one). Seedocs/backlog/adr-013-lint-autofix.mdfor the full reasoning, including whycheck-tag-namesis the one rule that stays report-only even here.
[1.18.0] - 2026-07-06
Added
--lintflag forgen-comments: native JSDoc content validation β no ESLint required. Newlib/lint.jsrule engine reusesextractModule()'s existing AST + parsed-JSDoc data (plus a new additiverawComment/badCommentfield per symbol) to check the same category of thingseslint-plugin-jsdoc'srecommendedconfig does:require-jsdoc,require-param/require-param-description,check-param-names(ordering),require-returns/require-returns-description/require-returns-check,require-description,check-tag-names,empty-tags,no-multi-asterisks,no-blank-block-descriptions,no-bad-blocks. Read-only, exits 1 if issues are found β same CI-gate contract as--check/--check-drift. Zero new npm dependency.--lintand--check-driftcan be passed together in one invocation; both share a singleextractModule()parse per file rather than parsing twice.
Changed
- Internal:
lib/extractor.js'sreadJSDoc()now also returnsrawComment(the exact/** */block text) andbadComment(a near-miss malformed block, e.g. wrong asterisk count) per symbol, threaded through every extractor function/class/method/constructor/getter/setter/property/interface/typeAlias/enum/variable output. Purely additive β verified zero output change on every existing field via a byte-diff dogfood run acrosslib/andbin/(same method astask-a10-03), excluding the two new fields. - Test suite grew from 101 to 132 assertions (31 new:
lib/lint.jsunit tests plus--lintCLI-level tests).
Known limitation (tracked, not a regression)
extractModule()'s traversal (existing behavior, inherited by--lint) walks into function bodies and picks up local variable declarations as documentable "symbols," not just top-level/exported declarations β the same reason--checkhas always reported jsdoc-scribe's own low internal-helper coverage number.--lint'srequire-jsdocinherits that same scope, so running--lintagainst this repository's ownlib/reports real (not false-positive) gaps on internal helpers that were never intended to carry a doc block, consistent with what--checkalready reports today. Scoping extraction to declaration depth or exported-only symbols is a larger, separate change candidate for a future story β not bundled into this release.
[1.17.0] - 2026-07-05
Added
- Internal AST ergonomics layer: new
lib/ast-utils.jsmodule with four small, purpose-built traversal/guard helpers built directly on thetypescriptCompiler API βgetDescendantsOfKind(node, kind),findFirstDescendant(node, predicate, stopAt),asClass(node),asFunctionLike(node). Not part of the public API; internal-only.
Changed
- Internal:
lib/extractor.js's traversal internals migrated onto the new helpers βhasReturnWithValue's hand-rolled walk is nowfindFirstDescendantwith astopAtboundary, and the localisFunctionLike/class-dispatch checks now go throughasFunctionLike/asClass. Zero output/behavior change (verified via a line-number-normalized diff ofextractModule()across every file inlib/andbin/, plus agen-comments --dry-runsanity check) β no new npm dependency, still justtypescript. - Test suite grew from 88 to 101 assertions (13 new, covering the ast-utils helpers directly, including nested-function boundary behavior).
[1.16.0] - 2026-07-02
Added
- Drift detection: new
--check-driftflag forgen-commentscompares existing JSDoc blocks against the current code and flags missing params, removed (stale) params, and return-type mismatches. Read-only and CI-friendly β exits 1 if drift is found, exits 0 otherwise, and never modifies source files. - Coverage badges: new
--coverage-badge <dir>flag forgen-commentsaggregates documentation coverage across a target path and writes a self-contained, shields.io-stylecoverage-badge.svgplus acoverage-summary.json. No network dependency β fully offline and deterministic. - N-level sidebar navigation: the generated docs site sidebar now renders an unbounded-depth collapsible folder tree (previously capped at 2 levels), with the active module's ancestor folders auto-expanded. Full keyboard navigation (arrow keys) and screen-reader support (ARIA tree semantics).
- Breadcrumb context on the index page: module cards for nested files now show a directory breadcrumb (e.g.
helpers / server) above the filename, so it's clear where a module lives at a glance. Deeply nested paths truncate to keep cards tidy.
Changed
- Internal:
--check's coverage math now goes through a sharedaggregateCoverage()helper (also used by--coverage-badge) instead of being computed inline β output is unchanged, this just avoids having the same math defined twice.
Fixed
- Module cards on the documentation index page now include a
titleattribute with the full relative path on hover, matching the tooltip behavior individual symbols already had.
[1.15.0] - 2026-07-01
Changed
- Phase H: Smart sidebar grouping β strips deeper common root from module labels, caps group nesting at 2 dir segments
- Sidebar group labels now show only the deepest relevant directory name (not the full path), fixing the verbose UPPERCASE path bug
- Module links inside groups show only the filename, with the full relative path in
titlefor hover tooltips - Sidebar section title and dir-toggle headers are now sticky (position:sticky) so they stay visible while scrolling
- Index page module cards display shortened labels (deep common root stripped)
- Empty-state message in
buildModuleBodyupgraded to styled<div class="empty">with explicit "No exported items in this module." text - Index page module cards show "No exported items" italic note for modules with zero exports
- New helpers:
deeperCommonRoot()andhasExports()added to renderer.js
[1.14.0] - 2026-07-01
Changed
- Phase G: Stripe-style documentation layout (v1.14.0)
- New sticky top navigation bar with project name (top-left) and centered search
- Sidebar redesigned: white background, uppercase section headers, accent-colored active links
- Right-side TOC restored ("On this page") with scroll-spy via IntersectionObserver
- Three-column CSS Grid layout: sidebar (240px) | main (1fr) | toc (200px)
- Card scroll-margin-top updated for topnav offset
- Search moved from sidebar to topnav center; "/" and Ctrl+K shortcuts
- Accent color: #625bf6 (Stripe purple)
- Hamburger toggle updated for overlay sidebar on mobile
Changelog
All notable changes to jsdoc-scribe are documented here. Format follows Keep a Changelog.
[1.13.0] - 2026-07-01
Changed β Phase F: Single-design documentation site
lib/renderer.js β complete visual redesign
- Removed
THEMESmap and all CSS-variable-based theming; single built-in design replaces it - Dark blue sidebar (
#0a2540) with white text and#00d4ffactive link highlight - Light content area (
#f6f9fcbackground) with white symbol cards - Two-column card layout: each symbol card is a CSS grid split β left prose panel (description, params, returns, throws) and right dark code panel (
#1a2e44) - Right code panel shows
@examplecontent when present; falls back to the type/class signature - Removed right-side TOC column entirely:
buildToc()deleted,has-tocclass gone,IntersectionObserverscroll-spy removed fromCLIENT_JS - Removed dark/light mode toggle button from sidebar
- Responsive: code panel collapses below prose at β€860 px; sidebar becomes hamburger overlay at β€720 px
- New helpers:
card(),codePanel(),buildFnSig(),buildClassSig(),buildIfaceSig() - Retained: Ctrl+K search, copy button, mobile hamburger, sidebar symbol tree with kind pills
bin/gen-docs.js β flag removal
- Removed
--theme/-Tflag andVALID_THEMESconstant; theme option silently ignored if present in config file for backward compatibility
---
[1.12.0] - 2026-06-30
Added β Phase E: Smart Comment Generation + CI tooling
lib/inferrer.js (new module) Pure heuristic engine β no AI, no external calls. Maps camelCase names to natural English descriptions at build time.
splitCamel(name)β splitsgetUserByIdβ["get","user","by","id"], handles consecutive uppercase runs (HTMLParserβ["html","parser"])inferFunctionDescription(name, mods)β 85+ verb-prefix templates coveringget/set/is/has/create/delete/validate/emit/handle/....isUserActiveβ "Returns whether the user is active."createPaymentIntentβ "Creates a new payment intent."inferParamDescription(name)β ~100 well-known parameter names mapped to concise descriptions.userIdβ "user unique identifier."callbackβ "callback function invoked on completion."maxRetriesβ "max number of retry attempts." Suffix matching handles compound names likefilePathβpath to the file.inferClassDescription(name)β 50+ class-name suffix templates.UserServiceβ "Service responsible for user operations."ValidationErrorβ "Error thrown when validation related issues occur."
lib/index.js β smarter comment generation
- Generated function comments now open with a meaningful description line instead of a
[Description]placeholder @paramlines now include inferred descriptions:@param {string} userId - user unique identifier.@throwsauto-detection: scans the function/method body AST forthrow new SomeError(...)and adds@throws {SomeError}tags automatically β without descending into nested functions- Getter/setter accessors get natural descriptions: "Returns the X." / "Sets the X."
- Removed redundant
@function,@class,@exportedtags from generated blocks voidreturn type no longer emitted on methods that have nothing to return- New
dryRunoption inprocessFile()β analyses without writing - New
analyseFile(filePath)export β returns{ documented, total, undocumented }without modifying the file
bin/cli.js β new flags
--dry-run/-n: shows which symbols would be documented (per file) without writing anything--check/-C: like--dry-runbut exits with code1if any symbols are undocumented β use as a CI gate to enforce coverage
Tests expanded from 25 β 30
- Verb-prefix description inference (
getUserByIdβ "Returns the user by id.") @throwsauto-detection from AST- Class-name description inference (
UserServiceβ "Service responsible forβ¦") analyseFile()returning correct undocumented countanalyseFile()returning 0 undocumented afterprocessFile
---
[1.11.0] - 2026-06-30
Changed β Code Quality (Phase D)
extractor.js robustness
- Added
sourceFilenull guard: ifts.createSourceFile()returns a falsy value the function logs to stderr and returns an empty-but-valid module object instead of throwing. - Wrapped the entire
visit(node)body intry/catch: a malformed or unexpected AST node now logs a warning (jsdoc-scribe: skipped node in <file> β <message>) and continues visiting sibling nodes instead of aborting the whole module.
renderer.js refactor
- Extracted
buildSymbolMap(modules)frombuildSite()β builds the{name β {anchorId, modulePath}}cross-reference map used by{@link}resolution. - Extracted
buildIndexBody(modules)β builds the module-grid HTML block for the index page (previously inlined inbuildSite()). - Extracted
buildModuleBody(mod, sourceUrl, symbolMap)β builds the sections HTML for a single module page (previously inlined inbuildSite()). buildSite()is now a slim coordinator (~25 lines) delegating to the three helpers above.
docs.js async upgrade
extractModules(files)is nowasyncand usesPromise.allSettled: all files are attempted in parallel; fulfilled results are collected, rejected ones are logged to stderr and skipped.generateSite(inputPaths, options)is nowasyncaccordingly.
---
[1.10.0] - 2026-06-30
Added β Test coverage (Phase C)
Expanded npm test from 7 tests to 25 tests across three suites.
test/extractor.test.js β 10 new tests (exercises lib/extractor.js):
- Module-level
@moduledescription and@sinceextracted from top-of-file JSDoc @paramtype and description parsed from JSDoc block@returnstype and description parsed@sinceand@deprecatedon individual items@throwswith type and description- 1-based source line numbers on all functions (ordered)
- Class with constructor, methods, properties, and static members
- Interface with optional properties
- Enum members and their values
- Type alias and exported
constvariable
test/renderer.test.js β 8 new tests (exercises lib/renderer.js via mock module objects):
buildSitereturns exactly 3 shared assets +index.html+ one page per module- Search index contains every symbol (function, class) with root-relative URLs
- Module page includes right-side TOC with
data-anchorattributes and "On this page" title @deprecatedbadge and notice rendered for deprecated items- Source link contains GitHub URL and
#L42line anchor whensourceUrlset @exampleblocks rendered withtok-kwsyntax-highlighting spans- Sidebar symbol tree includes
sym-rowsand kind pills for the active module {@link Symbol}in descriptions resolves to<a class="link-ref">withhref
Fixed β @description tag in JSDoc blocks
parseJSDocBlock() now recognises @description <text> as an alias for the plain-text description that precedes the first @tag. Previously @description was silently discarded as an unknown tag, leaving mod.description === null for any file that used it (including all built-in sample files).
---
[1.9.0] - 2026-06-30
Changed β Enterprise HTML Redesign (Phase B)
Complete visual overhaul of the generated documentation site.
Three-column layout
- Left sidebar (272 px) Β· Center content (flexible) Β· Right TOC (224 px)
- CSS Grid replaces the old flexbox layout:
.layout { grid-template-columns: 272px 1fr 224px } - Index page uses two-column grid (no right TOC); module pages use three columns automatically via the
has-tocclass
Right-side "On this page" TOC
- New
buildToc(mod)function generates a per-section TOC (Functions, Classes, Interfaces, etc.) with every symbol as a clickable anchor IntersectionObserverscroll spy inapp.jstracks which card is on screen and highlights the matching TOC item with an accent-colored left border- TOC hides at β€1280 px viewport width; three columns collapse to two
Symbol tree in sidebar
- Active module expands to show every symbol underneath its file link
- Each symbol is prefixed with a color-coded pill badge (
fn,cls,if,ty,en,$) matching its kind - New
.sym-rows,.sym-row,.sym-pill,.sym-linkCSS classes
Color-coded cards
- Each card now has a 3 px left accent border: green=function, blue=class, purple=interface, orange=enum, teal=type alias, gray=variable/const
- Added
card-fn,card-cls,card-iface,card-enum,card-type,card-varCSS classes to all render functions
Server-side syntax highlighting for @example blocks
- New
highlightCode(raw)function inrenderer.jstokenizes JS/TS code at build time - Handles line comments, block comments, strings (single/double/template), numbers, and 40+ keywords
- Produces
<span class="tok-kw|tok-str|tok-cmt|tok-num">spans; colors adapt to dark/light theme via CSS vars
Responsive layout
@media (max-width: 1280px): right TOC hidden, grid collapses to two columns@media (max-width: 860px): sidebar becomes a fixed overlay (off-screen by default), hamburger button appears in the top-left, main content uses mobile padding- Hamburger toggle with animated open/close icon (three-bar β X)
Print styles
@media print: sidebar, TOC, hamburger, copy buttons, theme toggle, and search box all hidden- Cards get
break-inside: avoidand a neutral border for clean PDF output
Section counts
- Section headings now show item count:
Functions (3)using a.section-countmonospace chip
Other polish
html { scroll-behavior: smooth }for smooth anchor navigation- Sidebar active link gets a 2 px accent left border instead of just a background change
- Card hover adds a subtle box-shadow
- All
section()calls updated with count display - CSS ~15 KB (up from ~9 KB), app.js ~4 KB (up from ~2.6 KB) β still cached after first load
---
[1.8.0] - 2026-06-30
Changed β Performance: Shared Static Assets (Phase A)
Previously every generated HTML page inlined the same 9 KB CSS block, 3 KB client JS, and 34 KB search index. On a 9-page site that wasted 630+ KB of duplicate payload.
- CSS extracted to
assets/style.cssβ written once per build, shared by all pages via<link rel="stylesheet">. - Client JS extracted to
assets/app.jsβ written once, shared via<script src>. Browsers cache it after the first page load. - Search index extracted to
search-index.jsβ written once aswindow.__SEARCH_INDEX__=[...], loaded beforeapp.jsvia<script src>. No more 34 KB inline JSON on every page. app.jsauto-detects its location (/modules/in the path) and adjusts search result URLs at runtime, so a single shared index file works for both the index page and all module pages.
Result
| Metric | Before | After | |---|---|---| | Total site size | 628 KB | 225 KB (β64%) | | Per-page HTML (avg) | ~70 KB | ~20 KB | | Cache benefit (2nd+ page) | ~70 KB reload | ~20 KB reload | | Shared assets (loaded once) | β | 46 KB |
Upgraded β buildSite() output
The return array now includes three additional entries: { path: 'assets/style.css', html: '...' }, { path: 'assets/app.js', html: '...' }, { path: 'search-index.js', html: '...' }. The CLI handles these automatically. Programmatic API users should use fs.mkdirSync(path.dirname(dest), { recursive: true }) before writing each file (the README example is updated).
---
[1.7.0] - 2026-06-29
Added
- Module-level JSDoc (
@module/@descriptionat top of file):extractModuleDoc()inextractor.jsreads the first/** */block before any declarations and extractsdescription,moduleName, and@since. Module pages now show a description paragraph below the file path; index cards show a 2-line truncated preview. - Per-module index stats: each index card now shows a symbol breakdown (
fn Β· class Β· iface Β· enum Β· const),@sinceversion range across all items (e.g.since v1.0.0βv1.1.0), and a deprecated-item badge count when deprecations are present. - Full-text search (
bodyfield in search index): search now matches against item descriptions,@paramdescriptions,@returnsdescriptions, and@throwsdescriptions β not just symbol names and module labels. Matched results display a one-line body preview in the search panel. E.g. searching"rate limit"surfacesRateLimitError. {@link Symbol}cross-references:resolveLinks()inrenderer.jsreplaces{@link ClassName}and{@link ClassName#method}tags in JSDoc descriptions with<a class="link-ref">anchor links pointing to the target card, within the same module page or across module pages..link-refand.module-descCSS for the above.
Changed
section()signature extended with an optionalsymbolMapargument, threaded into every item renderer sodescHtml()can resolve cross-references.buildSearchIndex()now callsbuildBody(item)to populatebodyon each entry; client-sidesearch()andrender()functions updated to match and display body previews.buildSite()now builds asymbolMap({name β {anchorId, modulePath}}) before rendering any page.
---
[1.6.0] - 2026-06-29
Added
- Config file support (
lib/config.js):.jsdoc-scribe.jsonin the project root storesout,title,theme,json,readme,ignore(array of globs), andsourceUrl. CLI flags always override config values. Custom path via--config/-c. - Ignore patterns (
--ignore <glob>/-I, repeatable): glob-like patterns (supports**/prefix and*wildcard) exclude files and directories from collection. Also reads theignorearray from the config file. - Source links (
--source-url <url>/-s): each documentation card shows the file path and line number. When a GitHub base URL is provided (e.g.https://github.com/user/repo/blob/main), links point directly to the source line on GitHub. - Enterprise sample modules in
sample/:errors.ts(full AppError hierarchy with 7 classes and 3 helpers),events.ts(typed async EventBus with priority, once(), bridgeEvent),middleware.ts(composable Pipeline class + 5 built-in middleware: logger, responseTime, timeout, cors, errorToResponse),container.ts(DI Container with singleton/transient/scoped lifetimes + ConsoleLogger + MemoryCache + buildRootContainer). - Line numbers extracted for every documented symbol (
node.getLineAndCharacterOfPosition()in extractor.js).
Changed
collectFiles()accepts a fourthignorePatternsarray argument.buildSite()acceptssourceUrlin the options object.- All item renderers (
renderFunction,renderClass, etc.) acceptfilePathandsourceUrlfor source link generation.
---
[1.5.0] - 2026-06-29
Added
- JSDoc tag extraction:
@param,@returns,@throws,@since,@deprecatedparsed from/** */blocks and attached to every extracted item.@paramdescriptions enrich the parameter table;@throwsrenders a dedicated table;@deprecatedshows a warning notice + badge;@sinceshows a version label. - README auto-generator (
--readme/-r): writesREADME.mdto the output directory summarising all exported symbols grouped by module with markdown tables. - Multiple themes (
--theme <name>/-T): three built-in themes βdefault(dark sidebar, light/dark toggle),minimal(clean light, no toggle),dark(forced dark, no toggle). - Deprecated badge and warning notice rendered for
@deprecateditems throughout all section types.
---
[1.4.0] - 2026-06-29
Added
- Programmatic API (
require('jsdoc-scribe/docs')):lib/docs.jsexportscollectFiles,extractModule,extractModules,buildSite,generateSite,moduleLabel,moduleHtmlPath, and constants.package.jsonexportsfield maps./docssubpath. - JSON export (
--json/-j):gen-docswritesdocs.jsonwith title, version,generatedAtISO timestamp, and full modules array alongside the HTML site. - GitHub Actions workflow (`.github/workflows