numberstring 0.2.0 → 1.0.1

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.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Mandarin Chinese number-to-words converter
3
+ * Uses the wan (万) system for grouping by 10,000
4
+ * @module languages/zh
5
+ */
6
+
7
+ const ZH_ONES = Object.freeze(['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']);
8
+ const ZH_UNITS = Object.freeze(['', '十', '百', '千']);
9
+ const ZH_ILLIONS = Object.freeze(['', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载']);
10
+
11
+ const MAX_VALUE = 10n ** 36n - 1n;
12
+
13
+ /**
14
+ * Convert a number to Mandarin Chinese words
15
+ * @param {number|bigint} n - The number to convert
16
+ * @returns {string|false} The Mandarin word representation
17
+ *
18
+ * @example
19
+ * chinese(42) // '四十二'
20
+ * chinese(1000) // '一千'
21
+ * chinese(10000) // '一万'
22
+ */
23
+ const chinese = (n) => {
24
+ let num;
25
+
26
+ if (typeof n === 'bigint') {
27
+ if (n < 0n || n > MAX_VALUE) return false;
28
+ num = n;
29
+ } else if (typeof n === 'number') {
30
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
31
+ if (n > Number.MAX_SAFE_INTEGER) return false;
32
+ num = BigInt(n);
33
+ } else {
34
+ return false;
35
+ }
36
+
37
+ if (num === 0n) return '零';
38
+
39
+ const str = num.toString();
40
+ let result = '';
41
+ let needZero = false;
42
+
43
+ // Process in groups of 4 (wan system)
44
+ const len = str.length;
45
+ const groups = [];
46
+ for (let i = len; i > 0; i -= 4) {
47
+ const start = Math.max(0, i - 4);
48
+ groups.unshift(str.slice(start, i));
49
+ }
50
+
51
+ for (let i = 0; i < groups.length; i++) {
52
+ const grp = parseInt(groups[i], 10);
53
+ const grpIdx = groups.length - 1 - i;
54
+
55
+ if (grp === 0) {
56
+ needZero = true;
57
+ continue;
58
+ }
59
+
60
+ if (needZero && result) {
61
+ result += '零';
62
+ }
63
+ needZero = false;
64
+
65
+ const thousands = Math.floor(grp / 1000);
66
+ const hundreds = Math.floor((grp % 1000) / 100);
67
+ const tens = Math.floor((grp % 100) / 10);
68
+ const ones = grp % 10;
69
+
70
+ let grpStr = '';
71
+ let innerZero = false;
72
+
73
+ if (thousands > 0) {
74
+ grpStr += ZH_ONES[thousands] + ZH_UNITS[3];
75
+ innerZero = false;
76
+ } else if (result || i > 0) {
77
+ innerZero = true;
78
+ }
79
+
80
+ if (hundreds > 0) {
81
+ if (innerZero && grpStr) grpStr += '零';
82
+ grpStr += ZH_ONES[hundreds] + ZH_UNITS[2];
83
+ innerZero = false;
84
+ } else if (thousands > 0) {
85
+ innerZero = true;
86
+ }
87
+
88
+ if (tens > 0) {
89
+ if (innerZero && grpStr) grpStr += '零';
90
+ // Special: 10-19 at start is just 十X, not 一十X
91
+ if (tens === 1 && !result && thousands === 0 && hundreds === 0) {
92
+ grpStr += ZH_UNITS[1];
93
+ } else {
94
+ grpStr += ZH_ONES[tens] + ZH_UNITS[1];
95
+ }
96
+ innerZero = false;
97
+ } else if (hundreds > 0 || thousands > 0) {
98
+ innerZero = true;
99
+ }
100
+
101
+ if (ones > 0) {
102
+ if (innerZero && grpStr) grpStr += '零';
103
+ grpStr += ZH_ONES[ones];
104
+ }
105
+
106
+ result += grpStr;
107
+ if (grpIdx > 0) {
108
+ result += ZH_ILLIONS[grpIdx];
109
+ }
110
+ }
111
+
112
+ return result;
113
+ };
114
+
115
+ export default chinese;
116
+ export { chinese };
package/package.json CHANGED
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "numberstring",
3
- "version": "0.2.0",
3
+ "version": "1.0.1",
4
4
  "description": "Number One Way to Makes Words from Numbers",
5
+ "type": "module",
5
6
  "main": "index.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./index.js"
10
+ }
11
+ },
6
12
  "scripts": {
7
- "test": "mocha test",
8
- "start": "node index.js",
9
- "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha"
13
+ "demo": "node demo.js",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:coverage": "vitest run --coverage",
17
+ "lint": "eslint . --format stylish",
18
+ "lint:report": "eslint . --format html -o coverage/lint-report.html"
10
19
  },
11
20
  "repository": {
12
21
  "type": "git",
@@ -16,10 +25,14 @@
16
25
  "number",
17
26
  "string",
18
27
  "word",
19
- "int",
28
+ "words",
20
29
  "integer",
30
+ "bigint",
21
31
  "text",
22
- "num"
32
+ "convert",
33
+ "english",
34
+ "quintillion",
35
+ "decillion"
23
36
  ],
24
37
  "author": "Brian Funk",
25
38
  "license": "MIT",
@@ -27,11 +40,14 @@
27
40
  "url": "https://github.com/brianfunk/numberstring/issues"
28
41
  },
29
42
  "homepage": "https://github.com/brianfunk/numberstring#readme",
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
30
46
  "devDependencies": {
31
- "chai": "^3.5.0",
32
- "codeclimate-test-reporter": "^0.3.3",
33
- "codecov": "^1.0.1",
34
- "istanbul": "^0.4.5",
35
- "mocha": "^3.0.2"
47
+ "@vitest/coverage-v8": "^4.0.18",
48
+ "eslint": "^9.17.0",
49
+ "express": "^4.21.2",
50
+ "supertest": "^7.0.0",
51
+ "vitest": "^4.0.18"
36
52
  }
37
53
  }
package/.npmignore DELETED
@@ -1,42 +0,0 @@
1
- # Tests
2
- test
3
- tests
4
- testing
5
-
6
- # Services
7
- .codeclimate.yml
8
- .travis.yml
9
- codecov.yml
10
- .gitignore
11
-
12
- # Logs
13
- logs
14
- *.log
15
- npm-debug.log*
16
-
17
- # Runtime data
18
- pids
19
- *.pid
20
- *.seed
21
-
22
- # Coverage directory used by tools like istanbul
23
- coverage
24
-
25
- # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26
- .grunt
27
-
28
- # node-waf configuration
29
- .lock-wscript
30
-
31
- # Compiled binary addons (http://nodejs.org/api/addons.html)
32
- build/Release
33
-
34
- # Dependency directories
35
- node_modules
36
- jspm_packages
37
-
38
- # Optional npm cache directory
39
- .npm
40
-
41
- # Optional REPL history
42
- .node_repl_history