# How the ego-lite help() Runtime Parses JSDoc to Generate Dynamic Documentation

> Discover how ego-lite's help() runtime efficiently generates dynamic documentation by parsing JSDoc into a pre-computed JSON dataset, deserializing it on demand.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-16

---

**The `help()` runtime in `citrolabs/ego-lite` avoids parsing raw JSDoc in the browser by embedding a pre-computed JSON data set at build time and lazily deserializing it into a `Map` when documentation is requested.**

The `citrolabs/ego-lite` browser SDK exposes a `help()` utility that lets consumers inspect public helper documentation on demand. Rather than parsing JSDoc comments directly at runtime, the `help()` runtime parses a serialized JSON payload that is injected during the build pipeline. This approach eliminates file-system I/O and keeps helper lookups to a strict O(1) operation.

## Build-Time JSDoc Extraction Pipeline

The dynamic documentation starts as source code comments and ends as an embedded string literal inside the bundle.

### Scanning the Bundle with Acorn

When you run `npm run build`, the SDK is bundled to [`dist/out/index.js`](https://github.com/citrolabs/ego-lite/blob/main/dist/out/index.js). Immediately after, `scripts/extract-help-docs.mjs` (lines 19–74) reads that bundle and parses it with **Acorn** using `parse(source, { onComment, locations: true })`. This produces an AST while preserving all block comments and their source locations. The public helpers whose JSDoc is harvested are declared in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and wired through [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts), so the bundled output contains every comment Acorn needs.

The script indexes every block comment by its ending line in a `commentsByEndLine` Map. A walker named `walkFunctions` then visits every `FunctionDeclaration` and `FunctionExpression`. For each function node, it fetches the JSDoc comment immediately preceding the function via `commentsByEndLine.get(startLine - 1)` and passes that comment to `parseJSDoc`.

### Extracting HelperDoc Objects

For every matched function, the extractor gathers parameter information from the AST through `extractParams` and enriches it with the parsed JSDoc data. It then builds a **signature string** and a `HelperDoc` object, storing both in a local `Map`. Alias variables such as `const click = doClick` are traced by `walkAliases` so the same documentation remains reachable under multiple exported names. The final extraction result is an array produced by `return [...docs.values()]`.

### Injecting the Serialized Docs into [`help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/help-runtime.ts)

The build script `scripts/build.mjs` (lines 96–118) performs the actual embedding. It locates the placeholder token `__EGO_EMBEDDED_HELP_DOCS__` inside [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts) and replaces it with a JSON-escaped string containing the extracted `HelperDoc` array. After this step, [`help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/help-runtime.ts) contains a string literal that directly holds the serialized documentation, eliminating any need for external assets.

## Runtime Parsing and Lookup

At execution time the runtime consumes that embedded string without touching the file system.

### Lazy `JSON.parse` and Caching

Inside [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts), line 26 defines `EMBEDDED_DOCS_JSON` as the embedded string literal. The function `getDocsMap()` (lines 79–86) lazily invokes `parseEmbeddedDocs`, which runs `JSON.parse` on the literal and caches the resulting `Map<string, HelperDoc>`. Because the SDK is loaded from a virtual `ego://` URL that has no file-system access, this in-memory deserialization is the only I/O the documentation system performs.

### The `help()` Function and `formatHelp()` Renderer

The exported `help()` function (lines 30–55) accepts a map of helper references and an optional name filter. It looks up the requested helper names in the cached `Map` and returns a single `HelperDoc`, an array of docs, or a fallback message such as `"Unknown helper: nonexistent"`.

The companion `formatHelp()` function (lines 57–77) turns the stored description, parameters, and return metadata into a human-readable string. This separation keeps the lookup logic lightweight while delegating presentation to a dedicated formatter.

## Practical Examples

You can call `help()` against the public helpers exported by the SDK to retrieve live documentation.

```javascript
// Retrieve full documentation for selected helpers
import { click, navigate, scroll } from "ego-browser";

const allDocs = help({ click, navigate, scroll });
console.log(allDocs); // → array of HelperDoc objects

```

```javascript
// Get documentation for a single helper and render it
const clickDoc = help({ click, navigate, scroll }, "click");
console.log(formatHelp(clickDoc));
// Output:
// Clicks an element.
// @param selector string — CSS selector of the element to click
// @returns Promise<void>
// click(selector)

```

```javascript
// Requesting a non-existent helper returns a friendly string
console.log(help({ click }, "nonexistent"));
// → "Unknown helper: nonexistent"

```

## Summary

- The `help()` runtime in `citrolabs/ego-lite` does not parse raw JSDoc in the browser; it deserializes a **pre-computed JSON payload** embedded at build time.
- `scripts/extract-help-docs.mjs` uses **Acorn** to walk the bundled source, match block comments to functions via `commentsByEndLine`, and produce `HelperDoc` objects.
- `scripts/build.mjs` injects the serialized docs into [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts) by replacing the `__EGO_EMBEDDED_HELP_DOCS__` placeholder.
- At runtime, `getDocsMap()` lazily calls `JSON.parse` on `EMBEDDED_DOCS_JSON` and caches the result in a `Map<string, HelperDoc>` for O(1) retrieval.
- `help()` performs the lookup while `formatHelp()` renders the human-readable text, keeping concerns separated.

## Frequently Asked Questions

### How does the `help()` runtime avoid file-system reads in the browser?

The SDK is loaded from a virtual `ego://` URL that has no file-system access. By embedding the documentation as a string literal inside [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts) during the build, the runtime can satisfy `help()` calls using only `JSON.parse` and an in-memory `Map`, avoiding any `fs` operations entirely.

### Why parse the bundled JavaScript instead of the raw TypeScript source?

The extraction pipeline in `scripts/extract-help-docs.mjs` targets the bundled [`dist/out/index.js`](https://github.com/citrolabs/ego-lite/blob/main/dist/out/index.js) because that file represents the exact code shipped to users. Running Acorn against the bundle guarantees that the extracted JSDoc signatures stay in sync with the final helper implementations, including any transformations introduced by the bundler.

### What happens if a helper is exported under multiple names?

The extractor handles aliases through `walkAliases`. When it encounters an assignment such as `const click = doClick`, it associates the alias name with the same `HelperDoc` object so that `help()` returns identical documentation whether you query by the canonical name or the alias.

### Where can I find the runtime implementation of `help()` and `formatHelp()`?

Both functions live in [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts). Lines 30–55 implement `help()`, lines 57–77 implement `formatHelp()`, and lines 79–86 define the lazy caching logic via `getDocsMap()`.