Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ build:
@echo "Building documentation..."
PYTHONPATH=$(CURDIR) uv run pipeline build

snippets:
@echo "Exporting and linting code snippets..."
PYTHONPATH=$(CURDIR) uv run pipeline snippets export
PYTHONPATH=$(CURDIR) uv run pipeline snippets lint

# Define a variable for the test file path.
TEST_FILE ?= tests/unit_tests

Expand All @@ -18,6 +23,7 @@ lint:
uv run ruff format $(PYTHON_FILES) --diff
uv run ruff check $(PYTHON_FILES) --diff
uv run mypy $(PYTHON_FILES)
uv run pipeline snippets

format:
uv run ruff format $(PYTHON_FILES)
Expand Down Expand Up @@ -78,10 +84,10 @@ help:
@echo "Available commands:"
@echo " make dev - Start development mode with file watching and mint dev"
@echo " make build - Build documentation to ./build directory"
@echo " make snippets - Export and lint code snippets"
@echo " make mint-broken-links - Check for broken links in built documentation (excludes integrations)"
@echo " make mint-broken-links-all - Check for broken links in built documentation (includes all directories)"
@echo " make format - Format code"
@echo " make lint - Lint code"
@echo " make lint_md - Lint markdown files"
@echo " make lint_md_fix - Lint and fix markdown files"
@echo " make test - Run tests"
Expand Down
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Documentation changes follow a PR workflow where all tests must pass before merg

```text
src/ # Source documentation files (edit these)
snippets/ # Code snippets output directory
build/ # Generated output files (do not edit)
pipeline/ # Build pipeline source code
tests/ # Test files for the pipeline
Expand Down Expand Up @@ -59,12 +60,17 @@ Makefile # Build automation
- `docs build` - Build documentation
- `docs migrate <path>` - Convert MkDocs markdown files to Mintlify format
- `docs migrate-docusaurus <path>` - Convert Docusaurus markdown files to Mintlify format
- `docs snippets` - Export and lint code snippets from documentation files
- `docs snippets clean` - Clean code snippets from documentation files
- `docs snippets export` - Export code snippets from documentation files
- `docs snippets lint` - Lint code snippets exported from documentation files

### Important rules

- **Only edit files in `src/`** - The `build/` directory is automatically generated
- **Use Mintlify syntax** - See [Mintlify documentation](https://mintlify.com/docs) for formatting guidelines
- **Test your changes** - Use `docs dev` to preview changes locally with hot reload (on save)
- **Lint all code snippets** - Use `make snippets` to export and lint code snippets
- **Use safe Mintlify commands** - Use `make mint-broken-links` instead of `mint broken-links` to check final built documentation

### Available commands
Expand All @@ -73,6 +79,7 @@ Makefile # Build automation

- `make dev` - Start development mode with file watching and live rebuild
- `make build` - Build documentation to `./build` directory
- `make snippets` - Export and lint code snippets
- `make mint-broken-links` - Check for broken links in built documentation (excludes integrations)
- `make mint-broken-links-all` - Check for broken links in built documentation (includes all directories)
- `make install` - Install all dependencies
Expand Down Expand Up @@ -102,6 +109,21 @@ The `docs` command (installed as `uv run docs`) provides additional functionalit
- **`docs mv <old_path> <new_path>`** - Move files and update cross-references
- `--dry-run` - Preview changes without moving files

- **`docs snippets`** - Export and lint code snippets from documentation files
- `--src-dir <path>` - Specify source directory (default: `src`)
- `--output-dir <path>` - Specify output directory (default: `snippets`)
- `--export-only` - Only export code snippets
- `--lint-only` - Only lint code snippets

- **`docs snippets clean`** - Clean code snippets from documentation files
- `--output-dir <path>` - Specify output directory (default: `snippets`)

- **`docs snippets export`** - Export code snippets from documentation files
- (an alias for `docs snippets --export-only`)

- **`docs snippets lint`** - Lint code snippets exported from documentation files
- (an alias for `docs snippets --lint-only`)

These can be used directly using the `Makefile` or via the `docs` CLI tool:

- **`docs dev`** - Start development mode with file watching and hot reload
Expand Down Expand Up @@ -152,6 +174,9 @@ make lint

# Fix markdown issues
make lint_md_fix

# Check for linting issues in code snippets
make snippets
```

**Note**: All pull requests are automatically checked by CI/CD. The same linting and formatting standards will be enforced, and PRs cannot be merged if these checks fail.
Expand Down Expand Up @@ -206,7 +231,7 @@ Once your branch has been merged into `main`, you need to push the changes to `p

**Problem**: Running `mint broken-links` or other Mintlify commands from the project root causes parsing errors like:

```
```txt
Unable to parse .venv/lib/python3.13/site-packages/soupsieve-2.7.dist-info/licenses/LICENSE.md
- 3:48: Unexpected character '@' (U+0040) in name
```
Expand Down
6 changes: 0 additions & 6 deletions package-lock.json

This file was deleted.

79 changes: 79 additions & 0 deletions pipeline/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from pipeline.commands.build import build_command
from pipeline.commands.dev import dev_command
from pipeline.commands.snippets import clean_snippets_command, snippets_command
from pipeline.tools.docusaurus_parser import convert_docusaurus_to_mintlify
from pipeline.tools.links import drop_suffix_from_links, move_file_with_link_updates
from pipeline.tools.notebook.convert import convert_notebook
Expand Down Expand Up @@ -337,6 +338,84 @@ def main() -> None:
func=migrate_command, migration_type="docusaurus"
)

