@@ -28,20 +28,23 @@ function makeIndent(visualLevels: number, cfg: PickierConfig): string {
2828}
2929
3030function convertDoubleToSingle ( str : string ) : string {
31- // strip surrounding quotes
31+ // strip surrounding quotes: "xyz" → xyz
3232 const inner = str . slice ( 1 , - 1 )
33- // unescape escaped double quotes, keep other escapes intact
34- const unescaped = inner . replace ( / \\ " / g, '"' )
35- // escape single quotes that aren't already escaped
36- const escapedSingles = unescaped . replace ( / (?< ! \\ ) ' / g, '\\\'' )
37- return `'${ escapedSingles } '`
33+ // Strategy: swap quote types for cleaner output
34+ // Unescape escaped double quotes: \" → "
35+ // Swap single quotes to double quotes: ' → " (since they're literals in the string)
36+ let result = inner . replace ( / \\ " / g, '"' ) // unescape \"
37+ result = result . replace ( / ' / g, '"' ) // swap ' to "
38+ return `'${ result } '`
3839}
3940
4041function convertSingleToDouble ( str : string ) : string {
4142 const inner = str . slice ( 1 , - 1 )
42- const unescaped = inner . replace ( / \\ ' / g, '\'' )
43- const escapedDoubles = unescaped . replace ( / " / g, '\\"' )
44- return `"${ escapedDoubles } "`
43+ // Unescape escaped single quotes: \' → '
44+ // Swap double quotes to single quotes: " → '
45+ let result = inner . replace ( / \\ ' / g, '\'' )
46+ result = result . replace ( / " / g, '\'' )
47+ return `"${ result } "`
4548}
4649
4750function fixQuotes ( content : string , preferred : 'single' | 'double' , filePath : string ) : string {
@@ -56,26 +59,11 @@ function fixQuotes(content: string, preferred: 'single' | 'double', filePath: st
5659 let output = ''
5760 let i = 0
5861 let inString : 'single' | 'double' | 'template' | null = null
59- let escaped = false
6062 let stringStart = 0
6163
6264 while ( i < line . length ) {
6365 const ch = line [ i ]
6466
65- if ( escaped ) {
66- output += ch
67- escaped = false
68- i ++
69- continue
70- }
71-
72- if ( ch === '\\' && inString ) {
73- escaped = true
74- output += ch
75- i ++
76- continue
77- }
78-
7967 // Check for string boundaries
8068 if ( ! inString ) {
8169 if ( ch === '"' ) {
@@ -100,8 +88,17 @@ function fixQuotes(content: string, preferred: 'single' | 'double', filePath: st
10088 i ++
10189 }
10290 else {
103- // Inside a string - check if we're exiting
104- if ( ( inString === 'double' && ch === '"' ) || ( inString === 'single' && ch === '\'' ) ) {
91+ // Inside a string - check if we're exiting (but not if escaped)
92+ // Count consecutive backslashes before this position
93+ let backslashCount = 0
94+ let j = i - 1
95+ while ( j >= 0 && line [ j ] === '\\' ) {
96+ backslashCount ++
97+ j --
98+ }
99+ const isEscaped = backslashCount % 2 === 1
100+
101+ if ( ! isEscaped && ( ( inString === 'double' && ch === '"' ) || ( inString === 'single' && ch === '\'' ) ) ) {
105102 // Found closing quote - convert if needed
106103 const stringContent = line . slice ( stringStart + 1 , i )
107104 if ( inString === 'double' && preferred === 'single' ) {
@@ -120,14 +117,14 @@ function fixQuotes(content: string, preferred: 'single' | 'double', filePath: st
120117 i ++
121118 continue
122119 }
123- if ( inString === 'template' && ch === '`' ) {
120+ if ( ! isEscaped && inString === 'template' && ch === '`' ) {
124121 // Template literal end
125122 output += ch
126123 inString = null
127124 i ++
128125 continue
129126 }
130- // Still inside string, buffer it
127+ // Still inside string, continue scanning
131128 i ++
132129 }
133130 }
0 commit comments