remark-lint rule to warn when lines are too long
npx @tessl/cli install tessl/npm-remark-lint-maximum-line-length@4.1.0remark-lint-maximum-line-length is a specialized lint rule for the unified/remark ecosystem that validates maximum line length in Markdown documents. It intelligently handles various Markdown constructs by ignoring non-wrappable elements and provides detailed error reporting with precise character counts and removal suggestions.
npm install remark-lint-maximum-line-lengthpluralize, mdast-util-mdx, unified-lint-rule, unist-util-position, unist-util-visitimport remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';For use with the unified processor:
import remarkLint from 'remark-lint';
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import { unified } from 'unified';import remarkLint from 'remark-lint';
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import { read } from 'to-vfile';
import { unified } from 'unified';
import { reporter } from 'vfile-reporter';
const file = await read('example.md');
await unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintMaximumLineLength) // Default: 60 characters
.use(remarkStringify)
.process(file);
console.error(reporter(file));With custom configuration:
// Configure maximum line length to 120 characters
await unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintMaximumLineLength, 120)
.use(remarkStringify)
.process(file);
// Configure with options object
await unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintMaximumLineLength, {
size: 100,
stringLength: (value) => value.length // Custom length calculation
})
.use(remarkStringify)
.process(file);The lint rule follows the unified ecosystem patterns:
unified-lint-rule framework for seamless integrationValidates that lines in Markdown documents don't exceed a specified character limit with intelligent content-aware processing.
/**
* remark-lint rule to warn when lines are too long
* @param options - Configuration options (number or Options object)
* @returns Transform function for unified processor
*/
function remarkLintMaximumLineLength(
options?: Options | number
): Transformer<Root, Root>;Parameters:
options (Options | number, optional): Configuration options (default: 60)Behavior:
Configure the maximum line length and string measurement behavior.
/**
* Configuration options for line length validation
*/
interface Options {
/**
* Preferred maximum line length in characters
* @default 60
*/
size?: number;
/**
* Custom function to calculate string length
* Useful for Unicode handling or visual width calculation
* @param value - The string to measure
* @returns The calculated length
*/
stringLength?: (value: string) => number;
}Usage Examples:
Simple numeric configuration:
// Set maximum line length to 120 characters
.use(remarkLintMaximumLineLength, 120)Options object configuration:
// Advanced configuration with custom string length function
.use(remarkLintMaximumLineLength, {
size: 100,
stringLength: (value) => {
// Example: Use visual width for CJK characters
return Array.from(value).length;
}
})The rule generates detailed error messages for lines that exceed the maximum length.
Error Message Format:
Unexpected `{actualLength}` character line, expected at most `{maxLength}` characters, remove `{difference}` character{s}Example Output:
1:24: Unexpected `23` character line, expected at most `20` characters, remove `3` characters
4:37: Unexpected `36` character line, expected at most `20` characters, remove `16` charactersThe rule intelligently handles different Markdown constructs:
Ignored Elements (Non-wrappable):
mdxjsEsm, mdxFlowExpression, mdxTextExpression (with remark-mdx)Special Handling (Inline Elements):
These elements are allowed to exceed the line limit when they:
/**
* Root node type from mdast
*/
interface Root {
type: 'root';
children: Array<Content>;
}
/**
* Transformer function type from unified
*/
type Transformer<In, Out> = (tree: In, file: VFile) => Out | undefined | void;
/**
* Virtual file type from unified ecosystem
*/
interface VFile {
// Core VFile properties and methods
message(reason: string, position?: Position): VFileMessage;
fail(reason: string, position?: Position): never;
}
/**
* Position information for error reporting
*/
interface Position {
line: number;
column: number;
offset?: number;
}# Basic usage with remark CLI
remark --frail --use remark-lint --use remark-lint-maximum-line-length .
# With custom configuration in package.json
{
"remarkConfig": {
"plugins": [
"remark-lint",
["remark-lint-maximum-line-length", 120]
]
}
}import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkLint from 'remark-lint';
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
import { read } from 'to-vfile';
import { reporter } from 'vfile-reporter';
// Process multiple files with different configurations
const files = ['README.md', 'CHANGELOG.md', 'docs/api.md'];
for (const filePath of files) {
const file = await read(filePath);
const processor = unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintMaximumLineLength, {
size: filePath.includes('CHANGELOG') ? 120 : 60,
stringLength: (value) => {
// Custom logic for different file types
return Array.from(value).length;
}
});
await processor.process(file);
if (file.messages.length > 0) {
console.error(`Issues in ${filePath}:`);
console.error(reporter(file));
}
}import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkLint from 'remark-lint';
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation';
import remarkLintListItemIndent from 'remark-lint-list-item-indent';
const processor = unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintMaximumLineLength, {
size: 100,
stringLength: (line) => {
// Account for tab characters as 4 spaces
return line.replace(/\t/g, ' ').length;
}
})
.use(remarkLintNoHeadingPunctuation)
.use(remarkLintListItemIndent, 'space');