@herb-tools/formatter 0.8.7 → 0.8.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herb-tools/formatter",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "description": "Auto-formatter for HTML+ERB templates with intelligent indentation, line wrapping, and ERB-aware pretty-printing.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://herb-tools.dev",
@@ -35,10 +35,10 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@herb-tools/config": "0.8.7",
39
- "@herb-tools/core": "0.8.7",
40
- "@herb-tools/printer": "0.8.7",
41
- "@herb-tools/rewriter": "0.8.7",
38
+ "@herb-tools/config": "0.8.9",
39
+ "@herb-tools/core": "0.8.9",
40
+ "@herb-tools/printer": "0.8.9",
41
+ "@herb-tools/rewriter": "0.8.9",
42
42
  "tinyglobby": "^0.2.15"
43
43
  },
44
44
  "files": [
@@ -97,6 +97,12 @@ import {
97
97
  import type { ERBNode } from "@herb-tools/core"
98
98
  import type { FormatOptions } from "./options.js"
99
99
 
100
+ /**
101
+ * ASCII whitespace pattern - use instead of \s to preserve Unicode whitespace
102
+ * characters like NBSP (U+00A0) and full-width space (U+3000)
103
+ */
104
+ const ASCII_WHITESPACE = /[ \t\n\r]+/g
105
+
100
106
  /**
101
107
  * Printer traverses the Herb AST using the Visitor pattern
102
108
  * and emits a formatted string with proper indentation, line breaks, and attribute wrapping.
@@ -551,7 +557,7 @@ export class FormatPrinter extends Printer {
551
557
  }
552
558
 
553
559
  private wouldClassAttributeBeMultiline(content: string, indentLength: number): boolean {
554
- const normalizedContent = content.replace(/\s+/g, ' ').trim()
560
+ const normalizedContent = content.replace(ASCII_WHITESPACE, ' ').trim()
555
561
  const hasActualNewlines = /\r?\n/.test(content)
556
562
 
557
563
  if (hasActualNewlines && normalizedContent.length > 80) {
@@ -601,7 +607,7 @@ export class FormatPrinter extends Printer {
601
607
  const name = attribute.name ? getCombinedAttributeName(attribute.name) : ""
602
608
 
603
609
  if (name === "class") {
604
- const normalizedContent = content.replace(/\s+/g, ' ').trim()
610
+ const normalizedContent = content.replace(ASCII_WHITESPACE, ' ').trim()
605
611
 
606
612
  return normalizedContent.length > 80
607
613
  }
@@ -609,7 +615,7 @@ export class FormatPrinter extends Printer {
609
615
  const lines = content.split(/\r?\n/)
610
616
 
611
617
  if (lines.length > 1) {
612
- return lines.slice(1).some(line => /^\s+/.test(line))
618
+ return lines.slice(1).some(line => /^[ \t\n\r]+/.test(line))
613
619
  }
614
620
  }
615
621
  }
@@ -619,7 +625,7 @@ export class FormatPrinter extends Printer {
619
625
  }
620
626
 
621
627
  private formatClassAttribute(content: string, name: string, equals: string, open_quote: string, close_quote: string): string {
622
- const normalizedContent = content.replace(/\s+/g, ' ').trim()
628
+ const normalizedContent = content.replace(ASCII_WHITESPACE, ' ').trim()
623
629
  const hasActualNewlines = /\r?\n/.test(content)
624
630
 
625
631
  if (hasActualNewlines && normalizedContent.length > 80) {
@@ -658,7 +664,7 @@ export class FormatPrinter extends Printer {
658
664
 
659
665
  private formatMultilineAttribute(content: string, name: string, open_quote: string, close_quote: string): string {
660
666
  if (name === 'srcset' || name === 'sizes') {
661
- const normalizedContent = content.replace(/\s+/g, ' ').trim()
667
+ const normalizedContent = content.replace(ASCII_WHITESPACE, ' ').trim()
662
668
 
663
669
  return open_quote + normalizedContent + close_quote
664
670
  }
@@ -899,7 +905,7 @@ export class FormatPrinter extends Printer {
899
905
  nodesToRender.forEach(child => {
900
906
  if (isNode(child, HTMLTextNode)) {
901
907
  if (hasTextFlow) {
902
- const normalizedContent = child.content.replace(/\s+/g, ' ')
908
+ const normalizedContent = child.content.replace(ASCII_WHITESPACE, ' ')
903
909
 
904
910
  if (normalizedContent && normalizedContent !== ' ') {
905
911
  this.push(normalizedContent)
@@ -907,7 +913,7 @@ export class FormatPrinter extends Printer {
907
913
  this.push(' ')
908
914
  }
909
915
  } else {
910
- const normalizedContent = child.content.replace(/\s+/g, ' ')
916
+ const normalizedContent = child.content.replace(ASCII_WHITESPACE, ' ')
911
917
 
912
918
  if (shouldPreserveSpaces && normalizedContent) {
913
919
  this.push(normalizedContent)
@@ -932,8 +938,8 @@ export class FormatPrinter extends Printer {
932
938
  const content = lines.join('')
933
939
 
934
940
  const inlineContent = shouldPreserveSpaces
935
- ? (hasTextFlow ? content.replace(/\s+/g, ' ') : content)
936
- : (hasTextFlow ? content.replace(/\s+/g, ' ').trim() : content.trim())
941
+ ? (hasTextFlow ? content.replace(ASCII_WHITESPACE, ' ') : content)
942
+ : (hasTextFlow ? content.replace(ASCII_WHITESPACE, ' ').trim() : content.trim())
937
943
 
938
944
  if (inlineContent) {
939
945
  this.pushToLastLine(inlineContent)
@@ -1177,7 +1183,7 @@ export class FormatPrinter extends Printer {
1177
1183
 
1178
1184
  visitHTMLTextNode(node: HTMLTextNode) {
1179
1185
  if (this.inlineMode) {
1180
- const normalizedContent = node.content.replace(/\s+/g, ' ').trim()
1186
+ const normalizedContent = node.content.replace(ASCII_WHITESPACE, ' ').trim()
1181
1187
 
1182
1188
  if (normalizedContent) {
1183
1189
  this.push(normalizedContent)
@@ -1191,7 +1197,7 @@ export class FormatPrinter extends Printer {
1191
1197
  if (!text) return
1192
1198
 
1193
1199
  const wrapWidth = this.maxLineLength - this.indent.length
1194
- const words = text.split(/\s+/)
1200
+ const words = text.split(/[ \t\n\r]+/)
1195
1201
  const lines: string[] = []
1196
1202
 
1197
1203
  let line = ""
@@ -1813,7 +1819,7 @@ export class FormatPrinter extends Printer {
1813
1819
  }
1814
1820
  }
1815
1821
 
1816
- const words = restText.split(/\s+/)
1822
+ const words = restText.split(/[ \t\n\r]+/)
1817
1823
  let toMerge = punctuation
1818
1824
  let mergedWordCount = 0
1819
1825
 
@@ -1999,7 +2005,7 @@ export class FormatPrinter extends Printer {
1999
2005
  } else if (unit.isAtomic) {
2000
2006
  words.push({ word: unit.content, isHerbDisable: unit.isHerbDisable || false })
2001
2007
  } else {
2002
- const text = unit.content.replace(/\s+/g, ' ')
2008
+ const text = unit.content.replace(ASCII_WHITESPACE, ' ')
2003
2009
  const hasLeadingSpace = text.startsWith(' ')
2004
2010
  const hasTrailingSpace = text.endsWith(' ')
2005
2011
  const trimmedText = text.trim()
@@ -2055,7 +2061,7 @@ export class FormatPrinter extends Printer {
2055
2061
  const firstWord = words[0]
2056
2062
  const firstChar = firstWord[0]
2057
2063
 
2058
- if (/\s/.test(firstChar)) {
2064
+ if (' \t\n\r'.includes(firstChar)) {
2059
2065
  return false
2060
2066
  }
2061
2067
 
@@ -2125,7 +2131,7 @@ export class FormatPrinter extends Printer {
2125
2131
  return true
2126
2132
  }
2127
2133
 
2128
- if (isNode(currentNode, HTMLTextNode) && /^\s/.test(currentNode.content)) {
2134
+ if (isNode(currentNode, HTMLTextNode) && /^[ \t\n\r]/.test(currentNode.content)) {
2129
2135
  return true
2130
2136
  }
2131
2137
 
@@ -2542,9 +2548,9 @@ export class FormatPrinter extends Printer {
2542
2548
 
2543
2549
  for (const child of children) {
2544
2550
  if (isNode(child, HTMLTextNode)) {
2545
- const normalizedContent = child.content.replace(/\s+/g, ' ')
2546
- const hasLeadingSpace = /^\s/.test(child.content)
2547
- const hasTrailingSpace = /\s$/.test(child.content)
2551
+ const normalizedContent = child.content.replace(ASCII_WHITESPACE, ' ')
2552
+ const hasLeadingSpace = /^[ \t\n\r]/.test(child.content)
2553
+ const hasTrailingSpace = /[ \t\n\r]$/.test(child.content)
2548
2554
  const trimmedContent = normalizedContent.trim()
2549
2555
 
2550
2556
  if (trimmedContent) {
@@ -2675,6 +2681,6 @@ export class FormatPrinter extends Printer {
2675
2681
  }
2676
2682
  }
2677
2683
 
2678
- return content.replace(/\s+/g, ' ').trim()
2684
+ return content.replace(ASCII_WHITESPACE, ' ').trim()
2679
2685
  }
2680
2686
  }