punctilio 2.1.1 → 2.2.0
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 +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/dashes.js +1 -1
- package/dist/dashes.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/quotes.d.ts +11 -0
- package/dist/quotes.d.ts.map +1 -1
- package/dist/quotes.js +27 -9
- package/dist/quotes.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/dashes.ts +1 -1
- package/src/index.ts +29 -4
- package/src/quotes.ts +42 -8
- package/src/tests/dashes.test.ts +13 -0
- package/src/tests/index.test.ts +1 -0
- package/src/tests/quotes.test.ts +245 -41
- package/src/tests/remark.test.ts +1 -1
package/README.md
CHANGED
|
@@ -149,3 +149,4 @@ transform(text, {
|
|
|
149
149
|
- Spaced en-dashes between words (word – word)
|
|
150
150
|
- Setting either style to `none` skips the entire transform category: `punctuationStyle: 'none'` preserves straight quotes, apostrophes, and prime marks; `dashStyle: 'none'` preserves all hyphens, number ranges, date ranges, and minus signs.
|
|
151
151
|
- `punctilio` is idempotent by design: `transform(transform(text))` always equals `transform(text)`. If performance is critical, set `checkIdempotency: false` to skip the verification pass.
|
|
152
|
+
- When `useModifierLetterApostrophe` is enabled, apostrophes use Modifier Letter Apostrophe (U+02BC) while closing single quotes use Right Single Quotation Mark (U+2019), producing semantically distinct codepoints in most cases. Contractions (don't), possessives (dog's), and leading abbreviations ('twas, '99) all output U+02BC. Bare trailing possessives like `dogs'` remain ambiguous. This option is off by default since U+02BC may not be matched by downstream regex patterns that expect standard quote characters.
|
package/dist/constants.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ 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 MODIFIER_LETTER_APOSTROPHE: "ʼ";
|
|
52
53
|
readonly NBSP: " ";
|
|
53
54
|
readonly SUPERSCRIPT_ST: "ˢᵗ";
|
|
54
55
|
readonly SUPERSCRIPT_ND: "ⁿᵈ";
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDlB,CAAA;AAEV;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,sFAAsF,CAAA;AAEhH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAA;AACzC,eAAO,MAAM,yBAAyB,QAAwC,CAAA;AAE9E;;;GAGG;AACH,eAAO,MAAM,mBAAmB,wEAAyE,CAAA;AAEzG;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,QAA6B,CAAA;AAErD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAEjE"}
|
package/dist/constants.js
CHANGED
|
@@ -50,6 +50,7 @@ export const UNICODE_SYMBOLS = {
|
|
|
50
50
|
RIGHT_DOUBLE_QUOTE: "\u201D",
|
|
51
51
|
LEFT_SINGLE_QUOTE: "\u2018",
|
|
52
52
|
RIGHT_SINGLE_QUOTE: "\u2019",
|
|
53
|
+
MODIFIER_LETTER_APOSTROPHE: "\u02BC",
|
|
53
54
|
NBSP: "\u00A0",
|
|
54
55
|
// Superscript ordinal suffixes
|
|
55
56
|
SUPERSCRIPT_ST: "\u02E2\u1D57", // ˢᵗ
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
package/dist/dashes.js
CHANGED
|
@@ -148,11 +148,11 @@ export function hyphenReplace(text, options = {}) {
|
|
|
148
148
|
if (style === "none")
|
|
149
149
|
return text;
|
|
150
150
|
text = minusReplace(text, options);
|
|
151
|
+
text = enDashDateRange(text, options);
|
|
151
152
|
text = convertParentheticalDashes(text, sep, style);
|
|
152
153
|
if (style === "american")
|
|
153
154
|
text = normalizeEmDashSpacing(text, sep);
|
|
154
155
|
text = enDashNumberRange(text, options);
|
|
155
|
-
text = enDashDateRange(text, options);
|
|
156
156
|
return text;
|
|
157
157
|
}
|
|
158
158
|
//# sourceMappingURL=dashes.js.map
|
package/dist/dashes.js.map
CHANGED
|
@@ -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
|
@@ -105,8 +105,20 @@ export interface TransformOptions {
|
|
|
105
105
|
* Default: true
|
|
106
106
|
*/
|
|
107
107
|
checkIdempotency?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Use modifier letter apostrophe (U+02BC) instead of right single quote
|
|
110
|
+
* (U+2019) for contractions and possessives. This distinguishes apostrophes
|
|
111
|
+
* from closing quotes in most cases, though some ambiguous patterns (e.g.,
|
|
112
|
+
* `dogs'` vs a closing single quote) cannot be resolved without semantic
|
|
113
|
+
* understanding. May also break downstream regex patterns that expect
|
|
114
|
+
* standard quote characters like /don\u2019t/ or /O\u2019Brien/.
|
|
115
|
+
*
|
|
116
|
+
* Default: false
|
|
117
|
+
*/
|
|
118
|
+
useModifierLetterApostrophe?: boolean;
|
|
108
119
|
}
|
|
109
120
|
export { assertSeparatorCountPreserved, countSeparators } from "./utils.js";
|
|
110
|
-
export { DEFAULT_SEPARATOR } from "./constants.js";
|
|
121
|
+
export { DEFAULT_SEPARATOR, UNICODE_SYMBOLS } from "./constants.js";
|
|
122
|
+
export declare const MODIFIER_LETTER_APOSTROPHE: "ʼ";
|
|
111
123
|
export declare function transform(text: string, options?: TransformOptions): string;
|
|
112
124
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,6BAA6B,EAC7B,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,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,KAAK,EACL,UAAU,EACV,uBAAuB,EACvB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAElB,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;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC;;;;;;;;;;;;;OAaG;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;IAEnB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,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,6BAA6B,EAC7B,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,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,KAAK,EACL,UAAU,EACV,uBAAuB,EACvB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAElB,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;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC;;;;;;;;;;;;;OAaG;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;IAEnB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAE1B;;;;;;;;;OASG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;AASD,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACnE,eAAO,MAAM,0BAA0B,KAA6C,CAAA;AAkDpF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CA8E9E"}
|
package/dist/index.js
CHANGED
|
@@ -16,9 +16,10 @@ import { hyphenReplace } from "./dashes.js";
|
|
|
16
16
|
import { symbolTransform, fractions as fractionsTransform, degrees as degreesTransform, superscriptOrdinal as superscriptTransform, primeMarks, collapseSpaces as collapseSpacesTransform, punctuationLigatures as ligaturesTransform } from "./symbols.js";
|
|
17
17
|
import { nbspTransform as nbspTransformFn } from "./nbsp.js";
|
|
18
18
|
import { assertSeparatorCountPreserved, formatErrorString } from "./utils.js";
|
|
19
|
-
import { DEFAULT_SEPARATOR } from "./constants.js";
|
|
19
|
+
import { DEFAULT_SEPARATOR, UNICODE_SYMBOLS } from "./constants.js";
|
|
20
20
|
export { assertSeparatorCountPreserved, countSeparators } from "./utils.js";
|
|
21
|
-
export { DEFAULT_SEPARATOR } from "./constants.js";
|
|
21
|
+
export { DEFAULT_SEPARATOR, UNICODE_SYMBOLS } from "./constants.js";
|
|
22
|
+
export const MODIFIER_LETTER_APOSTROPHE = UNICODE_SYMBOLS.MODIFIER_LETTER_APOSTROPHE;
|
|
22
23
|
/**
|
|
23
24
|
* Applies all typography transformations: smart quotes, proper dashes,
|
|
24
25
|
* and symbol improvements.
|
|
@@ -64,6 +65,7 @@ const defaultOpts = {
|
|
|
64
65
|
checkIdempotency: true,
|
|
65
66
|
punctuationStyle: "american",
|
|
66
67
|
dashStyle: "american",
|
|
68
|
+
useModifierLetterApostrophe: false,
|
|
67
69
|
};
|
|
68
70
|
export function transform(text, options = {}) {
|
|
69
71
|
const separator = options.separator ?? DEFAULT_SEPARATOR;
|
|
@@ -75,12 +77,17 @@ export function transform(text, options = {}) {
|
|
|
75
77
|
`Use a single-codepoint character like the default "\\uE000".`);
|
|
76
78
|
}
|
|
77
79
|
const original = text;
|
|
78
|
-
const { symbols, fractions, degrees, superscript, ligatures, nbsp, collapseSpaces, checkIdempotency, ...separatorOpts } = { ...defaultOpts, ...options };
|
|
80
|
+
const { symbols, fractions, degrees, superscript, ligatures, nbsp, collapseSpaces, checkIdempotency, useModifierLetterApostrophe, ...separatorOpts } = { ...defaultOpts, ...options };
|
|
79
81
|
text = hyphenReplace(text, separatorOpts);
|
|
80
82
|
if (separatorOpts.punctuationStyle !== "none") {
|
|
81
83
|
text = primeMarks(text, separatorOpts);
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
// niceQuotes must output MLA here regardless of the user's option,
|
|
86
|
+
// because NBSP's PUNCTUATION_OR_QUOTE class includes RSQ but not MLA.
|
|
87
|
+
// Without this, apostrophes in "don't" would act as word boundaries
|
|
88
|
+
// during NBSP insertion. The final MLA→RSQ conversion at line 286
|
|
89
|
+
// runs after all transforms complete.
|
|
90
|
+
text = niceQuotes(text, { ...separatorOpts, useModifierLetterApostrophe: true });
|
|
84
91
|
if (symbols) {
|
|
85
92
|
text = symbolTransform(text, separatorOpts);
|
|
86
93
|
}
|
|
@@ -103,6 +110,11 @@ export function transform(text, options = {}) {
|
|
|
103
110
|
text = nbspTransformFn(text, separatorOpts);
|
|
104
111
|
}
|
|
105
112
|
assertSeparatorCountPreserved(original, text, separator, "transform");
|
|
113
|
+
// Convert MLA→RSQ for regex-friendly output, after all transforms
|
|
114
|
+
// (especially NBSP which relies on MLA being letter-like).
|
|
115
|
+
if (!useModifierLetterApostrophe) {
|
|
116
|
+
text = text.replaceAll(UNICODE_SYMBOLS.MODIFIER_LETTER_APOSTROPHE, UNICODE_SYMBOLS.RIGHT_SINGLE_QUOTE);
|
|
117
|
+
}
|
|
106
118
|
if (checkIdempotency) {
|
|
107
119
|
const secondPass = transform(text, { ...options, checkIdempotency: false });
|
|
108
120
|
/* istanbul ignore if -- defensive check that should never trigger */
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAA4C,MAAM,aAAa,CAAA;AAElF,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,MAAM,EACN,6BAA6B,GAG9B,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,EACZ,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,GAEhB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,KAAK,EACL,UAAU,EACV,uBAAuB,GAExB,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/quotes.d.ts
CHANGED
|
@@ -7,6 +7,17 @@ export interface QuoteOptions {
|
|
|
7
7
|
separator?: string;
|
|
8
8
|
/** "american" (inside), "british" (outside), "none". Default: "american" */
|
|
9
9
|
punctuationStyle?: PunctuationStyle;
|
|
10
|
+
/**
|
|
11
|
+
* Use modifier letter apostrophe (U+02BC) instead of right single quote
|
|
12
|
+
* (U+2019) for contractions and possessives. This distinguishes apostrophes
|
|
13
|
+
* from closing quotes in most cases, though some ambiguous patterns (e.g.,
|
|
14
|
+
* `dogs'` vs a closing single quote) cannot be resolved without semantic
|
|
15
|
+
* understanding. May also break downstream regex patterns that expect
|
|
16
|
+
* standard quote characters like /don\u2019t/ or /O\u2019Brien/.
|
|
17
|
+
*
|
|
18
|
+
* Default: false
|
|
19
|
+
*/
|
|
20
|
+
useModifierLetterApostrophe?: boolean;
|
|
10
21
|
}
|
|
11
22
|
/** Convert straight quotes to smart quotes. */
|
|
12
23
|
export declare function niceQuotes(text: string, options?: QuoteOptions): string;
|
package/dist/quotes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../src/quotes.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../src/quotes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAA;AAE9D,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;;;;;OASG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;AAiHD,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAiB3E"}
|
package/dist/quotes.js
CHANGED
|
@@ -3,26 +3,38 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import escapeStringRegexp from "escape-string-regexp";
|
|
5
5
|
import { UNICODE_SYMBOLS, DEFAULT_SEPARATOR, LATIN_LETTERS } from "./constants.js";
|
|
6
|
-
const { EM_DASH, LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE, LEFT_SINGLE_QUOTE, RIGHT_SINGLE_QUOTE, ELLIPSIS, } = UNICODE_SYMBOLS;
|
|
6
|
+
const { EM_DASH, LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE, LEFT_SINGLE_QUOTE, RIGHT_SINGLE_QUOTE, MODIFIER_LETTER_APOSTROPHE, ELLIPSIS, } = UNICODE_SYMBOLS;
|
|
7
7
|
/** Convert straight single quotes to curly quotes and apostrophes */
|
|
8
8
|
function convertSingleQuotes(text, sep) {
|
|
9
9
|
const escapedSep = escapeStringRegexp(sep);
|
|
10
10
|
// Handle empty single quotes '' and whitespace-only quotes ' ' first
|
|
11
11
|
// Only match straight quotes, not already-converted curly quotes
|
|
12
|
-
const singleQuoteChars = `'${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}`;
|
|
12
|
+
const singleQuoteChars = `'${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}`;
|
|
13
13
|
text = text.replace(new RegExp(`(?<![${singleQuoteChars}])''(?![${singleQuoteChars}])`, "g"), `${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}`);
|
|
14
14
|
text = text.replace(new RegExp(`(?<![${singleQuoteChars}])'(\\s+)'(?![${singleQuoteChars}])`, "g"), `${LEFT_SINGLE_QUOTE}$1${RIGHT_SINGLE_QUOTE}`);
|
|
15
15
|
const afterEndingSinglePatterns = `\\s\\.!?;,\\)${EM_DASH}\\-\\]"`;
|
|
16
|
+
// Full pattern with optional 's' for lookahead detection in apostropheRegex
|
|
16
17
|
const afterEndingSingle = `(?=${escapedSep}?(?:s${escapedSep}?)?(?:[${afterEndingSinglePatterns}]|$))`;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
// Handle 'n' abbreviation (Rock 'n' Roll). Before MLA this wasn't needed —
|
|
19
|
+
// the general rules produced LSQ+n+RSQ which was fine. But MLA requires both
|
|
20
|
+
// quotes to be MLA (semantic apostrophes), and the general rules can't achieve
|
|
21
|
+
// that: neither quote is in a contraction context (no Latin letter on both sides).
|
|
22
|
+
text = text.replace(new RegExp(`(?<=\\w${escapedSep}? )[']n['](?= ${escapedSep}?\\w)`, "gm"), `${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE}`);
|
|
23
|
+
// Possessive: 's followed by ending context (e.g., dog's) → U+02BC
|
|
24
|
+
const afterPossessive = `(?=${escapedSep}?s${escapedSep}?(?:[${afterEndingSinglePatterns}]|$))`;
|
|
25
|
+
const possessiveSingle = `(?<=[^\\s${LEFT_DOUBLE_QUOTE}'])[']${afterPossessive}`;
|
|
26
|
+
text = text.replace(new RegExp(possessiveSingle, "gm"), MODIFIER_LETTER_APOSTROPHE);
|
|
27
|
+
// Closing single quote: ending context without 's' → U+2019
|
|
28
|
+
const afterClosingSingle = `(?=${escapedSep}?(?:[${afterEndingSinglePatterns}]|$))`;
|
|
29
|
+
const closingSingle = `(?<=[^\\s${LEFT_DOUBLE_QUOTE}'])[']${afterClosingSingle}`;
|
|
30
|
+
text = text.replace(new RegExp(closingSingle, "gm"), RIGHT_SINGLE_QUOTE);
|
|
31
|
+
const contraction = `(?<=[${LATIN_LETTERS}])['${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}](?=${escapedSep}?[${LATIN_LETTERS}])`;
|
|
32
|
+
text = text.replace(new RegExp(contraction, "gm"), MODIFIER_LETTER_APOSTROPHE);
|
|
33
|
+
const apostropheWhitelist = `(?=n${MODIFIER_LETTER_APOSTROPHE} )`;
|
|
34
|
+
const endQuoteNotContraction = `(?!${contraction})[${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}]${afterEndingSingle}`;
|
|
23
35
|
// Limit lookahead scan to 1000 chars to prevent catastrophic backtracking on pathological inputs
|
|
24
36
|
const apostropheRegex = new RegExp(`(?<=^|[^\\w])'(${apostropheWhitelist}|(?![^${LEFT_SINGLE_QUOTE}'\\n]{0,1000}${endQuoteNotContraction}))`, "gm");
|
|
25
|
-
text = text.replace(apostropheRegex,
|
|
37
|
+
text = text.replace(apostropheRegex, MODIFIER_LETTER_APOSTROPHE);
|
|
26
38
|
const beginningSingle = `(?<beforeContext>(?:^|[\\s${LEFT_DOUBLE_QUOTE}${RIGHT_DOUBLE_QUOTE}${EM_DASH}\\-\\(])${escapedSep}?)['](?=${escapedSep}?\\S)`;
|
|
27
39
|
text = text.replace(new RegExp(beginningSingle, "gm"), `$<beforeContext>${LEFT_SINGLE_QUOTE}`);
|
|
28
40
|
return text;
|
|
@@ -74,6 +86,12 @@ export function niceQuotes(text, options = {}) {
|
|
|
74
86
|
text = convertSingleQuotes(text, sep);
|
|
75
87
|
text = convertDoubleQuotes(text, sep);
|
|
76
88
|
text = applyPunctuationStyle(text, sep, punctuationStyle);
|
|
89
|
+
// MLA is used internally so applyPunctuationStyle can distinguish
|
|
90
|
+
// apostrophes (MLA, don't move) from closing quotes (RSQ, do move).
|
|
91
|
+
// Convert back to RSQ unless the caller opts into the distinction.
|
|
92
|
+
if (!options.useModifierLetterApostrophe) {
|
|
93
|
+
text = text.replaceAll(MODIFIER_LETTER_APOSTROPHE, RIGHT_SINGLE_QUOTE);
|
|
94
|
+
}
|
|
77
95
|
return text;
|
|
78
96
|
}
|
|
79
97
|
//# sourceMappingURL=quotes.js.map
|
package/dist/quotes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.js","sourceRoot":"","sources":["../src/quotes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,kBAAkB,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAElF,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,GACT,GAAG,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"quotes.js","sourceRoot":"","sources":["../src/quotes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,kBAAkB,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAElF,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,QAAQ,GACT,GAAG,eAAe,CAAA;AAsBnB,qEAAqE;AACrE,SAAS,mBAAmB,CAAC,IAAY,EAAE,GAAW;IACpD,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAE1C,qEAAqE;IACrE,iEAAiE;IACjE,MAAM,gBAAgB,GAAG,IAAI,iBAAiB,GAAG,kBAAkB,GAAG,0BAA0B,EAAE,CAAA;IAClG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,gBAAgB,WAAW,gBAAgB,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,iBAAiB,GAAG,kBAAkB,EAAE,CAAC,CAAA;IAC1I,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,gBAAgB,iBAAiB,gBAAgB,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,iBAAiB,KAAK,kBAAkB,EAAE,CAAC,CAAA;IAElJ,MAAM,yBAAyB,GAAG,gBAAgB,OAAO,SAAS,CAAA;IAClE,4EAA4E;IAC5E,MAAM,iBAAiB,GAAG,MAAM,UAAU,QAAQ,UAAU,UAAU,yBAAyB,OAAO,CAAA;IAEtG,2EAA2E;IAC3E,6EAA6E;IAC7E,+EAA+E;IAC/E,mFAAmF;IACnF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,UAAU,UAAU,iBAAiB,UAAU,OAAO,EAAE,IAAI,CAAC,EAAE,GAAG,0BAA0B,IAAI,0BAA0B,EAAE,CAAC,CAAA;IAE5J,mEAAmE;IACnE,MAAM,eAAe,GAAG,MAAM,UAAU,KAAK,UAAU,QAAQ,yBAAyB,OAAO,CAAA;IAC/F,MAAM,gBAAgB,GAAG,YAAY,iBAAiB,SAAS,eAAe,EAAE,CAAA;IAChF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE,0BAA0B,CAAC,CAAA;IAEnF,4DAA4D;IAC5D,MAAM,kBAAkB,GAAG,MAAM,UAAU,QAAQ,yBAAyB,OAAO,CAAA;IACnF,MAAM,aAAa,GAAG,YAAY,iBAAiB,SAAS,kBAAkB,EAAE,CAAA;IAChF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,kBAAkB,CAAC,CAAA;IAExE,MAAM,WAAW,GAAG,QAAQ,aAAa,OAAO,kBAAkB,GAAG,0BAA0B,OAAO,UAAU,KAAK,aAAa,IAAI,CAAA;IACtI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,0BAA0B,CAAC,CAAA;IAE9E,MAAM,mBAAmB,GAAG,OAAO,0BAA0B,IAAI,CAAA;IACjE,MAAM,sBAAsB,GAAG,MAAM,WAAW,KAAK,kBAAkB,GAAG,0BAA0B,IAAI,iBAAiB,EAAE,CAAA;IAC3H,iGAAiG;IACjG,MAAM,eAAe,GAAG,IAAI,MAAM,CAChC,kBAAkB,mBAAmB,SAAS,iBAAiB,gBAAgB,sBAAsB,IAAI,EACzG,IAAI,CACL,CAAA;IACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAA;IAEhE,MAAM,eAAe,GAAG,6BAA6B,iBAAiB,GAAG,kBAAkB,GAAG,OAAO,WAAW,UAAU,WAAW,UAAU,OAAO,CAAA;IACtJ,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,mBAAmB,iBAAiB,EAAE,CAAC,CAAA;IAE9F,OAAO,IAAI,CAAA;AACb,CAAC;AAED,qDAAqD;AACrD,SAAS,mBAAmB,CAAC,IAAY,EAAE,GAAW;IACpD,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAE1C,6EAA6E;IAC7E,oEAAoE;IACpE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,uCAAuC,EAAE,GAAG,iBAAiB,GAAG,kBAAkB,EAAE,CAAC,CAAA;IACzG,4EAA4E;IAC5E,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,yDAAyD,EAAE,GAAG,iBAAiB,gBAAgB,kBAAkB,EAAE,CAAC,CAAA;IAExI,MAAM,eAAe,GAAG,IAAI,MAAM,CAChC,4BAA4B,OAAO,GAAG,UAAU,kBAAkB,UAAU,oCAAoC,UAAU,aAAa,UAAU,WAAW,UAAU,cAAc,OAAO,MAAM,UAAU,WAAW,EACtN,IAAI,CACL,CAAA;IACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,eAAe,iBAAiB,aAAa,CAAC,CAAA;IAEnF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,uBAAuB,UAAU,SAAS,EAAE,GAAG,CAAC,EAAE,cAAc,iBAAiB,EAAE,CAAC,CAAA;IAEnH,MAAM,YAAY,GAAG,4CAA4C,UAAU,SAAS,UAAU,eAAe,OAAO,gBAAgB,CAAA;IACpI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE,iBAAiB,kBAAkB,aAAa,CAAC,CAAA;IAEpG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,gBAAgB,UAAU,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,kBAAkB,WAAW,CAAC,CAAA;IACvG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,kBAAkB,GAAG,EAAE,IAAI,CAAC,EAAE,kBAAkB,CAAC,CAAA;IAEvF,OAAO,IAAI,CAAA;AACb,CAAC;AAED,kDAAkD;AAClD,SAAS,qBAAqB,CAAC,IAAY,EAAE,GAAW,EAAE,KAAuB;IAC/E,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAE1C,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QACzB,+CAA+C;QAC/C,MAAM,kBAAkB,GAAG,IAAI,MAAM,CACnC,cAAc,QAAQ,kBAAkB,UAAU,eAAe,kBAAkB,GAAG,kBAAkB,iBAAiB,UAAU,oBAAoB,EACvJ,GAAG,CACJ,CAAA;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,kCAAkC,CAAC,CAAA;QAE3E,8CAA8C;QAC9C,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAClC,gBAAgB,UAAU,eAAe,kBAAkB,GAAG,kBAAkB,iBAAiB,UAAU,KAAK,EAChH,GAAG,CACJ,CAAA;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,kCAAkC,CAAC,CAAA;IAC5E,CAAC;SAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,+CAA+C;QAC/C,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAClC,cAAc,QAAQ,kBAAkB,UAAU,qBAAqB,UAAU,eAAe,kBAAkB,GAAG,kBAAkB,IAAI,EAC3I,GAAG,CACJ,CAAA;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,mCAAmC,CAAC,CAAA;QAE3E,8CAA8C;QAC9C,MAAM,gBAAgB,GAAG,IAAI,MAAM,CACjC,4BAA4B,UAAU,KAAK,kBAAkB,GAAG,kBAAkB,IAAI,EACtF,GAAG,CACJ,CAAA;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;IAC1D,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,UAAwB,EAAE;IACjE,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,IAAI,iBAAiB,CAAA;IAClD,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,UAAU,CAAA;IAC/D,IAAI,gBAAgB,KAAK,MAAM;QAAE,OAAO,IAAI,CAAA;IAE5C,IAAI,GAAG,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACrC,IAAI,GAAG,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACrC,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAA;IAEzD,kEAAkE;IAClE,oEAAoE;IACpE,mEAAmE;IACnE,IAAI,CAAC,OAAO,CAAC,2BAA2B,EAAE,CAAC;QACzC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,0BAA0B,EAAE,kBAAkB,CAAC,CAAA;IACxE,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -52,6 +52,7 @@ export const UNICODE_SYMBOLS = {
|
|
|
52
52
|
RIGHT_DOUBLE_QUOTE: "\u201D",
|
|
53
53
|
LEFT_SINGLE_QUOTE: "\u2018",
|
|
54
54
|
RIGHT_SINGLE_QUOTE: "\u2019",
|
|
55
|
+
MODIFIER_LETTER_APOSTROPHE: "\u02BC",
|
|
55
56
|
NBSP: "\u00A0",
|
|
56
57
|
// Superscript ordinal suffixes
|
|
57
58
|
SUPERSCRIPT_ST: "\u02E2\u1D57", // ˢᵗ
|
package/src/dashes.ts
CHANGED
|
@@ -218,9 +218,9 @@ export function hyphenReplace(text: string, options: DashOptions = {}): string {
|
|
|
218
218
|
const style = options.dashStyle ?? "american"
|
|
219
219
|
if (style === "none") return text
|
|
220
220
|
text = minusReplace(text, options)
|
|
221
|
+
text = enDashDateRange(text, options)
|
|
221
222
|
text = convertParentheticalDashes(text, sep, style)
|
|
222
223
|
if (style === "american") text = normalizeEmDashSpacing(text, sep)
|
|
223
224
|
text = enDashNumberRange(text, options)
|
|
224
|
-
text = enDashDateRange(text, options)
|
|
225
225
|
return text
|
|
226
226
|
}
|
package/src/index.ts
CHANGED
|
@@ -154,6 +154,18 @@ export interface TransformOptions {
|
|
|
154
154
|
* Default: true
|
|
155
155
|
*/
|
|
156
156
|
checkIdempotency?: boolean
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Use modifier letter apostrophe (U+02BC) instead of right single quote
|
|
160
|
+
* (U+2019) for contractions and possessives. This distinguishes apostrophes
|
|
161
|
+
* from closing quotes in most cases, though some ambiguous patterns (e.g.,
|
|
162
|
+
* `dogs'` vs a closing single quote) cannot be resolved without semantic
|
|
163
|
+
* understanding. May also break downstream regex patterns that expect
|
|
164
|
+
* standard quote characters like /don\u2019t/ or /O\u2019Brien/.
|
|
165
|
+
*
|
|
166
|
+
* Default: false
|
|
167
|
+
*/
|
|
168
|
+
useModifierLetterApostrophe?: boolean
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
import { niceQuotes } from "./quotes.js"
|
|
@@ -161,10 +173,11 @@ import { hyphenReplace } from "./dashes.js"
|
|
|
161
173
|
import { symbolTransform, fractions as fractionsTransform, degrees as degreesTransform, superscriptOrdinal as superscriptTransform, primeMarks, collapseSpaces as collapseSpacesTransform, punctuationLigatures as ligaturesTransform } from "./symbols.js"
|
|
162
174
|
import { nbspTransform as nbspTransformFn } from "./nbsp.js"
|
|
163
175
|
import { assertSeparatorCountPreserved, formatErrorString } from "./utils.js"
|
|
164
|
-
import { DEFAULT_SEPARATOR } from "./constants.js"
|
|
176
|
+
import { DEFAULT_SEPARATOR, UNICODE_SYMBOLS } from "./constants.js"
|
|
165
177
|
|
|
166
178
|
export { assertSeparatorCountPreserved, countSeparators } from "./utils.js"
|
|
167
|
-
export { DEFAULT_SEPARATOR } from "./constants.js"
|
|
179
|
+
export { DEFAULT_SEPARATOR, UNICODE_SYMBOLS } from "./constants.js"
|
|
180
|
+
export const MODIFIER_LETTER_APOSTROPHE = UNICODE_SYMBOLS.MODIFIER_LETTER_APOSTROPHE
|
|
168
181
|
|
|
169
182
|
/**
|
|
170
183
|
* Applies all typography transformations: smart quotes, proper dashes,
|
|
@@ -211,6 +224,7 @@ const defaultOpts: Required<Omit<TransformOptions, "separator">> = {
|
|
|
211
224
|
checkIdempotency: true,
|
|
212
225
|
punctuationStyle: "american",
|
|
213
226
|
dashStyle: "american",
|
|
227
|
+
useModifierLetterApostrophe: false,
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
export function transform(text: string, options: TransformOptions = {}): string {
|
|
@@ -227,13 +241,18 @@ export function transform(text: string, options: TransformOptions = {}): string
|
|
|
227
241
|
}
|
|
228
242
|
|
|
229
243
|
const original = text
|
|
230
|
-
const { symbols, fractions, degrees, superscript, ligatures, nbsp, collapseSpaces, checkIdempotency, ...separatorOpts } = { ...defaultOpts, ...options }
|
|
244
|
+
const { symbols, fractions, degrees, superscript, ligatures, nbsp, collapseSpaces, checkIdempotency, useModifierLetterApostrophe, ...separatorOpts } = { ...defaultOpts, ...options }
|
|
231
245
|
|
|
232
246
|
text = hyphenReplace(text, separatorOpts)
|
|
233
247
|
if (separatorOpts.punctuationStyle !== "none") {
|
|
234
248
|
text = primeMarks(text, separatorOpts)
|
|
235
249
|
}
|
|
236
|
-
|
|
250
|
+
// niceQuotes must output MLA here regardless of the user's option,
|
|
251
|
+
// because NBSP's PUNCTUATION_OR_QUOTE class includes RSQ but not MLA.
|
|
252
|
+
// Without this, apostrophes in "don't" would act as word boundaries
|
|
253
|
+
// during NBSP insertion. The final MLA→RSQ conversion at line 286
|
|
254
|
+
// runs after all transforms complete.
|
|
255
|
+
text = niceQuotes(text, { ...separatorOpts, useModifierLetterApostrophe: true })
|
|
237
256
|
|
|
238
257
|
if (symbols) {
|
|
239
258
|
text = symbolTransform(text, separatorOpts)
|
|
@@ -265,6 +284,12 @@ export function transform(text: string, options: TransformOptions = {}): string
|
|
|
265
284
|
|
|
266
285
|
assertSeparatorCountPreserved(original, text, separator, "transform")
|
|
267
286
|
|
|
287
|
+
// Convert MLA→RSQ for regex-friendly output, after all transforms
|
|
288
|
+
// (especially NBSP which relies on MLA being letter-like).
|
|
289
|
+
if (!useModifierLetterApostrophe) {
|
|
290
|
+
text = text.replaceAll(UNICODE_SYMBOLS.MODIFIER_LETTER_APOSTROPHE, UNICODE_SYMBOLS.RIGHT_SINGLE_QUOTE)
|
|
291
|
+
}
|
|
292
|
+
|
|
268
293
|
if (checkIdempotency) {
|
|
269
294
|
const secondPass = transform(text, { ...options, checkIdempotency: false })
|
|
270
295
|
/* istanbul ignore if -- defensive check that should never trigger */
|
package/src/quotes.ts
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
RIGHT_DOUBLE_QUOTE,
|
|
12
12
|
LEFT_SINGLE_QUOTE,
|
|
13
13
|
RIGHT_SINGLE_QUOTE,
|
|
14
|
+
MODIFIER_LETTER_APOSTROPHE,
|
|
14
15
|
ELLIPSIS,
|
|
15
16
|
} = UNICODE_SYMBOLS
|
|
16
17
|
|
|
@@ -21,6 +22,17 @@ export interface QuoteOptions {
|
|
|
21
22
|
separator?: string
|
|
22
23
|
/** "american" (inside), "british" (outside), "none". Default: "american" */
|
|
23
24
|
punctuationStyle?: PunctuationStyle
|
|
25
|
+
/**
|
|
26
|
+
* Use modifier letter apostrophe (U+02BC) instead of right single quote
|
|
27
|
+
* (U+2019) for contractions and possessives. This distinguishes apostrophes
|
|
28
|
+
* from closing quotes in most cases, though some ambiguous patterns (e.g.,
|
|
29
|
+
* `dogs'` vs a closing single quote) cannot be resolved without semantic
|
|
30
|
+
* understanding. May also break downstream regex patterns that expect
|
|
31
|
+
* standard quote characters like /don\u2019t/ or /O\u2019Brien/.
|
|
32
|
+
*
|
|
33
|
+
* Default: false
|
|
34
|
+
*/
|
|
35
|
+
useModifierLetterApostrophe?: boolean
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
/** Convert straight single quotes to curly quotes and apostrophes */
|
|
@@ -29,26 +41,41 @@ function convertSingleQuotes(text: string, sep: string): string {
|
|
|
29
41
|
|
|
30
42
|
// Handle empty single quotes '' and whitespace-only quotes ' ' first
|
|
31
43
|
// Only match straight quotes, not already-converted curly quotes
|
|
32
|
-
const singleQuoteChars = `'${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}`
|
|
44
|
+
const singleQuoteChars = `'${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}`
|
|
33
45
|
text = text.replace(new RegExp(`(?<![${singleQuoteChars}])''(?![${singleQuoteChars}])`, "g"), `${LEFT_SINGLE_QUOTE}${RIGHT_SINGLE_QUOTE}`)
|
|
34
46
|
text = text.replace(new RegExp(`(?<![${singleQuoteChars}])'(\\s+)'(?![${singleQuoteChars}])`, "g"), `${LEFT_SINGLE_QUOTE}$1${RIGHT_SINGLE_QUOTE}`)
|
|
35
47
|
|
|
36
48
|
const afterEndingSinglePatterns = `\\s\\.!?;,\\)${EM_DASH}\\-\\]"`
|
|
49
|
+
// Full pattern with optional 's' for lookahead detection in apostropheRegex
|
|
37
50
|
const afterEndingSingle = `(?=${escapedSep}?(?:s${escapedSep}?)?(?:[${afterEndingSinglePatterns}]|$))`
|
|
38
|
-
const endingSingle = `(?<=[^\\s${LEFT_DOUBLE_QUOTE}'])[']${afterEndingSingle}`
|
|
39
|
-
text = text.replace(new RegExp(endingSingle, "gm"), RIGHT_SINGLE_QUOTE)
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
// Handle 'n' abbreviation (Rock 'n' Roll). Before MLA this wasn't needed —
|
|
53
|
+
// the general rules produced LSQ+n+RSQ which was fine. But MLA requires both
|
|
54
|
+
// quotes to be MLA (semantic apostrophes), and the general rules can't achieve
|
|
55
|
+
// that: neither quote is in a contraction context (no Latin letter on both sides).
|
|
56
|
+
text = text.replace(new RegExp(`(?<=\\w${escapedSep}? )[']n['](?= ${escapedSep}?\\w)`, "gm"), `${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE}`)
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
const
|
|
58
|
+
// Possessive: 's followed by ending context (e.g., dog's) → U+02BC
|
|
59
|
+
const afterPossessive = `(?=${escapedSep}?s${escapedSep}?(?:[${afterEndingSinglePatterns}]|$))`
|
|
60
|
+
const possessiveSingle = `(?<=[^\\s${LEFT_DOUBLE_QUOTE}'])[']${afterPossessive}`
|
|
61
|
+
text = text.replace(new RegExp(possessiveSingle, "gm"), MODIFIER_LETTER_APOSTROPHE)
|
|
62
|
+
|
|
63
|
+
// Closing single quote: ending context without 's' → U+2019
|
|
64
|
+
const afterClosingSingle = `(?=${escapedSep}?(?:[${afterEndingSinglePatterns}]|$))`
|
|
65
|
+
const closingSingle = `(?<=[^\\s${LEFT_DOUBLE_QUOTE}'])[']${afterClosingSingle}`
|
|
66
|
+
text = text.replace(new RegExp(closingSingle, "gm"), RIGHT_SINGLE_QUOTE)
|
|
67
|
+
|
|
68
|
+
const contraction = `(?<=[${LATIN_LETTERS}])['${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}](?=${escapedSep}?[${LATIN_LETTERS}])`
|
|
69
|
+
text = text.replace(new RegExp(contraction, "gm"), MODIFIER_LETTER_APOSTROPHE)
|
|
70
|
+
|
|
71
|
+
const apostropheWhitelist = `(?=n${MODIFIER_LETTER_APOSTROPHE} )`
|
|
72
|
+
const endQuoteNotContraction = `(?!${contraction})[${RIGHT_SINGLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}]${afterEndingSingle}`
|
|
46
73
|
// Limit lookahead scan to 1000 chars to prevent catastrophic backtracking on pathological inputs
|
|
47
74
|
const apostropheRegex = new RegExp(
|
|
48
75
|
`(?<=^|[^\\w])'(${apostropheWhitelist}|(?![^${LEFT_SINGLE_QUOTE}'\\n]{0,1000}${endQuoteNotContraction}))`,
|
|
49
76
|
"gm"
|
|
50
77
|
)
|
|
51
|
-
text = text.replace(apostropheRegex,
|
|
78
|
+
text = text.replace(apostropheRegex, MODIFIER_LETTER_APOSTROPHE)
|
|
52
79
|
|
|
53
80
|
const beginningSingle = `(?<beforeContext>(?:^|[\\s${LEFT_DOUBLE_QUOTE}${RIGHT_DOUBLE_QUOTE}${EM_DASH}\\-\\(])${escapedSep}?)['](?=${escapedSep}?\\S)`
|
|
54
81
|
text = text.replace(new RegExp(beginningSingle, "gm"), `$<beforeContext>${LEFT_SINGLE_QUOTE}`)
|
|
@@ -129,5 +156,12 @@ export function niceQuotes(text: string, options: QuoteOptions = {}): string {
|
|
|
129
156
|
text = convertDoubleQuotes(text, sep)
|
|
130
157
|
text = applyPunctuationStyle(text, sep, punctuationStyle)
|
|
131
158
|
|
|
159
|
+
// MLA is used internally so applyPunctuationStyle can distinguish
|
|
160
|
+
// apostrophes (MLA, don't move) from closing quotes (RSQ, do move).
|
|
161
|
+
// Convert back to RSQ unless the caller opts into the distinction.
|
|
162
|
+
if (!options.useModifierLetterApostrophe) {
|
|
163
|
+
text = text.replaceAll(MODIFIER_LETTER_APOSTROPHE, RIGHT_SINGLE_QUOTE)
|
|
164
|
+
}
|
|
165
|
+
|
|
132
166
|
return text
|
|
133
167
|
}
|
package/src/tests/dashes.test.ts
CHANGED
|
@@ -111,6 +111,19 @@ describe("hyphenReplace", () => {
|
|
|
111
111
|
})
|
|
112
112
|
})
|
|
113
113
|
|
|
114
|
+
describe("month ranges to en dashes", () => {
|
|
115
|
+
it.each([
|
|
116
|
+
["Jan-Mar", `Jan${EN_DASH}Mar`],
|
|
117
|
+
["Mar - Nov", `Mar${EN_DASH}Nov`],
|
|
118
|
+
["January - March", `January${EN_DASH}March`],
|
|
119
|
+
["October 2012 - December 2014", `October 2012${EN_DASH}December 2014`],
|
|
120
|
+
["Jan 2000 - Feb", `Jan 2000${EN_DASH}Feb`],
|
|
121
|
+
["March - April 2025", `March${EN_DASH}April 2025`],
|
|
122
|
+
])('converts "%s"', (input, expected) => {
|
|
123
|
+
expect(hyphenReplace(input)).toBe(expected)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
114
127
|
describe("with separator character", () => {
|
|
115
128
|
const sep = "\uE000"
|
|
116
129
|
it.each([
|
package/src/tests/index.test.ts
CHANGED
package/src/tests/quotes.test.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { niceQuotes } from "../quotes.js"
|
|
2
|
-
import { UNICODE_SYMBOLS } from "../constants.js"
|
|
2
|
+
import { UNICODE_SYMBOLS, DEFAULT_SEPARATOR } from "../constants.js"
|
|
3
3
|
|
|
4
4
|
const {
|
|
5
5
|
LEFT_DOUBLE_QUOTE,
|
|
6
6
|
RIGHT_DOUBLE_QUOTE,
|
|
7
7
|
LEFT_SINGLE_QUOTE,
|
|
8
8
|
RIGHT_SINGLE_QUOTE,
|
|
9
|
+
MODIFIER_LETTER_APOSTROPHE,
|
|
9
10
|
EM_DASH,
|
|
10
11
|
ELLIPSIS,
|
|
11
12
|
} = UNICODE_SYMBOLS
|
|
12
13
|
|
|
14
|
+
/** Enable MLA output for tests that assert the apostrophe codepoint. */
|
|
15
|
+
const MLA = { useModifierLetterApostrophe: true } as const
|
|
16
|
+
|
|
13
17
|
describe("niceQuotes", () => {
|
|
14
18
|
describe("double quotes", () => {
|
|
15
19
|
it.each([
|
|
@@ -27,14 +31,14 @@ describe("niceQuotes", () => {
|
|
|
27
31
|
],
|
|
28
32
|
['"world model";', `${LEFT_DOUBLE_QUOTE}world model${RIGHT_DOUBLE_QUOTE};`],
|
|
29
33
|
['"party"/"wedding."', `${LEFT_DOUBLE_QUOTE}party${RIGHT_DOUBLE_QUOTE}/${LEFT_DOUBLE_QUOTE}wedding.${RIGHT_DOUBLE_QUOTE}`],
|
|
30
|
-
['"Hi \'Trout!"', `${LEFT_DOUBLE_QUOTE}Hi ${
|
|
34
|
+
['"Hi \'Trout!"', `${LEFT_DOUBLE_QUOTE}Hi ${MODIFIER_LETTER_APOSTROPHE}Trout!${RIGHT_DOUBLE_QUOTE}`],
|
|
31
35
|
[`${LEFT_DOUBLE_QUOTE}scope insensitivity${RIGHT_DOUBLE_QUOTE}`, `${LEFT_DOUBLE_QUOTE}scope insensitivity${RIGHT_DOUBLE_QUOTE}`],
|
|
32
36
|
[
|
|
33
37
|
'"how many ways can this function be implemented?".',
|
|
34
38
|
`${LEFT_DOUBLE_QUOTE}how many ways can this function be implemented?${RIGHT_DOUBLE_QUOTE}.`,
|
|
35
39
|
],
|
|
36
40
|
['SSL.")', `SSL.${RIGHT_DOUBLE_QUOTE})`],
|
|
37
|
-
["can't multiply\"?", `can${
|
|
41
|
+
["can't multiply\"?", `can${MODIFIER_LETTER_APOSTROPHE}t multiply${RIGHT_DOUBLE_QUOTE}?`],
|
|
38
42
|
['with "scope insensitivity":', `with ${LEFT_DOUBLE_QUOTE}scope insensitivity${RIGHT_DOUBLE_QUOTE}:`],
|
|
39
43
|
['("the best")', `(${LEFT_DOUBLE_QUOTE}the best${RIGHT_DOUBLE_QUOTE})`],
|
|
40
44
|
['"This is a quote"...', `${LEFT_DOUBLE_QUOTE}This is a quote${RIGHT_DOUBLE_QUOTE}...`],
|
|
@@ -49,7 +53,7 @@ describe("niceQuotes", () => {
|
|
|
49
53
|
// End-of-line quote becomes RIGHT quote
|
|
50
54
|
['not confident in that plan - "', `not confident in that plan - ${RIGHT_DOUBLE_QUOTE}`],
|
|
51
55
|
])('should convert double quotes in "%s"', (input, expected) => {
|
|
52
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
56
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
53
57
|
})
|
|
54
58
|
})
|
|
55
59
|
|
|
@@ -57,16 +61,16 @@ describe("niceQuotes", () => {
|
|
|
57
61
|
it.each([
|
|
58
62
|
["He said, 'Hi'", `He said, ${LEFT_SINGLE_QUOTE}Hi${RIGHT_SINGLE_QUOTE}`],
|
|
59
63
|
["He wanted 'power.'", `He wanted ${LEFT_SINGLE_QUOTE}power.${RIGHT_SINGLE_QUOTE}`],
|
|
60
|
-
["I'd", `I${
|
|
61
|
-
["I don't'nt want to go", `I don${
|
|
62
|
-
['"\'sup"', `${LEFT_DOUBLE_QUOTE}${
|
|
63
|
-
["'SUP", `${
|
|
64
|
-
["Rock 'n' Roll", `Rock ${
|
|
65
|
-
["I was born in '99", `I was born in ${
|
|
66
|
-
["'99 tigers weren't a match", `${
|
|
64
|
+
["I'd", `I${MODIFIER_LETTER_APOSTROPHE}d`],
|
|
65
|
+
["I don't'nt want to go", `I don${MODIFIER_LETTER_APOSTROPHE}t${MODIFIER_LETTER_APOSTROPHE}nt want to go`],
|
|
66
|
+
['"\'sup"', `${LEFT_DOUBLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}sup${RIGHT_DOUBLE_QUOTE}`],
|
|
67
|
+
["'SUP", `${MODIFIER_LETTER_APOSTROPHE}SUP`],
|
|
68
|
+
["Rock 'n' Roll", `Rock ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} Roll`],
|
|
69
|
+
["I was born in '99", `I was born in ${MODIFIER_LETTER_APOSTROPHE}99`],
|
|
70
|
+
["'99 tigers weren't a match", `${MODIFIER_LETTER_APOSTROPHE}99 tigers weren${MODIFIER_LETTER_APOSTROPHE}t a match`],
|
|
67
71
|
[
|
|
68
72
|
"I'm not the best, haven't you heard?",
|
|
69
|
-
`I${
|
|
73
|
+
`I${MODIFIER_LETTER_APOSTROPHE}m not the best, haven${MODIFIER_LETTER_APOSTROPHE}t you heard?`,
|
|
70
74
|
],
|
|
71
75
|
// Skipped: Complex edge case with 'sup and quoted phrase
|
|
72
76
|
// ["Hey, 'sup 'this is a single quote'", `Hey, ${RIGHT_SINGLE_QUOTE}sup ${LEFT_SINGLE_QUOTE}this is a single quote${RIGHT_SINGLE_QUOTE}`],
|
|
@@ -79,24 +83,92 @@ describe("niceQuotes", () => {
|
|
|
79
83
|
["('survival incentive')", `(${LEFT_SINGLE_QUOTE}survival incentive${RIGHT_SINGLE_QUOTE})`],
|
|
80
84
|
[
|
|
81
85
|
"strategy s's return is good, even as d's return is bad",
|
|
82
|
-
`strategy s${
|
|
86
|
+
`strategy s${MODIFIER_LETTER_APOSTROPHE}s return is good, even as d${MODIFIER_LETTER_APOSTROPHE}s return is bad`,
|
|
83
87
|
],
|
|
88
|
+
// Possessive plural (trailing apostrophe → closing RSQ)
|
|
89
|
+
["the dogs' owner", `the dogs${RIGHT_SINGLE_QUOTE} owner`],
|
|
90
|
+
["the bosses' meeting", `the bosses${RIGHT_SINGLE_QUOTE} meeting`],
|
|
91
|
+
// Chained possessives
|
|
92
|
+
["the dog's owner's car", `the dog${MODIFIER_LETTER_APOSTROPHE}s owner${MODIFIER_LETTER_APOSTROPHE}s car`],
|
|
93
|
+
// Hyphenated possessive
|
|
94
|
+
["mother-in-law's house", `mother-in-law${MODIFIER_LETTER_APOSTROPHE}s house`],
|
|
95
|
+
// Accented character before possessive
|
|
96
|
+
["café's menu", `café${MODIFIER_LETTER_APOSTROPHE}s menu`],
|
|
97
|
+
// Digit before possessive
|
|
98
|
+
["the 747's engine", `the 747${MODIFIER_LETTER_APOSTROPHE}s engine`],
|
|
99
|
+
// Compound contraction + possessive
|
|
100
|
+
["shouldn't's", `shouldn${MODIFIER_LETTER_APOSTROPHE}t${MODIFIER_LETTER_APOSTROPHE}s`],
|
|
101
|
+
// Contraction before em-dash
|
|
102
|
+
[`don't${EM_DASH}stop`, `don${MODIFIER_LETTER_APOSTROPHE}t${EM_DASH}stop`],
|
|
103
|
+
// Contraction + digit context
|
|
104
|
+
["it's 5 o'clock", `it${MODIFIER_LETTER_APOSTROPHE}s 5 o${MODIFIER_LETTER_APOSTROPHE}clock`],
|
|
105
|
+
// French contractions (single-letter prefix + accented Latin)
|
|
106
|
+
["l'homme", `l${MODIFIER_LETTER_APOSTROPHE}homme`],
|
|
107
|
+
["l'école", `l${MODIFIER_LETTER_APOSTROPHE}école`],
|
|
108
|
+
// Uppercase contractions and names
|
|
109
|
+
["O'NEILL", `O${MODIFIER_LETTER_APOSTROPHE}NEILL`],
|
|
110
|
+
["CAN'T", `CAN${MODIFIER_LETTER_APOSTROPHE}T`],
|
|
111
|
+
// Possessive in parenthetical
|
|
112
|
+
["(Brien's)", `(Brien${MODIFIER_LETTER_APOSTROPHE}s)`],
|
|
113
|
+
// Possessive inside single-quoted phrase
|
|
114
|
+
["'the dog's bowl'", `${LEFT_SINGLE_QUOTE}the dog${MODIFIER_LETTER_APOSTROPHE}s bowl${RIGHT_SINGLE_QUOTE}`],
|
|
115
|
+
// Straight apostrophe inside pre-converted double quotes
|
|
116
|
+
[`${LEFT_DOUBLE_QUOTE}the dog's${RIGHT_DOUBLE_QUOTE}`, `${LEFT_DOUBLE_QUOTE}the dog${MODIFIER_LETTER_APOSTROPHE}s${RIGHT_DOUBLE_QUOTE}`],
|
|
117
|
+
// Decade in double quotes
|
|
118
|
+
[`"the '90s"`, `${LEFT_DOUBLE_QUOTE}the ${MODIFIER_LETTER_APOSTROPHE}90s${RIGHT_DOUBLE_QUOTE}`],
|
|
119
|
+
// 'n' + possessive combination
|
|
120
|
+
["Rock 'n' Roll's greatest hits", `Rock ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} Roll${MODIFIER_LETTER_APOSTROPHE}s greatest hits`],
|
|
121
|
+
// Non-Latin character before possessive
|
|
122
|
+
["Привет's", `Привет${MODIFIER_LETTER_APOSTROPHE}s`],
|
|
123
|
+
// Closing RSQ then opening LDQ
|
|
124
|
+
[`'hello' "world"`, `${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE} ${LEFT_DOUBLE_QUOTE}world${RIGHT_DOUBLE_QUOTE}`],
|
|
84
125
|
])('should handle single quotes/apostrophes in "%s"', (input, expected) => {
|
|
85
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
126
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
86
127
|
})
|
|
87
128
|
})
|
|
88
129
|
|
|
89
130
|
describe("leading apostrophe contractions", () => {
|
|
90
131
|
it.each([
|
|
91
|
-
["'twas the night", `${
|
|
92
|
-
["'tis the season", `${
|
|
93
|
-
["'Twas brillig", `${
|
|
94
|
-
["'Tis but a scratch", `${
|
|
132
|
+
["'twas the night", `${MODIFIER_LETTER_APOSTROPHE}twas the night`],
|
|
133
|
+
["'tis the season", `${MODIFIER_LETTER_APOSTROPHE}tis the season`],
|
|
134
|
+
["'Twas brillig", `${MODIFIER_LETTER_APOSTROPHE}Twas brillig`],
|
|
135
|
+
["'Tis but a scratch", `${MODIFIER_LETTER_APOSTROPHE}Tis but a scratch`],
|
|
95
136
|
// Decade abbreviations
|
|
96
|
-
["the '90s", `the ${
|
|
97
|
-
["in '99", `in ${
|
|
137
|
+
["the '90s", `the ${MODIFIER_LETTER_APOSTROPHE}90s`],
|
|
138
|
+
["in '99", `in ${MODIFIER_LETTER_APOSTROPHE}99`],
|
|
139
|
+
["the '00s", `the ${MODIFIER_LETTER_APOSTROPHE}00s`],
|
|
140
|
+
// Additional leading contractions
|
|
141
|
+
["'cause", `${MODIFIER_LETTER_APOSTROPHE}cause`],
|
|
142
|
+
["'twas the night, 'twas indeed", `${MODIFIER_LETTER_APOSTROPHE}twas the night, ${MODIFIER_LETTER_APOSTROPHE}twas indeed`],
|
|
143
|
+
[`"'twas the night"`, `${LEFT_DOUBLE_QUOTE}${MODIFIER_LETTER_APOSTROPHE}twas the night${RIGHT_DOUBLE_QUOTE}`],
|
|
98
144
|
])('handles leading apostrophe in "%s"', (input, expected) => {
|
|
99
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
145
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("multiple decades in one sentence", () => {
|
|
149
|
+
expect(niceQuotes("The '60s, '70s, and '80s were different.", MLA)).toBe(
|
|
150
|
+
`The ${MODIFIER_LETTER_APOSTROPHE}60s, ${MODIFIER_LETTER_APOSTROPHE}70s, and ${MODIFIER_LETTER_APOSTROPHE}80s were different.`
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
describe("'n' abbreviation edge cases", () => {
|
|
156
|
+
it.each([
|
|
157
|
+
["uppercase 'N' is not abbreviated", "Rock 'N' Roll", `Rock ${LEFT_SINGLE_QUOTE}N${RIGHT_SINGLE_QUOTE} Roll`],
|
|
158
|
+
["multiple 'n' abbreviations", "Rock 'n' Roll and fish 'n' chips", `Rock ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} Roll and fish ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} chips`],
|
|
159
|
+
["digit neighbors (\\w includes digits)", "1 'n' 2", `1 ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} 2`],
|
|
160
|
+
["known false positive: quoted letter", "the letter 'n' is common", `the letter ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} is common`],
|
|
161
|
+
])('%s', (_label, input, expected) => {
|
|
162
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it.each([
|
|
166
|
+
["start of string (no preceding word)", "'n' Roll", `${LEFT_SINGLE_QUOTE}n${RIGHT_SINGLE_QUOTE} Roll`],
|
|
167
|
+
["end of string (no following word)", "Rock 'n'", `Rock ${LEFT_SINGLE_QUOTE}n${RIGHT_SINGLE_QUOTE}`],
|
|
168
|
+
["punctuation precedes first apostrophe", "said, 'n' is good", `said, ${LEFT_SINGLE_QUOTE}n${RIGHT_SINGLE_QUOTE} is good`],
|
|
169
|
+
["newline replaces space", "Rock 'n'\nRoll", `Rock ${LEFT_SINGLE_QUOTE}n${RIGHT_SINGLE_QUOTE}\nRoll`],
|
|
170
|
+
])('does not produce MLA-n-MLA: %s', (_label, input, expected) => {
|
|
171
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
100
172
|
})
|
|
101
173
|
})
|
|
102
174
|
|
|
@@ -137,11 +209,11 @@ describe("niceQuotes", () => {
|
|
|
137
209
|
|
|
138
210
|
// From Standard Ebooks: M'Donald-style names (archaic patterns)
|
|
139
211
|
it.each([
|
|
140
|
-
[`"M'Lord"`, `${LEFT_DOUBLE_QUOTE}M${
|
|
141
|
-
[`O'Brien's idea`, `O${
|
|
142
|
-
[`The O'Connors`, `The O${
|
|
212
|
+
[`"M'Lord"`, `${LEFT_DOUBLE_QUOTE}M${MODIFIER_LETTER_APOSTROPHE}Lord${RIGHT_DOUBLE_QUOTE}`],
|
|
213
|
+
[`O'Brien's idea`, `O${MODIFIER_LETTER_APOSTROPHE}Brien${MODIFIER_LETTER_APOSTROPHE}s idea`],
|
|
214
|
+
[`The O'Connors`, `The O${MODIFIER_LETTER_APOSTROPHE}Connors`],
|
|
143
215
|
])('handles Irish/Scottish name apostrophes: "%s"', (input, expected) => {
|
|
144
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
216
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
145
217
|
})
|
|
146
218
|
|
|
147
219
|
// From smartquotes.js: complex nested quotes
|
|
@@ -165,17 +237,17 @@ describe("niceQuotes", () => {
|
|
|
165
237
|
describe("complex real-world patterns", () => {
|
|
166
238
|
it.each([
|
|
167
239
|
// Dialogue with attribution
|
|
168
|
-
['"I can\'t," she said.', `${LEFT_DOUBLE_QUOTE}I can${
|
|
240
|
+
['"I can\'t," she said.', `${LEFT_DOUBLE_QUOTE}I can${MODIFIER_LETTER_APOSTROPHE}t,${RIGHT_DOUBLE_QUOTE} she said.`],
|
|
169
241
|
['"Why not?" he asked.', `${LEFT_DOUBLE_QUOTE}Why not?${RIGHT_DOUBLE_QUOTE} he asked.`],
|
|
170
242
|
// Multiple contractions
|
|
171
|
-
["I'm can't won't don't", `I${
|
|
243
|
+
["I'm can't won't don't", `I${MODIFIER_LETTER_APOSTROPHE}m can${MODIFIER_LETTER_APOSTROPHE}t won${MODIFIER_LETTER_APOSTROPHE}t don${MODIFIER_LETTER_APOSTROPHE}t`],
|
|
172
244
|
// Quote within parentheses
|
|
173
245
|
['("test")', `(${LEFT_DOUBLE_QUOTE}test${RIGHT_DOUBLE_QUOTE})`],
|
|
174
246
|
["('test')", `(${LEFT_SINGLE_QUOTE}test${RIGHT_SINGLE_QUOTE})`],
|
|
175
247
|
// Quote with slash separator
|
|
176
248
|
['"option1"/"option2"', `${LEFT_DOUBLE_QUOTE}option1${RIGHT_DOUBLE_QUOTE}/${LEFT_DOUBLE_QUOTE}option2${RIGHT_DOUBLE_QUOTE}`],
|
|
177
249
|
])('handles complex pattern: "%s"', (input, expected) => {
|
|
178
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
250
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
179
251
|
})
|
|
180
252
|
|
|
181
253
|
it("handles multi-line dialogue", () => {
|
|
@@ -189,22 +261,46 @@ describe("niceQuotes", () => {
|
|
|
189
261
|
it.each([
|
|
190
262
|
`${LEFT_DOUBLE_QUOTE}already curly${RIGHT_DOUBLE_QUOTE}`,
|
|
191
263
|
`${LEFT_SINGLE_QUOTE}single curly${RIGHT_SINGLE_QUOTE}`,
|
|
192
|
-
`I${
|
|
264
|
+
`I${MODIFIER_LETTER_APOSTROPHE}m already converted`,
|
|
193
265
|
`${LEFT_DOUBLE_QUOTE}nested ${LEFT_SINGLE_QUOTE}quotes${RIGHT_SINGLE_QUOTE}${RIGHT_DOUBLE_QUOTE}`,
|
|
266
|
+
// French contractions
|
|
267
|
+
`l${MODIFIER_LETTER_APOSTROPHE}homme d${MODIFIER_LETTER_APOSTROPHE}accord`,
|
|
268
|
+
// Uppercase
|
|
269
|
+
`O${MODIFIER_LETTER_APOSTROPHE}NEILL`,
|
|
270
|
+
`CAN${MODIFIER_LETTER_APOSTROPHE}T`,
|
|
271
|
+
// Plural possessive
|
|
272
|
+
`the dogs${RIGHT_SINGLE_QUOTE} owner`,
|
|
273
|
+
// Mixed quote types
|
|
274
|
+
`${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE} ${LEFT_DOUBLE_QUOTE}world${RIGHT_DOUBLE_QUOTE}`,
|
|
194
275
|
])('is idempotent for: "%s"', (input) => {
|
|
195
|
-
expect(niceQuotes(input)).toBe(input)
|
|
196
|
-
expect(niceQuotes(niceQuotes(input))).toBe(input)
|
|
276
|
+
expect(niceQuotes(input, MLA)).toBe(input)
|
|
277
|
+
expect(niceQuotes(niceQuotes(input, MLA), MLA)).toBe(input)
|
|
197
278
|
})
|
|
198
279
|
})
|
|
199
280
|
|
|
200
281
|
describe("with separator character", () => {
|
|
201
|
-
const sep =
|
|
282
|
+
const sep = DEFAULT_SEPARATOR
|
|
202
283
|
it.each([
|
|
203
284
|
[`"Hello${sep} world"`, `${LEFT_DOUBLE_QUOTE}Hello${sep} world${RIGHT_DOUBLE_QUOTE}`, "preserves separator positions"],
|
|
204
|
-
[`don${sep}'t`, `don${sep}${
|
|
285
|
+
[`don${sep}'t`, `don${sep}${MODIFIER_LETTER_APOSTROPHE}t`, "contractions across separator"],
|
|
205
286
|
[`"test${sep}"`, `${LEFT_DOUBLE_QUOTE}test${sep}${RIGHT_DOUBLE_QUOTE}`, "quotes at separator boundaries"],
|
|
287
|
+
// Possessive across separator
|
|
288
|
+
[`dog${sep}'s bone`, `dog${sep}${MODIFIER_LETTER_APOSTROPHE}s bone`, "possessive across separator"],
|
|
289
|
+
// Separator between apostrophe and s
|
|
290
|
+
[`the dog'${sep}s bone`, `the dog${MODIFIER_LETTER_APOSTROPHE}${sep}s bone`, "separator between apostrophe and s"],
|
|
291
|
+
// Opening after separator
|
|
292
|
+
[`${sep}'hello'`, `${sep}${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE}`, "opening quote after separator"],
|
|
293
|
+
// Closing after separator
|
|
294
|
+
[`'hello${sep}'`, `${LEFT_SINGLE_QUOTE}hello${sep}${RIGHT_SINGLE_QUOTE}`, "closing quote after separator"],
|
|
206
295
|
])("%s → %s (%s)", (input, expected) => {
|
|
207
|
-
expect(niceQuotes(input, { separator: sep })).toBe(expected)
|
|
296
|
+
expect(niceQuotes(input, { separator: sep, ...MLA })).toBe(expected)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it("'n' with separators around word boundaries", () => {
|
|
300
|
+
const input = `Rock${sep} 'n' ${sep}Roll`
|
|
301
|
+
const expected = `Rock${sep} ${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE} ${sep}Roll`
|
|
302
|
+
expect(niceQuotes(input, { separator: sep, ...MLA })).toBe(expected)
|
|
303
|
+
expect(niceQuotes(expected, { separator: sep, ...MLA })).toBe(expected)
|
|
208
304
|
})
|
|
209
305
|
})
|
|
210
306
|
|
|
@@ -213,8 +309,14 @@ describe("niceQuotes", () => {
|
|
|
213
309
|
['"unclosed', `${LEFT_DOUBLE_QUOTE}unclosed`],
|
|
214
310
|
["unclosed'", `unclosed${RIGHT_SINGLE_QUOTE}`],
|
|
215
311
|
['""', `${LEFT_DOUBLE_QUOTE}${RIGHT_DOUBLE_QUOTE}`],
|
|
312
|
+
// Minimal inputs
|
|
313
|
+
["", ""],
|
|
314
|
+
[" ", " "],
|
|
315
|
+
["'", `${MODIFIER_LETTER_APOSTROPHE}`],
|
|
316
|
+
["'a", `${MODIFIER_LETTER_APOSTROPHE}a`],
|
|
317
|
+
["a'", `a${RIGHT_SINGLE_QUOTE}`],
|
|
216
318
|
])('handles edge quote pattern: "%s"', (input, expected) => {
|
|
217
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
319
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
218
320
|
})
|
|
219
321
|
})
|
|
220
322
|
|
|
@@ -233,12 +335,12 @@ describe("niceQuotes", () => {
|
|
|
233
335
|
|
|
234
336
|
describe("consecutive contractions", () => {
|
|
235
337
|
it.each([
|
|
236
|
-
["I'd've", `I${
|
|
237
|
-
["y'all'd've", `y${
|
|
238
|
-
["wouldn't've", `wouldn${
|
|
239
|
-
["couldn't've", `couldn${
|
|
338
|
+
["I'd've", `I${MODIFIER_LETTER_APOSTROPHE}d${MODIFIER_LETTER_APOSTROPHE}ve`],
|
|
339
|
+
["y'all'd've", `y${MODIFIER_LETTER_APOSTROPHE}all${MODIFIER_LETTER_APOSTROPHE}d${MODIFIER_LETTER_APOSTROPHE}ve`],
|
|
340
|
+
["wouldn't've", `wouldn${MODIFIER_LETTER_APOSTROPHE}t${MODIFIER_LETTER_APOSTROPHE}ve`],
|
|
341
|
+
["couldn't've", `couldn${MODIFIER_LETTER_APOSTROPHE}t${MODIFIER_LETTER_APOSTROPHE}ve`],
|
|
240
342
|
])('handles double contraction: "%s"', (input, expected) => {
|
|
241
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
343
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
242
344
|
})
|
|
243
345
|
})
|
|
244
346
|
|
|
@@ -258,8 +360,12 @@ describe("niceQuotes", () => {
|
|
|
258
360
|
it.each([
|
|
259
361
|
['"Hello\nWorld"', `${LEFT_DOUBLE_QUOTE}Hello\nWorld${RIGHT_DOUBLE_QUOTE}`],
|
|
260
362
|
['"A"\n"B"', `${LEFT_DOUBLE_QUOTE}A${RIGHT_DOUBLE_QUOTE}\n${LEFT_DOUBLE_QUOTE}B${RIGHT_DOUBLE_QUOTE}`],
|
|
363
|
+
// Contractions across lines
|
|
364
|
+
["I can't\nbelieve it's\nnot butter", `I can${MODIFIER_LETTER_APOSTROPHE}t\nbelieve it${MODIFIER_LETTER_APOSTROPHE}s\nnot butter`],
|
|
365
|
+
// Newline stops lookahead → MLA (not LSQ) since no closing quote visible
|
|
366
|
+
["'hello\nworld'", `${MODIFIER_LETTER_APOSTROPHE}hello\nworld${RIGHT_SINGLE_QUOTE}`],
|
|
261
367
|
])('handles multiline: "%s"', (input, expected) => {
|
|
262
|
-
expect(niceQuotes(input)).toBe(expected)
|
|
368
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
263
369
|
})
|
|
264
370
|
})
|
|
265
371
|
|
|
@@ -315,6 +421,20 @@ describe("niceQuotes", () => {
|
|
|
315
421
|
})
|
|
316
422
|
})
|
|
317
423
|
|
|
424
|
+
describe("MLA excluded from punctuation movement", () => {
|
|
425
|
+
it.each([
|
|
426
|
+
// American: MLA period/comma stay put (MLA is not a quote character)
|
|
427
|
+
[`the dog${MODIFIER_LETTER_APOSTROPHE}s.`, `the dog${MODIFIER_LETTER_APOSTROPHE}s.`, "american"],
|
|
428
|
+
[`Brien${MODIFIER_LETTER_APOSTROPHE}s,`, `Brien${MODIFIER_LETTER_APOSTROPHE}s,`, "american"],
|
|
429
|
+
// American: RSQ period/comma move inside (contrast)
|
|
430
|
+
[`${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE}.`, `${LEFT_SINGLE_QUOTE}hello.${RIGHT_SINGLE_QUOTE}`, "american"],
|
|
431
|
+
// British: RSQ period moves outside (contrast)
|
|
432
|
+
[`${LEFT_SINGLE_QUOTE}hello.${RIGHT_SINGLE_QUOTE}`, `${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE}.`, "british"],
|
|
433
|
+
])('%s → %s (%s)', (input, expected, style) => {
|
|
434
|
+
expect(niceQuotes(input, { punctuationStyle: style as "american" | "british", ...MLA })).toBe(expected)
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
318
438
|
describe('when "none"', () => {
|
|
319
439
|
it.each([
|
|
320
440
|
[periodOutsideDouble],
|
|
@@ -339,4 +459,88 @@ describe("niceQuotes", () => {
|
|
|
339
459
|
})
|
|
340
460
|
})
|
|
341
461
|
|
|
462
|
+
describe("useModifierLetterApostrophe option", () => {
|
|
463
|
+
const RSQ = RIGHT_SINGLE_QUOTE
|
|
464
|
+
|
|
465
|
+
it.each([
|
|
466
|
+
["contractions", "don't", `don${RSQ}t`],
|
|
467
|
+
["possessives", "the dog's bone", `the dog${RSQ}s bone`],
|
|
468
|
+
["O'Brien-style names", "O'Brien's idea", `O${RSQ}Brien${RSQ}s idea`],
|
|
469
|
+
["'n' abbreviation", "Rock 'n' Roll", `Rock ${RSQ}n${RSQ} Roll`],
|
|
470
|
+
["decades", "the '90s", `the ${RSQ}90s`],
|
|
471
|
+
["leading apostrophes", "'twas", `${RSQ}twas`],
|
|
472
|
+
])('defaults to RSQ for %s', (_label, input, expected) => {
|
|
473
|
+
expect(niceQuotes(input)).toBe(expected)
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
it("uses MLA when option is true", () => {
|
|
477
|
+
expect(niceQuotes("don't", MLA)).toBe(`don${MODIFIER_LETTER_APOSTROPHE}t`)
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it("RSQ output is idempotent", () => {
|
|
481
|
+
const inputs = ["don't", "the dog's", "O'Brien", "Rock 'n' Roll", "'90s"]
|
|
482
|
+
for (const input of inputs) {
|
|
483
|
+
const first = niceQuotes(input)
|
|
484
|
+
expect(niceQuotes(first)).toBe(first)
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
it("MLA output is idempotent", () => {
|
|
489
|
+
const inputs = ["don't", "the dog's", "O'Brien", "Rock 'n' Roll", "'90s"]
|
|
490
|
+
for (const input of inputs) {
|
|
491
|
+
const first = niceQuotes(input, MLA)
|
|
492
|
+
expect(niceQuotes(first, MLA)).toBe(first)
|
|
493
|
+
}
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
describe("RSQ→MLA normalization (external system input)", () => {
|
|
497
|
+
it.each([
|
|
498
|
+
[`don${RIGHT_SINGLE_QUOTE}t`, `don${MODIFIER_LETTER_APOSTROPHE}t`],
|
|
499
|
+
[`I${RIGHT_SINGLE_QUOTE}m`, `I${MODIFIER_LETTER_APOSTROPHE}m`],
|
|
500
|
+
[`they${RIGHT_SINGLE_QUOTE}re`, `they${MODIFIER_LETTER_APOSTROPHE}re`],
|
|
501
|
+
[`O${RIGHT_SINGLE_QUOTE}Brien`, `O${MODIFIER_LETTER_APOSTROPHE}Brien`],
|
|
502
|
+
[`the dog${RIGHT_SINGLE_QUOTE}s bone`, `the dog${MODIFIER_LETTER_APOSTROPHE}s bone`],
|
|
503
|
+
// Mixed MLA + RSQ in same text
|
|
504
|
+
[`I${MODIFIER_LETTER_APOSTROPHE}m fine and you${RIGHT_SINGLE_QUOTE}re great`, `I${MODIFIER_LETTER_APOSTROPHE}m fine and you${MODIFIER_LETTER_APOSTROPHE}re great`],
|
|
505
|
+
])('normalizes RSQ contraction "%s"', (input, expected) => {
|
|
506
|
+
expect(niceQuotes(input, MLA)).toBe(expected)
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
it("does NOT normalize RSQ when it is a closing quote", () => {
|
|
510
|
+
const input = `${LEFT_SINGLE_QUOTE}hello${RIGHT_SINGLE_QUOTE} world`
|
|
511
|
+
expect(niceQuotes(input, MLA)).toBe(input)
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
it("does not convert RSQ after non-Latin character", () => {
|
|
515
|
+
const input = `[test]${RIGHT_SINGLE_QUOTE}`
|
|
516
|
+
expect(niceQuotes(input, MLA)).toBe(input)
|
|
517
|
+
})
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
describe("pathological inputs", () => {
|
|
522
|
+
it("handles 1500-char input without closing quote", () => {
|
|
523
|
+
const input = `'${"a".repeat(1500)}`
|
|
524
|
+
const start = performance.now()
|
|
525
|
+
const result = niceQuotes(input, MLA)
|
|
526
|
+
expect(performance.now() - start).toBeLessThan(1000)
|
|
527
|
+
expect(niceQuotes(result, MLA)).toBe(result)
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
it("handles 16 rapid apostrophes", () => {
|
|
531
|
+
const input = "a'b'c'd'e'f'g'h'i'j'k'l'm'n'o'p"
|
|
532
|
+
const expected = `a${MODIFIER_LETTER_APOSTROPHE}b${MODIFIER_LETTER_APOSTROPHE}c${MODIFIER_LETTER_APOSTROPHE}d${MODIFIER_LETTER_APOSTROPHE}e${MODIFIER_LETTER_APOSTROPHE}f${MODIFIER_LETTER_APOSTROPHE}g${MODIFIER_LETTER_APOSTROPHE}h${MODIFIER_LETTER_APOSTROPHE}i${MODIFIER_LETTER_APOSTROPHE}j${MODIFIER_LETTER_APOSTROPHE}k${MODIFIER_LETTER_APOSTROPHE}l${MODIFIER_LETTER_APOSTROPHE}m${MODIFIER_LETTER_APOSTROPHE}n${MODIFIER_LETTER_APOSTROPHE}o${MODIFIER_LETTER_APOSTROPHE}p`
|
|
533
|
+
const start = performance.now()
|
|
534
|
+
const result = niceQuotes(input, MLA)
|
|
535
|
+
expect(performance.now() - start).toBeLessThan(1000)
|
|
536
|
+
expect(result).toBe(expected)
|
|
537
|
+
expect(niceQuotes(result, MLA)).toBe(result)
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
it("handles 500-char word + possessive", () => {
|
|
541
|
+
const input = `${"a".repeat(500)}'s thing`
|
|
542
|
+
expect(niceQuotes(input, MLA)).toBe(`${"a".repeat(500)}${MODIFIER_LETTER_APOSTROPHE}s thing`)
|
|
543
|
+
})
|
|
544
|
+
})
|
|
545
|
+
|
|
342
546
|
})
|
package/src/tests/remark.test.ts
CHANGED
|
@@ -48,7 +48,7 @@ describe("remarkPunctilio", () => {
|
|
|
48
48
|
describe("basic transformations (nbsp enabled)", () => {
|
|
49
49
|
it.each([
|
|
50
50
|
["quotes", '"Hello," she said.', `${LDQ}Hello,${RDQ} she${NBSP}said.`],
|
|
51
|
-
["apostrophes", "It's a test.", `It${RSQ}s
|
|
51
|
+
["apostrophes", "It's a test.", `It${RSQ}s a${NBSP}test.`],
|
|
52
52
|
["em dashes", "Wait -- here it comes.", `Wait${EM_DASH}here it${NBSP}comes.`],
|
|
53
53
|
["en dashes (number range)", "Pages 1-5", `Pages${NBSP}1${EN_DASH}5`],
|
|
54
54
|
["ellipses", "Wait...", `Wait${ELLIPSIS}`],
|