diff --git a/README.md b/README.md index b080569..129c0c4 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ plugins: markdown_description: Long description of my project. sections: Usage documentation: - - file1.md - - file2.md + file1.md: Description of file1 + file2.md: Description of file2 ``` The resulting `/llms.txt` file will be available at the root of your documentation. With the previous example, it will be accessible at https://myproject.com/llms.txt and will contain the following: @@ -46,21 +46,22 @@ Long description of my project. ## Usage documentation -- [File1 title](https://myproject.com/file1.md) -- [File2 title](https://myproject.com/file2.md) +- [File1 title](https://myproject.com/file1.md): Description of file1 +- [File2 title](https://myproject.com/file2.md): Description of file2 ``` Each source file included in `sections` will have its own Markdown file available at the specified URL in the `/llms.txt`. See [Markdown generation](#markdown-generation) for more details. -File globbing is supported: +File globbing is supported, and you can mix files with and without descriptions: ```yaml title="mkdocs.yml" plugins: - llmstxt: sections: Usage documentation: - - index.md - - usage/*.md + index.md: Main documentation page + usage/*.md # Files without descriptions + api.md: API reference documentation ``` ## Full output diff --git a/src/mkdocs_llmstxt/_internal/plugin.py b/src/mkdocs_llmstxt/_internal/plugin.py index e89f2bb..291525a 100644 --- a/src/mkdocs_llmstxt/_internal/plugin.py +++ b/src/mkdocs_llmstxt/_internal/plugin.py @@ -36,6 +36,7 @@ class _MDPageInfo(NamedTuple): path_md: Path md_url: str content: str + description: str | None = None class MkdocsLLMsTxtPlugin(BasePlugin[_PluginConfig]): @@ -136,12 +137,18 @@ def on_page_content(self, html: str, *, page: Page, **kwargs: Any) -> str | None base += "/" md_url = urljoin(base, md_url) + # Get description if available in the config + description = None + if isinstance(file_list, dict) and page.file.src_uri in file_list: + description = file_list[page.file.src_uri] + self.md_pages[section_name].append( _MDPageInfo( title=page.title if page.title is not None else page.file.src_uri, path_md=path_md, md_url=md_url, content=page_md, + description=description, ), ) @@ -169,10 +176,13 @@ def on_post_build(self, *, config: MkDocsConfig, **kwargs: Any) -> None: # noqa for section_name, file_list in self.md_pages.items(): markdown += f"## {section_name}\n\n" - for page_title, path_md, md_url, content in file_list: - path_md.write_text(content, encoding="utf8") - _logger.debug(f"Generated MD file to {path_md}") - markdown += f"- [{page_title}]({md_url})\n" + for page_info in file_list: + page_info.path_md.write_text(page_info.content, encoding="utf8") + _logger.debug(f"Generated MD file to {page_info.path_md}") + if page_info.description: + markdown += f"- [{page_info.title}]({page_info.md_url}): {page_info.description}\n" + else: + markdown += f"- [{page_info.title}]({page_info.md_url})\n" markdown += "\n" output_file.write_text(markdown, encoding="utf8") diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b892496..b9c096f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2,6 +2,12 @@ import pytest from duty.tools import mkdocs +from mkdocs.config.defaults import MkDocsConfig +from mkdocs.structure.files import Files +from mkdocs.structure.pages import Page +from types import SimpleNamespace + +from mkdocs_llmstxt._internal.plugin import MkdocsLLMsTxtPlugin def test_plugin() -> None: @@ -9,3 +15,135 @@ def test_plugin() -> None: with pytest.raises(expected_exception=SystemExit) as exc: mkdocs.build()() assert exc.value.code == 0 + + +def test_page_descriptions() -> None: + """Test that page descriptions are correctly handled and included in output.""" + # Create a mock config + config = MkDocsConfig() + config.site_name = "Test Project" + config.site_description = "Test Description" + config.site_url = "https://test.com/" + + # Create plugin instance with test configuration + plugin = MkdocsLLMsTxtPlugin() + plugin.load_config({ + "sections": { + "Test Section": { + "page1.md": "Description of page 1", + "page2.md": "Description of page 2" + } + } + }) + + # Initialize plugin + plugin.on_config(config) + + # Create mock file and page + file = SimpleNamespace( + src_uri="page1.md", + dest_uri="page1.html", + abs_dest_path="/tmp/page1.html", + url="page1.html" + ) + page = Page( + title="Test Page", + file=file, + config=config + ) + + # Process page content + plugin.on_page_content("Test content", page=page) + + # Check that the page info was stored correctly + assert len(plugin.md_pages["Test Section"]) == 1 + page_info = plugin.md_pages["Test Section"][0] + assert page_info.title == "Test Page" + assert page_info.description == "Description of page 1" + + +def test_mixed_descriptions() -> None: + """Test that mixing files with and without descriptions works correctly.""" + # Create a mock config + config = MkDocsConfig() + config.site_name = "Test Project" + config.site_url = "https://test.com/" + + # Create plugin instance with test configuration + plugin = MkdocsLLMsTxtPlugin() + plugin.load_config({ + "sections": { + "Test Section": { + "page1.md": "Description of page 1", + "page2.md": None, + "page3.md": "Description of page 3" + } + } + }) + + # Initialize plugin + plugin.on_config(config) + + # Create and process mock pages + for page_num in range(1, 4): + file = SimpleNamespace( + src_uri=f"page{page_num}.md", + dest_uri=f"page{page_num}.html", + abs_dest_path=f"/tmp/page{page_num}.html", + url=f"page{page_num}.html" + ) + page = Page( + title=f"Test Page {page_num}", + file=file, + config=config + ) + plugin.on_page_content("Test content", page=page) + + # Check that all pages were processed + assert len(plugin.md_pages["Test Section"]) == 3 + + # Verify descriptions + descriptions = [info.description for info in plugin.md_pages["Test Section"]] + assert descriptions == ["Description of page 1", None, "Description of page 3"] + +def test_no_descriptions() -> None: + """Test that no descriptions are included when no descriptions are provided.""" + # Create a mock config + config = MkDocsConfig() + config.site_name = "Test Project" + config.site_url = "https://test.com/" + + # Create plugin instance with test configuration + plugin = MkdocsLLMsTxtPlugin() + plugin.load_config({ + "sections": { + "Test Section": [ + "page1.md", + "page2.md" + ] + } + }) + + # Initialize plugin + plugin.on_config(config) + + # Create and process mock pages + for page_num in range(1, 3): + file = SimpleNamespace( + src_uri=f"page{page_num}.md", + dest_uri=f"page{page_num}.html", + abs_dest_path=f"/tmp/page{page_num}.html", + url=f"page{page_num}.html" + ) + page = Page( + title=f"Test Page {page_num}", + file=file, + config=config + ) + plugin.on_page_content("Test content", page=page) + + # Check that all pages were processed + assert len(plugin.md_pages["Test Section"]) == 2 + + descriptions = [info.description for info in plugin.md_pages["Test Section"]] + assert descriptions == [None, None]