This document helps you contribute to md-to-pdf.
- Code of conduct
- Getting started
- Development setup
- Contributing guidelines
- Plugin development
- Testing
- Documentation
- Submitting changes
- Code review process
Follow our code of conduct. Be respectful, inclusive, and considerate in all interactions.
- Go: Version 1.21 or later
- Git: For version control
- Make: For build automation
- mermaid-cli: For mermaid plugin development (
npm install -g @mermaid-js/mermaid-cli)
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/md-to-pdf.git cd md-to-pdf -
Add the upstream repository:
git remote add upstream https://github.com/fredcamaral/md-to-pdf.git
# Install dependencies
go mod download
# Build the main binary
make build
# Build plugins
make build-plugins
# Run tests
make testmd-to-pdf/
├── cmd/ # CLI commands
│ ├── root.go # Root command setup
│ ├── convert.go # Convert command
│ └── config.go # Config commands
├── internal/ # Internal packages
│ ├── core/ # Core conversion engine
│ ├── parser/ # Markdown parsing
│ ├── renderer/ # PDF rendering
│ ├── plugins/ # Plugin system
│ └── config/ # Configuration management
├── examples/ # Example files and plugins
│ ├── plugins/ # Example plugin implementations
│ └── markdown/ # Sample markdown files
├── pkg/ # Public API packages
│ └── plugin/ # Plugin development API
├── plugins/ # Plugin directory and development guide
├── scripts/ # Build and deployment scripts
└── tests/ # Test files
-
Create a feature branch:
git checkout -b feature/your-feature-name
-
Make your changes following the coding standards
-
Run tests and ensure they pass:
make test make lint -
Commit your changes with a descriptive message
-
Push to your fork and create a pull request
- Follow standard Go conventions and idioms
- Use
gofmtandgolintto format code - Write clear, self-documenting code
- Add comments for complex logic
- Use meaningful variable and function names
Follow the conventional commit format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixrefactor: Code refactoringstyle: Code style changestest: Adding or updating testsdocs: Documentation changesbuild: Build system changesbreaking: Breaking changes
Examples:
feat(plugins): add new content generator interface
fix(renderer): resolve image positioning issue
docs(readme): update installation instructions
- Keep your functions small and focused (ideally under 50 lines)
- Use interfaces to define contracts
- Separate concerns clearly
- Follow the dependency inversion principle
- Handle errors explicitly
- Use Go's idiomatic error handling
- Create custom error types when appropriate
- Provide meaningful error messages
- Don't ignore errors
Example:
func (e *Engine) Convert(opts ConversionOptions) error {
if err := e.validateOptions(opts); err != nil {
return fmt.Errorf("invalid options: %w", err)
}
// ... rest of function
if err := e.render(doc); err != nil {
return fmt.Errorf("rendering failed: %w", err)
}
return nil
}md-to-pdf supports two types of plugins:
Modify the markdown AST before rendering:
type ASTTransformer interface {
Plugin
Transform(node ast.Node, ctx *TransformContext) (ast.Node, error)
Priority() int
SupportedNodes() []ast.NodeKind
}Generate additional content during PDF creation:
type ContentGenerator interface {
Plugin
GenerateContent(ctx *GenerationContext) error
Priority() int
}- Create a new directory in
examples/plugins/ - Implement the required interfaces
- Export the plugin creation function
- Build as a shared library
Example plugin structure:
package main
import (
"github.com/fredcamaral/md-to-pdf/pkg/plugin"
)
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "myplugin" }
func (p *MyPlugin) Version() string { return "1.0.0" }
func (p *MyPlugin) Description() string { return "My custom plugin" }
// Plugin creation function (required)
func NewPlugin() plugin.Plugin {
return &MyPlugin{}
}- Follow the plugin interface contracts
- Handle errors gracefully
- Document plugin functionality
- Include example usage
- Test plugin functionality
# Run all tests
make test
# Run tests with coverage
make test-coverage
# Run specific test
go test ./internal/core -v
# Run tests with race detection
go test -race ./...- Write unit tests for all public functions you create
- Use table-driven tests when appropriate
- Mock external dependencies
- Test error conditions
- Aim for high test coverage
Example test structure:
func TestEngine_Convert(t *testing.T) {
tests := []struct {
name string
opts ConversionOptions
wantErr bool
}{
{
name: "valid conversion",
opts: ConversionOptions{
InputFile: "test.md",
OutputFile: "test.pdf",
},
wantErr: false,
},
// ... more test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
engine := NewEngine()
err := engine.Convert(tt.opts)
if (err != nil) != tt.wantErr {
t.Errorf("Convert() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}- Use Go doc comments for exported functions and types
- Follow the standard Go documentation format
- Include examples in documentation when helpful
When adding new features:
- Update the feature list
- Add usage examples
- Update configuration options if applicable
Update CHANGELOG.md for all changes:
- Follow the Keep a Changelog format
- Include the type of change (Added, Changed, Deprecated, Removed, Fixed, Security)
- Reference issue numbers when applicable
- Update documentation: Update documentation to reflect your changes
- Add tests: Include tests for new functionality
- Update changelog: Add entry to CHANGELOG.md
- Clean commit history: Squash commits if necessary
- Fill PR template: Provide clear description of changes
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Tests pass
- [ ] New tests added
- [ ] Manual testing completed
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Changelog updated- Clear description of the problem and solution
- Screenshots for UI changes
- Performance impact assessment for significant changes
- Breaking change documentation
As a reviewer, you should:
- Focus on code quality, maintainability, and correctness
- Suggest improvements constructively
- Test the changes locally when possible
- Check documentation and tests
As an author, you should:
- Respond to feedback promptly
- Make requested changes or discuss alternatives
- Keep discussions focused and professional
- Update PR based on feedback
- At least one maintainer approval required
- All CI checks must pass
- No unresolved conversations
- Up-to-date with main branch
- Use the
-vflag for verbose output - Add logging statements for debugging
- Use Go's debugging tools (delve, pprof)
- Profile critical code paths
- Benchmark performance-sensitive code
- Consider memory allocation patterns
- Test with large files
- Start with the example plugins
- Use the provided plugin interfaces
- Test plugins with various markdown inputs
- Document plugin configuration options
- Issues: Create an issue for bugs or feature requests
- Discussions: Use GitHub Discussions for questions
- Documentation: Check the README.md and plugins/README.md
- Examples: Look at example plugins and markdown files
Contributors will be recognized in:
- CHANGELOG.md for significant contributions
- README.md contributors section
- Release notes for major features
Thank you for contributing to md-to-pdf.