No 3rd party dependencies. All dependencies and devDependencies, checked recursively, are Codsen packages.
Permalink to InstallationInstallation
Permalink to Quick TakeQuick Take
Permalink to ExamplesExamples
- An ambiguous hyphen is left alone
- Convert one em dash into an entity
- Disable the dash conversion
- Convert a letter range into an en dash
- Convert a number range into an en dash
- Convert a maths minus into an en dash
- Report the index offsets through a callback
- Supply the dash value separately
Idea
A hyphen (-) is not a dash. Typographically-correct English text uses three different characters:
| Character | Name | Used for |
|---|---|---|
- | ||
- | hyphen | compound words — well-known |
– (–) | ||
– (–) | en dash | ranges and minus — 1880–1912, 5 – 2 = 3 |
— (—) | ||
— (—) | em dash | a break in a sentence — like this one |
This program finds hyphens and dashes which are in the wrong place and reports what to replace them with. It reads the surrounding characters to decide, so it does not need you to mark anything up.
This is a sibling of string-apostrophes; both are used by detergent.
Sources used in rules logic and unit tests:
- Oxford A–Z of Grammar and Punctuation 2nd Ed., 2009, ISBN 978–0199564675
- Butterick’s Practical Typography 2nd Ed., “Hyphens and dashes” chapter
The rules
The program applies three rules. In each, the character being evaluated is a hyphen, an en dash or an em dash.
- Number range or
A-Zrange → en dash. A hyphen with a digit on each side (1880-1912) becomes an en dash. So does a hyphen between two standalone uppercase letters (A-Z). - Whitespace on both sides → em dash. A hyphen or en dash becomes
—; an existing prose em dash stays unchanged. The exception is arithmetic: numeric or currency operands (1 - 2,$5 - $2,5$ - 2$) receive an en dash for a hyphen, en dash, or em dash. This includes spaced em dashes such as1 — 2and any amount of whitespace. - Cut-off speech → em dash. A letter, then a hyphen, then a quote (
"I was just abo-") becomes an em dash.
Anything the rules don’t recognise is left alone.
API — convertAll()
The main function convertAll() is imported like this:
It’s a function which takes two input arguments:
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
strType: String Obligatory: yes | |||
str | String | yes | String to process. |
optsType: Plain object Obligatory: no | |||
opts | Plain object | no | Optional Options Object. |
The Optional Options Object has the following shape:
| Key | Type | Default | Obligatory | Description |
|---|---|---|---|---|
convertEntitiesType: Boolean Default: falseObligatory: no | ||||
convertEntities | Boolean | false | no | Set to true to insert –/— instead of the raw characters. |
convertDashesType: Boolean Default: trueObligatory: no | ||||
convertDashes | Boolean | true | no | Set to false to leave dashes unchanged. |
Omitted or undefined conversion flags use their defaults. An explicit convertDashes: false disables conversion.
Use convertOne() to provide your own span or decoded symbol. convertAll() scans literal characters; it does not decode HTML entities automatically.
Here are all defaults in one place for copying:
The function will return a plain object:
| Returned object’s key | Type | Description |
|---|---|---|
resultType: String | ||
result | String | Processed string, with all ranges applied |
rangesType: Array of arrays, or null | ||
ranges | Array of arrays, or null | Ranges that were gathered and applied to produce the result. It’s an empty array when nothing needed fixing, and null when str was empty. |
For example:
import { convertAll } from "string-dashes";
console.log(
convertAll("Dashes come in two sizes - the en dash and the em dash.", {
convertEntities: true,
}),
);
// => {
// result: "Dashes come in two sizes — the en dash and the em dash.",
// ranges: [[25, 26, "—"]],
// }
API — convertOne()
The main function convertOne() is imported like this:
It’s a function which takes two input arguments:
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
strType: String Obligatory: yes | |||
str | String | yes | A string to process |
optsType: Plain object Obligatory: yes | |||
opts | Plain object | yes | Obligatory Options Object. |
The Obligatory Options Object has the following shape:
| Key | Type | Default | Obligatory | Description |
|---|---|---|---|---|
fromType: Natural number, string index Default: undefinedObligatory: yes | ||||
from | Natural number, string index | undefined | yes | Inclusive UTF-16 index at which the supplied span starts. |
toType: Natural number, string index Default: from + 1Obligatory: no | ||||
to | Natural number, string index | from + 1 | no | Exclusive UTF-16 end index. An integer endpoint must satisfy from ≤ to ≤ str.length. |
valueType: String Default: undefinedObligatory: no | ||||
value | String | undefined | no | Override the value present at str.slice(from, to). Recognised values are -, – and —. |
convertEntitiesType: Boolean Default: falseObligatory: no | ||||
convertEntities | Boolean | false | no | Set to true to insert –/— instead of the raw characters. |
convertDashesType: Boolean Default: trueObligatory: no | ||||
convertDashes | Boolean | true | no | Set to false to leave dashes unchanged. |
offsetByType: Function Default: undefinedObligatory: no | ||||
offsetBy | Function | undefined | no | Accepted for API compatibility; the current dash rules do not invoke this callback. |
Pass an options object; null receives the required-options error. opts.from must be an integer from 0 to str.length - 1. opts.to defaults to from + 1. Backwards or out-of-bounds integer spans throw an error prefixed with string-dashes/convertOne().
Equal endpoints are valid: a supplied value can insert a dash without deleting source text. For example, convertOne("12", { from: 1, to: 1, value: "-" }) returns [[1, 1, "–"]].
The function returns ranges: an array of range arrays or null. Later you can use them in ranges-apply to process a string using those ranges (in other words, “to apply those ranges”).
import { convertOne } from "string-dashes";
console.log(
convertOne("Dashes come in two sizes - the en dash and the em dash.", {
from: 25,
convertEntities: true,
}),
);
// => [[25, 26, "—"]]
opts.value
Use opts.value when the character sits in the string encoded, or represented by something other than a literal -, – or —. You tell the program where the “symbol” starts (from) and ends (to), and what it stands for (value); the program then evaluates the surroundings as if a real dash were there:
import { convertOne } from "string-dashes";
console.log(
convertOne("Dashes come in two sizes - the en dash.", {
from: 25,
to: 30,
value: "-", // tell the program indexes 25-30 represent a hyphen
}),
);
// => [[25, 30, "—"]]
The context scan begins after the complete supplied span, including when multiple spaces, tabs, or newlines separate it from the next operand:
import { convertOne } from "string-dashes";
convertOne("1 - 2", { from: 2, to: 7, value: "-" });
// => [[2, 7, "–"]]
That’s how detergent uses this package. convertEntities controls the encoding of replacements; it does not encode already-correct prose em dashes.
API — defaults
You can import defaults:
It's a plain object:
The main function calculates the options to be used by merging the options you passed with these defaults.
These defaults are applicable for both convertOne() and convertAll() functions.
API — version
You can import version:
API — types
This package is written in TypeScript and exports the following types:
| Type | Description |
|---|---|
OptsType: Opts | |
Opts | The Options Object of both convertAll() and convertOne(), shown above. |
RangeType: Range | |
Range | A single range, re-exported from ranges-apply. |
RangesType: Ranges | |
Ranges | Zero or more Ranges, or null, re-exported from ranges-apply. |
import type { Opts, Range, Ranges } from "string-dashes";