diff --git a/.cursor/rules/main.mdc b/.cursor/rules/main.mdc new file mode 100644 index 0000000000..eef7fd7cea --- /dev/null +++ b/.cursor/rules/main.mdc @@ -0,0 +1,6 @@ +--- +description: Project-wide guidance for AI agents working in this repository +alwaysApply: true +--- + +All guidance for AI agents working in this repository lives in [AGENTS.md](mdc:AGENTS.md) at the repository root. Read it first. diff --git a/.cursor/settings.json b/.cursor/settings.json new file mode 100644 index 0000000000..36b564bdb5 --- /dev/null +++ b/.cursor/settings.json @@ -0,0 +1,7 @@ +{ + "plugins": { + "superpowers": { + "enabled": true + } + } +} diff --git a/.eslintignore b/.eslintignore index 03546876e2..bd53198caa 100644 --- a/.eslintignore +++ b/.eslintignore @@ -24,5 +24,6 @@ lib script test-jasmine test-jest +test-utils/snippets typedoc typings diff --git a/.eslintrc.js b/.eslintrc.js index 7d6108d36b..a5e9976e13 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -148,6 +148,6 @@ module.exports = { rules: { '@typescript-eslint/no-non-null-assertion': 'off', } - } + }, ], } diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3d9865bb32..e9de124dfd 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ contact_links: - name: Feature requests and questions - url: https://github.com/handsontable/hyperformula/discussions + url: https://forum.handsontable.com/t/about-the-hyperformula-category/9073 about: Start a new discussion about your idea. diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 015fbcdc13..936ef53fa2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,7 +28,18 @@ jobs: with: node-version: ${{ matrix.node-version }} - - uses: actions/checkout@722adc63f1aa60a57ec37892e133b1d319cae598 # https://github.com/actions/checkout/releases/tag/v2.0.0 + - name: Checkout main repository + uses: actions/checkout@722adc63f1aa60a57ec37892e133b1d319cae598 # https://github.com/actions/checkout/releases/tag/v2.0.0 + + - name: Checkout hyperformula-tests repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ssh-key: ${{ secrets.DEPLOY_TOKEN }} + repository: handsontable/hyperformula-tests + path: test/hyperformula-tests + + - name: Fetch hyperformula-tests and sync branches + run: cd test && ./fetch-tests.sh - name: Install dependencies run: npm ci diff --git a/.gitignore b/.gitignore index 98181e71c4..aaa6e36c27 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,10 @@ dev*.html .DS_Store /test/hyperformula-tests/ + +# Doc snippets are regenerated from the docs before every test run +# (the `test:*` scripts run `snippets:extract` first), so they are never committed. +/test-utils/snippets/*.generated.ts + +# Generated at docs:build from built-in-functions.tmpl.md (HF-249 single-source); do not commit. +docs/guide/built-in-functions.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..99b25536ca --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,44 @@ +# AGENTS.md + +Instructions for AI coding agents (Cursor, Claude Code, Codex, Aider, and any other AI tool) working in this repository. + +## Start here + +Whatever you do, start by reading entire [DEV_DOCS.md](DEV_DOCS.md). Only then proceed to your task. + +## Other important resources + +- the repository [README.md](README.md) — high-level project description and quick install/usage +- the markdown files in [`docs/guide/`](docs/guide/) — user-facing guides (installation, configuration, built-in functions, custom functions, integrations, etc.) +- the markdown files in [`docs/api/`](docs/api/) — API reference (generated from JSDoc; run `npm run docs:build` if the folder is missing) + +Prefer reading these local files over fetching the rendered documentation from the web. + +## Response style + +- Be concise by default. Use as few words as possible unless the user asks for more detail. +- When the user asks for specific content, lead the response with the requested information. +- Structure answers with bullet lists, numbered lists, tables, or code blocks where useful. +- Ask clarifying questions when the request is ambiguous rather than guessing. +- If you do not know something, say so and ask for help. +- When answering from project documentation, quote the exact relevant fragments to support your claim. + +## Common ways agents fail + +This section is maintained by the team. Whenever an AI agent makes a mistake worth flagging, an item is added here describing what the agent did wrong and what it should have done instead. Read this list before starting any non-trivial task. + + + +1. Often pull request descriptions becomes obsolete. Remember to update it as you work. + +## Skills, MCPs, and other agent tools + +This section is maintained by the team. Skills, MCP servers, and other tools vetted as useful for AI agents working on this codebase are listed here. + + + +_No items yet._ diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf79c6775..4199dd5321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- 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) +- Added the UNIQUE function. [#1708](https://github.com/handsontable/hyperformula/pull/1708) +- Added the SORT function. [#1707](https://github.com/handsontable/hyperformula/pull/1707) +- Added an Indonesian (Bahasa Indonesia) language pack. [#1674](https://github.com/handsontable/hyperformula/pull/1674) +- Added a `stringifyCurrency` config option that lets you plug in a custom currency formatter for the `TEXT` function. [#1145](https://github.com/handsontable/hyperformula/issues/1145) + +### Fixed + +- Fixed the behavior of `MATCH`, `VLOOKUP`, `HLOOKUP`, and `XLOOKUP` functions when the search range contained empty cells. [#1697](https://github.com/handsontable/hyperformula/pull/1697) +- Fixed the `VLOOKUP`, `HLOOKUP`, and `XLOOKUP` functions to return `0` instead of an empty value when the matched cell in the result range is empty. [#1697](https://github.com/handsontable/hyperformula/pull/1697) +- Fixed the page freezing when entering a long string of digits containing a non-digit character near the end (e.g. `012...789a` or `012...789 123`) into a cell. [#1520](https://github.com/handsontable/hyperformula/issues/1520) + +### Added + +- Now the calculateFormula let to specify the column and the row where the formula must be calculated. [#1720](https://github.com/handsontable/hyperformula/pull/1720) + ## [3.3.0] - 2026-05-20 ### Added diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 5a11d26cda..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,95 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -HyperFormula is a headless spreadsheet engine written in TypeScript. It parses and evaluates Excel-compatible formulas and can run in browser or Node.js environments. The library implements ~400 built-in functions with support for custom functions, undo/redo, CRUD operations, and i18n (17 languages). - -## Build & Development Commands - -```bash -npm install # Install dependencies -npm run compile # TypeScript compilation to lib/ -npm run bundle-all # Full build: compile + bundle all formats -npm run lint # Run ESLint -npm run lint:fix # Auto-fix lint issues -``` - -## Testing - -```bash -npm test # Full suite: lint + unit + browser + compatibility -npm run test:unit # Jest unit tests only -npm run test:watch # Jest watch mode (run tests on file changes) -npm run test:coverage # Unit tests with coverage report -npm run test:browser # Karma browser tests (Chrome/Firefox) -npm run test:performance # Run performance benchmarks -npm run test:compatibility # Excel compatibility tests -``` - -Test files are located in `test/unit/` and follow the pattern `*.spec.ts`. - -## Architecture - -### Core Components - -- **`src/HyperFormula.ts`** - Main engine class, public API entry point -- **`src/parser/`** - Formula parsing using Chevrotain parser generator -- **`src/interpreter/`** - Formula evaluation engine -- **`src/DependencyGraph/`** - Cell dependency tracking and recalculation order -- **`src/CrudOperations.ts`** - Create/Read/Update/Delete operations on sheets and cells - -### Function Plugins (`src/interpreter/plugin/`) - -All spreadsheet functions are implemented as plugins extending `FunctionPlugin`. Each plugin: -- Declares `implementedFunctions` static property mapping function names to metadata -- Uses `runFunction()` helper for argument validation, coercion, and array handling -- Registers function translations in `src/i18n/languages/` - -To add a new function: -1. Create or modify a plugin in `src/interpreter/plugin/` -2. Add function metadata to `implementedFunctions` -3. Implement the function method -4. Add translations to all language files in `src/i18n/languages/` -5. Add tests in `test/unit/interpreter/` - -### i18n (`src/i18n/languages/`) - -Function name translations for each supported language. When adding new functions, translations can be found at: -- https://support.microsoft.com/en-us/office/excel-functions-translator-f262d0c0-991c-485b-89b6-32cc8d326889 -- http://dolf.trieschnigg.nl/excel/index.php - -## Output Formats - -The build produces multiple output formats: -- `commonjs/` - CommonJS modules (main entry) -- `es/` - ES modules (.mjs files) -- `dist/` - UMD bundles for browsers -- `typings/` - TypeScript declaration files - -## Contributing Guidelines - -- Create feature branches, never commit directly to master -- Target the `develop` branch for pull requests -- Add tests for all changes in `test/` folder -- Run linter before submitting (`npm run lint`) -- Maintain compatibility with Excel and Google Sheets behavior -- In documentation, commit messages, pull request descriptions and code comments, do not mention Claude Code nor LLM models used for code generation - -## Response Guidelines - -- By default speak ultra-concisely, using as few words as you can, unless asked otherwise. -- Focus solely on instructions and provide relevant responses. -- Ask questions to remove ambiguity and make sure you're speaking about the right thing. -- Ask questions if you need more information to provide an accurate answer. -- If you don't know something, simply say, "I don't know," and ask for help. -- Present your answer in a structured way, use bullet lists, numbered lists, tables, etc. -- When asked for specific content, start the response with the requested info immediately. -- When answering based on context, support your claims by quoting exact fragments of available documents. - -## Code Style - -- When generating code, prefer functional approach whenever possible (in JS/TS use filter, map and reduce functions). -- Make the code self-documenting. Use meaningfull names for classes, functions, valiables etc. Add code comments only when necessary. -- Add jsdocs to all classes and functions. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index d3f946c214..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,47 +0,0 @@ -# Contributing - -You are welcome to contribute to HyperFormula development. Your help -is much appreciated in any of the following topics: - -* Making pull requests - * Adding new functions - * Adding new features - * Improving the quality of the existing code - * Improving performance - * Improving documentation and public API -* Reporting bugs -* Suggesting improvements -* Suggesting new features - -## Good first issue - -Adding a new function will be a huge help for the library growth and -should not be too problematic for the first issue. Extending the -library of translations is also a good task to start with. -[Here](https://docs.google.com/spreadsheets/d/1UUskn4ZDDjLGSpO6kg73DOvabNoeqLbkJYyVfLyYlYw) -you can find a list of functions' translations. - -Visit [building]( https://handsontable.com/docs/hyperformula/guide/building.html) section to -get more info about the development process and check the list of commands you -can run in this project. Check the `/i18n` -folder in the project - all translations are kept just right there. -For the functions see the `interpreter/plugin` folder. Both of them -are a good starting point. - -## How to get started - -1. First, sign this -[Contributor License Agreement](https://goo.gl/forms/yuutGuN0RjsikVpM2) -to allow us to use and publish your changes. -2. Always make your changes on a separate branch. This will speed up -the merging process. -3. Always make the target of your pull request the `develop` branch, -not `master`. -4. For any change you make, add test specs in the `test` folder. -5. Please lint the code. See the section about using linter. -6. Add a comprehensive description of all the changes. - -## Code of conduct - -By participating in this project, you are expected to uphold our -[Code of Conduct](https://github.com/handsontable/hyperformula/blob/master/CODE_OF_CONDUCT.md). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 120000 index 0000000000..6c263eead2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +docs/guide/contributing.md \ No newline at end of file diff --git a/DEV_DOCS.md b/DEV_DOCS.md index 3349b9e00f..13630fd4cb 100644 --- a/DEV_DOCS.md +++ b/DEV_DOCS.md @@ -1,26 +1,171 @@ -# Dev Docs +# Developer documentation -Random notes and things to know useful for maintainers and contributors. +Canonical reference for everyone working on the HyperFormula source code: maintainers, the internal team, and AI agents triggered by them. Everything a developer needs to know lives here or is linked from here. -## Definition of Done for the code changes +## Quick links -Each change to the production code (bugfixes, new features, improvements) must include these elements. They must be present in the pull request BEFORE requesting the code review. +- **[Building, testing, and linting](docs/guide/building.md)** — all `npm` commands and build outputs +- **[Test suite](test/README.md)** — smoke tests and how to attach the private test suite +- **[Public docs portal](https://hyperformula.handsontable.com/docs)** — main documentation +- **[Docs README](docs/README.md)** — how to run the docs portal locally +- **[Docs content guide](DOCS_CONTENT_GUIDE.md)** — how to create and edit docs content +- **[Changelog](CHANGELOG.md)** +- **[Pull request template](.github/pull_request_template.md)** -- changes to the production code - - including changes to all supported language packs in `src/i18n/languages` directory (if applicable) -- automatic tests - - for bugfixes: at least one test reproducing the bug - - for new features: a set of tests describing the feature specification precisely - - pull requests from external contributors should include tests in `tests/` directory (they will be moved to the private repository by the internal team) - - internal team adds tests directly to the private repository (through a separate pull request) -- updates to documentation related to the change +## Repository layout + +``` +. +├── src/ # Source code +│ ├── HyperFormula.ts # Main engine class, public API entry point +│ ├── parser/ # Formula parsing (uses Chevrotain parser generator) +│ ├── interpreter/ # Formula evaluation engine +│ │ └── plugin/ # Built-in spreadsheet function plugins +│ ├── DependencyGraph/ # Cell dependency tracking and recalculation order +│ ├── CrudOperations.ts # Create/read/update/delete operations on sheets and cells +│ └── i18n/ # Function-name translations per language +├── test/ # Test suite +├── docs/ # Public documentation portal (VuePress) +│ ├── guide/ # Markdown guides (building, contributing, usage…) +│ ├── api/ # API reference (generated from JSDoc) +│ ├── .vuepress/ # VuePress configuration, theme, components +│ └── README.md # How to run the docs portal locally +├── script/ # Maintenance and release scripts +├── .github/ # CI workflows, issue and PR templates +├── DEV_DOCS.md # Canonical developer documentation (this file) +├── AGENTS.md # Guidance for AI agents +├── CONTRIBUTING.md # Guide for external contributors +├── README.md # Project overview +├── CHANGELOG.md +├── LICENSE.txt +├── package.json +└── tsconfig.json +``` + +## Architecture + +### Core modules + +- `src/HyperFormula.ts` — main engine class, public API entry point +- `src/parser/` — formula parsing (uses the [Chevrotain](https://chevrotain.io/) parser generator) +- `src/interpreter/` — formula evaluation engine +- `src/DependencyGraph/` — cell dependency tracking and recalculation order +- `src/CrudOperations.ts` — create/read/update/delete operations on sheets and cells + +### Function plugins (`src/interpreter/plugin/`) + +All spreadsheet functions are implemented as plugins extending `FunctionPlugin`. Each plugin: + +- declares an `implementedFunctions` static property mapping function names to metadata +- uses the `runFunction()` helper for argument validation, coercion, and array handling +- registers function translations in `src/i18n/languages/` + +## Definition of Done + +Each change to the production code (bugfix, new feature, or improvement) must include the following elements **before** requesting a code review: + +- Changes to the production code + - including changes to all supported language packs in `src/i18n/languages` (if applicable) +- Automatic tests + - for bug fixes: at least one test reproducing the bug + - for new features: a set of tests precisely describing the feature + - pull requests from external contributors should include tests in the `test/` directory (they will be moved to the private repository by the internal team) + - the internal team adds tests directly to the private repository (through a separate pull request) +- Updates to documentation related to the change - for breaking changes: a section in the migration guide -- technical documentation in the form of the jsdoc comments (high-level description of the concepts used in the more complex code fragments) -- changelog entry -- pull request description +- Technical documentation in the form of JSDoc comments (high-level description of the concepts used in more complex code fragments) +- Changelog entry (not required for documentation-only changes (guides, JSDoc, README, etc.) +- Pull request description + +Every element of the change must not only be present but also correct: the changelog entry must describe the change accurately, and the documentation updates must match the new behaviour. + +Read through your own diff before requesting a review, and ask yourself what could be done better. Fix what you find while the change is still yours. + +A single pull request should contain an atomic self-contained functional change (single bugfix, single feature, single improvement). If a pull request contains multiple features or bugfixes, it should be split. Every change in the pull request must be relevant to the issue it solves — unrelated refactors, reformatting, or clean-ups belong in a separate pull request. + +## Code style + +- Prefer a functional approach where possible (`filter`, `map`, `reduce`). +- Write self-documenting code: use meaningful names for classes, functions, and variables. Add code comments only when they explain intent the code itself cannot. +- Add JSDoc to all classes and functions. +- Choose readability over brevity. Explicit, obvious code is better than a clever one-liner. +- Keep the logic straightforward: avoid convoluted control flow, prefer early returns and a flat structure, and make sure every branch and condition has a reason to exist. +- Follow clean code principles and general programming best practices: small functions with a single responsibility, no hidden side effects, no magic values. +- Avoid duplication. Extract shared logic instead of copying it, and reuse the existing helpers and abstractions of the codebase. +- Match the style of the surrounding code and of the project as a whole. New code should not stand out from its neighbours. +- Optimize for long-term maintainability: someone else should be able to read, extend, and safely change the code months from now. +- ESLint is the source of truth for formatting and code rules. Run `npm run lint` before submitting changes (see [building](docs/guide/building.md#run-the-linter)). + +## Performance + +HyperFormula is a calculation engine, so the performance of the production code is a feature, not an afterthought. + +- Consider the computational complexity of every change, especially in code that runs per cell, per formula, or per dependency-graph node. Nested loops over ranges and repeated work that could be computed once or cached are the usual suspects. +- Pick the best complexity that still keeps the code readable. When a faster algorithm is harder to follow, explain the trade-off in a JSDoc comment. +- Run `npm run test:performance` for changes that may affect the evaluation or CRUD hot paths. + +## Automatic tests + +- All changes to the production code (the `src/` directory) must be covered by automatic tests kept in the `test/` directory. +- Each test case must be very simple and focused on a single assertion. Don't use loops, conditionals, or other control flow statements in test cases. +- Cover more than the happy path: boundary values, empty and invalid input, error results, and interactions with related features. +- Before requesting a review, ask yourself which further tests would be valuable and add the ones that protect against realistic regressions. +- Don't add tests for code in the `docs/`, `examples/`, and `script/` directories. + +## Documentation + +- Follow the [documentation content guide](DOCS_CONTENT_GUIDE.md) when creating or editing docs (writing style, language, and how to structure guides). +- We try not to duplicate information in the documentation. The API reference (generated from JSDocs) should contain all the details about each function and class (it is the primary source of truth). Guides should provide high-level overview. They may duplicate some of the information from the API reference if they are relevant to the context but, above all, they should link to the API reference for the detailed information. -## Sources of the function translations +## How to add a new function + +Adding a built-in function is similar to adding a [custom function](docs/guide/custom-functions.md), so that guide is a useful reference for the function-implementation patterns (argument metadata, return types, array handling). The built-in flow on top of that is: + +1. Create or modify a plugin in `src/interpreter/plugin/`. +2. Add function metadata to `implementedFunctions`. +3. Implement the function method. +4. Add a catalogue entry to `src/interpreter/functionMetadata/categories/.ts` (see below). +5. Add translations to all language files in `src/i18n/languages/`. +6. Add tests in `test/unit/interpreter/`. + +### The function metadata catalogue + +`src/interpreter/functionMetadata/categories/` holds the human-readable metadata for every built-in function: `shortDescription`, `parameters` (`snake_case` names, each with a description), `examples`, `documentationUrl`, and the category. It is the single source of truth for two consumers: the public [`getAvailableFunctions`/`getFunctionDetails`](docs/api/classes/hyperformula.md) API, and the generated built-in functions guide page (see [docs/README.md](docs/README.md)). + +Every field is required, `documentationUrl` included. Each entry authors its own link rather than inheriting a shared default, so that the links can diverge per function without touching any code; they all happen to point at the same guide page today. + +An entry's `category` must be one of the categories in `FUNCTION_CATEGORIES` — the ones the generated guide page renders as `### ` sections. The separate `'Custom'` category is reserved for user-registered functions and must never appear in `FUNCTION_CATEGORIES` or in a catalogue file: it names no section, and the docs generator rejects an entry carrying it rather than silently dropping it from the page it is building. (That rejection is also what turns a missing catalogue entry into a failed docs build. Arity drift below needs no such guard: the function still reaches the generator, which renders its degraded syntax line.) + +The catalogue's key set decides which ids carry an authored **description**, not which ids the API lists. Both `getAvailableFunctions` and `getFunctionDetails` describe every registered function — custom ones included — and an entry is applied whenever the catalogue holds one for the id, whichever plugin currently provides it: the catalogue is keyed by id, not by implementation, so a custom plugin registered over a built-in id is described with that built-in's authored metadata. Nothing checks a key against a registered function either, so an entry left behind after a rename describes nothing and merely ships in the bundle. Remove or rename it in the same change as the function. + +Two ways to get this wrong: + +- **No catalogue entry.** A registered function with no entry is still listed and still resolves to details, but as a custom function: `category: 'Custom'`, no `shortDescription`, `documentationUrl` or `examples` (the API omits every authored field it has no source for, rather than reporting an empty one), and positional parameter names (`Arg1`, `Arg2`, …). `'Custom'` has no section on the generated docs page, so `npm run docs:generate-function-docs` fails rather than publishing a built-in with no description. +- **Arity drift.** If the entry's parameter **count** disagrees with the plugin's `implementedFunctions`, the implementation wins: `getFunctionDetails` reports one parameter per implemented argument under positional names, discarding the authored names and descriptions, and warns on the console naming the function. The entry's category, `shortDescription`, `examples` and `documentationUrl` are still used, and the function stays listed — the parameter prose degrades, not the availability. + +Keep the entry's parameters in step with `implementedFunctions` whenever you change a signature. + +When a description **refers to** a parameter, use that parameter's exact `snake_case` name, never a prose variant: write "shifts `start_date` by …", not "shifts the start date by …". The same strings are rendered next to the generated syntax line, where the `snake_case` name is what the reader sees, so a prose variant leaves the reader guessing which argument is meant. This applies to `shortDescription` and to every parameter description. + +It does **not** turn ordinary English into identifiers. A parameter's own description may open with a prose noun phrase for the thing it describes — `lower_bound` is fine as "The lower bound, rounded up to an integer" — and words that merely happen to match a name ("entries that appear exactly once") stay as they are. The rule is about naming a *different* argument, or naming one from the syntax line. + +`shortDescription` must not use docs-page-local markup (no relative links, no footnote references): the strings are rendered by API consumers as well as by the docs page. + +Note what the drift warning does **not** cover: **optionality is not cross-checked.** The catalogue authors no optionality of its own — a parameter's `optional` flag is derived entirely from `optionalArg`/`defaultValue` in `implementedFunctions` — so a description that calls an argument optional can sit next to `optional: false` with nothing failing. When a function accepts a call that arity alone does not express (`SHEET()`, `ROW()`, and anything else served by `runFunctionWithReferenceArgument`'s zero-argument path), the plugin must declare `optionalArg: true` explicitly, or the public API will advertise the argument as required. `ROW`, `COLUMN`, `SHEET` and `SHEETS` all declare it; `ISFORMULA` takes the same path and correctly does not, because its zero-argument call is an error rather than a shorthand. + +Descriptions must describe **HyperFormula's** behaviour, not Excel's. Much of the catalogue was seeded from a hand-written page that documented Excel, and HyperFormula deliberately deviates in places (`INT` truncates toward zero, `MOD` takes the sign of the dividend, `ISEVEN`/`ISODD` do not truncate, `CEILING.MATH`/`FLOOR.MATH` honour only `mode` = 1). Verify a claim against the implementation before authoring it, and record any deviation in [the list of differences](docs/guide/list-of-differences.md). + +## Internationalization and function translations + +HyperFormula supports internationalization and provides localized function names for all built-in languages. Translation files live in `src/i18n/languages/`. New functions must include translations for all built-in languages. + +When looking for the valid translations for new functions, try these sources: -HF supports internationalization and provides the localized function names for all built-in languages. When looking for the valid translations for the new functions, try these sources: - https://support.microsoft.com/en-us/office/excel-functions-translator-f262d0c0-991c-485b-89b6-32cc8d326889 - http://dolf.trieschnigg.nl/excel/index.php + +For languages not officially supported by Microsoft Excel, the two sources above do not apply. For these languages, use Google Sheets as the reference. Switch the `hl` query parameter to the target locale, for example: + +- https://support.google.com/docs/table/25273?hl=id (Indonesian) + +For functions that Google Sheets does not list either, fall back to the English name (matching the convention used by Excel in unsupported locales). diff --git a/DOCS_CONTENT_GUIDE.md b/DOCS_CONTENT_GUIDE.md new file mode 100644 index 0000000000..899c402fe8 --- /dev/null +++ b/DOCS_CONTENT_GUIDE.md @@ -0,0 +1,262 @@ +# Documentation content guide + +## The one principle + +**Every page and every section is retrieved and read in isolation, out of order, +by a reader with zero prior context.** RAG systems pull one chunk at a time; a +search result or an AI answer may show only part of a page. So: + +> If the answer to a question is not present on a single page, in plain text, in a +> self-contained place — it effectively does not exist. + +Write so any one page fully answers one real question. Everything below serves this. + +The good news: this is just *good documentation*. The same clarity that helps the AI +helps humans and search. You are not gaming an algorithm — you are writing clearly. + +--- + + + +## Project context + +- **Product:** HyperFormula — an open-source, headless spreadsheet and formula +engine written in TypeScript. It parses and evaluates ~400 Excel/Google +Sheets–compatible functions. It has **no UI**. It runs in the browser or Node.js. +- **Sibling product:** Handsontable (the data grid). HyperFormula is maintained by +the same team but is a **separate product** — do not blur the two. +- **Docs source:** Markdown in the `docs/` folder of the `handsontable/hyperformula` +repo. Guide pages live at `docs/guide/.md`; the landing page is +`docs/index.md`; API reference under `docs/api/` is **generated from source code +TSDoc — do not hand-edit it**, improve the code comments instead. +- **Toolchain:** VuePress 1.9.10 (static site generator). Pages support YAML +frontmatter, `::: tip / warning / danger` containers, and fenced code blocks. +- **Runnable demos:** hosted on StackBlitz (`handsontable/hyperformula-demos`), +linked per version branch (e.g. `3.3.x`). + +--- + + + +## What to write (coverage & gap prioritization) + +Optimization cannot recover a page that doesn't exist. Coverage comes first. + +1. **Write the "obvious" pages.** The journeys teams skip because they feel too + basic are the ones users ask first: installation (client- and server-side), basic + usage, configuration options, the **license key** (a top gotcha), a first working + formula, reading/writing cell values, named expressions, custom functions. +2. **Prioritize by real demand, not intuition.** Rank what to write next using: + - Support tickets and GitHub issues from the last quarter — each recurring one + should map to a single findable page. If it doesn't, that's your next page. + - The docs AI assistant / search logs — repeated questions signal a missing or + hard-to-find page; questions that span sections signal an organization problem; + requests for examples signal thin practical guidance. + - Start with the boring, high-traffic paths before advanced/edge topics. +3. **Prefer less, but better.** Quality beats coverage for retrieval. Delete or + archive outdated tutorials, deprecated-feature guides, near-duplicate pages, and + thin placeholder stubs. Stale content competes with correct content and the model + cannot tell which is current. +4. **Never contradict yourself across pages.** Two pages that disagree are worse than + one correct page, because retrieval may surface either. When you add or change a + fact, grep the docs for the old statement and fix every occurrence. +5. **Document errors with their exact text.** Users (and assistants) search by + pasting the literal error string. For each common error, use the exact message as + a heading, then give the cause and the fix: + `### Error: "Named expression 'X' already exists"` → why it happens → how to + resolve. Reference HyperFormula's error types (`#REF!`, `#VALUE!`, `#NAME?`, etc.) + by their real symbols. + +--- + + + +## How to structure a page + +Treat **every page as page one.** + +1. **Open with scope and prerequisites.** The first lines state what the page covers, + which version it applies to, and what the reader must already have done. Example + opener: "This guide covers custom functions in HyperFormula. You need HyperFormula + installed and a working instance (see Basic usage)." +2. **Name the subject in the body, not just the title.** Write "HyperFormula's + `buildFromArray` method…", not "the `buildFromArray` method…". A retrieved chunk + must carry a clear signal of what product/feature it belongs to, because the title + and breadcrumb may be stripped away. Don't overdo it — once per section is enough. +3. **Front-load the essential context**, then the details. A reader who sees only the + first chunk should still get the core answer. +4. **Self-containment beats DRY — resolve the tension deliberately.** RAG wants + repetition; engineering instinct wants "don't repeat yourself." For docs content, + **self-containment wins**: + - Repeat the small essential context a reader needs to act here (e.g. that an + instance is created with a `licenseKey`), even if it appears on other pages. + - **Link** to another page for *depth* or *related* topics — never make the reader + leave this page to understand or complete *this* task. + - Rule of thumb: if removing a link would make the current task impossible to + finish, that information belongs on the page, not behind the link. +5. **Author the frontmatter.** Every page gets a specific `title` and a one-sentence + `description` that names the concept (this becomes the meta description and helps + scoping). Titles describe the *thing*, not the category: "WebSocket configuration" + → good; "Configuration" → bad. Give the file a meaningful slug + (`custom-functions.md`, not `page3.md`). + +--- + + + +## How to structure sections (chunking) + +1. **One purpose per section.** Each `##`/`###` answers exactly one question. Don't + mix installation, configuration, and troubleshooting under one heading — split + them so each chunk retrieves cleanly. Keep procedures ("how to…") separate from + reference ("what the options are"). +2. **Keep related facts physically close.** A constraint and its consequence go in + the same paragraph or adjacent ones, so chunking can't separate them. Bad: state + a limit in section 1 and its handling in section 4. Good: state both together. +3. **Kill backward references.** Delete "as mentioned above," "now that you've done + X," "with everything configured." These break the moment the section is read + alone. Replace with the explicit context. +4. **Use a real heading hierarchy.** `#` once (page title), then `##` → `###`, in + order, reflecting true topic structure. Headings are descriptive and specific. + Never skip levels or use a heading purely for visual size. +5. **Never leave long, undivided prose — make it granular.** Break any long stretch + of text into smaller, focused pieces under their own `##` or `###` headings. A + wall of text buries several distinct answers in one chunk and retrieves poorly. + Each subsection should be short enough that its heading accurately describes + everything beneath it. If a section runs past a few paragraphs, or starts drifting + into a second idea, split it and give the new part its own descriptive H2/H3. + Prefer more, well-labeled subsections over fewer long ones. +6. **Give each variation its own section.** When documenting multiple methods, + modes, or versions (e.g. `buildFromArray` vs `buildFromSheets` vs `buildEmpty`), + write a labeled section per variation rather than interleaving them. + +--- + + + +## Language & terminology + +1. **Write plainly.** Aim for a ~6th–7th-grade reading level: short sentences, one + idea each. Simple prose reduces both human confusion and AI misinterpretation. +2. **Use active voice.** "HyperFormula evaluates the formula," not "the formula is + evaluated." +3. **One term per concept, everywhere.** Pick the canonical term and never drift. + HyperFormula-specific vocabulary to keep consistent: *instance* (not "object"), + *sheet*, *cell address*, *named expression*, *formula*, *custom function*, + *config/options object*. Don't alternate "config" / "settings" / "options" for + the same thing. +4. **Keep a glossary and spell out acronyms on first use** on each page — "Abstract + Syntax Tree (AST)", "Cyclic Reference (CREF)". Link related glossary terms. +5. **One language per page.** Write docs in English; do not interleave other natural + languages. (This is about prose, not about supporting HyperFormula's 17 formula + locales — those are a documented feature.) +6. **State everything explicitly; assume no prior knowledge.** List prerequisites + inside the procedure instead of assuming setup. If a step depends on an external + tool or concept, give one line of context or a link. The AI cannot infer what you + didn't write. + +--- + + + +## Code examples + +Code is the primary content of these docs. Models reproduce complete examples well +and hallucinate the parts you omit. + +1. **Make every example complete and runnable.** Include imports, instance creation + with the `licenseKey` (a required, commonly-missed step — + `HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' })`), the data setup, the call, + and how to read the result. No fragments that assume invisible surrounding code. +2. **Show where code lives when structure matters** — a filename comment + (`// src/calc.ts`) or a short file tree for multi-file examples, so the reader can + place it correctly. +3. **Tag every code block with its language** (````javascript`, ````ts`, + ````bash`). Never use an untagged fence. +4. **Use consistent, correct API shapes.** Cell addresses use a fixed property order + every time — `{ sheet, row, col }` — don't reorder it between examples. Prefer the + real method names (`setCellContents`, `getCellValue`, `addSheet`, `getSheetId`, + `addNamedExpression`). When a runnable demo exists, link the StackBlitz for the + matching version branch. +5. **Show the correct way; show a wrong way only when it prevents a frequent + mistake.** If you include an anti-pattern, label it clearly ("Don't do this — + it throws because…") so it can't be mistaken for a recommendation. + +--- + + + +## Visuals, tables & the "why" + +1. **Never put information only in an image, diagram, or video.** Assistants can't + read pixels reliably. Put the same information in text next to the visual. Add a + descriptive caption to every image. +2. **Write UI/interactive flows as numbered, literal steps** — name the exact method, + option, or button and what it does at each step, so the steps stand without the + screenshot. +3. **Keep diagrams simple:** one diagram per concept, readable labels, no vertical + text, and represent multi-step workflows as a numbered list *in addition to* any + flowchart. +4. **Tables: simple and self-describing.** Real header rows, one fact per cell, each + row understandable on its own. Avoid merged cells or layouts where meaning comes + from visual position — convert those to structured lists or sub-sections. If a + table is only decorative grouping, use headings instead. +5. **Explain the "why," not just the "what."** Document intent, design decisions, and + when to use a pattern — the context source code alone doesn't convey. Call out + edge cases, gotchas, and common mistakes with their resolution (e.g. why + `buildEmpty` needs a `licenseKey`, when to use named expressions vs. cell + references, precision/rounding caveats). + +--- + + + +## VuePress conventions (so your output fits the site) + +- Start each page with frontmatter: + ```yaml + --- + title: Custom functions + description: Register and use your own functions in HyperFormula. + --- + ``` +- Use containers for asides: `::: tip`, `::: warning`, `::: danger` … `:::`. Put +essential steps in the body, not hidden inside a tip. +- Use **relative links** between guide pages (`./named-expressions.md`) and link to +API symbols under `/docs/api/`. +- Leave the auto-generated "Help us improve this page" edit link and sidebar to +VuePress — don't hand-write navigation. +- Don't hand-edit `docs/api/**` — it's generated from TSDoc in the source. + +--- + + + + +## Self-review checklist (run before finishing any page) + +- [ ] **Stands alone:** a reader who lands here cold, seeing only this page, can + ``` + finish the task without opening another page. + ``` +- [ ] **One question per section**, descriptive headings, hierarchy in order. +- [ ] **No walls of text:** long prose is broken into granular H2/H3 subsections, + ``` + each short enough that its heading covers everything under it. + ``` +- [ ] **Product named** in the body; no bare "the library/the method/the grid." +- [ ] **No backward references** ("as above," "now that you've…"). +- [ ] **Prerequisites stated explicitly**; nothing assumed. +- [ ] **Terminology consistent** with the canonical terms; acronyms expanded once. +- [ ] **Every code block** is language-tagged, complete, includes the `licenseKey`, + ``` + and would actually run; cell-address property order is `{ sheet, row, col }`. + ``` +- [ ] **No info trapped in images/tables**; visuals have text equivalents and captions. +- [ ] **Frontmatter** has a specific `title` and a one-sentence `description`. +- [ ] **No contradiction** with other pages; old facts updated everywhere. +- [ ] **"Why" is covered:** intent, when-to-use, and known gotchas — not just syntax. +- [ ] Would an AI assistant quoting *only this page* give a correct, complete answer? + ``` + If not, fix the page. + ``` diff --git a/LICENSE.txt b/LICENSE.txt index 64a1b72d04..4e317bacaf 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -3,7 +3,7 @@ Copyright (c) HANDSONCODE sp. z o. o. HYPERFORMULA is a software distributed by HANDSONCODE sp. z o. o., a Polish corporation based in Gdynia, Poland, at Aleja Zwyciestwa 96-98, registered by the District Court in Gdansk under number 538651, -EU VAT: PL5862294002, share capital: PLN 62,800.00. +EU VAT: PL5862294002, share capital: PLN 67,200.00. This software is dual-licensed, giving you the option to use it under either a proprietary license or the GNU General Public License version 3 diff --git a/context7.json b/context7.json new file mode 100644 index 0000000000..b819d18d1e --- /dev/null +++ b/context7.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://context7.com/schema/context7.json", + "projectTitle": "HyperFormula", + "description": "Headless, Excel-compatible spreadsheet engine in TypeScript — parses and evaluates ~400 functions in the browser or Node.js. In-process library (no REST API).", + "folders": ["docs"], + "excludeFolders": ["docs/.vuepress", "docs/api"], + "rules": [ + "HyperFormula is an in-process library, not a REST API — there is no HTTP endpoint or base URL.", + "Public API cell addresses are 0-indexed: { sheet, col, row }.", + "There is no #CALC! error type.", + "EmptyValue is exported as a Symbol, not null/undefined.", + "A license key is required when constructing the engine (use 'gpl-v3' for open-source use)." + ] +} diff --git a/docs/.vuepress/components/CodingAgentWizard.vue b/docs/.vuepress/components/CodingAgentWizard.vue new file mode 100644 index 0000000000..6eb24284a4 --- /dev/null +++ b/docs/.vuepress/components/CodingAgentWizard.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/docs/.vuepress/components/ViewMarkdownLink.vue b/docs/.vuepress/components/ViewMarkdownLink.vue new file mode 100644 index 0000000000..287f4dfa7c --- /dev/null +++ b/docs/.vuepress/components/ViewMarkdownLink.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/docs/.vuepress/components/clipboard.js b/docs/.vuepress/components/clipboard.js new file mode 100644 index 0000000000..694b8b1bcc --- /dev/null +++ b/docs/.vuepress/components/clipboard.js @@ -0,0 +1,29 @@ +/** + * Copy text to the clipboard, falling back to a hidden `