HF-249 bullet 3: generate built-in functions docs from the HF API (single source of truth)#1699
Conversation
|
Task linked: HF-249 HF function documentation available via API |
Performance comparison of head (399dcbd) vs base (d256895) |
|
Paired tests PR: handsontable/hyperformula-tests#19 |
A traceability audit (run during HF-300) found migrated shortDescriptions that either misstate behavior or leak markup into the plain-text metadata API. Per Kuba's call (Slack #hyperformula-dev, 2026-07-15) they belong on the parent HF-249 branch, since the catalogue is becoming the single source of truth (docs/guide generated from it at build): - XNPV: was verbatim identical to NPV; now conveys its non-periodic/date-based nature. - MIRR: "modified internal value" -> "modified internal rate of return". - SPLIT/TEXT: dropped <br> + markdown (rendered literally through the API); TEXT now also mentions the stringifyCurrency option. - SECH: "given angle (in radians)" -> "given value" (the argument is a plain number). - BASE: "positive integer" -> "non-negative integer" (0 is accepted by the impl). - typos: MROUND "neares"/"multiplicity", Fischer->Fisher, densitity->density, skeweness->skewness (x2). Catalogue-only; docs/guide regenerates from these once #1699 lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation preserves it (HF-249)
…API (HF-249) getAvailableFunctions/getFunctionDetails excluded the protected functions OFFSET and VERSION because they have no plugin for resolveFunctionMetadata to read arity from. A user can still call both from a formula, so they must be documented too: getListableFunctionIds (static and instance) now include the protected ids, and resolveFunctionMetadata resolves them from a new authored PROTECTED_FUNCTION_METADATA map together with their catalogue FunctionDoc entries in the lookup-and-reference and information categories.
…d VERSION (HF-249)
…venance comments (HF-249) Mirror buildAvailableFunctions' canonicalName tiebreaker in the built-in functions table renderer so equal localizedNames sort deterministically regardless of input order. Also soften the information/lookup-and-reference category file comments, which claimed full ownership by scripts/hf249-migrate-function-docs.ts even though they now carry hand-authored protected-function entries (VERSION, OFFSET).
…1692 refactor (HF-249) Rebasing #1699 onto the refactored #1692 combined that branch's protected-aware resolveFunctionMetadata (which surfaces VERSION/OFFSET) with #1692's rewritten static getFunctionDetails, which bails at `plugin === undefined` before reaching the protected path. Result: getAvailableFunctions listed VERSION/OFFSET but getFunctionDetails returned undefined for them, so the docs generator threw "No details for listed function VERSION". Handle the protected ids explicitly in the plugin-less branch, mirroring getAvailableFunctions, so the list and the details API agree again. Protected ids without a catalogue doc still resolve to undefined, so both stay consistent there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…I drift gate (HF-249) Run docs:generate-functions as the first step of docs:build and docs:dev so the built-in-functions table is a build product of the catalogue, not a hand-kept artifact. With the table regenerated on every build it can never drift, so remove the redundant docs:functions:check CI step and its npm alias, and update DEV_DOCS to describe the build-time regeneration instead of a CI staleness check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion (HF-249) The catalogue short descriptions carry presentational markdown (<br>, links, footnote refs) that the guide table renders. Add stripDocMarkup and apply it when building Tier-2 details, so getFunctionDetails returns plain prose. The Tier-1 list entry keeps the raw markdown, so the generated table's links stay clickable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g references (HF-249) The catalogue is now the source of truth and the guide is generated from it, so the one-time, reverse-direction migration script (guide -> catalogue) is a footgun. Delete it and soften the ~16 provenance comments that named it so they no longer point at a removed file, while keeping the note about SUM/SUMIF carrying hand-authored examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ue convention (HF-249) OFFSET was added to the catalogue on this branch before #1692 standardized every parameter name to snake_case, so after the rebase it was the lone PascalCase entry (Reference/Rows/Columns/Height/Width) among snake_case siblings. Align it, so getFunctionDetails('OFFSET') and the generated table read consistently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4b5ef17 to
490c6eb
Compare
…ctural metadata is missing (HF-249)
resolveFunctionMetadata returned {doc, metadata: PROTECTED_FUNCTION_METADATA[id]} for any
protected id with a catalogue doc, even if the structural-metadata map had no entry for it —
buildFunctionDetails would then read repeatLastArgs/parameters off `undefined` and throw. The
two maps are a hand-maintained invariant (not enforced at runtime), so require both to be
authored: a protected id missing its structural metadata now stays unlisted rather than
crashing the metadata API. Addresses Cursor Bugbot review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… generator (HF-249) The --check drift mode existed only to back the CI drift gate, which this branch removed in favour of regenerating the table on every docs:build. With no caller left, --check was dead code (and its header comment still claimed a CI role). Reduce the generator to its single job — regenerate the table — and correct the header. Addresses Cursor Bugbot review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b1098fd. Configure here.
…tDescription (HF-249)" This reverts commit 66a5da5.
…duct from a committed template (HF-249) Per Kuba (#hyperformula-dev, 2026-07-15): the rendered guide page should not be committed. Split the page into a committed template `built-in-functions.tmpl.md` (hand-written prose + empty AUTOGENERATED markers) and the generated `built-in-functions.md`, which is now gitignored and produced at docs:build by splicing the API-rendered table into the template. No generated content is committed, so it cannot drift from the catalogue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a8aa6f5. Configure here.
…iptions (HF-300) (#1705) ## What & why HF-300 enriches the function-metadata API (`getFunctionDetails`) so every built-in function returns, in addition to the existing short description and parameter names: - a **usage example** (≥1 per function), - a **documentation URL** — a single shared URL for all functions in v1 (`https://hyperformula.handsontable.com/docs/guide/built-in-functions.html`), and - a **short description for each parameter**. Everything is English (translations are a later phase; the structure is already i18n-ready). Extends the `SUM`/`SUMIF` exemplar already on the base branch to the whole catalogue (363 functions). Base: `feature/hf-249-function-metadata-api` (#1692). Sibling: #1699 (docs single source). ## How - The single documentation URL is defined once as `DEFAULT_DOCUMENTATION_URL` in `buildFunctionDescriptions.ts` and applied as the built-in default — not repeated per function. Custom (user-registered) functions keep `''`. - `examples` + per-parameter `description` authored per category file (`categories/*.ts`). Parameter counts/names and entry order are unchanged; only bodies were filled. ## Testing (in the paired hyperformula-tests PR) - Coverage-parity: every listable built-in exposes the shared URL, ≥1 example, and a non-empty description for every parameter. - Example-validity: every authored example is built in HyperFormula and asserted not to be a parse (`#ERROR!`) or unknown-function (`#NAME?`) error. - Existing `SUM`/`SUMIF` assertions updated to the `.html` URL. - Arity guard (`buildFunctionDetails`) stays green across all 363 functions. Local gates: `tsc` (src+test) clean · `eslint` clean · full `jest` 0 failures · metadata+coverage+example-validity specs pass. ## Notes for review - **No i18n changes — by design.** DoD scopes HF-300 to English; no functions are added/renamed, so the 17-language-pack rule does not apply. English-form examples show under every locale for now (accepted "translations later" trade-off). - **Operators** (`HF.ADD`, …) are listable built-ins, so they get examples/descriptions per the DoD's "each function". - **Aliases** inherit their target's authored doc automatically (`resolveFunctionMetadata`), so they need no separate authoring. - **Guide table untouched** → the #1699 docs drift-check stays green (none of the new fields are rendered in the Function ID/Description/Syntax table). - Descriptions describe **HyperFormula's actual behavior** (verified against the plugins), not Excel where they diverge — e.g. `MOD` result takes the sign of the dividend, `FILTER` accepts a single row/column, `XNPV`'s first date is the reference point. Decisions and citations: ADR in the paired hyperformula-tests repo (`adr/2026-07-13-hf300-function-metadata-enrichment.md`). Source: https://app.clickup.com/t/86caprtgj <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Large, user-facing metadata expansion for every built-in via `getFunctionDetails`, but no formula-engine behavior changes; main risk is stale or incorrect authored docs and accidental loss if the migration script is re-run. > > **Overview** > **HF-300** fills out the built-in `FunctionDoc` catalogue so `getFunctionDetails` can return real **parameter descriptions**, at least one **usage example** per function, and a shared **documentation URL** (`.html` guide link) across the category files—not the previous empty placeholders. > > `FunctionDescription` / `buildFunctionDetails` comments and contracts are updated to treat those fields as authored for built-ins (custom functions still get `''` / `[]`). `buildFunctionDetails` now passes `documentationUrl` straight from the catalogue instead of defaulting missing values to `''`. > > The HF-249 migration script and category file headers **warn that re-running the generator wipes** hand-authored `examples` and parameter `description` text. One wording fix: **`INT`**’s short description now matches truncate-toward-zero behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1c6b6a9. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kuba Sekowski <sequba@users.noreply.github.com> Co-authored-by: Kuba Sekowski <jakub.sekowski@handsontable.com>
| "docs:code-examples:generate-all-js": "bash docs/code-examples-generator.sh --generateAll", | ||
| "docs:code-examples:format-all-ts": "bash docs/code-examples-generator.sh --formatAllTsExamples", | ||
| "snippets:extract": "node script/extract-doc-snippets.js", | ||
| "docs:generate-functions": "npm run tsnode scripts/generate-builtin-functions-doc.ts", |
There was a problem hiding this comment.
| "docs:generate-functions": "npm run tsnode scripts/generate-builtin-functions-doc.ts", | |
| "docs:generate-function-docs": "npm run tsnode scripts/generate-builtin-functions-doc.ts", |
There was a problem hiding this comment.
Renamed in fc9684d. The key is also referenced by docs:dev and docs:build (npm run docs:generate-functions), so I renamed all three sites to docs:generate-function-docs, plus the two doc references to the old name.
There was a problem hiding this comment.
This file should not be in src/. It belongs in scripts/ dir as it is only used by scripts. Never by the production code.
There was a problem hiding this comment.
Moved to scripts/renderBuiltinFunctionsTable.ts in fc9684d. It's imported only by the generator, and since tsconfig.json include is ["src"] it was being compiled into the shipped bundle as dead code — the move drops it from the build. Its FunctionDescription import now resolves via ../src/interpreter/functionMetadata/.
There was a problem hiding this comment.
This code should also be in the scripts/ dir
There was a problem hiding this comment.
Moved to scripts/formatFunctionSyntax.ts (same commit), next to the generator that consumes it.
| - Added the `getAvailableFunctions()` and `getFunctionDetails()` methods (both static and instance) for retrieving function metadata. [#1692](https://github.com/handsontable/hyperformula/pull/1692) | ||
| - Added new functions: VSTACK, HSTACK. [#1698](https://github.com/handsontable/hyperformula/pull/1698) | ||
| - Added a new function: `XIRR`. [#1701](https://github.com/handsontable/hyperformula/pull/1701) | ||
| - The built-in functions guide table is now generated from the function metadata API (single source of truth), and now also documents OFFSET and VERSION. [#1692](https://github.com/handsontable/hyperformula/pull/1692) |
There was a problem hiding this comment.
We don't need such entry in changelog. Our clients don't care about how we generate the docs.
| The built-in functions guide page `docs/guide/built-in-functions.md` is a **build product** and is | ||
| **gitignored** — do not commit or hand-edit it. It is generated from two committed sources: | ||
|
|
||
| - **prose** (intro, category list, footnotes) lives in `docs/guide/built-in-functions.tmpl.md`, which carries the | ||
| empty `AUTOGENERATED:FUNCTIONS` markers; | ||
| - the **function table** is rendered from HyperFormula's API (`getAvailableFunctions`/`getFunctionDetails`), i.e. | ||
| from the catalogue in `src/interpreter/functionMetadata/`. | ||
|
|
||
| `npm run docs:generate-functions` splices the table into the template and writes the gitignored page; it runs | ||
| automatically as the first step of `npm run docs:build` and `npm run docs:dev`. To change the wording, edit the | ||
| template; to change a function's row, edit its catalogue metadata. | ||
|
|
There was a problem hiding this comment.
It does not belong in DEV_DOCS.md. It shoiuld be described in docs/README.md and/or in building.md
There was a problem hiding this comment.
Moved to docs/README.md in fc9684d — new "Built-in functions guide page" section, plus a docs:generate-function-docs entry in the npm-scripts list, since that's where the docs build is documented.
…rename script, relocate docs (HF-249) - Move renderBuiltinFunctionsTable.ts and formatFunctionSyntax.ts from src/interpreter/functionMetadata/ to scripts/: they are used only by the doc generator, never by production code, and tsconfig `include` is ["src"] so they were being compiled into the shipped bundle as dead code. - Rename the npm script docs:generate-functions -> docs:generate-function-docs (and its callers in docs:dev/docs:build, plus doc references). - Drop the CHANGELOG entry (docs-generation infra; not client-facing). - Move the built-in-functions build-product note from DEV_DOCS.md to docs/README.md, where the docs build is documented. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f3cc5a5. Configure here.
| return nextIndex !== undefined ? nextIndex - start : NOT_FOUND | ||
| // orderingDirection === 'asc' | ||
| const nextIndex = foundIndex+1 | ||
| return nextIndex <= end ? nextIndex - start : NOT_FOUND |
There was a problem hiding this comment.
Lookup empty cells regressed
High Severity
The commit removes empty-cell skipping and compaction in ordered-range and linear lookup search. MATCH, VLOOKUP, HLOOKUP, and XLOOKUP can again treat gaps as ordered values or pick bounds at empty slots, diverging from the prior #1697 behavior and the removed compatibility docs entry.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f3cc5a5. Configure here.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## feature/hf-249-function-metadata-api #1699 +/- ##
=======================================================================
Coverage ? 96.44%
=======================================================================
Files ? 193
Lines ? 15598
Branches ? 3449
=======================================================================
Hits ? 15044
Misses ? 554
Partials ? 0
🚀 New features to boost your workflow:
|
b0b42c5 to
fc9684d
Compare


HF-249 bullet 3 — the docs functions table is generated from HyperFormula (single source of truth)
Third in-scope bullet of HF-249: the built-in functions guide is generated from the public metadata API (
getAvailableFunctions/getFunctionDetails) at docs-build time, so function data lives in one place — the catalogue insrc/interpreter/functionMetadata/.What's here
scripts/(dev-only, not part of the published package):formatFunctionSyntax,renderBuiltinFunctionsTable,spliceFunctionsTable. Deterministic (pinnedIntl.Collator, LF, byte-idempotent).scripts/generate-builtin-functions-doc.ts(excluded from the npm package viatsconfig include:["src"]): reads the committed templatedocs/guide/built-in-functions.tmpl.md(hand-written prose + emptyAUTOGENERATED:FUNCTIONSmarkers), splices the API-rendered table into the marker region, and writes the page.docs/guide/built-in-functions.mdis a build product and is gitignored — regenerated as the first step ofdocs:build/docs:dev, so it can never drift from the catalogue and no generated content is committed.getFunctionDetailsis protected-aware so it agrees withgetAvailableFunctions. OFFSET's parameter names are snake_case, matching the Function metadata API: getAvailableFunctions / getFunctionDetails (HF-249) #1692 convention.docs → cataloguemigration script is deleted (the catalogue is now the source); its dangling provenance comments are softened.<a id="canonicalName">) on every row; the table documents 421 functions.DEV_DOCS.mdtodocs/README.md; no CHANGELOG entry (docs-generation infra, not client-facing).Decisions (confirmed with Kuba — #hyperformula-dev, 2026-07-15)
docs:generate-function-docsprepended todocs:build/docs:dev) rather than a VuePress plugin — Kuba left the choice to me; the standalone script is simpler and deterministic.built-in-functions.mdis gitignored, generated from the committed.tmpl.mdtemplate — per Kuba's call (no committed generated artifact).docs:functions:checkstep + npm alias are removed.shortDescriptionmarkup is kept as-is in the API for now — per Kuba,getFunctionDetailsreturns the same string as the docs (links/<br>included); whether to strip for the Formula Builder is deferred until we see how it renders there. (NostripDocMarkupin this PR.)The separate shortDescription content fixes (XNPV, MIRR, SECH, BASE, typos) are not in this PR — Kuba asked for them on the parent branch #1692.
Verification
npm run docs:buildgreen; the page is generated from the template and correct in the built site (421 functions incl. OFFSET/VERSION, snake_case syntax).tsc --noEmit+lintclean.Base:
feature/hf-249-function-metadata-api(ships with the HF-249 API in #1692).Note
Medium Risk
Changes public metadata listing/details for protected functions and docs build prerequisites; core formula evaluation is largely unchanged, but API consumers and CI docs builds must run the new generate step.
Overview
Docs workflow: The built-in functions guide is no longer a committed hand-maintained table. Prose lives in
built-in-functions.tmpl.md;docs:generate-function-docssplices API-rendered category tables intoAUTOGENERATED:FUNCTIONSmarkers and writes gitignoredbuilt-in-functions.md. That step runs first indocs:devanddocs:build. The olddocs → cataloguemigration script is removed.Runtime API:
getAvailableFunctions/getFunctionDetailsnow include protected VERSION and OFFSET via catalogue docs plus newPROTECTED_FUNCTION_METADATA(no ordinary plugin). StaticgetFunctionDetailsis protected-aware so it matches the list.Generator: Dev-only scripts render deterministic markdown (syntax via
formatFunctionSyntax, per-row anchors, pinned English collator).Reviewed by Cursor Bugbot for commit b0b42c5. Bugbot is set up for automated code reviews on this repo. Configure here.