8The TypeDoc tier of ``oct docs``: API documentation for TypeScript
9subprojects, wired in the same shape as the Doxygen tier so the two are
12Doxygen has no TypeScript parser, and Sphinx autodoc has no TypeScript
13either, so before this module a TypeScript subproject (``oc_explorer``)
14had a Sphinx tier for its markdown and nothing for its API. TypeDoc runs
15the real TypeScript compiler and documents the type-checked program, which
16is why it beats any text-level tool for this job: generics, unions,
17overloads, inferred return types and re-exports all come from the checker.
19The declaration is a ``typedoc.json`` at the subproject root, mirroring
20"the Doxyfile is the declaration": the single-project build runs on it,
21the workspace fan-out includes the subproject on it, the hub shows a
22TypeDoc card on it, and ``--clean`` sweeps the output regardless.
26- Own :data:`TYPEDOC_CONFIG_REL` and :func:`has_typedoc`, the declaration
27 every consumer resolves.
28- :func:`typedoc_output_dir` - read ``out`` from the config the way
29 TypeDoc resolves it (relative to the config's directory).
30- :func:`run_docs_typedoc` - clear the previous output, run TypeDoc via
31 ``npx`` with the subproject as working directory, report the result.
37 L2 - build entry / exit
38 L3 - tool resolution, output directory decision
43- Nothing here installs anything. TypeDoc is a dev dependency of the
44 TypeScript subproject (``npm install --save-dev typedoc``) and ``npx``
45 resolves it from that subproject's ``node_modules``. A missing ``npx``
46 is reported and returns non-zero from a direct ``--typedoc`` request,
47 because the operator asked for that build by name; the workspace
48 fan-out is the caller that decides whether that is fatal.
49- Output is cleared before each run for the same reason Doxygen's is:
50 a generator does not delete pages for inputs it no longer sees. The
51 same two guards apply - the directory must resolve inside the project
52 and must carry TypeDoc's own marker - so a mis-set ``out`` cannot turn
53 a docs build into a recursive delete.
54- No shell. The subprocess is an argv list; on Windows ``npx`` is a
55 ``.cmd`` shim and is resolved through :func:`shutil.which` so the call
56 works without ``shell=True``.
60- oct.core.diagnostics (_dbg structured logger)
61- oct.tools.fs_remove (remove_tree_or_warn)
62- oct.docs.workspace_docs (lazy; is_workspace_root, run_workspace_docs - the workspace fan-out)
65from __future__
import annotations
71from pathlib
import Path
73from oct.core.diagnostics
import _dbg
74from oct.tools.fs_remove
import remove_tree_or_warn
78TYPEDOC_CONFIG_REL: Path = Path(
"typedoc.json")
81_DEFAULT_OUT: str =
"docs"
85_TYPEDOC_HTML_MARKERS: tuple[str, ...] = (
"index.html",
"hierarchy.html",
"assets")
89 """Return True when *project_dir* declares TypeDoc documentation."""
90 return (Path(project_dir) / TYPEDOC_CONFIG_REL).is_file()
94 """Resolve the HTML output directory the project's ``typedoc.json`` names.
96 TypeDoc resolves a relative ``out`` against the directory holding the
97 config file. A config that cannot be parsed, or that sets no ``out``,
98 resolves to TypeDoc's default.
100 func =
"typedoc_output_dir"
101 config_path = Path(project_dir) / TYPEDOC_CONFIG_REL
104 data = json.loads(config_path.read_text(encoding=
"utf-8"))
105 if isinstance(data, dict)
and isinstance(data.get(
"out"), str)
and data[
"out"]:
107 except (OSError, json.JSONDecodeError)
as exc:
108 _dbg(
"OCT-DOCS", func, f
"config unreadable ({exc}); default out", 3)
109 resolved = (config_path.parent / out).resolve()
110 _dbg(
"OCT-DOCS", func, f
"out={resolved}", 3)
115 """Delete the previous TypeDoc output, guarded the same way as Doxygen's."""
116 func =
"_clear_typedoc_output"
118 if not html_dir.is_dir():
121 html_dir.relative_to(Path(project_dir).resolve())
123 print(f
"Warning: TypeDoc output {html_dir} is outside the project; "
125 _dbg(
"OCT-DOCS", func, f
"refused: {html_dir} outside project", 2)
127 if not any((html_dir / m).exists()
for m
in _TYPEDOC_HTML_MARKERS):
128 print(f
"Warning: {html_dir} does not look like TypeDoc output "
129 f
"(no {_TYPEDOC_HTML_MARKERS[0]}); not clearing it.")
130 _dbg(
"OCT-DOCS", func, f
"refused: {html_dir} has no marker", 2)
132 if remove_tree_or_warn(html_dir, label=
"previous TypeDoc output"):
133 _dbg(
"OCT-DOCS", func, f
"cleared {html_dir}", 3)
137 """Return the ``npx`` executable path, or ``None`` when Node is absent.
139 On Windows the launcher is ``npx.cmd``; :func:`shutil.which` finds
140 either, and returning the resolved path lets the subprocess run as a
141 plain argv list with no shell.
143 return shutil.which(
"npx")
147 """Build the TypeDoc tier for the project at *root*.
149 Returns 0 on success; 1 when the project declares no ``typedoc.json``,
150 when ``npx`` cannot be found, or when TypeDoc itself fails.
152 Workspace mode: from a workspace root, dispatches to
153 :func:`oct.docs.workspace_docs.run_workspace_docs` with
154 ``mode="typedoc"`` unless *no_workspace* is set.
156 func =
"run_docs_typedoc"
159 if is_workspace_root(root):
160 return run_workspace_docs(root, mode=
"typedoc")
162 _dbg(
"OCT-DOCS", func, f
"start root={root}", 2)
165 f
"Error: no {TYPEDOC_CONFIG_REL} at {root}.\n"
166 f
"A TypeScript project declares TypeDoc by carrying that file; "
167 f
"see 'oct docs --help'.",
175 "Error: npx is not on PATH. TypeDoc runs through Node.js; "
176 "install Node and run 'npm install' in the project so its "
177 "typedoc dev dependency is present.",
180 _dbg(
"OCT-DOCS", func,
"npx missing", 1)
189 argv = [npx,
"--no-install",
"typedoc"]
190 _dbg(
"OCT-DOCS", func, f
"argv={argv} cwd={root}", 4)
191 print(f
"Running typedoc (cwd {root})...")
193 result = subprocess.run(argv, cwd=str(root))
194 except OSError
as exc:
195 print(f
"Error: could not run npx: {exc}", file=sys.stderr)
198 if result.returncode == 0:
201 print(f
"\nTypeDoc build complete. Open: {index}")
203 print(f
"\nTypeDoc build complete. Output: {typedoc_output_dir(root)}")
204 _dbg(
"OCT-DOCS", func, f
"end rc={result.returncode}", 2)
205 return result.returncode
209 "TYPEDOC_CONFIG_REL",
213 "typedoc_output_dir",
bool has_typedoc(Path project_dir)
int run_docs_typedoc(Path root, *, bool no_workspace=False)
Path typedoc_output_dir(Path project_dir)
None _clear_typedoc_output(Path project_dir)