{"id":48816391,"url":"https://github.com/kazupon/ox-jsdoc","last_synced_at":"2026-06-07T13:01:14.317Z","repository":{"id":350423071,"uuid":"1201228242","full_name":"kazupon/ox-jsdoc","owner":"kazupon","description":"High performance jsdoc parser","archived":false,"fork":false,"pushed_at":"2026-05-16T03:55:36.000Z","size":3937,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-16T04:51:14.829Z","etag":null,"topics":["jsdoc","parser"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kazupon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"kazupon"}},"created_at":"2026-04-04T11:42:27.000Z","updated_at":"2026-05-16T03:55:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"f1ce49ab-b202-47dd-a787-182ec3a2513a","html_url":"https://github.com/kazupon/ox-jsdoc","commit_stats":null,"previous_names":["kazupon/ox-jsdoc"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/kazupon/ox-jsdoc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazupon%2Fox-jsdoc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazupon%2Fox-jsdoc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazupon%2Fox-jsdoc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazupon%2Fox-jsdoc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kazupon","download_url":"https://codeload.github.com/kazupon/ox-jsdoc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazupon%2Fox-jsdoc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34022032,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jsdoc","parser"],"created_at":"2026-04-14T11:04:01.057Z","updated_at":"2026-06-07T13:01:14.309Z","avatar_url":"https://github.com/kazupon.png","language":"TypeScript","funding_links":["https://github.com/sponsors/kazupon"],"categories":[],"sub_categories":[],"readme":"# ox-jsdoc\n\nHigh-performance JSDoc parser inspired by the `oxc` project.\n\n## Motivation\n\n- Parse JSDoc at native Rust speed when generating JSDoc documentation in `ox-content`, a potential alternative to TypeDoc.\n- Speed up lint performance for `eslint-plugin-jsdoc` when it runs on Oxlint.\n\n## Benchmark Results\n\nBenchmarks were run on 2026-05-15 against the post-cutover canonical Binary AST package (`ox-jsdoc` / `@ox-jsdoc/wasm`) and the typed AST reference (`ox-jsdoc-origin` / `@ox-jsdoc/wasm-origin`). All Rust-direct rows were re-measured in the same run on the same machine. Treat these numbers as a snapshot for this workspace, not as a stable performance guarantee.\n\nSee [design/009-jsdoc-linter-benchmark/README.md](design/009-jsdoc-linter-benchmark/README.md) for the benchmark design, fixture selection, measurement methodology, and result interpretation notes.\n\n### TL;DR\n\n- `ox-jsdoc` is most useful when parsing can be batched. In the full-file parser-only benchmark, `ox-jsdoc` NAPI `parseBatch` parses 226 comments in 300.720 us (1.331 us/comment), while `comment-parser` loop is 1.86x slower and `jsdoccomment` loop is 2.82x slower. The main win is avoiding per-comment JavaScript/native calls and repeated parser setup.\n- For ESLint-only usage, replacing the parser does not produce a large end-to-end lint speedup by itself. These runs are still dominated by ESLint, config loading, AST traversal, and, for TypeScript, `@typescript-eslint/parser`. In this fixture set, `eslint-ox-jsdoc-batch` is essentially the same as upstream ESLint on JS (`1.03x faster`) and TS (`1.01x faster`).\n- The larger linting benefit appears when the same JSDoc rules run on Oxlint. `oxlint-ox-jsdoc-batch` is 1.22x faster on JS and 5.19x faster on TS than upstream ESLint, even with the JS plugin bridge. Oxlint's built-in native JSDoc path is the fastest linter reference in this benchmark (`3.11x` on JS, `93.76x` on TS).\n- The Rust-direct table shows that the canonical batch path (`parse_batch_to_bytes` at 885 ns/comment, `parse_batch_into` at 978 ns/comment) is in the same neighbourhood as `oxc_jsdoc`'s lazy parse (952 ns/comment), even though `ox-jsdoc` eagerly emits a full Binary AST while `oxc_jsdoc` only walks tags lazily. Per-comment usage (`parse_into`, `parse`) is 1.25x–1.39x slower than `oxc_jsdoc` because the writer setup cost is paid for every comment instead of being amortized across a batch.\n- For `ox-content` documentation generation, the parser-only numbers are the more relevant signal than the ESLint-only runs: a Rust-native batch parser can keep JSDoc parsing a small part of the total documentation pipeline when many comments are parsed from a file at once.\n- The Rust-direct Criterion table is a Rust-side reference, not an end-to-end JavaScript benchmark. Use it to separate parser-core lower bounds, typed-AST vs Binary-AST emission cost, batch vs per-comment writer-setup cost, and bytes-output cost; use the Node / NAPI table for the practical cost seen from JavaScript.\n\n### Measurement Environment\n\n| Item         | Value                                                 |\n| ------------ | ----------------------------------------------------- |\n| Machine      | MacBook Pro                                           |\n| CPU          | Apple M1 Max, 10 cores (8 performance + 2 efficiency) |\n| Memory       | 64 GB                                                 |\n| Architecture | arm64                                                 |\n| OS           | macOS 15.5 (24F74), Darwin 24.5.0                     |\n| Node.js      | v24.15.0                                              |\n| pnpm         | 10.33.0                                               |\n| Rust / Cargo | 1.94.1                                                |\n| Hyperfine    | 1.19.0                                                |\n| Mitata       | 1.0.34                                                |\n\n### Measurement Method\n\n- Linter benchmarks were run through `pnpm --filter @ox-jsdoc/benchmark benchmark:jsdoc-linter`.\n- The linter script generates configs, then invokes `hyperfine` directly from a shell script with `--warmup 1`, `--runs 10`, and `--ignore-failure`.\n- Each linter command is executed from inside the fixture directory so `.` is resolved against the intended project tree.\n- Parser-only Node.js benchmarks were run through `pnpm --filter @ox-jsdoc/benchmark benchmark:parse-batch`, which uses `node --expose-gc`.\n- Parser-only Node.js timings use `mitata.measure()` via `tasks/benchmark/scripts/lib/measure.mjs`: 10 rounds, discard the first round, trim the fastest and slowest remaining rounds, then report the mean of the remaining 7 round p50 values.\n- Rust-direct parser references were run with `cargo bench` / Criterion and are reported separately from Node / NAPI / WASM timings. The runs cover the canonical `ox_jsdoc` (Binary AST) entry points (`parse` / `parse_into` / `parse_batch` / `parse_batch_into` / `parse_to_bytes` / `parse_batch_to_bytes`) plus `parse_block_into_data` (phase 1 only), the typed AST entry point `ox_jsdoc_origin::parse_comment`, and `oxc_jsdoc` as the Oxlint-native reference.\n\n### JSDoc Linter\n\nEnd-to-end CLI benchmark using Hyperfine. The `combined` rule set enables:\n\n- `jsdoc/empty-tags`\n- `jsdoc/require-param-description`\n- `jsdoc/require-param-type`\n\nFixtures:\n\n- JS: `refers/eslint-plugin-jsdoc/src/` - 86 files, 28,741 lines, 1,170 JSDoc blocks\n- TS: `refers/vscode/src/` - 5,996 files, 1,951,540 lines, 25,238 JSDoc blocks\n\nThis README summarizes mean timings. Full median, p95, and standard deviation values are in `tasks/benchmark/results/jsdoc-linter-hyperfine.md`.\n\n| Pattern                  |  JS mean | JS speed vs baseline |  TS mean | TS speed vs baseline |\n| ------------------------ | -------: | -------------------: | -------: | -------------------: |\n| `eslint-jsdoc-upstream`  | 721.6 ms |     1.00x (baseline) | 40.316 s |     1.00x (baseline) |\n| `oxlint-jsdoc-native`    | 231.8 ms |         3.11x faster | 430.0 ms |        93.76x faster |\n| `eslint-ox-jsdoc-single` | 797.6 ms |         1.11x slower | 41.018 s |         1.02x slower |\n| `eslint-ox-jsdoc-batch`  | 701.3 ms |         1.03x faster | 39.994 s |         1.01x faster |\n| `oxlint-ox-jsdoc-batch`  | 592.6 ms |         1.22x faster |  7.766 s |         5.19x faster |\n\n### Parser-only\n\nIn-process Node.js benchmark using the 226 JSDoc comments from `fixtures/perf/source/typescript-checker.ts`. These rows compare parser entry points and keep linter integration out of the timed path.\n\nIn the parser-only tables, `loop` means the benchmark calls the parser once per JSDoc comment in a JavaScript or Rust loop. It is the non-batch baseline used to show how much overhead `parseBatch` removes by parsing all comments in one call.\n\n| Parser | Full file total | Per comment | vs canonical NAPI parseBatch |\n| --- | --: | --: | --: |\n| `ox-jsdoc NAPI (parseBatch)` | 300.720 us | 1.331 us | 1.00x |\n| `ox-jsdoc WASM (parseBatch)` | 462.214 us | 2.045 us | 1.54x slower |\n| `comment-parser (loop)` | 559.584 us | 2.476 us | 1.86x slower |\n| `jsdoccomment (loop)` | 847.494 us | 3.750 us | 2.82x slower |\n| `@ox-jsdoc/jsdoccomment (parseCommentBatch)` | 1.142 ms | 5.055 us | 3.80x slower |\n| `ox-jsdoc-origin NAPI (typed AST, loop)` | 1.684 ms | 7.452 us | 5.60x slower |\n| `@ox-jsdoc/jsdoccomment (parseComment loop)` | 4.613 ms | 20.412 us | 15.34x slower |\n\nRust-direct parser references are separate Criterion measurements. They do not include Node.js, NAPI / WASM boundary cost, or `@ox-jsdoc/jsdoccomment` normalization. `oxc_jsdoc` is included as a Rust reference because it is the public API used by Oxlint's native JSDoc support.\n\nThe last column compares each row against `oxc_jsdoc` using `faster` / `slower` wording. Rows with different scenarios are not interchangeable feature-for-feature comparisons.\n\n- `parse_block_into_data` is the canonical crate's internal phase-1 parser. It walks one JSDoc block and builds intermediate `BlockData` / `TagData` plus diagnostics, but does not emit Binary AST bytes. The benchmark loops over all 226 comments, so this is a parser-core lower-bound measurement.\n- `ox_jsdoc_origin::parse_comment` is the typed AST entry point — it parses one block into an arena-allocated typed AST and skips Binary AST emission entirely. The gap between this and the canonical `parse_into` is the cost of binary serialization.\n- `parse_batch_into` and `parse_batch` are the public batch APIs returning a typed `BatchResult\u003c'arena\u003e`. `parse_batch_into` reuses a caller-supplied `BinaryWriter` so per-call writer setup (`StringTableBuilder` prelude memcpy + arena buffer init) is amortized over the whole batch. `parse_batch` constructs a fresh writer per call.\n- `parse_batch_to_bytes` is the public bytes API used by the JS bindings. It accepts all comments as one batch, emits each parsed block through `BinaryWriter`, and returns one owned Binary AST byte buffer with shared string data. This is the full Rust-side pipeline behind `parseBatch`, before Node / NAPI / WASM overhead.\n- `parse_into` and `parse` are the public per-comment APIs returning `ParseResult\u003c'arena\u003e`. `parse_into` reuses a caller-supplied `BinaryWriter` so the writer setup cost is amortized across the loop. `parse` constructs a fresh writer per call.\n- `parse_to_bytes` is the per-comment bytes-only sibling of `parse_into` / `parse`; it allocates a fresh writer and returns the owned `Vec\u003cu8\u003e`.\n\n| Parser | Scenario | Estimate | Per comment | vs `oxc_jsdoc` |\n| --- | --- | --: | --: | --: |\n| `ox_jsdoc` | `parse_block_into_data`, phase 1 block parse only, loop | 78.081 us | 345 ns | 2.76x faster |\n| `ox_jsdoc_origin` | typed AST `parse_comment`, loop, shared arena | 121.21 us | 536 ns | 1.78x faster |\n| `oxc_jsdoc` | `JSDoc::new(inner, span).tags()`, loop lazy parse | 215.21 us | 952 ns | 1.00x (baseline) |\n| `ox_jsdoc` | `parse_batch_to_bytes`, single batch full pipeline | 199.93 us | 885 ns | 1.08x faster |\n| `ox_jsdoc` | `parse_batch`, single batch, shared arena | 221.10 us | 978 ns | 1.03x slower |\n| `ox_jsdoc` | `parse_batch_into`, single batch, shared writer | 221.11 us | 978 ns | 1.03x slower |\n| `ox_jsdoc` | `parse_into`, public single-comment API, loop, shared writer | 269.65 us | 1.193 us | 1.25x slower |\n| `ox_jsdoc` | `parse`, public single-comment API, loop, shared arena | 298.61 us | 1.321 us | 1.39x slower |\n| `ox_jsdoc` | `parse_to_bytes`, public single-comment bytes API, loop | 303.54 us | 1.343 us | 1.41x slower |\n\n## Development\n\nThis repository uses Vite+ as the task runner. Install `vp` before running the project tasks:\n\n```sh\ncurl -fsSL https://vite.plus | bash\n```\n\nCommon commands:\n\n```sh\nvpr build     # or `vp run build`, build for JavaScript codes\nvpr fmt       # or `vp run fmt`, format for Rust and JavaScript codes\nvpr check     # or `vp run check`, lint for Rust and JavaScript codes\nvpr test      # or `vp run test`, test for Rust and JavaScript codes\n```\n\n`vpr check` runs the Rust license-header task and `cargo check`. The header task checks Rust sources for:\n\n- non-empty `@author`\n- `@license MIT`\n\nThe first run builds the local `xtask` crate automatically through Cargo. You can also run the task directly:\n\n```sh\ncargo run -p xtask -- headers:check\n```\n\nRust commands can be run directly as well:\n\n```sh\ncargo fmt --check\ncargo check\ncargo test\n```\n\n## Sponsors\n\nThe development of ox-jsdoc is supported by my OSS sponsors!\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://cdn.jsdelivr.net/gh/kazupon/sponsors/sponsors.svg\"\u003e\n    \u003cimg alt=\"sponsor\" src=\"https://cdn.jsdelivr.net/gh/kazupon/sponsors/sponsors.svg\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazupon%2Fox-jsdoc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkazupon%2Fox-jsdoc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazupon%2Fox-jsdoc/lists"}