npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

markdownlint-rule-sembr

v0.2.0

Published

markdownlint rules in support of semantic line breaks

Readme

markdownlint-rule-sembr

Four markdownlint rules in support of semantic line breaks: breaking a line at a boundary of meaning rather than at a fixed column.

Where that boundary falls is a question of meaning, and no rule answers it. These check what a machine can decide around the break.

| Rule | Alias | Fixable | | --- | --- | --- | | SEMBR001 | sembr-orphan-punctuation | | | SEMBR002 | sembr-non-breaking-space | ✓ | | SEMBR003 | sembr-one-sentence-per-line | ✓ | | SEMBR004 | sembr-split-inline-span | |

Install

npm install --save-dev markdownlint-rule-sembr

Node 22 or later, which is what markdownlint-rule-helpers requires.

In .markdownlint-cli2.jsonc:

{
  "customRules": ["markdownlint-rule-sembr"],
  "config": {
    // One sentence per line is a target more often than a starting point: on an
    // existing repository it reports every wrapped paragraph at once. Turn it on
    // when the reflow is done, or run it on its own.
    "SEMBR003": false
  }
}

The rules are opt-out, not opt-in. Loading customRules enables all four, as it does for any markdownlint rule: with "default": true, or with no config at all, they are on. Name the ones you do not want.

Rules

SEMBR001 - Line starts with punctuation that belongs to the previous line

Tags: whitespace, sembr

Aliases: sembr-orphan-punctuation

A line opening on : ; ! ? » , or ) is a break made one character too early. The punctuation closes what the previous line said, and reading it alone at the start of a line costs the reader a beat.

<!-- Triggers -->
Une phrase coupée
: trop tôt.

<!-- Passes -->
Une phrase coupée :
au bon endroit.

SEMBR002 - Ordinary space where French typography requires a non-breaking one

Tags: whitespace, sembr

Aliases: sembr-non-breaking-space

Fixable: violations can be fixed by tooling

French puts a space before ; : ! ? % and inside « », and that space must not break. Written as an ordinary space, a renderer is free to end the line there and leave the punctuation stranded at the start of the next one.

One rule serves both languages: an ordinary space in front of that punctuation is a mistake in French and does not occur in English at all.

The fix inserts the right character — U+00A0 before : and inside the quotation marks, U+202F (thin) before ; ! ? %.

<!-- Triggers -->
Deux règles : une ; et une autre !

<!-- Passes: the same line, with the spaces before the punctuation
     replaced by U+00A0 and U+202F -->

[!NOTE] Those characters are invisible in a source file. Some teams would rather keep ordinary spaces in Markdown and apply French spacing when rendering; this rule is off in such a repository, which is what "SEMBR002": false is for.

SEMBR003 - A sentence ends mid-line

Tags: sembr

Aliases: sembr-one-sentence-per-line

Parameters:

  • abbreviations: full stops that do not end a sentence (string[], default [])

Fixable: violations can be fixed by tooling

The one MUST of the specification about where a break falls. Sentence boundaries come from sentence-splitter, which already knows about abbreviations, decimals and quotation marks; its list is English, so a French document wants at least p., ex. and cf..

The fix repeats whatever prefix keeps the line in its block — a blockquote marker, the indentation of a list item.

<!-- Triggers -->
La première. La seconde.

<!-- Passes -->
La première.
La seconde.

Headings are left alone: a heading is one unit whatever punctuation it carries.

SEMBR004 - A code span is split across lines

Tags: code, sembr

Aliases: sembr-split-inline-span

A code span broken in two still renders, so nothing looks wrong — but the identifier inside it can no longer be found by a search, which is what people actually do with code in prose.

<!-- Triggers -->
Call `myFunction(
argument)` here.

<!-- Passes -->
Call `myFunction(argument)` here.

Reflowing a document

A lint rule can only edit the line it reports on, so --fix breaks after a sentence and leaves the rest of the old wrapping where it was — one sentence per line, and a staircase of short lines after it. Reflowing means unwrapping the block first, which is a formatter's job.

npx sembr-format doc/*.md

It joins each prose block, splits it into sentences, and wraps each one at the latest boundary of meaning that fits — a clause end within reach, plain filling otherwise. SEMBR_WIDTH sets the target (88 by default). Headings, tables, code blocks and link definitions are copied through untouched.

It renders both versions and compares them before writing anything, and refuses the file if they differ. That is not ceremony: writing it turned up a blockquote continuation losing its marker, a break that left ** preceded by a space (which stops being emphasis), a bare > line silently merging two paragraphs, and a sentence split inside `overdueMinors !== 0` — the splitter does not know Markdown, so the atoms are masked before it runs.

Against the specification

SemBr 1.0 states thirteen rules. What this package covers, and what it does not:

| Spec | | Here | | --- | --- | --- | | 4 | A break MUST occur after a sentence | SEMBR003 — the only MUST about placement, and the one rule that can be fixed automatically | | 2 | A break MUST NOT alter the rendered output | Property-tested: the fixes are applied to a corpus and both renderings compared, whitespace collapsed as HTML collapses it | | 9 | A break MUST NOT occur within a hyphenated word | By construction — the fixer only ever breaks after sentence-final punctuation | | 12, 13 | 80 characters RECOMMENDED, longer lines allowed for links and code | Not this package's business: MD013 with strict: false already says exactly that | | 5 | A break SHOULD occur after an independent clause (, ; : ) | Not implemented. Deciding whether a comma separates independent clauses is the part no rule answers | | 1, 3, 6, 7, 8, 10, 11 | MAY and SHOULD, on grouping and emphasis | Editorial: not machine-checkable |

SEMBR001, SEMBR002 and SEMBR004 are outside the specification. It says where a break should fall; they say what a break must not damage on its way.

What the rules do not read

They run on the micromark token stream, so fenced and indented code blocks, HTML blocks and tables are skipped whole, and code spans, link destinations, autolinks and images are masked inside a line before any rule sees it. Link text stays visible: it is prose, and a sentence can end in it.

Issues and patches

The repository and its issue tracker live on Codeberg. The GitHub repository is a mirror: it accepts neither issues nor pull requests.

Licence

MIT.