punctilio 3.11.3 → 3.11.4

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.
Files changed (66) hide show
  1. package/dist/cli.d.ts +1 -12
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +4 -26
  4. package/dist/cli.js.map +1 -1
  5. package/dist/constants.d.ts +13 -61
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +12 -61
  8. package/dist/constants.js.map +1 -1
  9. package/dist/dashes.d.ts +0 -9
  10. package/dist/dashes.d.ts.map +1 -1
  11. package/dist/dashes.js +4 -26
  12. package/dist/dashes.js.map +1 -1
  13. package/dist/html.d.ts +1 -32
  14. package/dist/html.d.ts.map +1 -1
  15. package/dist/html.js +1 -29
  16. package/dist/html.js.map +1 -1
  17. package/dist/index.d.ts +0 -39
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +0 -39
  20. package/dist/index.js.map +1 -1
  21. package/dist/markdown.d.ts +1 -34
  22. package/dist/markdown.d.ts.map +1 -1
  23. package/dist/markdown.js +1 -34
  24. package/dist/markdown.js.map +1 -1
  25. package/dist/nbsp.d.ts +1 -68
  26. package/dist/nbsp.d.ts.map +1 -1
  27. package/dist/nbsp.js +6 -86
  28. package/dist/nbsp.js.map +1 -1
  29. package/dist/prettier-plugin.d.ts +0 -14
  30. package/dist/prettier-plugin.d.ts.map +1 -1
  31. package/dist/prettier-plugin.js +0 -19
  32. package/dist/prettier-plugin.js.map +1 -1
  33. package/dist/quotes.d.ts +0 -3
  34. package/dist/quotes.d.ts.map +1 -1
  35. package/dist/quotes.js +11 -58
  36. package/dist/quotes.js.map +1 -1
  37. package/dist/rehype.d.ts +4 -141
  38. package/dist/rehype.d.ts.map +1 -1
  39. package/dist/rehype.js +7 -144
  40. package/dist/rehype.js.map +1 -1
  41. package/dist/remark.d.ts +0 -30
  42. package/dist/remark.d.ts.map +1 -1
  43. package/dist/remark.js +0 -41
  44. package/dist/remark.js.map +1 -1
  45. package/dist/symbols.d.ts +1 -7
  46. package/dist/symbols.d.ts.map +1 -1
  47. package/dist/symbols.js +9 -53
  48. package/dist/symbols.js.map +1 -1
  49. package/dist/utils.d.ts +10 -58
  50. package/dist/utils.d.ts.map +1 -1
  51. package/dist/utils.js +10 -58
  52. package/dist/utils.js.map +1 -1
  53. package/package.json +5 -5
  54. package/src/cli.ts +5 -31
  55. package/src/constants.ts +13 -62
  56. package/src/dashes.ts +4 -27
  57. package/src/html.ts +1 -33
  58. package/src/index.ts +0 -40
  59. package/src/markdown.ts +1 -38
  60. package/src/nbsp.ts +6 -87
  61. package/src/prettier-plugin.ts +0 -20
  62. package/src/quotes.ts +11 -59
  63. package/src/rehype.ts +8 -162
  64. package/src/remark.ts +0 -42
  65. package/src/symbols.ts +9 -66
  66. package/src/utils.ts +10 -60
@@ -1,18 +1,3 @@
1
- /**
2
- * Prettier plugin applying punctilio's typographic transforms to Markdown.
3
- *
4
- * @example
5
- * ```jsonc
6
- * // .prettierrc
7
- * { "plugins": ["punctilio/prettier-plugin"] }
8
- *
9
- * // .punctiliorc.json (optional)
10
- * { "punctuationStyle": "british", "skipTags": ["pre", "code"] }
11
- * ```
12
- *
13
- * @packageDocumentation
14
- */
15
-
16
1
  import { dirname } from "node:path"
17
2
 
18
3
  import * as prettierMarkdown from "prettier/plugins/markdown"
@@ -30,11 +15,6 @@ async function loadPunctilioConfig(searchFrom: string): Promise<RemarkPunctilioO
30
15
  return !result || result.isEmpty ? {} : (result.config as RemarkPunctilioOptions)
31
16
  }
32
17
 
