@suluk/docs - v0.1.4
    Preparing search index...

    @suluk/docs - v0.1.4

    Suluk

    @suluk/docs

    Generate a static documentation site for a Bun/TS monorepo, straight from source — no build, no annotations beyond the code's own doc-comments.

    Part of Suluk — one typed OpenAPI v4 contract projecting into every full-stack layer.


    CANDIDATE tooling — not official OpenAPI. Suluk is a single-contributor candidate for OpenAPI Specification v4.0 ("Moonwalk"), unaffiliated with the OpenAPI Initiative and unable to ratify anything on the SIG's behalf.

    bun add @suluk/docs
    

    It harvests a Bun/TS workspace into a documentation model and renders it to a flat, self-hosting site — the same "one source, many projections" discipline as the rest of Suluk, applied to the monorepo itself.

    • Harvests from source. For each package directory it reads package.json (name / description / version / deps), the leading doc-comment of src/index.ts as the overview, the public exports of that barrel, and each module's leading doc-comment. No extra annotations — it uses the comments the code already carries.
    • Renders a complete site. A landing page, one page per package (install + overview + public API + deps + modules), an Architecture page with a generated package-dependency diagram, plus curated Get-started / Contributing / Community pages.
    • Zero-build output. Plain HTML + one stylesheet + a .nojekyll, with relative links — drop the files on GitHub Pages (even a project-pages subpath) and it serves verbatim. Suluk documents itself this way.
    • Composable primitives. A small dependency-free Markdown→HTML renderer (mdToHtml) and a D2 package-graph + kroki URL helper are exported on their own.

    Reach for it to document the monorepo / toolkit itself — the packages, how they depend on each other, what each one exports. It reads structure from package.json + barrel doc-comments, so it stays in sync as you add packages.

    It does not document your API. For an OpenAPI v4 reference site use @suluk/reference / @suluk/scalar / @suluk/swagger (those project the v4 document; this projects the codebase).

    The common case is two calls — harvest a packages directory into a FrameworkDoc, then generateSite it into files you write to disk.

    import { harvest, generateSite } from "@suluk/docs";
    import { writeFileSync, mkdirSync } from "node:fs";
    import { join, dirname } from "node:path";

    const fw = harvest({
    packagesDir: join(import.meta.dir, "packages"),
    title: "Suluk",
    tagline: "One typed contract — projected into your entire stack.",
    description: "**Suluk** derives the whole stack from one source.",
    repoUrl: "https://github.com/MahmoodKhalil57/suluk",
    architecturePath: join(import.meta.dir, "ARCHITECTURE.md"), // optional — prepends to the Architecture page
    // excludePrivate: true, // drop demo/private packages from the public docs (default: include, flagged)
    });

    const out = join(import.meta.dir, "site");
    for (const f of generateSite(fw)) {
    const p = join(out, f.path);
    mkdirSync(dirname(p), { recursive: true });
    writeFileSync(p, f.content);
    }
    // → site/index.html, site/<pkg>.html, site/architecture.html, site/style.css, site/.nojekyll, …

    generateSite returns SiteFile[] ({ path, content }) — no I/O of its own, so you own where the bytes land. Override the curated pages by passing Markdown:

    generateSite(fw, {
    gettingStarted: "# Get started\n\nYour own intro…",
    contributing: "# Contributing\n\n…",
    community: "# Community\n\n…",
    });

    The harvest and render steps are exposed individually:

    import { harvestPackage, renderIndex, renderPackage } from "@suluk/docs";

    const pkg = harvestPackage("./packages/core"); // → PackageDoc | null (null if no package.json)
    const indexHtml = renderIndex(fw); // the landing page
    const pageHtml = renderPackage(fw, fw.packages[0]); // one package page
    import {
    mdToHtml, parseExports, firstBlockComment,
    packageGraphD2, krokiD2Url, STYLE,
    } from "@suluk/docs";

    mdToHtml("# Hi\n\n`code`, **bold**, and a [link](https://suluk.dev)"); // → HTML string
    parseExports(`export { a, b as c } from "./x";`); // → ["a", "c"]
    firstBlockComment("/**\n * one\n * two\n */"); // → "one\ntwo"

    const d2 = packageGraphD2(fw.packages); // D2 of the @suluk dependency graph (private pkgs omitted)
    krokiD2Url(d2); // → https://kroki.io/d2/svg/… (deflate+base64url, renders the diagram)
    STYLE; // the site's single stylesheet, if you assemble pages yourself
    Export What it does
    harvest(opts) Read a packages dir → FrameworkDoc (every package's name, overview, exports, deps, modules).
    harvestPackage(dir) Harvest a single package dir → PackageDoc | null.
    generateSite(fw, opts?) Assemble the whole site → SiteFile[] ({ path, content }); no I/O.
    renderIndex(fw) Render just the landing page → HTML.
    renderPackage(fw, p) Render one package page → HTML.
    renderMarkdownPage(fw, file, title, md) Render a curated Markdown page into the site chrome → HTML.
    mdToHtml(md) / inline(text) / escapeHtml(s) Dependency-free Markdown / inline-span / HTML-escape helpers.
    parseExports(src) Collect the public symbol names a barrel re-exports.
    firstBlockComment(src) Extract + clean the first JSDoc block comment.
    packageGraphD2(packages) D2 source for the @suluk package-dependency graph.
    krokiD2Url(d2) A kroki.io render URL for D2 source.
    STYLE The site's single stylesheet (string).

    Types: FrameworkDoc, PackageDoc, ModuleDoc, HarvestOptions, SiteOptions, SiteFile.

    This package renders, never hosts (the L3 line). generateSite returns the files; you write them and serve them (GitHub Pages, any static host). It reads from a directory of packages and emits HTML strings — no network in the harvest/render path, and the only outbound URL it constructs is the kroki render link for the architecture diagram. What it documents is the codebase (package structure + doc-comments), not your runtime API — that projection belongs to @suluk/reference / @suluk/scalar / @suluk/swagger.

    Apache-2.0