selfdoc
Auto-generate API documentation from source code using inline directives
API Reference
selfdoc
selfdoc: Code-aware static site generator with directive-based content extraction.
_detect_version
def _detect_version()
Detect package version, preferring pyproject.toml over installed metadata.
Order: pyproject.toml in the source tree (accurate during editable installs) -> importlib.metadata (works for regular installs) -> "unknown".
main
def main()
Entry point for the selfdoc CLI.
selfdoc.build
Build pipeline for selfdoc: template scanning, directive resolution, HTML output.
_stub_resolver
def _stub_resolver(name, arg, body)
Placeholder resolver that produces a visible unresolved marker.
build
def build(dir_path='.', config=None)
Build docs from templates + directives.
- Load config from selfdoc.json
- Scan docs/ directory for .md template files
- For each template, resolve directives using language-specific extractor
- Convert resolved markdown to HTML
- Write HTML to output directory
- Copy non-.md files (images, CSS, etc.) to output
Args: dir_path: Project root directory. config: Pre-loaded config dict (if None, loads from selfdoc.json).
Returns: Dict of {output_path: True} for files written.
selfdoc.check
Check command -- validate directives and report documentation coverage.
Scans docs/ templates for directives, attempts to resolve each one, and reports per-directive status (OK or FAILED). For Python projects, also computes coverage: how many public symbols are referenced by :::module directives vs. the total public symbols in source files.
DirectiveResult
Result of validating a single directive.
CoverageStats
Coverage of public symbols by :::module directives.
CheckResult
Aggregate result of check_docs().
check_docs
def check_docs(dir_path='.', config=None)
Validate all directives in docs templates and report coverage.
Scans docs/ for .md templates, parses directives, attempts to resolve each one, and computes coverage for Python projects.
Args: dir_path: Project root directory. config: Pre-loaded config dict (if None, loads from selfdoc.json).
Returns: CheckResult with per-directive results and optional coverage stats.
_compute_python_coverage
def _compute_python_coverage(config, base_dir, referenced_modules)
Count public symbols in source files vs. those referenced by directives.
A "public symbol" is a top-level function or class whose name does not start with underscore. Each :::module directive that resolves to a file counts all public symbols in that file as "documented".
_extract_public_symbols
def _extract_public_symbols(filepath)
Parse a Python file and return names of public top-level functions/classes.
_resolve_module_to_relpath
def _resolve_module_to_relpath(arg, source_paths, base_dir)
Resolve a module argument to a path relative to base_dir.
Mirrors the resolution logic in extractors/python.py but returns the relative path instead of the absolute path.
print_results
def print_results(result)
Print check results to stdout in a human-readable format.
selfdoc.cli
CLI interface for selfdoc.
_detect_language
def _detect_language()
Auto-detect project language from project files.
Returns (language, source_paths) tuple or (None, None) if undetectable.
_detect_main_module
def _detect_main_module()
Detect the main module name for the starter template.
_cmd_init
def _cmd_init(args)
Initialize selfdoc in the current project.
_cmd_build
def _cmd_build(args)
Build the documentation site.
_cmd_serve
def _cmd_serve(args)
Serve the documentation site locally with SSE-based live reload.
_cmd_deploy
def _cmd_deploy(args)
Deploy the documentation site.
_detect_version
def _detect_version()
Detect project version from pyproject.toml or package.json.
_cmd_check
def _cmd_check(args)
Check documentation coverage and consistency.
run
def run()
Parse arguments and dispatch to the appropriate subcommand.
selfdoc.config
Config loader for selfdoc.json.
ConfigError
Raised when selfdoc.json is present but invalid.
_validate_deploy
def _validate_deploy(deploy)
Validate the deploy section if present.
Raises ConfigError if provider is unrecognized or required fields are missing.
load_config
def load_config(dir_path='.')
Load and validate selfdoc.json from dir_path.
Returns the validated config dict, or None if selfdoc.json does not exist. Raises ConfigError on malformed or invalid configuration.
selfdoc.deploy
Deploy providers for selfdoc documentation sites.
Supports:
- Cloudflare Pages (via wrangler CLI)
- GitHub Pages (via git force-push to gh-pages branch)
DeployError
Raised when a deploy operation fails.
deploy_cloudflare_pages
def deploy_cloudflare_pages(output_dir, project_name, version)
Deploy to Cloudflare Pages using the Wrangler CLI.
Requires wrangler (Cloudflare CLI) to be installed and authenticated (via wrangler login or CLOUDFLARE_API_TOKEN env var).
Args: output_dir: Path to the built HTML output directory. project_name: Cloudflare Pages project name. version: Version string for the commit message.
Raises: DeployError: If wrangler is not installed or the deploy fails.
deploy_github_pages
def deploy_github_pages(output_dir, version)
Deploy to GitHub Pages by force-pushing output to gh-pages branch.
Uses a temporary directory to build a fresh commit without touching the current working tree. Creates a .nojekyll file to prevent Jekyll processing on GitHub.
Args: output_dir: Path to the built HTML output directory. version: Version string for the commit message.
Raises: DeployError: If git operations fail or remote is unreachable.
_run_git
def _run_git(args, cwd)
Run a git command in the given directory, raising DeployError on failure.
selfdoc.directives
Directive parser for :::name arg blocks in markdown files.
Directive syntax: :::name arg optional body lines :::
Directives inside fenced code blocks ( or ~~~) are ignored. Unclosed directives at EOF raise DirectiveError.
DirectiveError
Raised when a directive is malformed (e.g. unclosed at EOF).
Directive
A parsed directive block.
parse_directives
def parse_directives(content: str) -> list[Directive]
Extract all directive blocks from markdown content.
Returns a list of Directive dataclass instances in document order. Directives inside fenced code blocks are ignored. Raises DirectiveError if a directive is opened but never closed.
resolve_directives
def resolve_directives(content: str, resolver: callable) -> str
Replace each directive block with the output of resolver(name, arg, body).
Non-directive content passes through unchanged. Directives inside fenced code blocks are left as-is (they are not directives).
selfdoc.html
Convert Markdown files to static HTML with a built-in minimal converter.
No external dependencies -- handles headings, code blocks, inline code, paragraphs, lists, links, bold/italic, and tables.
generate_html
def generate_html(markdown_files, project_name=None, version=None)
Convert Markdown files to static HTML.
Args: markdown_files: Dict mapping relative paths to MD content. project_name: Project name for titles and sidebar. version: Version string for display (optional).
Returns: Dict mapping file paths (.html) to HTML content.
md_to_html
def md_to_html(text)
Convert Markdown text to HTML.
Handles: headings, code blocks, inline code, paragraphs, unordered lists, ordered lists, links, bold, italic, tables.
_parse_table
def _parse_table(table_lines)
Parse markdown table lines into an HTML
| Header1 | Header2 |
|---|---|
| Cell1 | Cell2 |
The separator line (containing only |, -, and spaces) is detected and used to separate header from body rows.
_inline_format
def _inline_format(text)
Apply inline formatting: links, bold, italic, inline code.
_escape_html
def _escape_html(text)
Escape HTML special characters.
_md_to_html_path
def _md_to_html_path(md_path)
Convert a .md path to .html.
_build_nav
def _build_nav(markdown_files)
Build navigation items from the markdown file list.
Returns list of dicts: {"label": str, "path": str (html path)}
_render_nav
def _render_nav(nav_items, prefix, current_path='')
Render the sidebar navigation HTML.
_extract_title
def _extract_title(md_content, fallback)
Extract the first heading from markdown content as the page title.
_wrap_page
def _wrap_page(body_html, nav_html, title, project_name, version)
Wrap converted HTML body in the full page template.
selfdoc.resolver
Resolver factory -- dispatches directives to language-specific extractors.
_load_custom_directive
def _load_custom_directive(script_path, name)
Dynamically import a custom directive script and return its module.
Args: script_path: Absolute path to the Python script. name: Directive name (used as the module name for importlib).
Returns: The loaded module, or None if loading fails.
Raises: FileNotFoundError: If the script file does not exist. AttributeError: If the module has no 'resolve' callable. Exception: Any other import/load error.
make_resolver
def make_resolver(config, base_dir='.')
Create a resolver function for the project's language.
Custom directives from config["directives"] take priority over built-in language extractors. Each entry maps a directive name to a relative path (from project root) of a Python script that exports resolve(arg, config).
Args: config: Validated config dict from selfdoc.json. base_dir: Project root directory (for resolving relative paths).
Returns: A callable(name, arg, body) -> str that resolves directives to markdown.
extractors
selfdoc.extractors
selfdoc.extractorsthon
Python source extractor -- resolves directives by extracting from .py files.
Uses stdlib ast for parsing, no external dependencies. Handles:
- :::module -- extract module docstrings, functions, classes
- :::test -- extract test source code
- :::schema -- extract dataclass fields or JSON schema
- :::cli -- extract CLI help/usage info
- :::config -- extract config file contents as tables
resolve_python
def resolve_python(name, arg, body, source_paths, base_dir)
Dispatch a directive to the appropriate Python extraction handler.
Args: name: Directive name (module, test, schema, cli, config). arg: Directive argument (path, module name, etc.). body: Body lines of the directive block. source_paths: List of source directories from selfdoc.json. base_dir: Project root directory.
Returns: Markdown string with the extracted content.
_handle_module
def _handle_module(arg, body, source_paths, base_dir)
Extract module docstring, functions, and classes.
Resolves dotted.path or file path to a .py file, parses with ast, and formats the result as markdown.
_resolve_module_path
def _resolve_module_path(arg, source_paths, base_dir)
Try to resolve a module argument to an actual .py file path.
Tries: dotted-to-path conversion within each source path, then direct path.
_format_function
def _format_function(node, heading_level=2)
Format a function/method node as markdown.
Skips private items (leading _) unless they have a docstring.
_format_class
def _format_class(node)
Format a class node as markdown, including its public methods.
_build_signature
def _build_signature(node)
Build a human-readable function signature string from ast.arguments.
_annotation_str
def _annotation_str(node)
Convert an annotation AST node to a string, or empty string if None.
_handle_test
def _handle_test(arg, body, source_paths, base_dir)
Extract test source code from a test file.
arg format:
_extract_node_source
def _extract_node_source(source_lines, node)
Extract source lines for an AST node, stripping common indent.
_handle_schema
def _handle_schema(arg, body, source_paths, base_dir)
Extract schema information from JSON or Python dataclass.
arg format: - path/to/file.json -> render JSON keys as table - dotted.module ClassName -> extract dataclass fields
_schema_from_json
def _schema_from_json(file_path, base_dir)
Render a JSON file as a documented table of keys, types, and values.
_json_type_name
def _json_type_name(value)
Get a human-readable type name for a JSON value.
_json_value_repr
def _json_value_repr(value)
Get a compact representation of a JSON value for table display.
_schema_from_dataclass
def _schema_from_dataclass(module_path, class_name, source_paths, base_dir)
Extract dataclass/class fields with types and defaults from source.
_extract_class_fields
def _extract_class_fields(class_node, source)
Extract fields from a class (dataclass or regular class with annotations).
Produces a markdown table with Field, Type, Default, and Description columns.
_get_inline_comment
def _get_inline_comment(source_lines, lineno)
Extract an inline # comment from a source line (1-based lineno).
_format_default
def _format_default(default_str)
Format a default value for table display.
_handle_cli
def _handle_cli(arg, body, source_paths, base_dir)
Extract CLI help/usage information from a module.
For v1: extracts the module docstring and any string constants named HELP or USAGE, formatted as a code block.
_handle_config
def _handle_config(arg, body, source_paths, base_dir)
Extract config file contents as a documented table.
Supports JSON and TOML. Detects format from file extension.
_config_from_json
def _config_from_json(full_path, display_path)
Parse JSON config and render as a key-value table.
_config_from_toml
def _config_from_toml(full_path, display_path)
Parse TOML config and render as a key-value table.
_flatten_toml
def _flatten_toml(data, prefix, rows)
Recursively flatten TOML data into table rows.
selfdoc.extractors.go
Go source extractor -- resolves directives by extracting from .go files.
Uses regex-based parsing (no Go toolchain required). Handles:
- :::module -- extract package doc, exported funcs/types/consts/vars
- :::test -- extract test source code
- :::schema -- extract struct fields as a table
- :::cli -- extract usage constants and flag.* calls
- :::config -- extract config file contents as tables (JSON/TOML/YAML)
resolve_go
def resolve_go(name, arg, body, source_paths, base_dir)
Dispatch a directive to the appropriate Go extraction handler.
Args: name: Directive name (module, test, schema, cli, config). arg: Directive argument (path, package name, etc.). body: Body lines of the directive block. source_paths: List of source directories from selfdoc.json. base_dir: Project root directory.
Returns: Markdown string with the extracted content.
_handle_module
def _handle_module(arg, body, source_paths, base_dir)
Extract package doc, exported funcs, types, consts, and vars.
arg is a package directory path (e.g. "internal/commit"). Finds all .go files in that directory (excluding _test.go), extracts the package doc comment and all exported declarations.
_resolve_package_dir
def _resolve_package_dir(arg, source_paths, base_dir)
Resolve a package path argument to an actual directory.
Tries each source_path prefix, then the base_dir directly.
_extract_package_doc
def _extract_package_doc(file_contents)
Extract the package name and doc comment from Go source files.
The package doc is the contiguous // comment block immediately above the package declaration. Returns (package_name, doc_string).
_collect_comment_block_above
def _collect_comment_block_above(lines, target_line_idx)
Collect contiguous // comment lines immediately above target_line_idx.
Skips blank lines between the comment block and the declaration. Returns the comment text with // prefixes stripped.
_extract_exported_declarations
def _extract_exported_declarations(source)
Extract all exported declarations from a Go source file.
Returns a list of dicts with keys: kind, name, signature, doc.
_extract_const_block
def _extract_const_block(lines, block_start_idx, declarations, seen_names)
Extract exported constants from a const (...) block.
The doc comment for the entire block is attached to the first exported constant. Individual constants may also have their own // comments.
_extract_var_block
def _extract_var_block(lines, block_start_idx, declarations, seen_names)
Extract exported variables from a var (...) block.
_handle_test
def _handle_test(arg, body, source_paths, base_dir)
Extract test source code from a Go test file.
arg format:
_resolve_file_path
def _resolve_file_path(file_path, source_paths, base_dir)
Resolve a file path relative to base_dir or source_paths.
_extract_go_function
def _extract_go_function(source, func_name)
Extract a complete function from Go source by name.
Uses brace-counting to find the function body boundaries.
_handle_schema
def _handle_schema(arg, body, source_paths, base_dir)
Extract struct type fields as a markdown table.
arg format:
_extract_structs
def _extract_structs(source)
Extract all exported struct type declarations from Go source.
Returns a list of dicts: {name, doc, fields: [{name, type, tag, comment}]}.
_parse_struct_field
def _parse_struct_field(field_line, lines, line_idx)
Parse a single struct field line.
Returns {name, type, tag, comment} or None if not a field.
_format_struct_table
def _format_struct_table(struct_info)
Format a struct's fields as a markdown table.
_handle_cli
def _handle_cli(arg, body, source_paths, base_dir)
Extract CLI usage/help text and flag definitions from Go source.
Looks for:
- String constants named usage, helpText, usageText (case-insensitive match)
- flag.StringVar, flag.BoolVar, etc. calls
_extract_usage_constants
def _extract_usage_constants(source)
Find string constants/functions that return usage text.
Looks for patterns like:
- const usage =
... - func usageText() string { return
...} - Any variable/const with "usage" or "help" in the name containing a string
_extract_flag_calls
def _extract_flag_calls(source)
Extract flag.XxxVar and flag.Xxx calls from Go source.
Returns list of {name, type, default, desc}.
_handle_config
def _handle_config(arg, body, source_paths, base_dir)
Extract config file contents as a documented table.
Supports JSON and TOML. Detects format from file extension. Delegates to the same logic as the Python extractor.
_config_from_json
def _config_from_json(full_path, display_path)
Parse JSON config and render as a key-value table.
_config_from_toml
def _config_from_toml(full_path, display_path)
Parse TOML config and render as a key-value table.
_flatten_toml
def _flatten_toml(data, prefix, rows)
Recursively flatten TOML data into table rows.
_json_type_name
def _json_type_name(value)
Get a human-readable type name for a JSON value.
_json_value_repr
def _json_value_repr(value)
Get a compact representation of a JSON value for table display.
selfdoc.extractors.typescript
TypeScript/JavaScript source extractor -- resolves directives by extracting from .ts/.js files.
Uses regex-based parsing (no external dependencies). Handles:
- :::module -- extract module-level JSDoc, exported functions/classes/interfaces/types
- :::test -- extract test blocks (describe/it/test) by name
- :::schema -- extract interface/type fields as table
- :::cli -- extract help/usage strings or module-level JSDoc
- :::config -- extract JSON/JSONC/TOML config files as tables
resolve_typescript
def resolve_typescript(name, arg, body, source_paths, base_dir)
Dispatch a directive to the appropriate TypeScript/JS extraction handler.
Args: name: Directive name (module, test, schema, cli, config). arg: Directive argument (path, type name, etc.). body: Body lines of the directive block. source_paths: List of source directories from selfdoc.json. base_dir: Project root directory.
Returns: Markdown string with the extracted content.
_resolve_file_path
def _resolve_file_path(arg, source_paths, base_dir)
Resolve a file path argument to an actual TS/JS file on disk.
Tries the arg as-is (relative to base_dir and each source path), then with common extensions appended.
_read_file
def _read_file(filepath)
Read a file and return its contents, or raise an error string.
_parse_jsdoc_text
def _parse_jsdoc_text(raw_jsdoc)
Parse raw JSDoc content (the lines between /* and /).
Returns a dict with: - description: the main description text - params: list of {name, description} - returns: return description or None - tags: list of {tag, name, description} for other tags
_find_jsdoc_before
def _find_jsdoc_before(source, pos)
Find the JSDoc block that ends right before pos in the source.
Looks backwards from pos for a / that's part of a / ... / block, allowing only whitespace between the end of the JSDoc and pos.
_format_jsdoc_as_markdown
def _format_jsdoc_as_markdown(jsdoc)
Format a parsed JSDoc dict as markdown text.
_handle_module
def _handle_module(arg, body, source_paths, base_dir)
Extract module-level JSDoc and exported declarations from a TS/JS file.
_extract_module_jsdoc
def _extract_module_jsdoc(source)
Extract the first JSDoc block that appears before any declaration.
A module-level JSDoc is a /* / block at the top of the file, before any import/export/declaration.
_extract_exports
def _extract_exports(source)
Extract all exported declarations with their JSDoc and signatures.
Returns a list of dicts with: name, signature, jsdoc (parsed or None).
_extract_name_from_signature
def _extract_name_from_signature(sig)
Extract the declaration name from an export signature string.
_handle_test
def _handle_test(arg, body, source_paths, base_dir)
Extract test source code from a test file.
arg format:
For TS/JS test files, looks for describe("TestName", ...), it("TestName", ...), or test("TestName", ...) blocks.
_extract_test_block
def _extract_test_block(source, target_name)
Find a describe/it/test block by name and extract its source.
Searches for patterns like: describe("Name", ... it("Name", ... test("Name", ... Then tracks brace depth to find the end of the block.
_handle_schema
def _handle_schema(arg, body, source_paths, base_dir)
Extract interface or type definition fields as a markdown table.
arg format: - path/to/file.json -> render JSON keys as table - path/to/file.ts TypeName -> extract interface/type fields - path/to/file.ts -> if only one interface, extract it
_schema_from_json
def _schema_from_json(file_path, base_dir)
Render a JSON file as a documented table of keys, types, and values.
_schema_from_ts
def _schema_from_ts(source, type_name, display_path)
Extract interface or type fields from TypeScript source as a markdown table.
_extract_brace_block
def _extract_brace_block(source, open_brace_pos)
Extract the content between matched braces starting at open_brace_pos.
Returns the content between { and } (exclusive), or None if unmatched.
_parse_interface_fields
def _parse_interface_fields(body)
Parse fields from an interface/type body string.
Each field looks like: fieldName: Type; fieldName?: Type; Possibly preceded by a JSDoc comment or inline comment.
_find_inline_comment
def _find_inline_comment(line)
Find the position of an inline // comment, ignoring those inside strings.
Returns the index of the // or None if no inline comment found.
_handle_cli
def _handle_cli(arg, body, source_paths, base_dir)
Extract CLI help/usage information from a TS/JS file.
Looks for:
- Module-level JSDoc comment
- const help = "..." or const usage = "..." string constants
- yargs/commander setup patterns
_handle_config
def _handle_config(arg, body, source_paths, base_dir)
Extract config file contents as a documented table.
Supports JSON, JSONC (strips // and / / comments), and TOML.
_config_from_json
def _config_from_json(full_path, display_path)
Parse JSON config and render as a key-value table.
_config_from_jsonc
def _config_from_jsonc(full_path, display_path)
Parse JSONC config (strip comments) and render as a key-value table.
_strip_jsonc_comments
def _strip_jsonc_comments(text)
Strip // and / / comments from JSONC text, respecting strings.
_config_from_toml
def _config_from_toml(full_path, display_path)
Parse TOML config and render as a key-value table.
_flatten_toml
def _flatten_toml(data, prefix, rows)
Recursively flatten TOML data into table rows.
_render_json_table
def _render_json_table(data)
Render a JSON dict as a markdown key-value table.
_json_type_name
def _json_type_name(value)
Get a human-readable type name for a JSON value.
_json_value_repr
def _json_value_repr(value)
Get a compact representation of a JSON value for table display.