Skip to content

Commit 746a566

Browse files
committed
refactor: move escape functions to own file
1 parent 35b5364 commit 746a566

File tree

5 files changed

+154
-151
lines changed

5 files changed

+154
-151
lines changed

src/escape.spec.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import * as properties from '.'
2+
3+
describe('escapeKey', () => {
4+
it.each([
5+
['foo1', 'foo1'],
6+
['foo2:', 'foo2\\:'],
7+
['foo3=', 'foo3\\='],
8+
['foo4\t', 'foo4\\t'],
9+
['foo5 ', 'foo5\\ '],
10+
[' foo6', '\\ foo6'],
11+
['#foo7', '\\#foo7'],
12+
['!foo8#', '\\!foo8\\#'],
13+
['fo o9', 'fo\\ \\ o9'],
14+
['foo10\n', 'foo10\\n'],
15+
['f\r\f\n\too11', 'f\\r\\f\\n\\too11'],
16+
['\\foo12\\', '\\\\foo12\\\\'],
17+
['\0\u0001', '\\u0000\\u0001'],
18+
['\u3053\u3093\u306B\u3061\u306F', '\\u3053\\u3093\\u306b\\u3061\\u306f'],
19+
['こんにちは', '\\u3053\\u3093\\u306b\\u3061\\u306f']
20+
])('should escape key "%s" as "%s"', (key: string, expected: string) => {
21+
const result = properties.escapeKey(key)
22+
expect(result).toEqual(expected)
23+
})
24+
})
25+
26+
describe('escapeValue', () => {
27+
it.each([
28+
['foo1', 'foo1'],
29+
['foo2:', 'foo2\\:'],
30+
['foo3=', 'foo3\\='],
31+
['foo4\t', 'foo4\\t'],
32+
['foo5 ', 'foo5 '],
33+
[' foo6', '\\ foo6'],
34+
['#foo7', '\\#foo7'],
35+
['!foo8#', '\\!foo8\\#'],
36+
['fo o9', 'fo o9'],
37+
['foo10\n', 'foo10\\n'],
38+
['f\r\f\n\too11', 'f\\r\\f\\n\\too11'],
39+
['\\foo12\\', '\\\\foo12\\\\'],
40+
['\0\u0001', '\\u0000\\u0001'],
41+
['\u3053\u3093\u306B\u3061\u306F', '\\u3053\\u3093\\u306b\\u3061\\u306f'],
42+
['こんにちは', '\\u3053\\u3093\\u306b\\u3061\\u306f']
43+
])('should escape value "%s" as "%s"', (key: string, expected: string) => {
44+
const result = properties.escapeValue(key)
45+
expect(result).toEqual(expected)
46+
})
47+
})

