Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
••••••••••••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Inside gomarklint: Architecture, Rule Engine, and How to Extend It

When I released gomarklint, most people asked the same two questions: “How does it run so fast?” and “Can I add my own lint rules?” In this post, we’ll dive int…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

When I released gomarklint, most people asked the same two questions:

“How does it run so fast?” and “Can I add my own lint rules?”



In this post, we’ll dive into the internals of gomarklint — how it parses Markdown efficiently, how rules are defined and executed, and how you can extend it to fit your own documentation style guide.

If you’re a Go developer interested in building your own linter or contributing to gomarklint, this article is your roadmap inside the engine.







GitHub logo

shinagawa-web
/
gomarklint



A fast and configurable Markdown linter written in Go







gomarklint



Test
codecov
Go Report Card
Go Reference
License: MIT



A fast and lightweight Markdown linter written in Go.



gomarklint checks your Markdown files for common issues such as heading structure problems, trailing blank lines, unclosed code blocks, and more. Designed to be minimal, fast, and CI-friendly.




✨ Features




  • ✅ Lint individual .md files or entire directories

  • ✅ Checks for heading level consistency (# → ## → ###)

  • ✅ Detects duplicate headings (case-insensitive, trims trailing spaces)

  • ✅ Detects missing trailing blank lines

  • ✅ Detects unclosed code blocks

  • ✅ Ignores YAML frontmatter correctly when linting

  • ✅ Detects broken external links (e.g. [text](https://...), https://...) with --enable-link-check

  • ✅ Supports config file (.gomarklint.json) to store default options

  • ✅ Supports ignore patterns (e.g. **/CHANGELOG.md) via config file

  • ✅ Supports structured JSON output via --output=json

  • ⚡️ Blazing fast — 157 files and 52,000+ lines scanned in under 50ms

  • 🐢 External link checking is slower (e.g. ~160s for 157…











Introduction: Why Architecture Matters



When I released gomarklint, most people asked me the same two questions:




“How does it run so fast?”

“Can I add my own lint rules?”




Speed and simplicity don’t happen by accident — they come from design.

gomarklint was built around three principles:




  1. Performance first: it should scan tens of thousands of lines in milliseconds.

  2. Simplicity: configuration and execution must stay minimal.

  3. Extensibility: rules should be easy to read, add, and maintain.



In this post, I’ll open the hood of gomarklint — showing how it parses Markdown efficiently, how rules are executed, and how you can extend it to match your documentation style guide.





Parsing Markdown Efficiently



At the heart of gomarklint is an extremely lightweight file parser.

It doesn’t try to build a full Markdown AST. Instead, it performs structured text scanning that’s optimized for speed and memory locality.



The typical flow looks like this:




  1. File Discovery: gomarklint walks through your repository recursively, ignoring hidden directories (.git, .vscode) and symbolic links.

  2. Line Reader: files are streamed line-by-line instead of loaded entirely into memory.

  3. Frontmatter Skipping: YAML frontmatters (--- ... ---) are automatically ignored to prevent false positives.

  4. Rule Evaluation: each active rule receives a slice of lines and emits lint errors.



This minimal I/O footprint lets gomarklint handle 50,000+ lines in under 50ms — even on modest CI runners.





The Rule Engine: Anatomy of a Lint Rule



Rules in gomarklint are intentionally simple.

Each rule is just a Go function that accepts file content and returns a list of LintErrors:




type LintError struct {
File string
Line int
Message string
}

type RuleFunc func(filePath string, lines []string) []LintError






A small example — detecting duplicate headings:



func CheckDuplicateHeadings(filePath string, lines []string) []LintError {
seen := make(map[string]int)
var errs []LintError

for i, line := range lines {
if strings.HasPrefix(line, "#") {
if _, ok := seen[line]; ok {
errs = append(errs, LintError{
File: filePath,
Line: i + 1,
Message: fmt.Sprintf("duplicate heading: %s", line),
})
}
seen[line] = i
}
}
return errs
}






This pattern repeats across all built-in rules — headings, code blocks, blank lines, and more.

gomarklint aggregates rule results concurrently, merges them, sorts by file/line, and prints them in a deterministic order.





Adding Your Own Rules



Creating a custom rule is straightforward.




  1. Create a new file under internal/rule/

  2. Implement your rule function — for example, forbid “TODO” comments:



func NoTODO(filePath string, lines []string) []LintError {
var errs []LintError
for i, line := range lines {
if strings.Contains(line, "TODO") {
errs = append(errs, LintError{
File: filePath,
Line: i + 1,
Message: "found TODO comment",
})
}
}
return errs
}







  1. Register it in the rule set (usually in rule/register.go):



var Rules = []RuleFunc{
CheckDuplicateHeadings,
NoTODO,
// ...other rules
}






  1. Rebuild and test:



# Run all tests
go test ./...

# Show CLI help from local source
go run . --help

# Generate a default .gomarklint.json (from your local build)
go run . init

# Lint the included sample files in ./testdata
go run . testdata





You can immediately see your new rule in action.

If it’s useful for others, open a Pull Request — gomarklint’s rule library welcomes contributions.





Configuration and Extensibility



All configuration lives in .gomarklint.json, allowing teams to share consistent rules:



{
"ignore": ["CHANGELOG.md"],
"rules": {
"CheckDuplicateHeadings": true,
"NoTODO": true,
"ExternalLinks": false
}
}






Each rule can be toggled on/off, and some accept parameters (like link checking patterns or minimum heading depth).



Future versions of gomarklint will likely support pluggable external rules, where Go plugins or YAML-based configuration can load custom logic at runtime — without forking the repo.





Lessons from Designing for Extensibility



Building gomarklint taught me that simplicity scales better than abstraction.



Many linters become slower or harder to extend as they grow, because every rule depends on a shared complex framework.

gomarklint took the opposite path — keep each rule isolated, independent, and stateless.



That design choice allowed:




  • Linear scalability with repository size

  • Easier testing and debugging

  • Low entry barrier for contributors



Extensibility doesn’t always mean adding layers.

Sometimes, it’s about not adding them.





Conclusion: Build Your Own Linter, or Improve This One



gomarklint isn’t just a tool — it’s a lightweight foundation for building your own Markdown analysis workflows.



Whether you:




  • want to enforce internal documentation standards,

  • prototype your own linter ideas, or

  • contribute a new rule back to the community —



the architecture is open, simple, and waiting for you.



Repository:







GitHub logo

shinagawa-web
/
gomarklint



A fast and configurable Markdown linter written in Go







gomarklint




Test
codecov
Go Report Card
Go Reference
License: MIT



A fast and lightweight Markdown linter written in Go.



gomarklint checks your Markdown files for common issues such as heading structure problems, trailing blank lines, unclosed code blocks, and more. Designed to be minimal, fast, and CI-friendly.




✨ Features




  • ✅ Lint individual .md files or entire directories

  • ✅ Checks for heading level consistency (# → ## → ###)

  • ✅ Detects duplicate headings (case-insensitive, trims trailing spaces)

  • ✅ Detects missing trailing blank lines

  • ✅ Detects unclosed code blocks

  • ✅ Ignores YAML frontmatter correctly when linting

  • ✅ Detects broken external links (e.g. [text](https://...), https://...) with --enable-link-check

  • ✅ Supports config file (.gomarklint.json) to store default options

  • ✅ Supports ignore patterns (e.g. **/CHANGELOG.md) via config file

  • ✅ Supports structured JSON output via --output=json

  • ⚡️ Blazing fast — 157 files and 52,000+ lines scanned in under 50ms

  • 🐢 External link checking is slower (e.g. ~160s for 157…





Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Inside gomarklint: Architecture, Rule Engine, and How to Extend It

Thematisch verwandte Begriffe: Inside, gomarklint, Architecture, Rule · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag