Find broken links, missing images, etc in your HTML. Scurry around your site and find all those broken links.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Configuration system supporting both programmatic options and file-based configuration with CLI flag precedence. Enables flexible setup for various scanning scenarios from simple one-off checks to complex CI/CD integrations.
Load and merge configuration from files with CLI flag override capabilities.
/**
* Load configuration from files and merge with CLI flags
* @param flags - CLI flags and options to merge with file-based config
* @returns Promise resolving to merged configuration
*/
function getConfig(flags: Flags): Promise<Flags>;Usage Examples:
import { getConfig } from "linkinator";
// Load config with CLI flags taking precedence
const config = await getConfig({
concurrency: 50,
retry: true,
config: "./custom-linkinator.config.js"
});
// Use in scanning
const result = await check({
path: "https://example.com",
...config
});Comprehensive configuration interface for controlling all aspects of link checking behavior.
interface CheckOptions {
/** Number of simultaneous connections (default: 100) */
concurrency?: number;
/** Port for local server when scanning files */
port?: number;
/** URLs or file paths to check */
path: string | string[];
/** Recursively follow links on same root domain */
recurse?: boolean;
/** Request timeout in milliseconds */
timeout?: number;
/** Automatically parse and scan markdown files */
markdown?: boolean;
/** Links to skip - regex patterns or function */
linksToSkip?: string[] | ((link: string) => Promise<boolean>);
/** Root directory for local static server */
serverRoot?: string;
/** Enable directory index when linking to directories */
directoryListing?: boolean;
/** Automatically retry HTTP 429 responses with retry-after header */
retry?: boolean;
/** Automatically retry 5xx and network errors */
retryErrors?: boolean;
/** Maximum retry attempts for errors (default: 5) */
retryErrorsCount?: number;
/** Random jitter applied to error retry in milliseconds (default: 3000) */
retryErrorsJitter?: number;
/** URL rewrite patterns for transforming URLs before checking */
urlRewriteExpressions?: UrlRewriteExpression[];
/** Custom user agent string for HTTP requests (defaults to DEFAULT_USER_AGENT) */
userAgent?: string;
}Usage Examples:
import { check } from "linkinator";
// Basic local file scanning
const basicConfig = {
path: "./docs/",
markdown: true,
recurse: true,
};
// Advanced configuration with retries and filtering
const advancedConfig = {
path: ["https://example.com", "https://api.example.com"],
concurrency: 25,
timeout: 15000,
retry: true,
retryErrors: true,
retryErrorsCount: 3,
retryErrorsJitter: 5000,
linksToSkip: [
"mailto:.*",
"tel:.*",
".*\\.pdf$"
],
userAgent: "Custom Bot 1.0" // or use DEFAULT_USER_AGENT
};
// URL rewriting for development/staging environments
const rewriteConfig = {
path: "https://example.com",
urlRewriteExpressions: [
{
pattern: /https:\/\/production\.example\.com/g,
replacement: "https://staging.example.com"
}
]
};
const result = await check(advancedConfig);Configuration flags available for command-line usage and config files.
interface Flags {
/** Number of simultaneous connections */
concurrency?: number;
/** Path to configuration file */
config?: string;
/** Enable recursive crawling */
recurse?: boolean;
/** Links to skip (regex patterns) */
skip?: string | string[];
/** Output format (JSON, CSV, TEXT) */
format?: string;
/** Silent mode - suppress output */
silent?: boolean;
/** Log verbosity level (debug, info, warning, error, none) */
verbosity?: string;
/** Request timeout in milliseconds */
timeout?: number;
/** Enable markdown parsing */
markdown?: boolean;
/** Static server root directory */
serverRoot?: string;
/** Enable directory listings */
directoryListing?: boolean;
/** Enable retry on HTTP 429 */
retry?: boolean;
/** Enable retry on errors */
retryErrors?: boolean;
/** Maximum retry count */
retryErrorsCount?: number;
/** Retry jitter in milliseconds */
retryErrorsJitter?: number;
/** URL rewrite search pattern */
urlRewriteSearch?: string;
/** URL rewrite replacement string */
urlRewriteReplace?: string;
}Transform URLs before checking them, useful for testing staging environments or handling redirects.
interface UrlRewriteExpression {
/** Regular expression pattern to match in URLs */
pattern: RegExp;
/** Replacement string for matched patterns */
replacement: string;
}Usage Examples:
import { check } from "linkinator";
// Rewrite production URLs to staging for testing
const result = await check({
path: "https://example.com/docs/",
recurse: true,
urlRewriteExpressions: [
{
pattern: /https:\/\/cdn\.example\.com/g,
replacement: "https://staging-cdn.example.com"
},
{
pattern: /https:\/\/api\.example\.com/g,
replacement: "https://staging-api.example.com"
}
]
});Control which links are checked using regex patterns or custom functions.
Usage Examples:
import { check } from "linkinator";
// Skip common non-checkable links using regex patterns
const regexSkipResult = await check({
path: "https://example.com",
linksToSkip: [
"mailto:.*", // Skip email links
"tel:.*", // Skip phone links
".*\\.pdf$", // Skip PDF files
".*linkedin\\.com.*", // Skip LinkedIn
"#.*" // Skip fragment-only links
]
});
// Custom function-based filtering
const customSkipResult = await check({
path: "https://example.com",
linksToSkip: async (url) => {
// Skip URLs containing sensitive paths
if (url.includes('/admin') || url.includes('/private')) {
return true;
}
// Skip external URLs in test environment
if (process.env.NODE_ENV === 'test' && !url.includes('example.com')) {
return true;
}
return false;
}
});Linkinator supports multiple configuration file formats that can be automatically discovered or explicitly specified:
JSON Configuration (linkinator.config.json):
{
"concurrency": 50,
"recurse": true,
"retry": true,
"retryErrors": true,
"retryErrorsCount": 3,
"timeout": 10000,
"skip": ["mailto:.*", "tel:.*"],
"verbosity": "warning"
}JavaScript Configuration (linkinator.config.js):
module.exports = {
concurrency: 50,
recurse: true,
retry: true,
linksToSkip: async (url) => {
return url.includes('private') || url.includes('admin');
},
timeout: 10000
};ES Module Configuration (linkinator.config.mjs):
export default {
concurrency: 50,
recurse: true,
retry: true,
timeout: 10000,
urlRewriteSearch: "https://production.example.com",
urlRewriteReplace: "https://staging.example.com"
};enum LogLevel {
DEBUG = 0,
INFO = 1,
WARNING = 2,
ERROR = 3,
NONE = 4,
}
enum Format {
TEXT = 0,
JSON = 1,
CSV = 2,
}
class Logger {
constructor(level: LogLevel, format: Format);
debug(message?: string): void;
info(message?: string): void;
warn(message?: string): void;
error(message?: string): void;
}interface QueueOptions {
/** Number of simultaneous operations to allow */
concurrency: number;
}
interface QueueItemOptions {
/** Delay in milliseconds before executing the queued item */
delay?: number;
}
type AsyncFunction = () => Promise<void>;
class Queue extends EventEmitter {
constructor(options: QueueOptions);
add(fn: AsyncFunction, options?: QueueItemOptions): void;
onIdle(): Promise<void>;
on(event: 'done', listener: () => void): this;
}interface WebServerOptions {
/** Local path to mount as static web server root */
root: string;
/** Port for the local web server */
port?: number;
/** Whether to automatically compile and serve markdown */
markdown?: boolean;
/** Whether directories should automatically serve index pages */
directoryListing?: boolean;
}