102 lines
3.3 KiB
Plaintext
102 lines
3.3 KiB
Plaintext
|
|
#!/usr/bin/env nu
|
||
|
|
# Check for malformed closing code fences in markdown files
|
||
|
|
# Malformed: ```plaintext (closing fence with language specifier)
|
||
|
|
# Correct: ``` (just three backticks)
|
||
|
|
#
|
||
|
|
# Markdownlint does NOT catch this natively (MD040 only checks opening fences)
|
||
|
|
# This is a CommonMark violation that must be checked separately
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# nu check-malformed-fences.nu # Check all markdown files
|
||
|
|
# nu check-malformed-fences.nu file.md # Check specific file
|
||
|
|
# nu check-malformed-fences.nu **/*.md # Check pattern
|
||
|
|
|
||
|
|
def main [
|
||
|
|
...files: string # Files to check (or empty for all)
|
||
|
|
] {
|
||
|
|
let md_files = if ($files | is-empty) {
|
||
|
|
glob **/*.md
|
||
|
|
| where { |f| $f !~ "node_modules" }
|
||
|
|
| where { |f| $f !~ "target" }
|
||
|
|
| where { |f| $f !~ ".git" }
|
||
|
|
| where { |f| $f !~ "build" }
|
||
|
|
| where { |f| $f !~ "dist" }
|
||
|
|
| where { |f| $f !~ ".coder" }
|
||
|
|
| where { |f| $f !~ ".claude" }
|
||
|
|
| where { |f| $f !~ "old_config" }
|
||
|
|
} else {
|
||
|
|
$files
|
||
|
|
}
|
||
|
|
|
||
|
|
mut errors = []
|
||
|
|
mut checked_count = 0
|
||
|
|
|
||
|
|
for file in $md_files {
|
||
|
|
$checked_count = $checked_count + 1
|
||
|
|
|
||
|
|
let content = open $file | lines
|
||
|
|
mut in_fence = false
|
||
|
|
mut fence_lang = ""
|
||
|
|
mut fence_start = 0
|
||
|
|
|
||
|
|
for line in ($content | enumerate) {
|
||
|
|
let idx = $line.index
|
||
|
|
let text = $line.item
|
||
|
|
|
||
|
|
# Check if this is a code fence line
|
||
|
|
if ($text =~ '^```') {
|
||
|
|
if not $in_fence {
|
||
|
|
# Opening fence
|
||
|
|
if ($text =~ '^```\w+') {
|
||
|
|
$in_fence = true
|
||
|
|
$fence_lang = ($text | str substring 3..)
|
||
|
|
$fence_start = $idx + 1
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
# We're inside a fence, so this must be a closing fence
|
||
|
|
if ($text =~ '^```\w+\s*$') {
|
||
|
|
# MALFORMED: closing fence has language
|
||
|
|
$errors = ($errors | append {
|
||
|
|
file: $file,
|
||
|
|
line: ($idx + 1),
|
||
|
|
fence_opened: $fence_start,
|
||
|
|
opening_lang: $fence_lang,
|
||
|
|
closing_text: $text,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
# Reset fence state regardless
|
||
|
|
$in_fence = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print $"🔍 Checked ($checked_count) markdown files"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
if ($errors | is-empty) {
|
||
|
|
print "✅ No malformed closing fences found"
|
||
|
|
print ""
|
||
|
|
print "All closing fences are correct: ``` (no language specifier)"
|
||
|
|
exit 0
|
||
|
|
} else {
|
||
|
|
print $"❌ Found ($errors | length) malformed closing fences"
|
||
|
|
print ""
|
||
|
|
print "Closing fences must NOT have language specifiers (CommonMark violation)"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
for e in $errors {
|
||
|
|
print $" ($e.file):($e.line)"
|
||
|
|
print $" Opened at line ($e.fence_opened) with: ```($e.opening_lang)"
|
||
|
|
print $" ❌ WRONG: ($e.closing_text | str trim)"
|
||
|
|
print $" ✅ FIX TO: ```"
|
||
|
|
print ""
|
||
|
|
}
|
||
|
|
|
||
|
|
print "How to fix:"
|
||
|
|
print " Replace closing fences like ```plaintext with just ```"
|
||
|
|
print ""
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
}
|