# Snippets command
snippets_parser = subparsers.add_parser(
"snippets",
help="Export and lint code snippets extracted from documentation files",
)
snippets_parser.add_argument(
"--src-dir",
type=Path,
default="src",
help="Path to the source directory containing documentation files",
)
snippets_parser.add_argument(
"--output-dir",
type=Path,
default="snippets",
help="Path to the output directory where files will be exported and linted",
)
snippets_parser.set_defaults(func=snippets_command)

# Snippets subcommands
snippets_subparsers = snippets_parser.add_subparsers(
dest="snippets_subcommand",
help="Snippets subcommands",
metavar="{export,lint,clean}",
required=False,
)

# Export snippets subcommand
snippets_export_parser = snippets_subparsers.add_parser(
"export",
help="Export code snippets from documentation files",
)
snippets_export_parser.add_argument(
"--src-dir",
type=Path,
default="src",
help="Path to the source directory containing documentation files",
)
snippets_export_parser.add_argument(
"--output-dir",
type=Path,
default="snippets",
help="Path to the output directory where files will be exported",
)
snippets_export_parser.set_defaults(func=snippets_command, export_only=True)

# Lint snippets subcommand
snippets_lint_parser = snippets_subparsers.add_parser(
"lint",
help="Lint code snippets exported from documentation files",
)
snippets_lint_parser.add_argument(
"--src-dir",
type=Path,
default="src",
help="Path to the source directory containing documentation files",
)
snippets_lint_parser.add_argument(
"--output-dir",
type=Path,
default="snippets",
help="Path to the lint output directory where files will be exported",
)
snippets_lint_parser.set_defaults(func=snippets_command, lint_only=True)

# Clean snippets subcommand
snippets_clean_parser = snippets_subparsers.add_parser(
"clean",
help="Clean exported code snippets from the output directory",
)
snippets_clean_parser.add_argument(
"--output-dir",
type=Path,
default="snippets",
help="Path to the output directory to clean",
)
snippets_clean_parser.set_defaults(func=clean_snippets_command)

args = parser.parse_args()

if not hasattr(args, "func"):
Expand Down
50 changes: 50 additions & 0 deletions pipeline/commands/snippets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Lint command implementation."""

import logging
from pathlib import Path
from typing import Any

from pipeline.core.snippets import DocumentationSnippetsParser

logger = logging.getLogger(__name__)


def clean_snippets_command(args: Any) -> int: # noqa: ANN401
"""Cleans the snippets directory."""
src_dir_path = Path(getattr(args, "src_dir", "src"))
output_dir_path = Path(getattr(args, "output_dir", "snippets"))
snippets_parser = DocumentationSnippetsParser(src_dir_path, output_dir_path)
snippets_parser.clean_all()
return 0


def snippets_command(args: Any) -> int: # noqa: ANN401
"""Exports code snippets from doc files.

This function serves as the entry point for the snippets command, handling
the process of exporting code snippets from doc files in the source directory
to the lint output directory to be linted by external tools.

Returns:
Exit code: 0 for success, 1 for failure (e.g., source directory
not found).
"""
src_dir_path = Path(getattr(args, "src_dir", "src"))
output_dir_path = Path(getattr(args, "output_dir", "snippets"))
export_only = getattr(args, "export_only", False)
lint_only = getattr(args, "lint_only", False)

if not src_dir_path.exists():
logger.error("Error: src directory not found")
return 1

# Create lint output directory
output_dir_path.mkdir(exist_ok=True)

# Initialize snippets parser
snippets_parser = DocumentationSnippetsParser(src_dir_path, output_dir_path)
if lint_only is False:
snippets_parser.export_all()
if export_only is False:
snippets_parser.lint_all()
return 0
51 changes: 51 additions & 0 deletions pipeline/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Constants used for docs building."""

import re

CONDITIONAL_FENCE_PATTERN = re.compile(
r"""
^ # Start of line
(?P<indent>[ \t]*) # Optional indentation (spaces or tabs)
::: # Literal fence marker
(?P<language>\w+)? # Optional language identifier (named group: language)
\s* # Optional trailing whitespace
$ # End of line
""",
re.VERBOSE,
)

CONDITIONAL_BLOCK_PATTERN = re.compile(
r"(?P<indent>[ \t]*)(?<!\\):::(?P<language>\w+)\s*\n"
r"(?P<content>((?:.*\n)*?))" # Capture content inside the block
r"(?P=indent)[ \t]*(?<!\\):::" # Match closing, same indentation, not escaped
)

CROSS_REFERENCE_PATTERN = re.compile(
r"""
(?: # Non-capturing group for two possible formats:
@\[ # @ symbol followed by opening bracket for title
(?P<title>[^\]]+) # Custom title - one or more non-bracket characters
\] # Closing bracket for title
\[ # Opening bracket for link name
(?P<link_name_with_title>[^\]]+) # Link name - non-bracket chars
\] # Closing bracket for link name
| # OR
@\[ # @ symbol followed by opening bracket
(?P<link_name>[^\]]+) # Link name - one or more non-bracket characters
\] # Closing bracket
)
""",
re.VERBOSE,
)

# Pattern to find code blocks with highlight comments, supporting optional indentation
CODE_BLOCK_PATTERN = re.compile(
r"(?P<indent>[ \t]*)```(?P<language>\w+)[ ]*(?P<attributes>[^\n]*)\n"
r"(?P<code>((?:.*\n)*?))" # Capture the code inside the block using named group
r"(?P=indent)```" # Match closing backticks with the same indentation
)

LANGUAGE_COMMENT_SYNTAX = {
"python": "# ",
"typescript": "// ",
}
Loading