src/escape.ts

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* Escape property key.
3+
*
4+
* @param unescapedKey Property key to be escaped.
5+
* @param escapeUnicode Escape unicode chars (below 0x0020 and above 0x007e). Default is true.
6+
* @return Escaped string.
7+
*/
8+
export const escapeKey = (
9+
unescapedKey: string,
10+
escapeUnicode = true
11+
): string => {
12+
return escape(unescapedKey, true, escapeUnicode)
13+
}
14+
15+
/**
16+
* Escape property value.
17+
*
18+
* @param unescapedValue Property value to be escaped.
19+
* @param escapeUnicode Escape unicode chars (below 0x0020 and above 0x007e). Default is true.
20+
* @return Escaped string.
21+
*/
22+
export const escapeValue = (
23+
unescapedValue: string,
24+
escapeUnicode = true
25+
): string => {
26+
return escape(unescapedValue, false, escapeUnicode)
27+
}
28+
29+
/**
30+
* Internal escape method.
31+
*
32+
* @param unescapedContent Text to be escaped.
33+
* @param escapeSpace Whether all spaces should be escaped
34+
* @param escapeUnicode Whether unicode chars should be escaped
35+
* @return Escaped string.
36+
*/
37+
const escape = (
38+
unescapedContent: string,
39+
escapeSpace: boolean,
40+
escapeUnicode: boolean
41+
): string => {
42+
const result: string[] = []
43+
44+
for (let index = 0; index < unescapedContent.length; index++) {
45+
const char = unescapedContent[index]
46+
switch (char) {
47+
case ' ': {
48+
// Escape space if required, or if it is first character
49+
if (escapeSpace || index === 0) {
50+
result.push('\\ ')
51+
} else {
52+
result.push(' ')
53+
}
54+
break
55+
}
56+
case '\\': {
57+
result.push('\\\\')
58+
break
59+
}
60+
case '\f': {
61+
// Form-feed
62+
result.push('\\f')
63+
break
64+
}
65+
case '\n': {
66+
// Newline
67+
result.push('\\n')
68+
break
69+
}
70+
case '\r': {
71+
// Carriage return
72+
result.push('\\r')
73+
break
74+
}
75+
case '\t': {
76+
// Tab
77+
result.push('\\t')
78+
break
79+
}
80+
case '=': // Fall through
81+
case ':': // Fall through
82+
case '#': // Fall through
83+
case '!': {
84+
result.push('\\', char)
85+
break
86+
}
87+
default: {
88+
if (escapeUnicode) {
89+
const codePoint: number = char.codePointAt(0) as number // can never be undefined
90+
if (codePoint < 0x0020 || codePoint > 0x007e) {
91+
result.push('\\u', codePoint.toString(16).padStart(4, '0'))
92+
break
93+
}
94+
}
95+
// Normal char
96+
result.push(char)
97+
break
98+
}
99+
}
100+
}
101+
102+
return result.join('')
103+
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './escape'
12
export * from './properties'

src/properties.spec.ts

+1-47
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as fs from 'node:fs/promises'
22

3-
import properties from '.'
3+
import * as properties from '.'
44

55
describe('parse', () => {
66
it('should parse all lines', () => {
@@ -411,49 +411,3 @@ describe('data access', () => {
411411
})
412412
})
413413
})
414-
415-
describe('escapeKey', () => {
416-
it.each([
417-
['foo1', 'foo1'],
418-
['foo2:', 'foo2\\:'],
419-
['foo3=', 'foo3\\='],
420-
['foo4\t', 'foo4\\t'],
421-
['foo5 ', 'foo5\\ '],
422-
[' foo6', '\\ foo6'],
423-
['#foo7', '\\#foo7'],
424-
['!foo8#', '\\!foo8\\#'],
425-
['fo o9', 'fo\\ \\ o9'],
426-
['foo10\n', 'foo10\\n'],
427-
['f\r\f\n\too11', 'f\\r\\f\\n\\too11'],
428-
['\\foo12\\', '\\\\foo12\\\\'],
429-
['\0\u0001', '\\u0000\\u0001'],
430-
['\u3053\u3093\u306B\u3061\u306F', '\\u3053\\u3093\\u306b\\u3061\\u306f'],
431-
['こんにちは', '\\u3053\\u3093\\u306b\\u3061\\u306f']
432-
])('should escape key "%s" as "%s"', (key: string, expected: string) => {
433-
const result = properties.escapeKey(key)
434-
expect(result).toEqual(expected)
435-
})
436-
})
437-
438-
describe('escapeValue', () => {
439-
it.each([
440-
['foo1', 'foo1'],
441-
['foo2:', 'foo2\\:'],
442-
['foo3=', 'foo3\\='],
443-
['foo4\t', 'foo4\\t'],
444-
['foo5 ', 'foo5 '],
445-
[' foo6', '\\ foo6'],
446-
['#foo7', '\\#foo7'],
447-
['!foo8#', '\\!foo8\\#'],
448-
['fo o9', 'fo o9'],
449-
['foo10\n', 'foo10\\n'],
450-
['f\r\f\n\too11', 'f\\r\\f\\n\\too11'],
451-
['\\foo12\\', '\\\\foo12\\\\'],
452-
['\0\u0001', '\\u0000\\u0001'],
453-
['\u3053\u3093\u306B\u3061\u306F', '\\u3053\\u3093\\u306b\\u3061\\u306f'],
454-
['こんにちは', '\\u3053\\u3093\\u306b\\u3061\\u306f']
455-
])('should escape value "%s" as "%s"', (key: string, expected: string) => {
456-
const result = properties.escapeValue(key)
457-
expect(result).toEqual(expected)
458-
})
459-
})

src/properties.ts

+2-104
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {escapeKey, escapeValue} from './escape'
2+
13
/**
24
* Wrapper for java properties file contents
35
*/
@@ -495,107 +497,3 @@ const parseUnicode = (sequence: string, line: number): string => {
495497
}
496498
return String.fromCodePoint(parseInt(sequence, 16))
497499
}
498-
499-
/**
500-
* Escape property key.
501-
*
502-
* @param unescapedKey Property key to be escaped.
503-
* @param escapeUnicode Escape unicode chars (below 0x0020 and above 0x007e). Default is true.
504-
* @return Escaped string.
505-
*/
506-
export const escapeKey = (
507-
unescapedKey: string,
508-
escapeUnicode = true
509-
): string => {
510-
return escape(unescapedKey, true, escapeUnicode)
511-
}
512-
513-
/**
514-
* Escape property value.
515-
*
516-
* @param unescapedValue Property value to be escaped.
517-
* @param escapeUnicode Escape unicode chars (below 0x0020 and above 0x007e). Default is true.
518-
* @return Escaped string.
519-
*/
520-
export const escapeValue = (
521-
unescapedValue: string,
522-
escapeUnicode = true
523-
): string => {
524-
return escape(unescapedValue, false, escapeUnicode)
525-
}
526-
527-
/**
528-
* Internal escape method.
529-
*
530-
* @param unescapedContent Text to be escaped.
531-
* @param escapeSpace Whether all spaces should be escaped
532-
* @param escapeUnicode Whether unicode chars should be escaped
533-
* @return Escaped string.
534-
*/
535-
const escape = (
536-
unescapedContent: string,
537-
escapeSpace: boolean,
538-
escapeUnicode: boolean
539-
): string => {
540-
const result: string[] = []
541-
542-
for (let index = 0; index < unescapedContent.length; index++) {
543-
const char = unescapedContent[index]
544-
switch (char) {
545-
case ' ': {
546-
// Escape space if required, or if it is first character
547-
if (escapeSpace || index === 0) {
548-
result.push('\\ ')
549-
} else {
550-
result.push(' ')
551-
}
552-
break
553-
}
554-
case '\\': {
555-
result.push('\\\\')
556-
break
557-
}
558-
case '\f': {
559-
// Form-feed
560-
result.push('\\f')
561-
break
562-
}
563-
case '\n': {
564-
// Newline
565-
result.push('\\n')
566-
break
567-
}
568-
case '\r': {
569-
// Carriage return
570-
result.push('\\r')
571-
break
572-
}
573-
case '\t': {
574-
// Tab
575-
result.push('\\t')
576-
break
577-
}
578-
case '=': // Fall through
579-
case ':': // Fall through
580-
case '#': // Fall through
581-
case '!': {
582-
result.push('\\', char)
583-
break
584-
}
585-
default: {
586-
if (escapeUnicode) {
587-
const codePoint: number = char.codePointAt(0) as number // can never be undefined
588-
if (codePoint < 0x0020 || codePoint > 0x007e) {
589-
result.push('\\u', codePoint.toString(16).padStart(4, '0'))
590-
break
591-
}
592-
}
593-
// Normal char
594-
result.push(char)
595-
break
596-
}
597-
}
598-
}
599-
600-
return result.join('')
601-
}

0 commit comments

Comments
 (0)