GitHub - oleg-koval/drop-em-dash-eslint-rule: ESLint plugin: disallow em dash (U+2014) and autofix to hyphen

3 min read Original article ↗

Catch the em dash. The one character that screams “this was written by an LLM” (or pasted from a doc editor). Autofix it to a plain hyphen, in CI, everywhere.

npm version CI License: MIT ESLint

npm i -D eslint eslint-plugin-drop-em-dash
// eslint.config.js
import dropEmDash from 'eslint-plugin-drop-em-dash';

export default [...dropEmDash.configs['flat/recommended']];

Done. No more - sneaking into your commits, comments, strings, or JSX copy.

This rule flags a Unicode character (U+2014) - it does not infer authorship. Plenty of humans use em dashes; the point here is consistency and ASCII in codebases that want it.

Why you need this

The Unicode em dash (-, U+2014) is a known punctuation tell of AI-generated text. It also leaks in constantly from:

  • ChatGPT / Claude / Gemini output pasted into code and docs
  • Notion, Google Docs, Word, Slack (they auto-convert -- to -)
  • Designers pasting marketing copy into JSX
  • macOS “smart punctuation” on by default

Once in your repo, it causes real problems:

  • Grep-unfriendly. grep -- patterns will not find -. Refactors can skip it.
  • Noisy diffs. Mixed dash styles break reviewers’ flow.
  • Inconsistent rendering across terminals, logs, emails, and error messages.
  • Copy drift. Marketing and UI strings drift toward LLM-default punctuation.
  • Not ASCII. Legacy tooling, logs, SMS, CSV, or email headers get weird.

This plugin is the smallest possible fix: one rule, one character, autofix on save. No config sprawl, no opinions about en dashes, no typography debate for your JS/TS tree - just ship ASCII where you lint.

What it does

  • Flags every literal - (U+2014) in files ESLint parses: code, comments, string literals, JSX text.
  • Autofixes each one to - (U+002D, hyphen-minus).
  • Ignores the en dash (U+2013) and other dash-like characters on purpose, so you can adopt it without a style war.

Usage

Flat config (ESLint 9+)

import dropEmDash from 'eslint-plugin-drop-em-dash';

export default [...dropEmDash.configs['flat/recommended']];

Manual:

import dropEmDash from 'eslint-plugin-drop-em-dash';

export default [
  {
    plugins: { 'drop-em-dash': dropEmDash },
    rules: { 'drop-em-dash/drop-em-dash': 'error' },
  },
];

Legacy .eslintrc.*

{
  "extends": ["plugin:drop-em-dash/recommended"]
}

Or wire the rule explicitly:

{
  "plugins": ["drop-em-dash"],
  "rules": {
    "drop-em-dash/drop-em-dash": "error"
  }
}

Other package managers

pnpm add -D eslint eslint-plugin-drop-em-dash
yarn add -D eslint eslint-plugin-drop-em-dash

Example

If a file contains a literal em dash (U+2014) between tokens (for example in a string or comment), eslint . --fix rewrites each occurrence to a hyphen-minus (-). See tests/fixtures/has-em-dash.cjs for a minimal file used in integration tests.

Illustrative after state:

const title = 'Pricing - overview'; // best plan - for teams

Requirements

  • Node.js ^18.18.0 || ^20.9.0 || >=21.1.0
  • ESLint >=8.57.0

Cursor / AI agent skill

This repo ships a Cursor skill so coding agents auto-discover how to install and wire the plugin: .cursor/skills/drop-em-dash-eslint/SKILL.md. Copy that folder to ~/.cursor/skills/drop-em-dash-eslint (or symlink) to enable globally - see root AGENTS.md. Fitting, since agents and doc tools are a common source of stray em dashes in code.

Docs

Full docs mirror: https://oleg-koval.github.io/drop-em-dash-eslint-rule/

The workflow .github/workflows/pages.yml bootstraps Pages (build_type: workflow) if needed, then deploys docs/. If Pages was disabled or pointed at a branch, use Settings → Pages → Source → GitHub Actions, or push again after the bootstrap step runs.

Testing

  • Unit tests - ESLint RuleTester for the rule.
  • Integration tests - real ESLint API over tests/fixtures/ (literal em dash) for diagnostics and autofix.

Releases (maintainers)

Pushes to main run .github/workflows/release.ymlsemantic-release after tests pass (Conventional Commits; release commits use [skip ci]).

Add repository secret NPM_TOKEN (npm automation or granular publish token). GITHUB_TOKEN covers GitHub Releases and the version bump push.

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

License

MIT - see LICENSE.

Related