DocForge is a small tooling pair that lets you:
- Harvest JSDoc from a TypeScript API surface into a portable YAML docs database (
docs-db/*.yaml). - Apply those harvested docs into other source trees (currently focused on Objective‑C
.hheaders) by looking for<!-- doc-id: ... -->markers.
This repo contains two scripts:
harvest.py— scans TypeScript, extracts/normalizes docs, and writesdocs-db/*.yaml.apply-docs.py— scans source files (eg. iOS SDK headers), findsdoc-idmarkers, and rewrites the surrounding docblocks with the harvested content.
- Python 3.10+
pyyaml
pyyaml is already declared in pyproject.toml, so you generally don’t need to install it manually.
If you use uv (recommended), install/sync dependencies from pyproject.toml + uv.lock:
uv syncIf you’re not using uv, you can install with pip:
python -m pip install -r <(python -c 'import tomllib; print("\\n".join(tomllib.load(open("pyproject.toml","rb"))["project"]["dependencies"]))')Or simply:
python -m pip install pyyamlWhen adding a new dependency to this project, use:
uv add <package>harvest.py emits one YAML file per symbol, named after the doc id:
Foo.yamlFoo.bar.yaml
Each YAML entry looks like:
id: BackgroundGeolocation.onLocation
source_file: background-geolocation-types/src/core/api/BackgroundGeolocation.ts
signature: 'onLocation(cb: ...): Subscription;'
description: |-
Subscribe to location events.
@example example-1
examples:
example-1:
title: Example 1
code:
ts: |-
// ...
objc: |-
// ...Important conventions:
descriptionis markdown-ish and may contain example placeholders like@example example-1.examples.<key>.codeis a map of language → snippet (ts,objc,kotlin, …).- When applying, Docforge inserts the chosen language snippet at the placeholder location.
# Harvest from the default TypeScript root (background-geolocation-types/src)
uv run harvest.py --seed --out-dir docs-db
# Or harvest from a custom TypeScript root
uv run harvest.py --seed --source /path/to/your/types/src --out-dir docs-dbUseful options:
--source PATH— TypeScript source root to harvest (default:background-geolocation-types/src).--limit N— stop after writing N YAML files (debugging).--prune— delete YAML files in--out-dirthat were not generated in this run (keeps the db tidy).
To make it easy to reference symbols later, you can insert/update a doc-id marker at the top of each harvestable JSDoc block:
# Insert into the default TS root
uv run harvest.py --insert-doc-ids
# Or target a custom TS root
uv run harvest.py --insert-doc-ids --source /path/to/your/types/srcThis produces JSDoc blocks like:
/**
* <!-- doc-id: ActivityType -->
* iOS Activity Type used with {@link GeoConfig.activityType}.
*/
export const ActivityType = {
// ...
}Notes:
--insert-doc-idsskips blocks marked@internalor@hidden.- Some internal/hidden mixin-style interfaces may still have public member docs; Docforge can alias containers when harvesting (see code comments in
harvest.py).
# Dump normalized doc blocks from a file (paths are relative to --source)
uv run harvest.py --dump core/api/BackgroundGeolocation.ts --max-blocks 3
# Or with an explicit source root
uv run harvest.py --dump --source /path/to/your/types/src core/api/BackgroundGeolocation.ts --max-blocks 3
# Dump extracted description/examples summary
uv run harvest.py --dump-extracted core/api/BackgroundGeolocation.ts --max-blocks 3apply-docs.py walks a source tree, finds Objective‑C docblocks containing:
<!-- doc-id: Module.member -->…and replaces the surrounding /** ... */ block with the harvested description + examples.
Dry-run (no writes):
uv run apply-docs.py --docs-db docs-db --root ../ios-sdk --ext .h --lang objc --dry-runWrite changes:
uv run apply-docs.py --docs-db docs-db --root ../ios-sdk --ext .h --lang objc --writeExactly one of these behaviors is used:
--dry-run— compute changes and print a summary (no writes). (Default if no mode is specified.)--check— like--dry-run, but exits 1 if any changes would be made (useful for CI).--write— apply changes to disk.
--docs-db PATH(required): directory containing*.yamldocs.--root PATH(required): root of the source tree to scan.--ext .h(default:.h): comma-separated list of extensions to scan.--exclude-dirs a,b,c(optional): directory names to skip (defaults includePods,DerivedData,node_modules, etc.).--lang <key>(required): single example language to apply (eg.objc).--verbose: print per-file/per-id updates and full diffs.--strict: fail if a referenced doc-id is missing from the docs-db.
The YAML description can contain placeholders like:
@example example-1
When applying:
- The placeholder is replaced in-place with a rendered block:
@example <Title>- a fenced code block in your selected
--lang
If an example is referenced but missing:
// MISSING example example-1
// Filename: BackgroundGeolocation.onLocation.yamlIf the example exists, but not for the requested language:
// WARNING: No example block found for lang "objc" for example-1
// Filename: /absolute/path/to/docs-db/BackgroundGeolocation.onLocation.yamlapply-docs.py normalizes Objective‑C docblocks to the canonical style:
/**
* <!-- doc-id: BackgroundGeolocation.onLocation -->
* Subscribe to location events.
*
* @example Example 1
* ```objc
* // ...
* ```
*/This ensures consistent alignment and clean diffs, even if the original header used a compact /**\n* ... style.
-
Harvest from TypeScript:
uv run harvest.py --seed --out-dir docs-db --prune # (optional) override the TS root # uv run harvest.py --seed --source /path/to/your/types/src --out-dir docs-db --prune
-
Add/edit non-TS examples (eg.
objc) directly indocs-db/*.yamlas needed. -
Apply into the iOS SDK headers:
uv run apply-docs.py --docs-db docs-db --root ../ios-sdk --ext .h --lang objc --write
-
Use
--checkin CI to enforce that generated docs stay up to date:uv run apply-docs.py --docs-db docs-db --root ../ios-sdk --ext .h --lang objc --check
-
If you’re missing doc-ids in a destination source tree, add markers like:
/** * <!-- doc-id: BackgroundGeolocation.onLocation --> */ - (void)onLocation:...;
Then re-run
apply-docs.py. -
Keep
docs-db/in version control — it’s the canonical “single source of truth” that can be applied to multiple SDKs.
MIT License
Copyright (c) 2026 Transistor Software
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.