Skip to content

Commit 1608b7c

Browse files
committed
wip: Implement prototype
1 parent 79a9244 commit 1608b7c

30 files changed

Lines changed: 973 additions & 55 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![gitpod](https://img.shields.io/badge/gitpod-workspace-blue.svg?style=flat)](https://gitpod.io/#https://github.com/mkdocstrings/typescript)
77
[![gitter](https://badges.gitter.im/join%20chat.svg)](https://gitter.im/mkdocstrings/typescript)
88

9-
A Typescript handler for mkdocstrings.
9+
A Typescript handler for mkdocstrings. :warning: **Work in progress** :warning:
1010

1111
## Installation
1212

docs/insiders/goals.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
goals: {}
1+
goals:
2+
2000:
3+
name: FusionDrive Ejection Configuration
4+
features:
5+
- name: "[Project] A TypeScript handler for mkdocstrings"
6+
ref: /

mkdocs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ plugins:
112112
- coverage
113113
- mkdocstrings:
114114
handlers:
115+
typescript:
116+
options:
117+
heading_level: 1
118+
show_root_heading: true
119+
show_symbol_type_toc: true
120+
show_symbol_type_heading: true
115121
python:
116122
import:
117123
- https://docs.python.org/3/objects.inv

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ classifiers = [
2828
"Typing :: Typed",
2929
]
3030
dependencies = [
31-
"mkdocstrings>=0.18",
31+
"mkdocstrings>=0.24",
32+
"griffe-typedoc>=0.0",
3233
]
3334

3435
[project.urls]

scripts/gen_credits.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,34 @@
2525
lock_data = toml.load(project_dir / "pdm.lock")
2626
lock_pkgs = {pkg["name"].lower(): pkg for pkg in lock_data["package"]}
2727
project_name = project["name"]
28-
regex = re.compile(r"(?P<dist>[\w.-]+)(?P<spec>.*)$")
28+
with project_dir.joinpath("devdeps.txt").open() as devdeps_file:
29+
devdeps = [line.strip() for line in devdeps_file if not line.startswith("-e")]
2930

31+
PackageMetadata = Dict[str, Union[str, Iterable[str]]]
32+
Metadata = Dict[str, PackageMetadata]
3033

31-
def _get_license(pkg_name: str) -> str:
34+
35+
def _merge_fields(metadata: dict) -> PackageMetadata:
36+
fields = defaultdict(list)
37+
for header, value in metadata.items():
38+
fields[header.lower()].append(value.strip())
39+
return {
40+
field: value if len(value) > 1 or field in ("classifier", "requires-dist") else value[0]
41+
for field, value in fields.items()
42+
}
43+
44+
45+
def _norm_name(name: str) -> str:
46+
return name.replace("_", "-").replace(".", "-").lower()
47+
48+
49+
def _requirements(deps: list[str]) -> dict[str, Requirement]:
50+
return {_norm_name((req := Requirement(dep)).name): req for dep in deps}
51+
52+
53+
def _extra_marker(req: Requirement) -> str | None:
54+
if not req.marker:
55+
return None
3256
try:
3357
data = metadata(pkg_name)
3458
except PackageNotFoundError:
@@ -48,7 +72,32 @@ def _get_license(pkg_name: str) -> str:
4872
return license_name or "?"
4973

5074

51-
def _get_deps(base_deps: Mapping[str, Mapping[str, str]]) -> dict[str, dict[str, str]]:
75+
def _get_metadata() -> Metadata:
76+
metadata = {}
77+
for pkg in distributions():
78+
name = _norm_name(pkg.name) # type: ignore[attr-defined,unused-ignore]
79+
metadata[name] = _merge_fields(pkg.metadata) # type: ignore[arg-type]
80+
metadata[name]["spec"] = set()
81+
metadata[name]["extras"] = set()
82+
metadata[name].setdefault("summary", "")
83+
_set_license(metadata[name])
84+
return metadata
85+
86+
87+
def _set_license(metadata: PackageMetadata) -> None:
88+
license_field = metadata.get("license-expression", metadata.get("license", ""))
89+
license_name = license_field if isinstance(license_field, str) else " + ".join(license_field)
90+
check_classifiers = license_name in ("UNKNOWN", "Dual License", "") or license_name.count("\n")
91+
if check_classifiers:
92+
license_names = []
93+
for classifier in metadata["classifier"]:
94+
if classifier.startswith("License ::"):
95+
license_names.append(classifier.rsplit("::", 1)[1].strip())
96+
license_name = " + ".join(license_names)
97+
metadata["license"] = license_name or "?"
98+
99+
100+
def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata:
52101
deps = {}
53102
for dep in base_deps:
54103
parsed = regex.match(dep).groupdict() # type: ignore[union-attr]

scripts/gen_ref_nav.py

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/mkdocstrings_handlers/typescript/handler.py

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44

55
from typing import TYPE_CHECKING, Any, ClassVar, Mapping, MutableMapping
66

7-
from mkdocs.exceptions import PluginError
7+
from markdown import Markdown
88
from mkdocstrings.handlers.base import BaseHandler, CollectionError, CollectorItem
99
from mkdocstrings.loggers import get_logger
1010

11-
if TYPE_CHECKING:
12-
from markdown import Markdown
1311

12+
from griffe_typedoc.loader import load as load_typedoc
13+
from griffe_typedoc.logger import patch_loggers
14+
from griffe_typedoc.dataclasses import ReflectionKind
1415

16+
patch_loggers(get_logger)
1517
logger = get_logger(__name__)
1618

1719

@@ -31,9 +33,53 @@ class TypescriptHandler(BaseHandler):
3133
"""The configuration used to collect item during autorefs fallback."""
3234

3335
default_config: ClassVar[dict] = {
36+
"base_file_path": ".",
37+
"docstring_style": "google",
38+
"docstring_options": {},
39+
"show_symbol_type_heading": False,
40+
"show_symbol_type_toc": False,
3441
"show_root_heading": False,
3542
"show_root_toc_entry": True,
43+
"show_root_full_path": True,
44+
"show_root_members_full_path": False,
45+
"show_object_full_path": False,
46+
"show_category_heading": False,
47+
"show_if_no_docstring": False,
48+
"show_signature": True,
49+
"show_signature_annotations": False,
50+
"signature_crossrefs": False,
51+
"separate_signature": False,
52+
"line_length": 60,
53+
"merge_init_into_class": False,
54+
"show_docstring_attributes": True,
55+
"show_docstring_functions": True,
56+
"show_docstring_classes": True,
57+
"show_docstring_modules": True,
58+
"show_docstring_description": True,
59+
"show_docstring_examples": True,
60+
"show_docstring_other_parameters": True,
61+
"show_docstring_parameters": True,
62+
"show_docstring_raises": True,
63+
"show_docstring_receives": True,
64+
"show_docstring_returns": True,
65+
"show_docstring_warns": True,
66+
"show_docstring_yields": True,
67+
"show_source": True,
68+
"show_bases": True,
69+
"show_submodules": False,
70+
"group_by_category": True,
3671
"heading_level": 2,
72+
"members_order": "alphabetical",
73+
"docstring_section_style": "table",
74+
"members": None,
75+
"inherited_members": False,
76+
"filters": ["!^_[^_]"],
77+
"annotations_path": "brief",
78+
"preload_modules": None,
79+
"allow_inspection": True,
80+
"summary": False,
81+
"unwrap_annotated": False,
82+
"parameter_headings": False,
3783
}
3884
"""The default configuration options.
3985
@@ -44,6 +90,11 @@ class TypescriptHandler(BaseHandler):
4490
**`heading_level`** | `int` | The initial heading level to use. | `2`
4591
"""
4692

93+
def __init__(self, *args, **kwargs) -> None:
94+
config_file_path = kwargs.pop("config_file_path", None)
95+
super().__init__(*args, **kwargs)
96+
self._collected = {}
97+
4798
def collect(self, identifier: str, config: MutableMapping[str, Any]) -> CollectorItem: # noqa: ARG002
4899
"""Collect data given an identifier and selection configuration.
49100
@@ -60,8 +111,22 @@ def collect(self, identifier: str, config: MutableMapping[str, Any]) -> Collecto
60111
Returns:
61112
Anything you want, as long as you can feed it to the `render` method.
62113
"""
63-
raise CollectionError("Implement me!")
64-
114+
if config.get("fallback", False):
115+
raise CollectionError("Not loading modules during fallback")
116+
if identifier not in self._collected:
117+
data = load_typedoc(["typedoc"])
118+
if data.kind is ReflectionKind.PROJECT:
119+
self._collected.update({module.name: module for module in data.children})
120+
else:
121+
self._collected[data.name] = data
122+
logger.debug(f"Collected {', '.join(self._collected.keys())}")
123+
if identifier in self._collected:
124+
for child in self._collected[identifier].children:
125+
if child.kind is ReflectionKind.MODULE and child.name == "index":
126+
return child
127+
return self._collected[identifier]
128+
raise CollectionError(f"Could not collect {identifier}")
129+
65130
def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: # noqa: ARG002
66131
"""Render a template using provided data and configuration options.
67132
@@ -73,13 +138,12 @@ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: # noqa
73138
Returns:
74139
The rendered template as HTML.
75140
"""
76-
# final_config = {**self.default_config, **config}
77-
# heading_level = final_config["heading_level"]
78-
# template = self.env.get_template(f"{data...}.html")
79-
# return template.render(
80-
# **{"config": final_config, data...: data, "heading_level": heading_level, "root": True},
81-
# )
82-
raise PluginError("Implement me!")
141+
final_config = {**self.default_config, **config}
142+
heading_level = final_config["heading_level"]
143+
template = self.env.get_template(f"module.html")
144+
return template.render(
145+
**{"config": final_config, "module": data, "heading_level": heading_level, "root": True},
146+
)
83147

84148
def update_env(self, md: Markdown, config: dict) -> None:
85149
"""Update the Jinja environment with any custom settings/filters/options for this handler.
@@ -116,7 +180,5 @@ def get_handler(
116180
handler="typescript",
117181
theme=theme,
118182
custom_templates=custom_templates,
119-
# To pass the following argument,
120-
# you'll need to override the handler's __init__ method.
121-
# config_file_path=config_file_path,
183+
config_file_path=config_file_path,
122184
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{% if obj.kind.value == "module" and obj.exports %}
2+
{% with children = obj.exports %}
3+
{% if children %}
4+
{{ log.debug("Rendering children of " + obj.path) }}
5+
<div class="doc doc-children">
6+
{% for obj in children %}
7+
{% include "dispatch.html" with context %}
8+
{% endfor %}
9+
</div>
10+
{% endif %}
11+
{% endwith %}
12+
{% elif obj.groups %}
13+
{{ log.debug("Rendering groups of " + obj.path) }}
14+
<div class="doc doc-children">
15+
{% for group in obj.resolved_groups %}
16+
<b>{{ group.title }}</b>
17+
{% for obj in group.children %}
18+
{% include "dispatch.html" with context %}
19+
{% endfor %}
20+
{% endfor %}
21+
</div>
22+
{% elif obj.children %}
23+
{{ log.debug("Rendering children of " + obj.path) }}
24+
<div class="doc doc-children">
25+
{% for obj in obj.children %}
26+
{% include "dispatch.html" with context %}
27+
{% endfor %}
28+
</div>
29+
{% endif %}

0 commit comments

Comments
 (0)