bahttext 2.3.0 → 2.3.2

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/.eslintignore ADDED
File without changes
package/.eslintrc ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "overrides": [
3
+ {
4
+ "files": [
5
+ "**/*.test.js"
6
+ ],
7
+ "env": {
8
+ "jest": true
9
+ }
10
+ }
11
+ ]
12
+ }
package/README-en.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # bahttext
2
2
 
3
3
  [![Version - npm](https://img.shields.io/npm/v/bahttext.svg)](https://www.npmjs.com/package/bahttext)
4
+ [![Download - npm](https://img.shields.io/npm/dt/bahttext.svg)](https://www.npmjs.com/package/bahttext)
4
5
  [![License - npm](https://img.shields.io/npm/l/bahttext.svg)](http://opensource.org/licenses/MIT)
5
6
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release) [![Greenkeeper badge](https://badges.greenkeeper.io/jojoee/bahttext.svg)](https://greenkeeper.io/)
6
7
  [![Codecov](https://img.shields.io/codecov/c/github/jojoee/bahttext.svg)](https://codecov.io/github/jojoee/bahttext)
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # bahttext
2
2
 
3
3
  [![Version - npm](https://img.shields.io/npm/v/bahttext.svg)](https://www.npmjs.com/package/bahttext)
4
+ [![Download - npm](https://img.shields.io/npm/dt/bahttext.svg)](https://www.npmjs.com/package/bahttext)
4
5
  [![License - npm](https://img.shields.io/npm/l/bahttext.svg)](http://opensource.org/licenses/MIT)
5
6
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release) [![Greenkeeper badge](https://badges.greenkeeper.io/jojoee/bahttext.svg)](https://greenkeeper.io/)
6
7
  [![Codecov](https://img.shields.io/codecov/c/github/jojoee/bahttext.svg)](https://codecov.io/github/jojoee/bahttext)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bahttext",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Change number to Thai pronunciation string",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
@@ -35,16 +35,15 @@
35
35
  },
36
36
  "homepage": "https://github.com/jojoee/bahttext#readme",
37
37
  "devDependencies": {
38
- "@stryker-mutator/core": "^6.3.1",
39
- "@stryker-mutator/jest-runner": "^6.3.1",
40
- "@to-da-moon/thai-baht-lib": "^0.0.12",
38
+ "@stryker-mutator/core": "^8.7.1",
39
+ "@stryker-mutator/jest-runner": "^8.7.1",
41
40
  "baht": "^0.7.1",
42
- "jest": "^26.6.3",
43
- "jest-expect-message": "^1.0.2",
44
- "semantic-release": "^19.0.5",
45
- "standard": "^15.0.1",
46
- "thai-baht-text": "^1.0.8",
47
- "thai-baht-text-ts": "^1.1.0",
41
+ "bahttext230": "npm:bahttext@2.3.0",
42
+ "bahttext231": "npm:bahttext@2.3.1",
43
+ "jest": "^29.7.0",
44
+ "jest-expect-message": "^1.1.3",
45
+ "semantic-release": "^24.2.2",
46
+ "standard": "^17.1.2",
48
47
  "typescript": "*"
49
48
  },
50
49
  "jest": {
package/src/index.d.ts CHANGED
@@ -3,33 +3,43 @@ declare namespace _default {
3
3
  }
4
4
  export default _default;
5
5
  /**
6
- * Change number to Thai pronunciation string
6
+ * Convert number Thai Baht text (supports number or numeric string)
7
+ * Mirrors behaviour of previous implementation but with faster core.
7
8
  *
8
9
  * @public
9
- * @param {number} num
10
+ * @param {number|string} input
10
11
  * @returns {string}
11
12
  */
12
- export function bahttext(num: number): string;
13
+ export function bahttext(input: number | string): string;
13
14
  /**
14
15
  * @private
15
- * @param {number[]} nums
16
- * @returns {string}
16
+ * Handle numeric input and extract baht/satang values
17
+ * @param {number} input - numeric input (positive or negative)
18
+ * @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
17
19
  */
18
- export function bahtxtNum2Word(nums: number[]): string;
20
+ export function handleNumericInput(input: number): {
21
+ baht: number;
22
+ bahtStr: string;
23
+ satang: number;
24
+ isNegative: boolean;
25
+ } | false;
19
26
  /**
20
27
  * @private
21
- * @todo improve performance
22
- * @param {string} str
23
- * @returns {string}
28
+ * Handle string input and extract baht/satang values
29
+ * @param {string} input - string input (numeric string, positive or negative)
30
+ * @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
24
31
  */
25
- export function bahtxtGrammarFix(str: string): string;
32
+ export function handleStringInput(input: string): {
33
+ baht: number;
34
+ bahtStr: string;
35
+ satang: number;
36
+ isNegative: boolean;
37
+ } | false;
26
38
  /**
27
- * bahtxtCombine baht and satang
28
- * and also adding unit
29
- *
30
39
  * @private
31
- * @param {string} baht
32
- * @param {string} satang
33
- * @returns {string}
40
+ * Format satang portion of the output
41
+ * @param {number} baht - baht amount
42
+ * @param {number} satang - satang amount
43
+ * @returns {string} formatted satang string
34
44
  */
35
- export function bahtxtCombine(baht: string, satang: string): string;
45
+ export function formatSatang(baht: number, satang: number): string;
package/src/index.js CHANGED
@@ -1,125 +1,258 @@
1
- const bahtxtConst = {
2
- defaultResult: 'ศูนย์บาทถ้วน',
3
- singleUnitStrs: ['', 'หนึ่ง', 'สอง', 'สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า'],
4
- placeNameStrs: ['', 'สิบ', 'ร้อย', 'พัน', 'หมื่น', 'แสน', 'ล้าน']
1
+ const EMPTY = ''
2
+ const ONE = 'หนึ่ง'
3
+ const TWO = 'สอง'
4
+ const THREE_TO_NINE = ['สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า']
5
+ const ED = 'เอ็ด'
6
+ const YEE = 'ยี่'
7
+ const LAN = 'ล้าน'
8
+
9
+ // Index 0-5 correspond to units,สิบ,ร้อย,พัน,หมื่น,แสน
10
+ const DIGIT = [EMPTY, 'สิบ', 'ร้อย', 'พัน', 'หมื่น', 'แสน']
11
+
12
+ // Helpers to build word tables quickly
13
+ const ONES = [EMPTY, ED, TWO, ...THREE_TO_NINE]
14
+ const TENS = [
15
+ EMPTY,
16
+ ...[EMPTY, YEE, ...THREE_TO_NINE].map(t => t + DIGIT[1]) // "สิบ" family
17
+ ]
18
+ // 0-99 lookup (fast satang conversion)
19
+ const SUB_HUNDRED = TENS.flatMap(t => ONES.map(o => t + o))
20
+ // Special case: 1 should read "หนึ่ง" not "เอ็ด"
21
+ SUB_HUNDRED[1] = ONE
22
+
23
+ // Single-digit lookup for generic conversion (no suffix)
24
+ const SUB_TEN = [EMPTY, ONE, TWO, ...THREE_TO_NINE]
25
+
26
+ // Pre-compiled constants for frequently used combinations
27
+ const YEE_SIB = YEE + DIGIT[1] // "ยี่สิบ"
28
+ const SIB = DIGIT[1] // "สิบ"
29
+
30
+ // Default fallback text used throughout tests
31
+ const DEFAULT_RESULT = 'ศูนย์บาทถ้วน'
32
+
33
+ /**
34
+ * Convert number → Thai Baht text (supports number or numeric string)
35
+ * Mirrors behaviour of previous implementation but with faster core.
36
+ *
37
+ * @public
38
+ * @param {number|string} input
39
+ * @returns {string}
40
+ */
41
+ function bahttext (input) {
42
+ // Early invalidation – keep behaviour expected by test suite
43
+ if (input === null || input === undefined ||
44
+ typeof input === 'boolean' || Array.isArray(input) ||
45
+ (typeof input !== 'number' && typeof input !== 'string')) {
46
+ return DEFAULT_RESULT
47
+ }
48
+
49
+ const converted = convertInternal(input)
50
+ return converted === false ? DEFAULT_RESULT : converted
5
51
  }
6
52
 
7
53
  /**
8
54
  * @private
9
- * @param {number[]} nums
55
+ * @param {string} numStr – e.g. "123" (no sign, may include leading zeros)
10
56
  * @returns {string}
11
57
  */
12
- function bahtxtNum2Word (nums) {
13
- let result = ''
14
- const len = nums.length
15
- const maxLen = 7
16
-
17
- if (len > maxLen) {
18
- // more than million
19
- const overflowIndex = len - maxLen + 1
20
- const overflowNums = nums.slice(0, overflowIndex)
21
- const remainingNumbs = nums.slice(overflowIndex)
22
- return bahtxtNum2Word(overflowNums) + 'ล้าน' + bahtxtNum2Word(remainingNumbs)
23
- } else {
24
- for (let i = 0; i < len; i++) {
25
- const digit = nums[i]
26
- if (digit > 0) {
27
- result += bahtxtConst.singleUnitStrs[digit] + bahtxtConst.placeNameStrs[len - i - 1]
58
+ function numberToWords (numStr) {
59
+ // Short-circuit trivial "0"
60
+ if (numStr === '0') return ''
61
+
62
+ let output = EMPTY
63
+ const len = numStr.length
64
+
65
+ for (let i = 0; i < len; i++) {
66
+ const d = numStr[i]
67
+ const di = len - i - 1 // distance from rightmost digit
68
+ const diMod = di % 6 // position inside a 6-digit group
69
+
70
+ if (d === '0') {
71
+ // skip zero – no wording
72
+ } else if (diMod === 1) {
73
+ // tens column inside group
74
+ if (d === '1') {
75
+ // "สิบ"
76
+ output += SIB
77
+ } else if (d === '2') {
78
+ // "ยี่สิบ"
79
+ output += YEE_SIB
80
+ } else {
81
+ const digitCode = d.charCodeAt(0) - 48
82
+ output += SUB_TEN[digitCode] + DIGIT[diMod]
28
83
  }
84
+ } else if (diMod === 0 && d === '1' && i !== 0) {
85
+ // trailing 1 (in ones place, not first digit in overall number) → "เอ็ด"
86
+ // BUT: if it's a stand-alone digit in hundreds place (like 201), use "หนึ่ง"
87
+ const isStandAloneInHundreds = (di === 0 && len > 1 && numStr[i - 1] === '0')
88
+ output += isStandAloneInHundreds ? ONE : ED
89
+ } else {
90
+ const digitCode = d.charCodeAt(0) - 48 // faster than Number(d)
91
+ output += SUB_TEN[digitCode] + DIGIT[diMod]
92
+ }
93
+
94
+ // add "ล้าน" after finishing each 6-digit block (except last group)
95
+ if (diMod === 0 && di) {
96
+ output += LAN
29
97
  }
30
98
  }
31
99
 
32
- return result
100
+ return output
33
101
  }
34
102
 
35
103
  /**
36
104
  * @private
37
- * @todo improve performance
38
- * @param {string} str
39
- * @returns {string}
105
+ * Handle numeric input and extract baht/satang values
106
+ * @param {number} input - numeric input (positive or negative)
107
+ * @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
40
108
  */
41
- function bahtxtGrammarFix (str) {
42
- return str.replace(/หนึ่งสิบ/g, 'สิบ')
43
- .replace(/สองสิบ/g, 'ยี่สิบ')
44
- .replace(/สิบหนึ่ง/g, 'สิบเอ็ด')
109
+ function handleNumericInput (input) {
110
+ if (!Number.isFinite(input)) return false
111
+ if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) {
112
+ return false
113
+ }
114
+
115
+ const isNegative = input < 0
116
+ if (isNegative) {
117
+ input = -input
118
+ }
119
+
120
+ const baht = Math.floor(input)
121
+ const satang = Number.isInteger(input)
122
+ ? 0
123
+ : Math.floor(((input + Number.EPSILON * (baht || 1)) * 100) % 100)
124
+ const bahtStr = baht.toString() // slightly faster than String(baht)
125
+
126
+ return { baht, bahtStr, satang, isNegative }
45
127
  }
46
128
 
47
129
  /**
48
- * bahtxtCombine baht and satang
49
- * and also adding unit
50
- *
51
130
  * @private
52
- * @param {string} baht
53
- * @param {string} satang
54
- * @returns {string}
131
+ * Handle string input and extract baht/satang values
132
+ * @param {string} input - string input (numeric string, positive or negative)
133
+ * @returns {{baht: number, bahtStr: string, satang: number, isNegative: boolean} | false}
55
134
  */
56
- function bahtxtCombine (baht, satang) {
57
- if (!baht && !satang) {
58
- return bahtxtConst.defaultResult
59
- } else if (baht && !satang) {
60
- return baht + 'บาท' + 'ถ้วน'
61
- } else if (!baht && satang) {
62
- return satang + 'สตางค์'
135
+ function handleStringInput (input) {
136
+ let formatted = input.trim()
137
+
138
+ if (formatted.startsWith('-')) {
139
+ formatted = formatted.replace(/^-0+/, '-') // keep sign, drop leading zeros
140
+ if (formatted === '-') formatted = '0' // catch "-" or "-0"
63
141
  } else {
64
- return baht + 'บาท' + satang + 'สตางค์'
142
+ formatted = formatted.replace(/^0+/, '') || '0' // combine with empty check
65
143
  }
144
+
145
+ // Not a valid number?
146
+ if (formatted === '') formatted = '0'
147
+ let inputNum = Number(formatted)
148
+ if (Number.isNaN(inputNum)) return false
149
+
150
+ const isNegative = inputNum < 0
151
+ if (isNegative) {
152
+ inputNum = -inputNum
153
+ // remove sign for string slicing later
154
+ formatted = formatted.slice(1)
155
+ }
156
+
157
+ // Safe-range check after absolute value
158
+ if (inputNum < Number.MIN_SAFE_INTEGER || inputNum > Number.MAX_SAFE_INTEGER) {
159
+ return false
160
+ }
161
+
162
+ let baht, bahtStr, satang, satangStr
163
+
164
+ // Handle decimal part by string slicing to avoid FP errors
165
+ const periodIdx = formatted.lastIndexOf('.')
166
+ if (periodIdx !== -1) {
167
+ bahtStr = formatted.slice(0, periodIdx) || '0'
168
+ baht = Number(bahtStr)
169
+
170
+ satangStr = formatted.slice(periodIdx + 1)
171
+ satang = satangStr
172
+ ? Number(satangStr.slice(0, 2)) * (satangStr.length >= 2 ? 1 : [100, 10][satangStr.length])
173
+ : 0
174
+ } else {
175
+ baht = inputNum
176
+ bahtStr = formatted
177
+ satang = 0
178
+ }
179
+
180
+ return { baht, bahtStr, satang, isNegative }
66
181
  }
67
182
 
68
183
  /**
69
- * Change number to Thai pronunciation string
70
- *
71
- * @public
72
- * @param {number} num
73
- * @returns {string}
184
+ * @private
185
+ * Format satang portion of the output
186
+ * @param {number} baht - baht amount
187
+ * @param {number} satang - satang amount
188
+ * @returns {string} formatted satang string
74
189
  */
75
- function bahttext (num) {
76
- if (!num || // no null
77
- typeof num === 'boolean' || // no boolean
78
- isNaN(Number(num)) || // must be number only
79
- num < Number.MIN_SAFE_INTEGER || // not less than Number.MIN_SAFE_INTEGER
80
- num > Number.MAX_SAFE_INTEGER // no more than Number.MAX_SAFE_INTEGER
81
- ) {
82
- return bahtxtConst.defaultResult
190
+ function formatSatang (baht, satang) {
191
+ if (satang) {
192
+ let output = ''
193
+ if (baht) output += 'บาท'
194
+ output += SUB_HUNDRED[satang] + 'สตางค์'
195
+ return output
196
+ } else {
197
+ return 'บาทถ้วน'
83
198
  }
199
+ }
84
200
 
85
- // set
86
- const positiveNum = Math.abs(num)
201
+ /**
202
+ * @private
203
+ * Returns false when input cannot be parsed as numeric.
204
+ */
205
+ function convertInternal (input) {
206
+ let baht, bahtStr, satang
207
+ let isNegative = false
87
208
 
88
- // split baht and satang e.g. 432.214567 >> 432, 21
89
- const bahtStr = Math.floor(positiveNum).toString()
90
- /** @type {string} */
91
- const satangStr = (positiveNum % 1 * 100).toFixed(0)
209
+ if (typeof input === 'number') {
210
+ // Numeric input handling
211
+ const result = handleNumericInput(input)
212
+ if (result === false) return false
213
+
214
+ baht = result.baht
215
+ bahtStr = result.bahtStr
216
+ satang = result.satang
217
+ isNegative = result.isNegative
218
+ } else if (typeof input === 'string') {
219
+ // String input handling
220
+ const result = handleStringInput(input)
221
+ if (result === false) return false
222
+
223
+ baht = result.baht
224
+ bahtStr = result.bahtStr
225
+ satang = result.satang
226
+ isNegative = result.isNegative
227
+ } else {
228
+ // unreachable due to early guard, but for safety
229
+ return false
230
+ }
92
231
 
93
- /** @type {number[]} */
94
- const bahtArr = Array.from(bahtStr).map(Number)
95
- /** @type {number[]} */
96
- const satangArr = Array.from(satangStr).map(Number)
232
+ // No value at all → zero
233
+ if (!baht && !satang) {
234
+ return DEFAULT_RESULT
235
+ }
97
236
 
98
- // proceed
99
- let baht = bahtxtNum2Word(bahtArr)
100
- let satang = bahtxtNum2Word(satangArr)
237
+ // Build output
238
+ let output = isNegative ? 'ลบ' : EMPTY
101
239
 
102
- // grammar
103
- baht = bahtxtGrammarFix(baht)
104
- satang = bahtxtGrammarFix(satang)
240
+ // Baht part – using fast converter
241
+ const bahtWords = numberToWords(bahtStr)
242
+ output += bahtWords
105
243
 
106
- // combine
107
- const result = bahtxtCombine(baht, satang)
244
+ // Satang
245
+ output += formatSatang(baht, satang)
108
246
 
109
- return num >= 0 ? result : 'ลบ' + result
247
+ return output
110
248
  }
111
249
 
112
- if (typeof module !== 'undefined' &&
113
- module.exports != null) {
114
- module.exports = {
115
- bahttext,
116
-
117
- // export for testing only
118
- bahtxtNum2Word,
119
- bahtxtGrammarFix,
120
- bahtxtCombine
121
- }
122
- exports.default = {
123
- bahttext
124
- }
250
+ module.exports = {
251
+ bahttext,
252
+ // Export private functions for testing
253
+ handleNumericInput,
254
+ handleStringInput,
255
+ formatSatang
125
256
  }
257
+
258
+ exports.default = { bahttext }