class

Hwaro::CLI::Commands::Tool::DeadlinkCommand

Inherits Reference < Object

Constants

AUTOLINK_RE = /(?<!\\)(?:\\\\)*<((https?:\/\/)([^\s<>]*))>/i

A CommonMark autolink (<https://example.com/x>) renders as an <a href> just like [text](url), but neither the inline-link pass nor the raw-HTML pass (which wants a tag name) saw it, so an autolinked URL was never checked.

An escaped \<https://…> (odd run of backslashes) is literal text.

DEFAULT_CONCURRENCY = 8
DEFAULT_TIMEOUT = 10
DESCRIPTION = "Check for dead links in content files"
FLAGS = [CONTENT_DIR_FLAG, FlagInfo.new(short: nil, long: "--timeout", description: "HTTP request timeout in seconds (default: 10)", takes_value: true, value_hint: "SECONDS"), FlagInfo.new(short: nil, long: "--concurrency", description: "Max concurrent requests (default: 8)", takes_value: true, value_hint: "N"), FlagInfo.new(short: nil, long: "--external-only", description: "Check external links only"), FlagInfo.new(short: nil, long: "--internal-only", description: "Check internal links only"), FlagInfo.new(short: nil, long: "--ignore-url", description: "Skip links whose URL matches PATTERN (substring; * wildcards; repeatable)", takes_value: true, value_hint: "PATTERN"), FlagInfo.new(short: nil, long: "--allow-status", description: "Treat these HTTP status codes as healthy (comma-separated, e.g. 403,429)", takes_value: true, value_hint: "CODES"), FlagInfo.new(short: nil, long: "--skip-anchors", description: "Do not check #fragment links against the built HTML"), JSON_FLAG, HELP_FLAG]

Flags defined here are used both for OptionParser and completion generation

HTML_ATTR_RE = /\b(href|src|srcset)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i
HTML_BLOCK_START_RE = /\A {0,3}(?:<\/?(?:address|article|aside|blockquote|body|center|details|dialog|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h[1-6]|header|hr|html|iframe|legend|li|main|menu|nav|ol|p|pre|script|section|style|summary|table|tbody|td|tfoot|th|thead|tr|ul)(?:[\s>\/]|\z)|<\/?[a-zA-Z][a-zA-Z0-9-]*(?:\s[^<>]*)?\/?>\s*$|<!--)/i

A raw HTML block (a block opening with a block-level tag, or with a line holding one complete tag) is passed through verbatim, so an autolink-shaped <https://…> inside it is not a link.

HTML_TAG_RE = /<(a|img|source)\b(#{Utils::TextUtils::HTML_TAG_ATTRS_QUOTED_VALUES})>/i

<a href> / <img src|srcset> / <source src|srcset> written directly in Markdown. <source> matters because <picture> and <video> blocks are the common hand-written HTML in docs content. The value may be unquoted (href=/x/), which stops at whitespace or >; a quoted value may contain > (title="a > b"). Matched in two passes — tag, then each attribute inside it — because a single regex consumes the whole tag and so only ever reports the FIRST link attribute: <img srcset="…" src="…"> lost its src.

LINK_DEST = /((?:[^()]|\([^()]*\))*)/

Markdown link/image destination, allowing ONE level of balanced parentheses inside it. Plain ([^\)]+) stopped at the first ), so [x](/docs/foo_(bar)) was scanned as /docs/foo_(bar and reported dead — a false positive on a link the build resolves fine. The alternation branches start with disjoint characters, so there is no backtracking ambiguity.

NAME = "check-links"

Single source of truth for command metadata

POSITIONAL_ARGS = [] of String
POSITIONAL_CHOICES = [] of String
REFERENCE_DEFINITION_RE = /^ {0,3}\[([^\]\n]+)\]:[ \t]*(\S+)/m

A Markdown link reference definition ([id]: /target/ "title").

Three guards keep this from inventing links: footnote definitions ([^1]: …) are skipped, the destination must look like a URL or path, and — decisively — the label must actually be USED somewhere in the document. Shape alone is not enough: the ordinary prose line

[Note]: /usr/bin is where the binary lives on most systems

has a path-shaped first token and was reported as a dead link. CommonMark only treats such a line as a definition when a matching reference exists, so requiring one removes the whole false-positive class rather than blacklisting shapes.

REFERENCE_USE_RES = {/\]\[([^\]\n]+)\]/, /\[([^\]\n]+)\]\[\]/, /\[([^\]\n]+)\](?![\(\[:])/}

[text][label], [label][], and the shortcut form [label] — the last excluding anything followed by (, [ or :, which is an inline link, a full reference, or another definition.

SECTION_INDEX_EXTENSIONS = {".md", ".markdown"}

ReadContent recognizes a section index only when the extension is already lowercase (_index.md); _index.MD builds as a plain page. Section probes (feeds, pagination) keep that exact match.

Class methods

metadata
Source

Instance methods

run(args : Array(String))
Source

Nested types