33
- /**
34
- * Runs `remarkPunctilio`'s transformer against an mdast tree. The plugin
35
- * mutates the tree in place; awaiting handles any future async variant
36
- * without churning callers.
37
- */
38
18
  async function runRemarkPunctilio(opts: RemarkPunctilioOptions, ast: Root): Promise<void> {
39
19
  // The unified `Transformer` signature requires a VFile and a callback,
40
20
  // but `remarkPunctilio` only ever reads the tree. Narrow to the actual
package/src/quotes.ts CHANGED
@@ -1,9 +1,4 @@
1
- /**
2
- * Smart quote transformation: straight quotes → curly quotes.
3
- */
4
-
5
- import escapeStringRegexp from "escape-string-regexp"
6
- import { cachedRegExp, DEFAULT_SEPARATOR, getEscapedSeparator, LATIN_LETTERS, TERMINAL_PUNCTUATION, UNICODE_SYMBOLS } from "./constants.js"
1
+ import { cachedRegExp, DEFAULT_SEPARATOR, escapeStringRegexp, getEscapedSeparator, LATIN_LETTERS, TERMINAL_PUNCTUATION, UNICODE_SYMBOLS } from "./constants.js"
7
2
 
8
3
  const {
9
4
  EM_DASH,
@@ -14,15 +9,9 @@ const {
14
9
  MODIFIER_LETTER_APOSTROPHE,
15
10
  } = UNICODE_SYMBOLS
16
11
 
17
- /** Joined string of all terminal punctuation characters for use in regex character classes. */
18
12
  const TERMINAL_PUNCTUATION_CLASS = TERMINAL_PUNCTUATION.join("")
19
13
 
20
- /**
21
- * Maximum consecutive closing quotes the punctuation-placement regexes will
22
- * traverse in a single match (e.g., '". for two-deep nesting). Bounded to keep
23
- * the regex linear on pathological inputs that contain many closing quotes
24
- * without any following period or comma.
25
- */
14
+ // Bounded to keep punctuation-placement regexes linear on pathological inputs.
26
15
  const MAX_NESTED_QUOTES = 4
27
16
 
28
17
  export const PUNCTUATION_STYLES = ["american", "british", "german", "french", "none"] as const
@@ -35,7 +24,6 @@ export interface QuoteOptions {
35
24
  punctuationStyle?: PunctuationStyle
36
25
  }
37
26
 
38
- /** Convert straight single quotes to curly quotes and apostrophes */
39
27
  function convertSingleQuotes(text: string, sep: string): string {
40
28
  const escapedSep = getEscapedSeparator({ separator: sep })
41
29
 
@@ -86,11 +74,8 @@ function convertSingleQuotes(text: string, sep: string): string {
86
74
  return text
87
75
  }
88
76
 
89
- /**
90
- * Convert unmatched RSQ after s/S to MLA (plural possessives like dogs', Bayes').
91
- * Tracks LSQ/RSQ balance left-to-right so that paired closing quotes
92
- * (e.g., 'yes' where s precedes RSQ) remain RSQ.
93
- */
77
+ // Converts unmatched RSQ after s/S to MLA (plural possessives).
78
+ // Tracks LSQ/RSQ balance so paired closing quotes remain RSQ.
94
79
  function convertUnmatchedPluralPossessives(text: string, sep: string): string {
95
80
  let singleQuoteBalance = 0
96
81
  return text.replace(
@@ -116,9 +101,6 @@ function convertUnmatchedPluralPossessives(text: string, sep: string): string {
116
101
  )
117
102
  }
118
103
 
119
- /**
120
- * Build the beginning-double-quote regex pattern from named fragments.
121
- */
122
104
  function buildBeginningDoublePattern(escapedSep: string, rawEscSep: string): string {
123
105
  // Consuming boundary (not a lookbehind): variable-width lookbehinds
124
106
  // with alternation cause ReDoS. Re-emitted via $<boundary> in the replacement.
@@ -140,7 +122,6 @@ function buildBeginningDoublePattern(escapedSep: string, rawEscSep: string): str
140
122
  return `${boundary}${beforeCapture}["](?:${afterConditions.join("|")})`
141
123
  }
142
124
 
143
- /** Convert straight double quotes to curly quotes */
144
125
  function convertDoubleQuotes(text: string, sep: string): string {
145
126
  const rawEscSep = escapeStringRegexp(sep)
146
127
  const escapedSep = getEscapedSeparator({ separator: sep })
@@ -166,18 +147,8 @@ function convertDoubleQuotes(text: string, sep: string): string {
166
147
  }
167
148
 
168
149
  /**
169
- * Build a regex that matches a punctuation mark sitting OUTSIDE one or more
170
- * consecutive closing quotes, for American-style inside-the-quotes placement.
171
- *
172
- * Shape: `<non-terminal>(sepBefore)(quotes)(sepAfter)<punctuation>`
173
- *
174
- * - The lookbehind ensures the char just before the match isn't already terminal
175
- * punctuation (so we don't rewrite, e.g., "Stop!".).
176
- * - `quotes` matches 1 to MAX_NESTED_QUOTES closing quotes (possibly separated
177
- * by element-boundary markers). The bound keeps the regex linear on
178
- * pathological inputs — unbounded `*` causes catastrophic backtracking when
179
- * many closing quotes appear with no following punctuation.
180
- * - `punctuationPattern` is the trailing pattern (e.g. `,` or `(?!\.\.\.)\.`).
150
+ * Matches punctuation OUTSIDE consecutive closing quotes (American style).
151
+ * `quotes` is bounded to MAX_NESTED_QUOTES to prevent catastrophic backtracking.
181
152
  */
182
153
  function buildOutsidePunctuationRegex(escapedSep: string, punctuationPattern: string): RegExp {
183
154
  const closingQuoteClass = `[${RIGHT_SINGLE_QUOTE}${RIGHT_DOUBLE_QUOTE}]`
@@ -192,10 +163,6 @@ function buildOutsidePunctuationRegex(escapedSep: string, punctuationPattern: st
192
163
  return cachedRegExp(pattern, "g")
193
164
  }
194
165
 
195
- /**
196
- * Move a punctuation mark sitting outside one or more consecutive closing
197
- * quotes to the inside (American style): `Hello'".` → `Hello.'"`.
198
- */
199
166
  function moveOutsideToInside(
200
167
  text: string,
201
168
  escapedSep: string,
@@ -206,7 +173,6 @@ function moveOutsideToInside(
206
173
  return text.replace(regex, `$<sepBefore>${replacementChar}$<quotes>$<sepAfter>`)
207
174
  }
208
175
 
209
- /** Apply American or British punctuation style */
210
176
  function applyPunctuationStyle(text: string, sep: string, style: PunctuationStyle): string {
211
177
  const escapedSep = getEscapedSeparator({ separator: sep })
212
178
 
@@ -238,11 +204,8 @@ function applyPunctuationStyle(text: string, sep: string, style: PunctuationStyl
238
204
  return text
239
205
  }
240
206
 
241
- /**
242
- * Normalize German quotes back to American for idempotent re-processing.
243
- * Tracks „..." (U+201E/U+201C) and ‚...' (U+201A/U+2018) pairs so that
244
- * the ambiguous closer characters are mapped to the correct American equivalents.
245
- */
207
+ // Normalize German quotes back to American for idempotent re-processing.
208
+ // Tracks „..." and ‚...' pairs so ambiguous closers map correctly.
246
209
  function normalizeGermanQuotes(text: string): string {
247
210
  const DLQ = UNICODE_SYMBOLS.DOUBLE_LOW_9_QUOTE
248
211
  const SLQ = UNICODE_SYMBOLS.SINGLE_LOW_9_QUOTE
@@ -280,7 +243,6 @@ function normalizeGermanQuotes(text: string): string {
280
243
  return chars.join("")
281
244
  }
282
245
 
283
- /** Remap American curly quotes to German low-9 style. Safe because apostrophes are still MLA at this stage. */
284
246
  function applyGermanQuotes(text: string): string {
285
247
  return text
286
248
  .replaceAll(LEFT_DOUBLE_QUOTE, UNICODE_SYMBOLS.DOUBLE_LOW_9_QUOTE)
@@ -289,11 +251,8 @@ function applyGermanQuotes(text: string): string {
289
251
  .replaceAll(RIGHT_SINGLE_QUOTE, LEFT_SINGLE_QUOTE)
290
252
  }
291
253
 
292
- /**
293
- * Normalize French guillemets back to American for idempotent re-processing.
294
- * Strips either NBSP or NNBSP padding — older outputs used NBSP before the
295
- * fix to the Unicode CLDR / Imprimerie nationale prescription of NNBSP.
296
- */
254
+ // Normalize French guillemets back to American for idempotent re-processing.
255
+ // Strips either NBSP or NNBSP padding (older outputs used NBSP).
297
256
  function normalizeFrenchQuotes(text: string): string {
298
257
  const innerSpace = `[${UNICODE_SYMBOLS.NBSP}${UNICODE_SYMBOLS.NNBSP}]`
299
258
  return text
@@ -301,12 +260,7 @@ function normalizeFrenchQuotes(text: string): string {
301
260
  .replace(cachedRegExp(`${innerSpace}?${UNICODE_SYMBOLS.RIGHT_GUILLEMET}`, "g"), RIGHT_DOUBLE_QUOTE)
302
261
  }
303
262
 
304
- /**
305
- * Remap American curly double quotes to French guillemets with NNBSP padding.
306
- * Uses U+202F (NARROW NO-BREAK SPACE) per Unicode CLDR fr locale and
307
- * Imprimerie nationale's "Lexique des règles typographiques" (6th ed., 2002),
308
- * which prescribe a narrow non-breaking space — not U+00A0 — inside `« … »`.
309
- */
263
+ // Uses U+202F (NARROW NO-BREAK SPACE) per Unicode CLDR and Imprimerie nationale.
310
264
  function applyFrenchQuotes(text: string): string {
311
265
  return text
312
266
  .replaceAll(LEFT_DOUBLE_QUOTE, `${UNICODE_SYMBOLS.LEFT_GUILLEMET}${UNICODE_SYMBOLS.NNBSP}`)
@@ -318,13 +272,11 @@ interface LocaleQuoteTransform {
318
272
  apply: (text: string) => string
319
273
  }
320
274
 
321
- /** Locale-specific normalize (pre-pipeline) and apply (post-pipeline) functions. */
322
275
  const localeQuoteTransforms: Partial<Record<PunctuationStyle, LocaleQuoteTransform>> = {
323
276
  german: { normalize: normalizeGermanQuotes, apply: applyGermanQuotes },
324
277
  french: { normalize: normalizeFrenchQuotes, apply: applyFrenchQuotes },
325
278
  }
326
279
 
327
- /** Shared quote-processing pipeline. Returns text with MLA for apostrophes. */
328
280
  function processQuotes(text: string, options: QuoteOptions): string {
329
281
  const sep = options.separator ?? DEFAULT_SEPARATOR
330
282
  const punctuationStyle = options.punctuationStyle ?? "american"
package/src/rehype.ts CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Rehype plugin for applying punctilio typography transformations to HTML.
3
- *
4
- * This plugin integrates punctilio with the unified/rehype ecosystem,
5
- * allowing you to apply smart typography transformations to HTML ASTs.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
1
  import type { Element, ElementContent, Parent, Root, RootContent, Text } from "hast"
11
2
  import type { Transformer } from "unified"
12
3
 
@@ -16,29 +7,16 @@ import { transform, type TransformOptions } from "./index.js"
16
7
  import { DEFAULT_SEPARATOR, MAX_RECURSION_DEPTH } from "./constants.js"
17
8
  import { formatErrorString, transformTextNodes } from "./utils.js"
18
9
 
19
- /** Predicate that decides whether an HTML element should be skipped during transformation. */
20
10
  type ElementPredicate = (node: Element) => boolean
21
11
 
22
- /** Function that transforms a plain-text string (e.g. smart quotes, dashes). */
23
12
  type TextTransformer = (input: string) => string
24
13
 
25
- /**
26
- * Predicate that decides whether an individual text node should be excluded
27
- * from transformation. Called after element-level `shouldSkip`, so it is
28
- * never invoked for text inside elements that are already skipped.
29
- *
30
- * `ancestors` is the chain of Element parents for the text node, ordered
31
- * root first, nearest last.
32
- */
14
+ /** Per-text-node skip predicate, called after element-level `shouldSkip`. */
33
15
  export type TextNodeSkipPredicate = (
34
16
  textNode: Text,
35
17
  ancestors: readonly Element[],
36
18
  ) => boolean
37
19
 
38
- /**
39
- * Options shared by functions that flatten and transform text nodes inside
40
- * an element tree.
41
- */
42
20
  export interface ElementTransformOptions {
43
21
  /**
44
22
  * Optional per-text-node skip predicate. When it returns `true` for a
@@ -49,9 +27,6 @@ export interface ElementTransformOptions {
49
27
  shouldSkipText?: TextNodeSkipPredicate
50
28
  }
51
29
 
52
- /**
53
- * Options for the rehype-punctilio plugin.
54
- */
55
30
  export interface RehypePunctilioOptions
56
31
  extends TransformOptions,
57
32
  ElementTransformOptions {
@@ -75,18 +50,6 @@ export interface RehypePunctilioOptions
75
50
 
76
51
  const DEFAULT_SKIP_TAGS = ["code", "pre", "script", "style", "kbd", "var", "samp", "template", "math", "svg"]
77
52
 
78
- /**
79
- * Flattens text nodes from an element tree into a single array.
80
- *
81
- * Pass `options.shouldSkipText` to exclude individual text nodes from the
82
- * result. Applied after element-level `shouldSkip`, so the hook is never
83
- * invoked for text inside an already-skipped element.
84
- *
85
- * @example
86
- * ```ts
87
- * const textNodes = flattenTextNodes(paragraphElement, (el) => el.tagName === 'code')
88
- * ```
89
- */
90
53
  export function flattenTextNodes(
91
54
  node: Element | ElementContent,
92
55
  shouldSkip: ElementPredicate,
@@ -136,15 +99,6 @@ function flattenTextNodesImpl(
136
99
  return []
137
100
  }
138
101
 
139
- /**
140
- * Extracts concatenated text content from an element.
141
- *
142
- * @example
143
- * ```ts
144
- * const text = getTextContent(paragraphElement)
145
- * // Returns "Hello, world!" for <p>Hello, <em>world!</em></p>
146
- * ```
147
- */
148
102
  export function getTextContent(
149
103
  node: Element,
150
104
  shouldSkip: ElementPredicate = () => false
@@ -154,16 +108,6 @@ export function getTextContent(
154
108
  .join("")
155
109
  }
156
110
 
157
- /**
158
- * Recursively finds the first text node in a tree of HTML elements.
159
- * Returns null if no text nodes exist.
160
- *
161
- * @example
162
- * ```ts
163
- * const textNode = getFirstTextNode(divElement)
164
- * if (textNode) console.log(textNode.value)
165
- * ```
166
- */
167
111
  export function getFirstTextNode(
168
112
  node: Parent | RootContent,
169
113
  depth: number = 0
@@ -187,21 +131,8 @@ export function getFirstTextNode(
187
131
  const QUOTE_OPENERS = new Set(["\u201C"])
188
132
  const CLOSER_TO_OPENER: Record<string, string> = { "\u201D": "\u201C" }
189
133
 
190
- /**
191
- * Validates that smart double quotes in a text string are properly matched.
192
- *
193
- * Only checks double quotes (\u201C/\u201D). Single quotes are intentionally
194
- * excluded because \u2019 (right single quote) doubles as an apostrophe,
195
- * making balance-checking unreliable.
196
- *
197
- * @throws Error if double quotes are mismatched
198
- *
199
- * @example
200
- * ```ts
201
- * assertSmartQuotesMatch('\u201CHello\u201D') // OK
202
- * assertSmartQuotesMatch('\u201CHello') // throws Error
203
- * ```
204
- */
134
+ // Only checks double quotes \u2014 single quotes are excluded because
135
+ // U+2019 doubles as an apostrophe, making balance-checking unreliable.
205
136
  export function assertSmartQuotesMatch(input: string): void {
206
137
  if (!input) return
207
138
 
@@ -225,38 +156,9 @@ export function assertSmartQuotesMatch(input: string): void {
225
156
  }
226
157
 
227
158
  /**
228
- * Applies a transformation to element text content while preserving HTML structure.
229
- *
230
- * This function uses a marker technique to handle text that spans multiple
231
- * HTML elements. It:
232
- * 1. Appends a private-use Unicode character to each child's text content
233
- * 2. Concatenates and transforms the whole paragraph
234
- * 3. Splits the result back into the original text nodes
235
- *
236
- * When `checkInvariance` is true, also verifies that the transform produces
237
- * the same result with and without markers — i.e.,
238
- * `stripMarkers(transform(textWithMarkers)) === transform(stripMarkers(text))`.
239
- * Useful for debugging transforms that accidentally interact with markers.
240
- *
241
- * Pass `options.shouldSkipText` to opt individual text nodes out of
242
- * transformation without skipping their enclosing element; skipped text
243
- * nodes keep their original `.value`.
244
- *
245
- * @throws Error if transformation alters the number of text nodes
246
- * @throws Error if `checkInvariance` is true and the invariance check fails
247
- *
248
- * @example
249
- * ```ts
250
- * import { transformElement, DEFAULT_SEPARATOR } from 'punctilio/rehype'
251
- *
252
- * // Apply a custom transform to an element
253
- * transformElement(
254
- * paragraphElement,
255
- * (text) => text.replace(/eg\b/g, 'e.g.'),
256
- * (el) => el.tagName === 'code',
257
- * DEFAULT_SEPARATOR
258
- * )
259
- * ```
159
+ * Transforms element text using the separator-marking technique. When
160
+ * `checkInvariance` is true, verifies `stripMarkers(transform(marked)) ===
161
+ * transform(stripMarkers(text))` for debugging marker interactions.
260
162
  */
261
163
  export function transformElement(
262
164
  node: Element,
@@ -299,12 +201,8 @@ export function transformElement(
299
201
  }
300
202
  }
301
203
 
302
- /**
303
- * HTML block-level elements. When a transformable element has a block-level
304
- * child, its children are processed independently rather than as a single
305
- * unit. This prevents merging text across semantically independent blocks
306
- * (e.g., separate paragraphs inside a div).
307
- */
204
+ // Block children cause per-block processing to avoid merging text across
205
+ // semantically independent blocks (e.g., separate <p>s inside a <div>).
308
206
  const BLOCK_ELEMENTS = new Set([
309
207
  "address", "article", "aside", "blockquote", "details", "dialog",
310
208
  "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure",
@@ -313,10 +211,6 @@ const BLOCK_ELEMENTS = new Set([
313
211
  "p", "pre", "section", "table", "ul",
314
212
  ])
315
213
 
316
- /**
317
- * HTML elements that can contain transformable text content.
318
- * We traverse into these to find text nodes to transform.
319
- */
320
214
  const TRANSFORMABLE_ELEMENTS = new Set([
321
215
  "p",
322
216
  "em",
@@ -374,11 +268,6 @@ const TRANSFORMABLE_ELEMENTS = new Set([
374
268
  "rp",
375
269
  ])
376
270
 
377
- /**
378
- * Checks whether an element has any text node descendants (skipping elements
379
- * matched by shouldSkip). Returns as soon as one is found — avoids allocating
380
- * an array of all text nodes just to check existence.
381
- */
382
271
  function hasTextDescendant(
383
272
  node: Element | ElementContent,
384
273
  shouldSkip: ElementPredicate,
@@ -403,20 +292,6 @@ function hasTextDescendant(
403
292
  return false
404
293
  }
405
294
 
406
- /**
407
- * Collects elements that should have text transformations applied.
408
- *
409
- * An element is collected as a single transformation unit when it:
410
- * 1. Has direct text children (original behavior), OR
411
- * 2. Has text descendants and NO block-level children — meaning all its
412
- * content is inline (phrasing) and should share transformation context
413
- * (e.g., `<p><em>"Hello</em><span>, world"</span></p>`).
414
- *
415
- * When an element has block-level children, we recurse so each block
416
- * is processed independently (e.g., `<div><p>A</p><p>B</p></div>`).
417
- *
418
- * @param alreadyTransformed - Elements already processed; skipped during traversal to avoid redundant work
419
- */
420
295
  export function collectTransformableElements(
421
296
  node: Element,
422
297
  shouldSkip: ElementPredicate,
@@ -462,11 +337,6 @@ export function collectTransformableElements(
462
337
  return results
463
338
  }
464
339
 
465
- /**
466
- * Recursively marks all element descendants as transformed.
467
- * Used to prevent redundant processing of nested elements whose text
468
- * was already processed as part of a parent element.
469
- */
470
340
  function markDescendants(node: Element, set: Set<Element>, depth: number = 0): void {
471
341
  /* istanbul ignore if -- defensive: prevents stack overflow from malicious HTML */
472
342
  if (depth > MAX_RECURSION_DEPTH) {
@@ -481,30 +351,6 @@ function markDescendants(node: Element, set: Set<Element>, depth: number = 0): v
481
351
  }
482
352
  }
483
353
 
484
- /**
485
- * Rehype plugin that applies punctilio typography transformations to HTML.
486
- *
487
- * @example
488
- * ```ts
489
- * import { unified } from 'unified'
490
- * import remarkParse from 'remark-parse'
491
- * import remarkRehype from 'remark-rehype'
492
- * import rehypeStringify from 'rehype-stringify'
493
- * import { rehypePunctilio } from 'punctilio/rehype'
494
- *
495
- * const result = await unified()
496
- * .use(remarkParse)
497
- * .use(remarkRehype)
498
- * .use(rehypePunctilio, {
499
- * punctuationStyle: 'american',
500
- * dashStyle: 'american',
501
- * })
502
- * .use(rehypeStringify)
503
- * .process('"Hello," she said -- "it\'s nice."')
504
- *
505
- * // Output HTML will have smart quotes and em-dashes
506
- * ```
507
- */
508
354
  export function rehypePunctilio(
509
355
  options: RehypePunctilioOptions = {}
510
356
  ): Transformer<Root, Root> {
package/src/remark.ts CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Remark plugin for applying punctilio typography transformations to Markdown.
3
- *
4
- * This plugin integrates punctilio with the unified/remark ecosystem,
5
- * allowing you to apply smart typography transformations to Markdown ASTs.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
1
  import type { PhrasingContent, Root, Text } from "mdast"
11
2
  import type { Transformer } from "unified"
12
3
 
@@ -18,21 +9,10 @@ import { transformTextNodes } from "./utils.js"
18
9
 
19
10
  export type RemarkPunctilioOptions = TransformOptions
20
11
 
21
- /**
22
- * MDAST node types that contain phrasing (inline) content and should
23
- * have their text transformed as a single unit.
24
- */
25
12
  const PHRASING_CONTAINERS = new Set(["paragraph", "heading", "tableCell"])
26
13
 
27
- /**
28
- * MDAST node types whose text content should not be transformed.
29
- */
30
14
  const SKIP_TYPES = new Set(["inlineCode", "html"])
31
15
 
32
- /**
33
- * Recursively collects text nodes from a phrasing content tree,
34
- * skipping code and HTML nodes.
35
- */
36
16
  function flattenTextNodes(
37
17
  node: PhrasingContent,
38
18
  depth: number = 0
@@ -59,28 +39,6 @@ function flattenTextNodes(
59
39
  return []
60
40
  }
61
41
 
62
- /**
63
- * Remark plugin that applies punctilio typography transformations to Markdown.
64
- *
65
- * @example
66
- * ```ts
67
- * import { unified } from 'unified'
68
- * import remarkParse from 'remark-parse'
69
- * import remarkStringify from 'remark-stringify'
70
- * import remarkPunctilio from 'punctilio/remark'
71
- *
72
- * const result = await unified()
73
- * .use(remarkParse)
74
- * .use(remarkPunctilio, {
75
- * punctuationStyle: 'american',
76
- * dashStyle: 'american',
77
- * })
78
- * .use(remarkStringify)
79
- * .process('"Hello," she said -- "it\'s nice."')
80
- *
81
- * // Output Markdown will have smart quotes and em-dashes
82
- * ```
83
- */
84
42
  export function remarkPunctilio(
85
43
  options: RemarkPunctilioOptions = {}
86
44
  ): Transformer<Root, Root> {