83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 | class TerokPlugin(BasePlugin[TerokPluginConfig]):
"""ProperDocs plugin that drives mkdocs-terok generators."""
def on_config(self, config: ProperDocsConfig) -> ProperDocsConfig:
"""Inject brand CSS and Mermaid zoom JS into the site configuration."""
css_uri = "_assets/extra.css"
js_uri = "_assets/mermaid_zoom.js"
if self.config.inject_css and css_uri not in config.extra_css:
config.extra_css.append(css_uri)
if self.config.inject_js and js_uri not in [
s if isinstance(s, str) else getattr(s, "path", s) for s in config.extra_javascript
]:
config.extra_javascript.append(js_uri)
return config
def on_files(self, files: Files, /, *, config: ProperDocsConfig) -> Files:
"""Generate virtual files for each enabled generator.
When [`INVENTORY_ONLY_ENV`][mkdocs_terok.INVENTORY_ONLY_ENV]
is set, the four generators that don't feed ``objects.inv``
(``ci_map``, ``code_metrics``, ``test_map``, ``module_map``)
are skipped, so a stripped-down ``poetry install --only main,docs``
environment can still produce the inventory without ``pytest``,
``scc``, ``vulture``, etc. on PATH. ``ref_pages`` always runs
(when enabled in user config) because every inventory entry
comes from an mkdocstrings render of those pages.
"""
if self.config.inject_css:
files.append(
File.generated(config, "_assets/extra.css", abs_src_path=str(brand_css_path()))
)
if self.config.inject_js:
files.append(
File.generated(
config, "_assets/mermaid_zoom.js", abs_src_path=str(mermaid_zoom_js_path())
)
)
inventory_only = os.environ.get(INVENTORY_ONLY_ENV) == "1"
if not inventory_only:
if self.config.ci_map:
self._generate_ci_map(files, config)
if self.config.code_metrics:
self._generate_code_metrics(files, config)
if self.config.test_map:
self._generate_test_map(files, config)
if self.config.module_map:
self._generate_module_map(files, config)
if self.config.ref_pages:
self._generate_ref_pages(files, config)
return files
# -- private generators -------------------------------------------------
def _generate_ci_map(self, files: Files, config: ProperDocsConfig) -> None:
"""Emit a virtual CI map page from GitHub Actions workflows."""
from mkdocs_terok.ci_map import generate_ci_map
markdown = generate_ci_map()
files.append(File.generated(config, self.config.ci_map_path, content=markdown))
log.info(_LOG_GENERATED, self.config.ci_map_path)
def _generate_code_metrics(self, files: Files, config: ProperDocsConfig) -> None:
"""Emit quality report page and companion files (e.g. treemap SVGs)."""
from mkdocs_terok.code_metrics import CodeMetricsConfig, generate_code_metrics
coverage_json_path = (
Path(self.config.code_metrics_coverage_json_path)
if self.config.code_metrics_coverage_json_path
else None
)
qr_config = CodeMetricsConfig(
complexity_threshold=self.config.code_metrics_complexity_threshold,
graph_depth=self.config.code_metrics_graph_depth,
vulture_min_confidence=self.config.code_metrics_vulture_min_confidence,
file_level_loc=self.config.code_metrics_file_level_loc,
include_layer_overview=self.config.code_metrics_include_layer_overview,
include_graph_coarsening=self.config.code_metrics_include_graph_coarsening,
coverage_json_path=coverage_json_path,
treemap_group_depth=self.config.code_metrics_treemap_group_depth,
codecov_repo=self.config.code_metrics_codecov_repo,
src_label=self.config.code_metrics_src_label,
tests_label=self.config.code_metrics_tests_label,
)
result = generate_code_metrics(qr_config)
report_path = self.config.code_metrics_path
files.append(File.generated(config, report_path, content=result.markdown))
# Place companion files (e.g. treemap SVG) as siblings of the rendered page.
# The generator references them by bare filename; the plugin places them so
# that the bare name resolves correctly regardless of use_directory_urls.
report_posix = PurePosixPath(report_path)
for name, content in result.companion_files.items():
if config.use_directory_urls and report_posix.stem != "index":
companion_base = report_posix.with_suffix("")
else:
companion_base = report_posix.parent
files.append(File.generated(config, str(companion_base / name), content=content))
log.info(_LOG_GENERATED, report_path)
def _generate_test_map(self, files: Files, config: ProperDocsConfig) -> None:
"""Emit a virtual test map page from pytest collection."""
from mkdocs_terok.test_map import TestMapConfig, generate_test_map
integration_dir = (
Path(self.config.test_map_integration_dir)
if self.config.test_map_integration_dir
else None
)
tm_config = TestMapConfig(
show_markers=self.config.test_map_show_markers,
title=self.config.test_map_title,
integration_dir=integration_dir,
)
markdown = generate_test_map(config=tm_config)
files.append(File.generated(config, self.config.test_map_path, content=markdown))
log.info(_LOG_GENERATED, self.config.test_map_path)
def _generate_module_map(self, files: Files, config: ProperDocsConfig) -> None:
"""Emit a virtual module map page from source docstrings."""
from mkdocs_terok.module_map import ModuleMapConfig, generate_module_map
mm_config = ModuleMapConfig(title=self.config.module_map_title)
markdown = generate_module_map(mm_config)
files.append(File.generated(config, self.config.module_map_path, content=markdown))
log.info(_LOG_GENERATED, self.config.module_map_path)
def _generate_ref_pages(self, files: Files, config: ProperDocsConfig) -> None:
"""Emit API reference stubs and a literate-nav SUMMARY.md."""
from mkdocs_terok.ref_pages import RefPagesConfig, generate_ref_pages
output_prefix = self.config.ref_pages_path.rstrip("/")
rp_config = RefPagesConfig(
skip_patterns=tuple(self.config.ref_pages_skip_patterns),
output_prefix=output_prefix,
)
def write_file(doc_path: str, content: str) -> None:
"""Callback that appends a generated File to the files collection."""
files.append(File.generated(config, doc_path, content=content))
entries = generate_ref_pages(
rp_config,
write_file=write_file,
set_edit_path=lambda _doc, _src: None,
)
# Build literate-nav SUMMARY.md for the reference tree
prefix = rp_config.output_prefix + "/"
nav_lines = _build_literate_nav(entries, prefix)
summary_path = f"{rp_config.output_prefix}/SUMMARY.md"
files.append(File.generated(config, summary_path, content="".join(nav_lines)))
log.info("Generated %d reference pages", len(entries))
|