-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransform.ts
More file actions
138 lines (134 loc) · 4.32 KB
/
transform.ts
File metadata and controls
138 lines (134 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* @fileoverview String transformations: `stripBom`, `toKebabCase`,
* `trimNewlines`. All three are pure functions with no side effects.
*/
import {
StringPrototypeCharCodeAt,
StringPrototypeReplace,
StringPrototypeSlice,
} from '../primordials/string'
/**
* Strip the Byte Order Mark (BOM) from the beginning of a string.
*
* The BOM (U+FEFF) is a Unicode character that can appear at the start of
* a text file to indicate byte order and encoding. In UTF-16 (JavaScript's
* internal string representation), it appears as 0xFEFF. This function
* removes it if present, leaving the rest of the string unchanged.
*
* Most text processing doesn't need to handle the BOM explicitly, but it
* can cause issues when parsing JSON, CSV, or other structured data formats
* that don't expect a leading invisible character.
*
* @param str - The string to strip BOM from
* @returns The string without BOM
*
* @example
* ```ts
* stripBom('hello world') // 'hello world'
* stripBom('hello world') // 'hello world'
* stripBom('') // ''
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function stripBom(str: string): string {
// In JavaScript, string data is stored as UTF-16, so BOM is 0xFEFF.
// https://tc39.es/ecma262/#sec-unicode-format-control-characters
return str.length > 0 && StringPrototypeCharCodeAt(str, 0) === 0xfe_ff
? StringPrototypeSlice(str, 1)
: str
}
/**
* Convert a string to kebab-case (handles camelCase and snake_case).
*
* Transforms strings from camelCase or snake_case to kebab-case by:
* - Converting uppercase letters to lowercase
* - Inserting hyphens before uppercase letters (for camelCase)
* - Replacing underscores with hyphens (for snake_case)
*
* Handles mixed formats (camelCase, snake_case, acronyms) in one pass.
* Returns empty string for empty input.
*
* @param str - The string to convert
* @returns The kebab-case string
*
* @example
* ```ts
* toKebabCase('helloWorld') // 'hello-world'
* toKebabCase('hello_world') // 'hello-world'
* toKebabCase('XMLHttpRequest') // 'xmlhttp-request'
* toKebabCase('iOS_Version') // 'i-os-version'
* toKebabCase('') // ''
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function toKebabCase(str: string): string {
if (!str.length) {
return str
}
return (
StringPrototypeReplace(str, /([a-z]+[0-9]*)([A-Z])/g, '$1-$2')
// Convert underscores to hyphens
.replace(/_/g, '-')
.toLowerCase()
)
}
/**
* Trim newlines from the beginning and end of a string.
*
* Removes all leading and trailing newline characters (both `\n` and `\r`)
* from a string, while preserving any newlines in the middle. This is similar
* to `String.prototype.trim()` but specifically targets newlines instead of
* all whitespace.
*
* Optimized for performance by checking the first and last characters before
* doing any string manipulation. Returns the original string unchanged if no
* newlines are found at the edges.
*
* @param str - The string to trim
* @returns The string with leading and trailing newlines removed
*
* @example
* ```ts
* trimNewlines('\n\nhello\n\n') // 'hello'
* trimNewlines('\r\nworld\r\n') // 'world'
* trimNewlines('hello\nworld') // 'hello\nworld' (middle preserved)
* trimNewlines(' hello ') // ' hello ' (spaces not trimmed)
* trimNewlines('hello') // 'hello'
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function trimNewlines(str: string): string {
const { length } = str
if (length === 0) {
return str
}
const first = StringPrototypeCharCodeAt(str, 0)
const noFirstNewline = first !== 13 /*'\r'*/ && first !== 10 /*'\n'*/
if (length === 1) {
return noFirstNewline ? str : ''
}
const last = StringPrototypeCharCodeAt(str, length - 1)
const noLastNewline = last !== 13 /*'\r'*/ && last !== 10 /*'\n'*/
if (noFirstNewline && noLastNewline) {
return str
}
let start = 0
let end = length
while (start < end) {
const code = StringPrototypeCharCodeAt(str, start)
if (code !== 13 /*'\r'*/ && code !== 10 /*'\n'*/) {
break
}
start += 1
}
while (end > start) {
const code = StringPrototypeCharCodeAt(str, end - 1)
if (code !== 13 /*'\r'*/ && code !== 10 /*'\n'*/) {
break
}
end -= 1
}
return start === 0 && end === length
? str
: StringPrototypeSlice(str, start, end)
}