punctilio 0.3.0 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,246 +1,99 @@
1
- # punctilio
2
-
3
- > *punctilio* (n.): a fine point of conduct or procedure
4
-
5
- Smart typography transformations for JavaScript/TypeScript. Converts ASCII punctuation to typographically correct Unicode characters. Originally built for [my personal website](https://turntrout.com/design).
6
-
7
- ## Features
8
-
9
- | **Feature** | **Before** | **After** |
10
- |---------|--------|-------|
11
- | Smart quotes | "straight" | “curly” |
12
- | | 'apostrophes' | ‘apostrophes’ |
13
- | Em dashes | wait - why did you | wait—why did you |
14
- | En dashes | 1-5 | 1–5 |
15
- | | January-March | January–March |
16
- | Minus signs | -5 | −5 (proper Unicode minus) |
17
- | Ellipsis | ... | … |
18
- | Multiplication | 5x5 | 5×5 |
19
- | | 3*4 | 3×4 |
20
- | Math symbols | != | ≠ |
21
- | | +- | ± |
22
- | | <= | ≤ |
23
- | | >= | ≥ |
24
- | | ~= | ≈ |
25
- | Legal symbols | (c) | © |
26
- | | (r) | ® |
27
- | | (tm) | ™ |
28
- | Arrows | -> | → |
29
- | | <- | ← |
30
- | | <-> | ↔ |
31
- | Prime marks | 5'10" | 5′10″ |
32
- | Fractions (optional) | 1/2 | ½ |
33
- | | 3/4 | ¾ |
34
- | Degrees (optional) | 20 C | 20 °C |
35
-
36
- **Handles edge cases**: contractions, possessives, nested quotes, year abbreviations ('99), "rock 'n' roll"
37
-
38
- ## Why another typography library?
39
-
40
- Existing solutions like [SmartyPants](https://daringfireball.net/projects/smartypants/) struggle with:
41
-
42
- - **Apostrophe ambiguity**: Is `'Twas` an opening quote or apostrophe? (It's an apostrophe)
43
- - **Cross-element text**: When quotes span `<em>"Hello</em> world"`, most libraries fail
44
- - **Context sensitivity**: `'99` (year) vs `'hello'` (quoted) vs `don't` (contraction)
45
-
46
- `punctilio` handles these through thorough regex patterns and an optional separator character for processing text that spans HTML elements.
47
-
48
- ## Installation
1
+ > *punctilio* (n.): precise observance of formalities.
49
2
 
50
- ```bash
51
- npm install punctilio
52
- # or
53
- pnpm add punctilio
54
- ```
55
-
56
- ## Usage
57
-
58
- ### Basic
59
-
60
- ```typescript
61
- import { transform, niceQuotes, hyphenReplace } from 'punctilio'
62
-
63
- // Apply all transformations
64
- transform('"Hello," she said - "it\'s pages 1-5."')
65
- // → "Hello," she said—"it's pages 1–5."
66
-
67
- // Symbol transforms are included by default
68
- transform('Wait... 5x5 != 25 (c) 2024')
69
- // → Wait… 5×5 ≠ 25 © 2024
70
-
71
- // Or use individual functions
72
- niceQuotes('"Hello", she said.')
73
- // → "Hello", she said.
74
-
75
- hyphenReplace('word - word')
76
- // → word—word
77
- ```
78
-
79
- ### Transform Options
3
+ The best typography package for English.
80
4
 
81
5
  ```typescript
82
6
  import { transform } from 'punctilio'
83
7
 
84
- // Enable optional transforms
85
- transform('Add 1/2 cup at 20 C', {
86
- fractions: true, // 1/2 → ½
87
- degrees: true // 20 C → 20 °C
88
- })
89
- // → Add ½ cup at 20 °C
90
-
91
- // Disable symbol transforms if you only want quotes/dashes
92
- transform('5x5 = 25', { symbols: false })
93
- // → 5x5 = 25 (unchanged)
94
-
95
- // Punctuation style: american (default), british, or none
96
- transform('"Hello".', { punctuationStyle: 'american' }) // → "Hello."
97
- transform('"Hello."', { punctuationStyle: 'british' }) // → "Hello".
98
-
99
- // Dash style: american (default), british, or none
100
- transform('word - word', { dashStyle: 'american' }) // → word—word
101
- transform('word - word', { dashStyle: 'british' }) // → word – word
8
+ transform('"It\'s a beautiful thing, the destruction of words..." -- 1984')
9
+ // “It’s a beautiful thing, the destruction of words…” — 1984
102
10
  ```
103
11
 
104
- ### With HTML Element Boundaries
105
-
106
- When processing text that spans multiple HTML elements, use a separator character to mark boundaries:
107
-
108
- ```typescript
109
- import { transform, DEFAULT_SEPARATOR } from 'punctilio'
110
-
111
- // Your HTML: <p>"Hello <em>world</em>"</p>
112
- // Extract text with separator between elements:
113
- const text = `"Hello ${DEFAULT_SEPARATOR}world${DEFAULT_SEPARATOR}"`
114
-
115
- const result = transform(text, { separator: DEFAULT_SEPARATOR })
116
- // → "Hello \uE000world\uE000"
117
- // The separator is preserved; split on it to restore to your elements
12
+ [![Test](https://github.com/alexander-turner/punctilio/actions/workflows/test.yml/badge.svg)](https://github.com/alexander-turner/punctilio/actions/workflows/test.yml)
13
+ [![Lint](https://github.com/alexander-turner/punctilio/actions/workflows/lint.yml/badge.svg)](https://github.com/alexander-turner/punctilio/actions/workflows/lint.yml)
14
+ [![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/alexander-turner/punctilio)
15
+
16
+ ```bash
17
+ npm install punctilio
118
18
  ```
119
19
 
120
- For a complete implementation showing how to use this with a HAST (HTML AST) tree, see the [`transformElement` function in TurnTrout.com](https://github.com/alexander-turner/TurnTrout.com/blob/main/quartz/plugins/transformers/formatting_improvement_html.ts).
121
-
122
- ## API
123
-
124
- ### `transform(text, options?)`
125
-
126
- Applies all typography transformations. Options:
127
- - `separator`: Boundary marker for HTML elements (default: `"\uE000"`)
128
- - `symbols`: Include symbol transforms (default: `true`)
129
- - `fractions`: Convert common fractions like 1/2 → ½ (default: `false`)
130
- - `degrees`: Convert temperature notation like 20 C → 20 °C (default: `false`)
131
- - `punctuationStyle`: `"american"` (default) puts periods/commas inside quotes; `"british"` puts them outside; `"none"` leaves unchanged
132
- - `dashStyle`: `"american"` (default) uses unspaced em dash (—); `"british"` uses spaced en dash ( – ); `"none"` skips dash conversion
20
+ ## Why punctilio?
133
21
 
134
- ### Quote Functions
22
+ As far as I can tell, `punctilio` is the most reliable and feature-complete. I built `punctilio` for [my website](https://turntrout.com/design). I wrote[^wrote] and sharpened the core regexes sporadically over several months, exhaustively testing edge cases. Eventually, I decided to spin off the functionality into its own package.
135
23
 
136
- #### `niceQuotes(text, options?)`
24
+ [^wrote]: While Claude is the number one contributor to this repository, that’s just because Claude has helped me port my existing code and add minor features. The core regular expressions (e.g. dashes, quotes, multiplication signs) are human-written.
137
25
 
138
- Converts straight quotes to curly quotes. Options: `separator`, `punctuationStyle`.
26
+ I tested `punctilio` 0.4 against [`smartypants`](https://www.npmjs.com/package/smartypants) 0.2.2, [`tipograph`](https://www.npmjs.com/package/tipograph) 0.7.4, and [`smartquotes`](https://www.npmjs.com/package/smartquotes) 2.3.2.[^python] These other packages have spotty feature coverage and inconsistent impact on text. For example, `smartypants` ignores leading apostrophes:
139
27
 
140
- Handles: opening/closing quotes, contractions (`don't`), possessives (`dog's`), year abbreviations (`'99`), special cases (`'n'`).
28
+ [^python]: The Python libraries I found were closely related to the JavaScript packages, so I don’t include Python tests.
141
29
 
142
- ### Dash Functions
30
+ | Input | `smartypants` | `punctilio` |
31
+ |:-----:|:-----------------:|:-------:|
32
+ | 'Twas the night | ‘Twas the night ✗ | ’Twas the night ✓ |
33
+ | the '99 season | the ‘99 season ✗ | the ’99 season ✓ |
34
+ | rock 'n' roll | rock ‘n’ roll ✗ | rock ’n’ roll ✓ |
143
35
 
144
- #### `hyphenReplace(text, options?)`
36
+ By running [`benchmark.mjs`](./benchmark.mjs), I basically graded all libraries on a subset of [my unit tests](./src/tests/), selected to represent a wide range of features.
145
37
 
146
- Converts hyphens to proper dashes. Options: `separator`, `dashStyle`.
38
+ | Package | Score |
39
+ |--------:|:------|
40
+ | `punctilio` | 79/82 (96%) |
41
+ | `tipograph` | 48/82 (59%) |
42
+ | `smartquotes` | 30/82 (37%) |
43
+ | `smartypants` | 28/82 (35%) |
147
44
 
148
- Handles: em dashes (`word - word``word—word`), en dashes for ranges (`1-5` `1–5`, `Jan-Mar` `Jan–Mar`), minus signs (`-5` → `−5`). Preserves horizontal rules and compound words.
45
+ | Feature | Example | `smartypants` | `tipograph` | `smartquotes` | `punctilio` |
46
+ |--------:|:-------:|:-------:|:-------:|:-------:|:-------:|
47
+ | Smart quotes | "hello" → “hello” | ✓ | ✓ | ✓ | ✓ |
48
+ | Leading apostrophe | 'Twas → ’Twas | ✗ | ✗ | ✓ | ✓ |
49
+ | Em dash | -- → — | ✓ | ✗ | ✗ | ✓ |
50
+ | En dash (ranges) | 1-5 → 1–5 | ✗ | ✓ | ✗ | ✓ |
51
+ | Minus sign | -5 → −5 | ✗ | ✓ | ✗ | ✓ |
52
+ | Ellipsis | ... → … | ✓ | ✓ | ✗ | ✓ |
53
+ | Multiplication | 5x5 → 5×5 | ✗ | ✗ | ✗ | ✓ |
54
+ | Math symbols | != → ≠ | ✗ | ✓ | ✗ | ✓ |
55
+ | Legal symbols | (c) → © | ✗ | © only | ✗ | ✓ |
56
+ | Arrows | -> → → | ✗ | ✓ | ✗ | ✓ |
57
+ | Prime marks | 5'10" → 5′10″ | ✗ | ✓ | ✓ | ✓ |
58
+ | Degrees | 20 C → 20 °C | ✗ | ✗ | ✗ | ✓ |
59
+ | Fractions | 1/2 → ½ | ✗ | ✗ | ✗ | ✓ |
60
+ | Superscripts | 1st → 1ˢᵗ | ✗ | ✗ | ✗ | ✓ |
61
+ | Localization | American/British | ✗ | ✗ | ✗ | ✓ |
62
+ | Ligatures | ?? → ⁇ | ✗ | ✓ | ✗ | ✓ |
63
+ | Non-English quotes | „Hallo" (German) | ✗ | ✓ | ✗ | ✗ |
149
64
 
150
- #### `enDashNumberRange(text, options?)`
65
+ As far as I can tell, `punctilio`’s only missing feature is non-English quote support. I don’t have a personal reason to use non-English localization, but feel free to make a pull request!
151
66
 
152
- Converts number ranges only: `pages 10-20` `pages 10–20`
67
+ ## Works with HTML DOMs via separation boundaries
153
68
 
154
- #### `enDashDateRange(text, options?)`
69
+ Other typography libraries either transform plain strings or operate on AST nodes individually (`retext-smartypants` [can't map changes back to HTML](https://github.com/rehypejs/rehype-retext)). But real HTML has text spanning multiple elements—if you concatenate text from `<em>Wait</em>...`, transform it, then try to split it back, you've lost track of where `</em>` belonged.
155
70
 
156
- Converts month ranges only: `January-March` `January–March`
71
+ `punctilio` introduces _separation boundaries_. First, insert a “separator” character (default: `U+E000`) at each element boundary before transforming (like at the start and end of an `<em>`). Every regex allows this character mid-pattern without breaking matches. For example, `.[SEP]..` still becomes `…[SEP]`. `punctilio` validates the output by ensuring the separator count remains the same.
157
72
 
158
- #### `minusReplace(text, options?)`
159
-
160
- Converts hyphens to minus signs in numerical contexts: `-5` → `−5`
161
-
162
- ### Symbol Functions
163
-
164
- #### `ellipsis(text, options?)`
165
-
166
- Converts three periods to ellipsis: `...` → `…`
167
-
168
- #### `multiplication(text, options?)`
169
-
170
- Converts multiplication patterns: `5x5` → `5×5`, `3*4` → `3×4`
171
-
172
- #### `mathSymbols(text)`
173
-
174
- Converts math operators:
175
- - `!=` → `≠`
176
- - `+-` or `+/-` → `±`
177
- - `<=` → `≤`
178
- - `>=` → `≥`
179
- - `~=` or `=~` → `≈`
180
-
181
- #### `legalSymbols(text)`
182
-
183
- Converts legal symbols:
184
- - `(c)` → `©`
185
- - `(r)` → `®`
186
- - `(tm)` → `™`
187
-
188
- #### `arrows(text, options?)`
189
-
190
- Converts arrow patterns:
191
- - `->` or `-->` → `→`
192
- - `<-` or `<--` → `←`
193
- - `<->` or `<-->` → `↔`
194
-
195
- #### `primeMarks(text, options?)`
196
-
197
- Converts straight quotes after numbers to prime marks:
198
- - `5'10"` → `5′10″` (feet and inches)
199
- - `45° 30' 15"` → `45° 30′ 15″` (coordinates)
200
-
201
- #### `fractions(text)`
202
-
203
- Converts common fractions: `1/2` → `½`, `1/4` → `¼`, `3/4` → `¾`, etc.
204
-
205
- #### `degrees(text)`
206
-
207
- Converts temperature notation: `20 C` → `20 °C`, `68 F` → `68 °F`
208
-
209
- #### `symbolTransform(text, options?)`
210
-
211
- Applies all symbol transforms except fractions and degrees.
212
-
213
- ### Constants
73
+ ```typescript
74
+ import { transform, DEFAULT_SEPARATOR } from 'punctilio'
214
75
 
215
- - `DEFAULT_SEPARATOR`: The default separator character (`"\uE000"`)
216
- - `months`: Regex-ready string of month names for date range detection
76
+ transform(`"Wait${DEFAULT_SEPARATOR}"`)
77
+ // → `“Wait”${DEFAULT_SEPARATOR}`
78
+ // The separator doesn't block the information that this should be an end-quote!
79
+ ```
217
80
 
218
- ## Character Reference
81
+ Your DOM walker tracks which text node each segment came from, inserts separators between them, transforms the combined string, then splits on separators to update each node. Use the `separator` option if `U+E000` conflicts with your content. For an example of how to integrate this functionality, see [my website’s code](https://github.com/alexander-turner/TurnTrout.com/blob/main/quartz/plugins/transformers/formatting_improvement_html.ts).
219
82
 
220
- | Input | Output | Unicode | Name |
221
- |-------|--------|---------|------|
222
- | `"` | `"` | U+201C | Left double quotation mark |
223
- | `"` | `"` | U+201D | Right double quotation mark |
224
- | `'` | `'` | U+2018 | Left single quotation mark |
225
- | `'` | `'` | U+2019 | Right single quotation mark (apostrophe) |
226
- | `--` | `—` | U+2014 | Em dash |
227
- | `-` (range) | `–` | U+2013 | En dash |
228
- | `-` (negative) | `−` | U+2212 | Minus sign |
229
- | `...` | `…` | U+2026 | Ellipsis |
230
- | `x` (multiply) | `×` | U+00D7 | Multiplication sign |
231
- | `!=` | `≠` | U+2260 | Not equal |
232
- | `+-` | `±` | U+00B1 | Plus-minus |
233
- | `<=` | `≤` | U+2264 | Less than or equal |
234
- | `>=` | `≥` | U+2265 | Greater than or equal |
235
- | `(c)` | `©` | U+00A9 | Copyright |
236
- | `(r)` | `®` | U+00AE | Registered |
237
- | `(tm)` | `™` | U+2122 | Trademark |
238
- | `->` | `→` | U+2192 | Right arrow |
239
- | `<-` | `←` | U+2190 | Left arrow |
240
- | `<->` | `↔` | U+2194 | Left-right arrow |
241
- | `'` (after digit) | `′` | U+2032 | Prime (feet, arcminutes) |
242
- | `"` (after digit) | `″` | U+2033 | Double prime (inches, arcseconds) |
83
+ ## Options
243
84
 
244
- ## License
85
+ `punctilio` doesn’t enable all transformations by default. Fractions and degrees tend to match too aggressively (perfectly applying the degree transformation requires semantic meaning). Superscript letters and punctuation ligatures have spotty font support—this README’s font doesn’t even support the example superscript! Furthermore, `ligatures = true` can change the meaning of text by collapsing question and exclamation marks.
245
86
 
246
- MIT © Alexander Turner
87
+ ```typescript
88
+ transform(text, {
89
+ punctuationStyle: 'american' | 'british' | 'none', // default: 'american'
90
+ dashStyle: 'american' | 'british' | 'none', // default: 'american'
91
+
92
+ symbols: true, // math, legal, arrows
93
+ collapseSpaces: true, // normalize whitespace
94
+ fractions: false, // 1/2 → ½
95
+ degrees: false, // 20 C → 20 °C
96
+ superscript: false, // 1st → 1ˢᵗ
97
+ ligatures: false, // ??? → ⁇, ?! → ⁈, !? → ⁉, !!! → !
98
+ })
99
+ ```
@@ -49,6 +49,15 @@ export declare const UNICODE_SYMBOLS: {
49
49
  readonly RIGHT_DOUBLE_QUOTE: "”";
50
50
  readonly LEFT_SINGLE_QUOTE: "‘";
51
51
  readonly RIGHT_SINGLE_QUOTE: "’";
52
+ readonly NBSP: " ";
53
+ readonly SUPERSCRIPT_ST: "ˢᵗ";
54
+ readonly SUPERSCRIPT_ND: "ⁿᵈ";
55
+ readonly SUPERSCRIPT_RD: "ʳᵈ";
56
+ readonly SUPERSCRIPT_TH: "ᵗʰ";
57
+ readonly DOUBLE_QUESTION: "⁇";
58
+ readonly QUESTION_EXCLAMATION: "⁈";
59
+ readonly EXCLAMATION_QUESTION: "⁉";
60
+ readonly DOUBLE_EXCLAMATION: "‼";
52
61
  };
53
62
  /**
54
63
  * Default separator character for text spanning HTML elements.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuClB,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAA;AACzC,eAAO,MAAM,yBAAyB,QAA2D,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDlB,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAA;AACzC,eAAO,MAAM,yBAAyB,QAA2D,CAAA"}
package/dist/constants.js CHANGED
@@ -49,6 +49,17 @@ export const UNICODE_SYMBOLS = {
49
49
  RIGHT_DOUBLE_QUOTE: "\u201D",
50
50
  LEFT_SINGLE_QUOTE: "\u2018",
51
51
  RIGHT_SINGLE_QUOTE: "\u2019",
52
+ NBSP: "\u00A0",
53
+ // Superscript ordinal suffixes
54
+ SUPERSCRIPT_ST: "\u02E2\u1D57", // ˢᵗ
55
+ SUPERSCRIPT_ND: "\u207F\u1D48", // ⁿᵈ
56
+ SUPERSCRIPT_RD: "\u02B3\u1D48", // ʳᵈ
57
+ SUPERSCRIPT_TH: "\u1D57\u02B0", // ᵗʰ
58
+ // Punctuation ligatures
59
+ DOUBLE_QUESTION: "\u2047", // ⁇
60
+ QUESTION_EXCLAMATION: "\u2048", // ⁈
61
+ EXCLAMATION_QUESTION: "\u2049", // ⁉
62
+ DOUBLE_EXCLAMATION: "\u203C", // ‼
52
63
  };
53
64
  /**
54
65
  * Default separator character for text spanning HTML elements.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,QAAQ;IAClB,cAAc,EAAE,QAAQ;IACxB,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,gBAAgB,EAAE,QAAQ;IAC1B,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,aAAa,EAAE,QAAQ;IACvB,KAAK,EAAE,QAAQ;IACf,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,QAAQ;IACf,iBAAiB,EAAE,QAAQ;IAC3B,kBAAkB,EAAE,QAAQ;IAC5B,iBAAiB,EAAE,QAAQ;IAC3B,kBAAkB,EAAE,QAAQ;CACpB,CAAA;AAEV;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAA;AACzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,QAAQ;IAClB,cAAc,EAAE,QAAQ;IACxB,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,QAAQ;IACnB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,gBAAgB,EAAE,QAAQ;IAC1B,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,aAAa,EAAE,QAAQ;IACvB,KAAK,EAAE,QAAQ;IACf,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,QAAQ;IACtB,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,QAAQ;IACf,iBAAiB,EAAE,QAAQ;IAC3B,kBAAkB,EAAE,QAAQ;IAC5B,iBAAiB,EAAE,QAAQ;IAC3B,kBAAkB,EAAE,QAAQ;IAC5B,IAAI,EAAE,QAAQ;IACd,+BAA+B;IAC/B,cAAc,EAAE,cAAc,EAAE,KAAK;IACrC,cAAc,EAAE,cAAc,EAAE,KAAK;IACrC,cAAc,EAAE,cAAc,EAAE,KAAK;IACrC,cAAc,EAAE,cAAc,EAAE,KAAK;IACrC,wBAAwB;IACxB,eAAe,EAAE,QAAQ,EAAE,IAAI;IAC/B,oBAAoB,EAAE,QAAQ,EAAE,IAAI;IACpC,oBAAoB,EAAE,QAAQ,EAAE,IAAI;IACpC,kBAAkB,EAAE,QAAQ,EAAE,IAAI;CAC1B,CAAA;AAEV;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAA;AACzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA"}
package/dist/dashes.d.ts CHANGED
@@ -36,6 +36,13 @@ export declare const months: string;
36
36
  export declare function enDashNumberRange(text: string, options?: DashOptions): string;
37
37
  /**
38
38
  * Replaces hyphens with en-dashes in month/date ranges.
39
+ * Supports formats like "January-March", "Jan-Mar", "February-April 2024",
40
+ * and "October 2012 - December 2014".
41
+ *
42
+ * Spacing around the en-dash is controlled by dashStyle:
43
+ * - "american" (default): No spaces (October 2012–December 2014)
44
+ * - "british": Spaced (October 2012 – December 2014)
45
+ * - "none": Preserve original spacing
39
46
  */
40
47
  export declare function enDashDateRange(text: string, options?: DashOptions): string;
41
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dashes.d.ts","sourceRoot":"","sources":["../src/dashes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAA;AAEvD,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAID;;GAEG;AACH,eAAO,MAAM,MAAM,QAKR,CAAA;AAEX;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CASjF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAM/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAI5E;AAuDD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAe7E"}
1
+ {"version":3,"file":"dashes.d.ts","sourceRoot":"","sources":["../src/dashes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAA;AAEvD,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAID;;GAEG;AACH,eAAO,MAAM,MAAM,QAKR,CAAA;AAEX;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CASjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CA8B/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAI5E;AAuDD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAe7E"}
package/dist/dashes.js CHANGED
@@ -20,22 +20,51 @@ export const months = [
20
20
  */
21
21
  export function enDashNumberRange(text, options = {}) {
22
22
  const chr = options.separator ?? DEFAULT_SEPARATOR;
23
- return text.replace(new RegExp(`\\b(?<![a-zA-Z.])((?:p\\.?|\\$)?\\d[\\d.,]*${chr}?)-(${chr}?\\$?\\d[\\d.,]*)(?!\\.\\d)\\b`, "g"), `$1${EN_DASH}$2`);
23
+ return text.replace(new RegExp(`\\b(?<![a-zA-Z.])(?<startNum>(?:p\\.?|\\$)?\\d[\\d.,]*${chr}?)-(?<endNum>${chr}?\\$?\\d[\\d.,]*)(?!\\.\\d)\\b`, "g"), `$<startNum>${EN_DASH}$<endNum>`);
24
24
  }
25
25
  /**
26
26
  * Replaces hyphens with en-dashes in month/date ranges.
27
+ * Supports formats like "January-March", "Jan-Mar", "February-April 2024",
28
+ * and "October 2012 - December 2014".
29
+ *
30
+ * Spacing around the en-dash is controlled by dashStyle:
31
+ * - "american" (default): No spaces (October 2012–December 2014)
32
+ * - "british": Spaced (October 2012 – December 2014)
33
+ * - "none": Preserve original spacing
27
34
  */
28
35
  export function enDashDateRange(text, options = {}) {
29
36
  const chr = options.separator ?? DEFAULT_SEPARATOR;
30
- return text.replace(new RegExp(`\\b(${months}${chr}?)-(${chr}?(?:${months}))\\b`, "g"), `$1${EN_DASH}$2`);
37
+ const dashStyle = options.dashStyle ?? "american";
38
+ const startPattern = `(?<startMonth>${months})(?<startYear>${chr}? \\d{4})?(?<preSep>${chr}?)`;
39
+ const endPattern = `(?<postSep>${chr}?)(?<endMonth>${months})(?<endYear> \\d{4})?`;
40
+ const dateRangeRegex = new RegExp(`\\b${startPattern}(?<preSpace> ?)-(?<postSpace> ?)${endPattern}\\b`, "g");
41
+ return text.replace(dateRangeRegex, (...args) => {
42
+ const groups = args.at(-1);
43
+ const { startMonth, startYear = "", preSep, postSep, endMonth, endYear = "", preSpace, postSpace } = groups;
44
+ let pre, post;
45
+ if (dashStyle === "british") {
46
+ pre = " ";
47
+ post = " ";
48
+ }
49
+ else if (dashStyle === "none") {
50
+ pre = preSpace;
51
+ post = postSpace;
52
+ }
53
+ else {
54
+ // american (default)
55
+ pre = "";
56
+ post = "";
57
+ }
58
+ return `${startMonth}${startYear}${preSep}${pre}${EN_DASH}${post}${postSep}${endMonth}${endYear}`;
59
+ });
31
60
  }
32
61
  /**
33
62
  * Replaces hyphens with proper minus signs (−) in numerical contexts.
34
63
  */
35
64
  export function minusReplace(text, options = {}) {
36
65
  const chr = options.separator ?? DEFAULT_SEPARATOR;
37
- const minusRegex = new RegExp(`(^|[\\s\\(${chr}""])-(\\s?\\d*\\.?\\d+)`, "gm");
38
- return text.replaceAll(minusRegex, `$1${MINUS}$2`);
66
+ const minusRegex = new RegExp(`(?<beforeMinus>^|[\\s\\(${chr}""])-(?<number>\\s?\\d*\\.?\\d+)`, "gm");
67
+ return text.replaceAll(minusRegex, `$<beforeMinus>${MINUS}$<number>`);
39
68
  }
40
69
  /** Convert surrounded dashes and multiple dashes to em/en dashes */
41
70
  function convertParentheticalDashes(text, sep, style) {
@@ -44,8 +73,8 @@ function convertParentheticalDashes(text, sep, style) {
44
73
  const dash = style === "british" ? EN_DASH : EM_DASH;
45
74
  const spaced = style === "british";
46
75
  // Handle dashes with potential spaces
47
- const preDash = new RegExp(`((?<markerBeforeTwo>${sep}?)[ ]+|(?<markerBeforeThree>${sep}))`);
48
- const surroundedDash = new RegExp(`(?<=[^\\s>]|^)${preDash.source}[~${EN_DASH}${EM_DASH}-]+[ ]*(?<markerAfter>${sep}?)([ ]+|$)`, "g");
76
+ const preDash = new RegExp(`(?:(?<markerBeforeTwo>${sep}?)[ ]+|(?<markerBeforeThree>${sep}))`);
77
+ const surroundedDash = new RegExp(`(?<=[^\\s>]|^)${preDash.source}[~${EN_DASH}${EM_DASH}-]+[ ]*(?<markerAfter>${sep}?)(?<trailingSpace>[ ]+|$)`, "g");
49
78
  const replacement = spaced
50
79
  ? `$<markerBeforeTwo>$<markerBeforeThree> ${dash} $<markerAfter>`
51
80
  : `$<markerBeforeTwo>$<markerBeforeThree>${dash}$<markerAfter>`;
@@ -57,7 +86,7 @@ function convertParentheticalDashes(text, sep, style) {
57
86
  : `$<markerBefore>${dash}$<markerAfter>`;
58
87
  text = text.replace(multipleDashInWords, multiReplacement);
59
88
  // Handle dashes at start of line
60
- text = text.replace(new RegExp(`^(${sep})?[-]+ `, "gm"), `$1${dash} `);
89
+ text = text.replace(new RegExp(`^(?<sepStart>${sep})?[-]+ `, "gm"), `$<sepStart>${dash} `);
61
90
  return text;
62
91
  }
63
92
  /** Normalize spacing around em dashes for American style */
@@ -1 +1 @@
1
- {"version":3,"file":"dashes.js","sourceRoot":"","sources":["../src/dashes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dashes.js","sourceRoot":"","sources":["../src/dashes.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { niceQuotes, type QuoteOptions, type PunctuationStyle } from "./quotes.j
11
11
  import type { PunctuationStyle } from "./quotes.js";
12
12
  export { hyphenReplace, enDashNumberRange, enDashDateRange, minusReplace, months, type DashOptions, type DashStyle, } from "./dashes.js";
13
13
  import type { DashStyle } from "./dashes.js";
14
- export { ellipsis, multiplication, mathSymbols, legalSymbols, arrows, degrees, fractions, primeMarks, symbolTransform, type SymbolOptions, } from "./symbols.js";
14
+ export { ellipsis, multiplication, mathSymbols, legalSymbols, arrows, degrees, fractions, primeMarks, collapseSpaces, superscript, punctuationLigatures, symbolTransform, type SymbolOptions, } from "./symbols.js";
15
15
  export interface TransformOptions {
16
16
  /**
17
17
  * A boundary marker character used when transforming text that spans
@@ -28,15 +28,15 @@ export interface TransformOptions {
28
28
  */
29
29
  symbols?: boolean;
30
30
  /**
31
- * Whether to include fraction transforms (1/2 → ½)
32
- * Default: false (can be aggressive)
33
- */
34
- fractions?: boolean;
35
- /**
36
- * Whether to include degree symbol transforms (20 C → 20 °C)
37
- * Default: false (can be aggressive)
31
+ * Whether to collapse multiple consecutive spaces (including non-breaking
32
+ * spaces) into a single space. Keeps the first space in the sequence.
33
+ *
34
+ * - `true` (default): "hello world" → "hello world"
35
+ * - `false`: Preserve multiple spaces
36
+ *
37
+ * Default: true
38
38
  */
39
- degrees?: boolean;
39
+ collapseSpaces?: boolean;
40
40
  /**
41
41
  * How to handle punctuation placement around quotation marks.
42
42
  *
@@ -59,7 +59,31 @@ export interface TransformOptions {
59
59
  * Default: "american"
60
60
  */
61
61
  dashStyle?: DashStyle;
62
+ /**
63
+ * Whether to include fraction transforms (1/2 → ½)
64
+ * Default: false (can be aggressive)
65
+ */
66
+ fractions?: boolean;
67
+ /**
68
+ * Whether to include degree symbol transforms (20 C → 20 °C)
69
+ * Default: false (can be aggressive)
70
+ */
71
+ degrees?: boolean;
72
+ /**
73
+ * Whether to convert ordinal suffixes to Unicode superscript characters.
74
+ * Transforms numbers like "1st", "2nd", "3rd", "4th" to "1ˢᵗ", "2ⁿᵈ", "3ʳᵈ", "4ᵗʰ".
75
+ * Default: false
76
+ */
77
+ superscript?: boolean;
78
+ /**
79
+ * Whether to convert repeated punctuation marks to Unicode ligature characters.
80
+ * Squashes multiple marks: "???" → "⁇", "?!" → "⁈", "!?" → "⁉", "!!!" → "!"
81
+ * Default: false (poor font support)
82
+ */
83
+ ligatures?: boolean;
62
84
  }
85
+ export { assertSeparatorCountPreserved, countSeparators } from "./utils.js";
86
+ export { DEFAULT_SEPARATOR } from "./constants.js";
63
87
  /**
64
88
  * Applies all typography transformations: smart quotes, proper dashes,
65
89
  * and symbol improvements.
@@ -69,8 +93,11 @@ export interface TransformOptions {
69
93
  * 2. primeMarks (feet/inches, arcminutes/arcseconds)
70
94
  * 3. niceQuotes (smart quotes)
71
95
  * 4. symbolTransform (ellipses, multiplication, math symbols, legal symbols, arrows)
72
- * 5. fractions (optional, disabled by default)
73
- * 6. degrees (optional, disabled by default)
96
+ * 5. fractions (disabled by default)
97
+ * 6. degrees (disabled by default)
98
+ * 7. superscript (disabled by default)
99
+ * 8. ligatures (disabled by default)
100
+ * 9. collapseSpaces (collapses multiple spaces into one)
74
101
  *
75
102
  * @param text - The text to transform
76
103
  * @param options - Configuration options
@@ -91,9 +118,4 @@ export interface TransformOptions {
91
118
  * ```
92
119
  */
93
120
  export declare function transform(text: string, options?: TransformOptions): string;
94
- /**
95
- * Default separator character for boundary marking.
96
- * Uses Unicode Private Use Area character U+E000.
97
- */
98
- export declare const DEFAULT_SEPARATOR = "\uE000";
99
121
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,SAAS,GACf,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,EACZ,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAoB9E;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,SAAS,GACf,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,EACZ,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IAExB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAQD,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAoC9E"}