numeric-quantity 1.0.4 → 2.0.0-beta.0
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/README.md +37 -29
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/numeric-quantity.cjs.development.js +234 -0
- package/dist/cjs/numeric-quantity.cjs.development.js.map +1 -0
- package/dist/cjs/numeric-quantity.cjs.production.js +2 -0
- package/dist/cjs/numeric-quantity.cjs.production.js.map +1 -0
- package/dist/numeric-quantity.d.ts +124 -0
- package/dist/numeric-quantity.legacy-esm.js +199 -0
- package/dist/numeric-quantity.legacy-esm.js.map +1 -0
- package/dist/numeric-quantity.mjs +199 -0
- package/dist/numeric-quantity.mjs.map +1 -0
- package/dist/numeric-quantity.production.mjs +2 -0
- package/dist/numeric-quantity.production.mjs.map +1 -0
- package/package.json +32 -21
- package/CHANGELOG.md +0 -37
- package/dist/index.d.ts +0 -6
- package/dist/numeric-quantity.cjs.js +0 -2
- package/dist/numeric-quantity.cjs.js.map +0 -1
- package/dist/numeric-quantity.es.js +0 -62
- package/dist/numeric-quantity.es.js.map +0 -1
- package/dist/numeric-quantity.umd.js +0 -2
- package/dist/numeric-quantity.umd.js.map +0 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var vulgarFractionToAsciiMap = {
|
|
3
|
+
"\xBC": "1/4",
|
|
4
|
+
"\xBD": "1/2",
|
|
5
|
+
"\xBE": "3/4",
|
|
6
|
+
"\u2150": "1/7",
|
|
7
|
+
"\u2151": "1/9",
|
|
8
|
+
"\u2152": "1/10",
|
|
9
|
+
"\u2153": "1/3",
|
|
10
|
+
"\u2154": "2/3",
|
|
11
|
+
"\u2155": "1/5",
|
|
12
|
+
"\u2156": "2/5",
|
|
13
|
+
"\u2157": "3/5",
|
|
14
|
+
"\u2158": "4/5",
|
|
15
|
+
"\u2159": "1/6",
|
|
16
|
+
"\u215A": "5/6",
|
|
17
|
+
"\u215B": "1/8",
|
|
18
|
+
"\u215C": "3/8",
|
|
19
|
+
"\u215D": "5/8",
|
|
20
|
+
"\u215E": "7/8",
|
|
21
|
+
"\u215F": "1/"
|
|
22
|
+
};
|
|
23
|
+
var numericRegex = /^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/;
|
|
24
|
+
var vulgarFractionsRegex = new RegExp(
|
|
25
|
+
`(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
|
|
26
|
+
);
|
|
27
|
+
var romanNumeralValues = {
|
|
28
|
+
MMM: 3e3,
|
|
29
|
+
MM: 2e3,
|
|
30
|
+
M: 1e3,
|
|
31
|
+
CM: 900,
|
|
32
|
+
DCCC: 800,
|
|
33
|
+
DCC: 700,
|
|
34
|
+
DC: 600,
|
|
35
|
+
D: 500,
|
|
36
|
+
CD: 400,
|
|
37
|
+
CCC: 300,
|
|
38
|
+
CC: 200,
|
|
39
|
+
C: 100,
|
|
40
|
+
XC: 90,
|
|
41
|
+
LXXX: 80,
|
|
42
|
+
LXX: 70,
|
|
43
|
+
LX: 60,
|
|
44
|
+
L: 50,
|
|
45
|
+
XL: 40,
|
|
46
|
+
XXX: 30,
|
|
47
|
+
XX: 20,
|
|
48
|
+
// Twelve is only here for tests; not used in practice
|
|
49
|
+
XII: 12,
|
|
50
|
+
// Eleven is only here for tests; not used in practice
|
|
51
|
+
XI: 11,
|
|
52
|
+
X: 10,
|
|
53
|
+
IX: 9,
|
|
54
|
+
VIII: 8,
|
|
55
|
+
VII: 7,
|
|
56
|
+
VI: 6,
|
|
57
|
+
V: 5,
|
|
58
|
+
IV: 4,
|
|
59
|
+
III: 3,
|
|
60
|
+
II: 2,
|
|
61
|
+
I: 1
|
|
62
|
+
};
|
|
63
|
+
var romanNumeralUnicodeToAsciiMap = {
|
|
64
|
+
// Roman Numeral One (U+2160)
|
|
65
|
+
"\u2160": "I",
|
|
66
|
+
// Roman Numeral Two (U+2161)
|
|
67
|
+
"\u2161": "II",
|
|
68
|
+
// Roman Numeral Three (U+2162)
|
|
69
|
+
"\u2162": "III",
|
|
70
|
+
// Roman Numeral Four (U+2163)
|
|
71
|
+
"\u2163": "IV",
|
|
72
|
+
// Roman Numeral Five (U+2164)
|
|
73
|
+
"\u2164": "V",
|
|
74
|
+
// Roman Numeral Six (U+2165)
|
|
75
|
+
"\u2165": "VI",
|
|
76
|
+
// Roman Numeral Seven (U+2166)
|
|
77
|
+
"\u2166": "VII",
|
|
78
|
+
// Roman Numeral Eight (U+2167)
|
|
79
|
+
"\u2167": "VIII",
|
|
80
|
+
// Roman Numeral Nine (U+2168)
|
|
81
|
+
"\u2168": "IX",
|
|
82
|
+
// Roman Numeral Ten (U+2169)
|
|
83
|
+
"\u2169": "X",
|
|
84
|
+
// Roman Numeral Eleven (U+216A)
|
|
85
|
+
"\u216A": "XI",
|
|
86
|
+
// Roman Numeral Twelve (U+216B)
|
|
87
|
+
"\u216B": "XII",
|
|
88
|
+
// Roman Numeral Fifty (U+216C)
|
|
89
|
+
"\u216C": "L",
|
|
90
|
+
// Roman Numeral One Hundred (U+216D)
|
|
91
|
+
"\u216D": "C",
|
|
92
|
+
// Roman Numeral Five Hundred (U+216E)
|
|
93
|
+
"\u216E": "D",
|
|
94
|
+
// Roman Numeral One Thousand (U+216F)
|
|
95
|
+
"\u216F": "M",
|
|
96
|
+
// Small Roman Numeral One (U+2170)
|
|
97
|
+
"\u2170": "I",
|
|
98
|
+
// Small Roman Numeral Two (U+2171)
|
|
99
|
+
"\u2171": "II",
|
|
100
|
+
// Small Roman Numeral Three (U+2172)
|
|
101
|
+
"\u2172": "III",
|
|
102
|
+
// Small Roman Numeral Four (U+2173)
|
|
103
|
+
"\u2173": "IV",
|
|
104
|
+
// Small Roman Numeral Five (U+2174)
|
|
105
|
+
"\u2174": "V",
|
|
106
|
+
// Small Roman Numeral Six (U+2175)
|
|
107
|
+
"\u2175": "VI",
|
|
108
|
+
// Small Roman Numeral Seven (U+2176)
|
|
109
|
+
"\u2176": "VII",
|
|
110
|
+
// Small Roman Numeral Eight (U+2177)
|
|
111
|
+
"\u2177": "VIII",
|
|
112
|
+
// Small Roman Numeral Nine (U+2178)
|
|
113
|
+
"\u2178": "IX",
|
|
114
|
+
// Small Roman Numeral Ten (U+2179)
|
|
115
|
+
"\u2179": "X",
|
|
116
|
+
// Small Roman Numeral Eleven (U+217A)
|
|
117
|
+
"\u217A": "XI",
|
|
118
|
+
// Small Roman Numeral Twelve (U+217B)
|
|
119
|
+
"\u217B": "XII",
|
|
120
|
+
// Small Roman Numeral Fifty (U+217C)
|
|
121
|
+
"\u217C": "L",
|
|
122
|
+
// Small Roman Numeral One Hundred (U+217D)
|
|
123
|
+
"\u217D": "C",
|
|
124
|
+
// Small Roman Numeral Five Hundred (U+217E)
|
|
125
|
+
"\u217E": "D",
|
|
126
|
+
// Small Roman Numeral One Thousand (U+217F)
|
|
127
|
+
"\u217F": "M"
|
|
128
|
+
};
|
|
129
|
+
var romanNumeralUnicodeRegex = new RegExp(
|
|
130
|
+
`(${Object.keys(romanNumeralUnicodeToAsciiMap).join("|")})`,
|
|
131
|
+
"gi"
|
|
132
|
+
);
|
|
133
|
+
var romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;
|
|
134
|
+
|
|
135
|
+
// src/parseRomanNumerals.ts
|
|
136
|
+
var parseRomanNumerals = (romanNumerals) => {
|
|
137
|
+
const normalized = `${romanNumerals}`.replace(
|
|
138
|
+
romanNumeralUnicodeRegex,
|
|
139
|
+
(_m, rn) => romanNumeralUnicodeToAsciiMap[rn]
|
|
140
|
+
).toUpperCase();
|
|
141
|
+
const regexResult = romanNumeralRegex.exec(normalized);
|
|
142
|
+
if (!regexResult) {
|
|
143
|
+
return NaN;
|
|
144
|
+
}
|
|
145
|
+
const [, thousands, hundreds, tens, ones] = regexResult;
|
|
146
|
+
return (romanNumeralValues[thousands] || 0) + (romanNumeralValues[hundreds] || 0) + (romanNumeralValues[tens] || 0) + (romanNumeralValues[ones] || 0);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/numericQuantity.ts
|
|
150
|
+
var spaceThenSlashRegex = /^\s*\//;
|
|
151
|
+
var numericQuantity = (quantity) => {
|
|
152
|
+
let finalResult = NaN;
|
|
153
|
+
const quantityAsString = `${quantity}`.replace(
|
|
154
|
+
vulgarFractionsRegex,
|
|
155
|
+
(_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`
|
|
156
|
+
).replace("\u2044", "/").trim();
|
|
157
|
+
if (quantityAsString.length === 0) {
|
|
158
|
+
return NaN;
|
|
159
|
+
}
|
|
160
|
+
const regexResult = numericRegex.exec(quantityAsString);
|
|
161
|
+
if (!regexResult) {
|
|
162
|
+
return parseRomanNumerals(quantityAsString);
|
|
163
|
+
}
|
|
164
|
+
const [, dash, ng1temp, numberGroup2] = regexResult;
|
|
165
|
+
const numberGroup1 = ng1temp.replace(/[,_]/g, "");
|
|
166
|
+
if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
|
|
167
|
+
finalResult = 0;
|
|
168
|
+
} else {
|
|
169
|
+
finalResult = parseInt(numberGroup1);
|
|
170
|
+
}
|
|
171
|
+
if (!numberGroup2) {
|
|
172
|
+
return finalResult * (dash === "-" ? -1 : 1);
|
|
173
|
+
}
|
|
174
|
+
if (numberGroup2.startsWith(".")) {
|
|
175
|
+
const numerator = parseFloat(numberGroup2);
|
|
176
|
+
finalResult += Math.round(numerator * 1e3) / 1e3;
|
|
177
|
+
} else if (spaceThenSlashRegex.test(numberGroup2)) {
|
|
178
|
+
const numerator = parseInt(numberGroup1);
|
|
179
|
+
const denominator = parseInt(numberGroup2.replace("/", ""));
|
|
180
|
+
finalResult = Math.round(numerator * 1e3 / denominator) / 1e3;
|
|
181
|
+
} else {
|
|
182
|
+
const fractionArray = numberGroup2.split("/");
|
|
183
|
+
const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
|
|
184
|
+
finalResult += Math.round(numerator * 1e3 / denominator) / 1e3;
|
|
185
|
+
}
|
|
186
|
+
return finalResult * (dash === "-" ? -1 : 1);
|
|
187
|
+
};
|
|
188
|
+
export {
|
|
189
|
+
numericQuantity,
|
|
190
|
+
numericRegex,
|
|
191
|
+
parseRomanNumerals,
|
|
192
|
+
romanNumeralRegex,
|
|
193
|
+
romanNumeralUnicodeRegex,
|
|
194
|
+
romanNumeralUnicodeToAsciiMap,
|
|
195
|
+
romanNumeralValues,
|
|
196
|
+
vulgarFractionToAsciiMap,
|
|
197
|
+
vulgarFractionsRegex
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=numeric-quantity.legacy-esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":";AAUO,IAAM,2BAA2D;AAAA,EACtE,QAAK;AAAA,EACL,QAAK;AAAA,EACL,QAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AACP;AAkCO,IAAM,eACX;AAKK,IAAM,uBAAuB,IAAI;AAAA,EACtC,IAAI,OAAO,KAAK,wBAAwB,EAAE,KAAK,GAAG;AACpD;AAUO,IAAM,qBAAqB;AAAA,EAChC,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA;AAAA,EAEJ,KAAK;AAAA;AAAA,EAEL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AACL;AAKO,IAAM,gCAGT;AAAA;AAAA,EAEF,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AAAA;AAAA,EAEH,UAAG;AACL;AAKO,IAAM,2BAA2B,IAAI;AAAA,EAC1C,IAAI,OAAO,KAAK,6BAA6B,EAAE,KAAK,GAAG;AAAA,EACvD;AACF;AA0BO,IAAM,oBACX;;;ACvNK,IAAM,qBAAqB,CAAC,kBAA0B;AAC3D,QAAM,aAAa,GAAG,gBACnB;AAAA,IACC;AAAA,IACA,CAAC,IAAI,OACH,8BAA8B,EAAE;AAAA,EACpC,EACC,YAAY;AAEf,QAAM,cAAc,kBAAkB,KAAK,UAAU;AAErD,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,EAAE,WAAW,UAAU,MAAM,IAAI,IAAI;AAE5C,UACG,mBAAmB,SAAgB,KAAK,MACxC,mBAAmB,QAAe,KAAK,MACvC,mBAAmB,IAAW,KAAK,MACnC,mBAAmB,IAAW,KAAK;AAExC;;;AC1BA,IAAM,sBAAsB;AAOrB,IAAM,kBAAkB,CAAC,aAAqB;AACnD,MAAI,cAAc;AAGlB,QAAM,mBAAmB,GAAG,WAGzB;AAAA,IACC;AAAA,IACA,CAAC,IAAI,OACH,IAAI,yBAAyB,EAAE;AAAA,EACnC,EAEC,QAAQ,UAAK,GAAG,EAChB,KAAK;AAGR,MAAI,iBAAiB,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,aAAa,KAAK,gBAAgB;AAGtD,MAAI,CAAC,aAAa;AAChB,WAAO,mBAAmB,gBAAgB;AAAA,EAC5C;AAEA,QAAM,CAAC,EAAE,MAAM,SAAS,YAAY,IAAI;AACxC,QAAM,eAAe,QAAQ,QAAQ,SAAS,EAAE;AAGhD,MAAI,CAAC,gBAAgB,gBAAgB,aAAa,WAAW,GAAG,GAAG;AACjE,kBAAc;AAAA,EAChB,OAAO;AACL,kBAAc,SAAS,YAAY;AAAA,EACrC;AAIA,MAAI,CAAC,cAAc;AACjB,WAAO,eAAe,SAAS,MAAM,KAAK;AAAA,EAC5C;AAEA,MAAI,aAAa,WAAW,GAAG,GAAG;AAEhC,UAAM,YAAY,WAAW,YAAY;AACzC,mBAAe,KAAK,MAAM,YAAY,GAAI,IAAI;AAAA,EAChD,WAAW,oBAAoB,KAAK,YAAY,GAAG;AAEjD,UAAM,YAAY,SAAS,YAAY;AACvC,UAAM,cAAc,SAAS,aAAa,QAAQ,KAAK,EAAE,CAAC;AAC1D,kBAAc,KAAK,MAAO,YAAY,MAAQ,WAAW,IAAI;AAAA,EAC/D,OAAO;AAEL,UAAM,gBAAgB,aAAa,MAAM,GAAG;AAC5C,UAAM,CAAC,WAAW,WAAW,IAAI,cAAc,IAAI,OAAK,SAAS,CAAC,CAAC;AACnE,mBAAe,KAAK,MAAO,YAAY,MAAQ,WAAW,IAAI;AAAA,EAChE;AAEA,SAAO,eAAe,SAAS,MAAM,KAAK;AAC5C;","names":[]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var vulgarFractionToAsciiMap = {
|
|
3
|
+
"\xBC": "1/4",
|
|
4
|
+
"\xBD": "1/2",
|
|
5
|
+
"\xBE": "3/4",
|
|
6
|
+
"\u2150": "1/7",
|
|
7
|
+
"\u2151": "1/9",
|
|
8
|
+
"\u2152": "1/10",
|
|
9
|
+
"\u2153": "1/3",
|
|
10
|
+
"\u2154": "2/3",
|
|
11
|
+
"\u2155": "1/5",
|
|
12
|
+
"\u2156": "2/5",
|
|
13
|
+
"\u2157": "3/5",
|
|
14
|
+
"\u2158": "4/5",
|
|
15
|
+
"\u2159": "1/6",
|
|
16
|
+
"\u215A": "5/6",
|
|
17
|
+
"\u215B": "1/8",
|
|
18
|
+
"\u215C": "3/8",
|
|
19
|
+
"\u215D": "5/8",
|
|
20
|
+
"\u215E": "7/8",
|
|
21
|
+
"\u215F": "1/"
|
|
22
|
+
};
|
|
23
|
+
var numericRegex = /^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/;
|
|
24
|
+
var vulgarFractionsRegex = new RegExp(
|
|
25
|
+
`(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
|
|
26
|
+
);
|
|
27
|
+
var romanNumeralValues = {
|
|
28
|
+
MMM: 3e3,
|
|
29
|
+
MM: 2e3,
|
|
30
|
+
M: 1e3,
|
|
31
|
+
CM: 900,
|
|
32
|
+
DCCC: 800,
|
|
33
|
+
DCC: 700,
|
|
34
|
+
DC: 600,
|
|
35
|
+
D: 500,
|
|
36
|
+
CD: 400,
|
|
37
|
+
CCC: 300,
|
|
38
|
+
CC: 200,
|
|
39
|
+
C: 100,
|
|
40
|
+
XC: 90,
|
|
41
|
+
LXXX: 80,
|
|
42
|
+
LXX: 70,
|
|
43
|
+
LX: 60,
|
|
44
|
+
L: 50,
|
|
45
|
+
XL: 40,
|
|
46
|
+
XXX: 30,
|
|
47
|
+
XX: 20,
|
|
48
|
+
// Twelve is only here for tests; not used in practice
|
|
49
|
+
XII: 12,
|
|
50
|
+
// Eleven is only here for tests; not used in practice
|
|
51
|
+
XI: 11,
|
|
52
|
+
X: 10,
|
|
53
|
+
IX: 9,
|
|
54
|
+
VIII: 8,
|
|
55
|
+
VII: 7,
|
|
56
|
+
VI: 6,
|
|
57
|
+
V: 5,
|
|
58
|
+
IV: 4,
|
|
59
|
+
III: 3,
|
|
60
|
+
II: 2,
|
|
61
|
+
I: 1
|
|
62
|
+
};
|
|
63
|
+
var romanNumeralUnicodeToAsciiMap = {
|
|
64
|
+
// Roman Numeral One (U+2160)
|
|
65
|
+
"\u2160": "I",
|
|
66
|
+
// Roman Numeral Two (U+2161)
|
|
67
|
+
"\u2161": "II",
|
|
68
|
+
// Roman Numeral Three (U+2162)
|
|
69
|
+
"\u2162": "III",
|
|
70
|
+
// Roman Numeral Four (U+2163)
|
|
71
|
+
"\u2163": "IV",
|
|
72
|
+
// Roman Numeral Five (U+2164)
|
|
73
|
+
"\u2164": "V",
|
|
74
|
+
// Roman Numeral Six (U+2165)
|
|
75
|
+
"\u2165": "VI",
|
|
76
|
+
// Roman Numeral Seven (U+2166)
|
|
77
|
+
"\u2166": "VII",
|
|
78
|
+
// Roman Numeral Eight (U+2167)
|
|
79
|
+
"\u2167": "VIII",
|
|
80
|
+
// Roman Numeral Nine (U+2168)
|
|
81
|
+
"\u2168": "IX",
|
|
82
|
+
// Roman Numeral Ten (U+2169)
|
|
83
|
+
"\u2169": "X",
|
|
84
|
+
// Roman Numeral Eleven (U+216A)
|
|
85
|
+
"\u216A": "XI",
|
|
86
|
+
// Roman Numeral Twelve (U+216B)
|
|
87
|
+
"\u216B": "XII",
|
|
88
|
+
// Roman Numeral Fifty (U+216C)
|
|
89
|
+
"\u216C": "L",
|
|
90
|
+
// Roman Numeral One Hundred (U+216D)
|
|
91
|
+
"\u216D": "C",
|
|
92
|
+
// Roman Numeral Five Hundred (U+216E)
|
|
93
|
+
"\u216E": "D",
|
|
94
|
+
// Roman Numeral One Thousand (U+216F)
|
|
95
|
+
"\u216F": "M",
|
|
96
|
+
// Small Roman Numeral One (U+2170)
|
|
97
|
+
"\u2170": "I",
|
|
98
|
+
// Small Roman Numeral Two (U+2171)
|
|
99
|
+
"\u2171": "II",
|
|
100
|
+
// Small Roman Numeral Three (U+2172)
|
|
101
|
+
"\u2172": "III",
|
|
102
|
+
// Small Roman Numeral Four (U+2173)
|
|
103
|
+
"\u2173": "IV",
|
|
104
|
+
// Small Roman Numeral Five (U+2174)
|
|
105
|
+
"\u2174": "V",
|
|
106
|
+
// Small Roman Numeral Six (U+2175)
|
|
107
|
+
"\u2175": "VI",
|
|
108
|
+
// Small Roman Numeral Seven (U+2176)
|
|
109
|
+
"\u2176": "VII",
|
|
110
|
+
// Small Roman Numeral Eight (U+2177)
|
|
111
|
+
"\u2177": "VIII",
|
|
112
|
+
// Small Roman Numeral Nine (U+2178)
|
|
113
|
+
"\u2178": "IX",
|
|
114
|
+
// Small Roman Numeral Ten (U+2179)
|
|
115
|
+
"\u2179": "X",
|
|
116
|
+
// Small Roman Numeral Eleven (U+217A)
|
|
117
|
+
"\u217A": "XI",
|
|
118
|
+
// Small Roman Numeral Twelve (U+217B)
|
|
119
|
+
"\u217B": "XII",
|
|
120
|
+
// Small Roman Numeral Fifty (U+217C)
|
|
121
|
+
"\u217C": "L",
|
|
122
|
+
// Small Roman Numeral One Hundred (U+217D)
|
|
123
|
+
"\u217D": "C",
|
|
124
|
+
// Small Roman Numeral Five Hundred (U+217E)
|
|
125
|
+
"\u217E": "D",
|
|
126
|
+
// Small Roman Numeral One Thousand (U+217F)
|
|
127
|
+
"\u217F": "M"
|
|
128
|
+
};
|
|
129
|
+
var romanNumeralUnicodeRegex = new RegExp(
|
|
130
|
+
`(${Object.keys(romanNumeralUnicodeToAsciiMap).join("|")})`,
|
|
131
|
+
"gi"
|
|
132
|
+
);
|
|
133
|
+
var romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;
|
|
134
|
+
|
|
135
|
+
// src/parseRomanNumerals.ts
|
|
136
|
+
var parseRomanNumerals = (romanNumerals) => {
|
|
137
|
+
const normalized = `${romanNumerals}`.replace(
|
|
138
|
+
romanNumeralUnicodeRegex,
|
|
139
|
+
(_m, rn) => romanNumeralUnicodeToAsciiMap[rn]
|
|
140
|
+
).toUpperCase();
|
|
141
|
+
const regexResult = romanNumeralRegex.exec(normalized);
|
|
142
|
+
if (!regexResult) {
|
|
143
|
+
return NaN;
|
|
144
|
+
}
|
|
145
|
+
const [, thousands, hundreds, tens, ones] = regexResult;
|
|
146
|
+
return (romanNumeralValues[thousands] || 0) + (romanNumeralValues[hundreds] || 0) + (romanNumeralValues[tens] || 0) + (romanNumeralValues[ones] || 0);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/numericQuantity.ts
|
|
150
|
+
var spaceThenSlashRegex = /^\s*\//;
|
|
151
|
+
var numericQuantity = (quantity) => {
|
|
152
|
+
let finalResult = NaN;
|
|
153
|
+
const quantityAsString = `${quantity}`.replace(
|
|
154
|
+
vulgarFractionsRegex,
|
|
155
|
+
(_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`
|
|
156
|
+
).replace("\u2044", "/").trim();
|
|
157
|
+
if (quantityAsString.length === 0) {
|
|
158
|
+
return NaN;
|
|
159
|
+
}
|
|
160
|
+
const regexResult = numericRegex.exec(quantityAsString);
|
|
161
|
+
if (!regexResult) {
|
|
162
|
+
return parseRomanNumerals(quantityAsString);
|
|
163
|
+
}
|
|
164
|
+
const [, dash, ng1temp, numberGroup2] = regexResult;
|
|
165
|
+
const numberGroup1 = ng1temp.replace(/[,_]/g, "");
|
|
166
|
+
if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
|
|
167
|
+
finalResult = 0;
|
|
168
|
+
} else {
|
|
169
|
+
finalResult = parseInt(numberGroup1);
|
|
170
|
+
}
|
|
171
|
+
if (!numberGroup2) {
|
|
172
|
+
return finalResult * (dash === "-" ? -1 : 1);
|
|
173
|
+
}
|
|
174
|
+
if (numberGroup2.startsWith(".")) {
|
|
175
|
+
const numerator = parseFloat(numberGroup2);
|
|
176
|
+
finalResult += Math.round(numerator * 1e3) / 1e3;
|
|
177
|
+
} else if (spaceThenSlashRegex.test(numberGroup2)) {
|
|
178
|
+
const numerator = parseInt(numberGroup1);
|
|
179
|
+
const denominator = parseInt(numberGroup2.replace("/", ""));
|
|
180
|
+
finalResult = Math.round(numerator * 1e3 / denominator) / 1e3;
|
|
181
|
+
} else {
|
|
182
|
+
const fractionArray = numberGroup2.split("/");
|
|
183
|
+
const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
|
|
184
|
+
finalResult += Math.round(numerator * 1e3 / denominator) / 1e3;
|
|
185
|
+
}
|
|
186
|
+
return finalResult * (dash === "-" ? -1 : 1);
|
|
187
|
+
};
|
|
188
|
+
export {
|
|
189
|
+
numericQuantity,
|
|
190
|
+
numericRegex,
|
|
191
|
+
parseRomanNumerals,
|
|
192
|
+
romanNumeralRegex,
|
|
193
|
+
romanNumeralUnicodeRegex,
|
|
194
|
+
romanNumeralUnicodeToAsciiMap,
|
|
195
|
+
romanNumeralValues,
|
|
196
|
+
vulgarFractionToAsciiMap,
|
|
197
|
+
vulgarFractionsRegex
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=numeric-quantity.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var I={"\xBC":"1/4","\xBD":"1/2","\xBE":"3/4","\u2150":"1/7","\u2151":"1/9","\u2152":"1/10","\u2153":"1/3","\u2154":"2/3","\u2155":"1/5","\u2156":"2/5","\u2157":"3/5","\u2158":"4/5","\u2159":"1/6","\u215A":"5/6","\u215B":"1/8","\u215C":"3/8","\u215D":"5/8","\u215E":"7/8","\u215F":"1/"},p=/^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/,N=new RegExp(`(${Object.keys(I).join("|")})`),t={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},l={"\u2160":"I","\u2161":"II","\u2162":"III","\u2163":"IV","\u2164":"V","\u2165":"VI","\u2166":"VII","\u2167":"VIII","\u2168":"IX","\u2169":"X","\u216A":"XI","\u216B":"XII","\u216C":"L","\u216D":"C","\u216E":"D","\u216F":"M","\u2170":"I","\u2171":"II","\u2172":"III","\u2173":"IV","\u2174":"V","\u2175":"VI","\u2176":"VII","\u2177":"VIII","\u2178":"IX","\u2179":"X","\u217A":"XI","\u217B":"XII","\u217C":"L","\u217D":"C","\u217E":"D","\u217F":"M"},R=new RegExp(`(${Object.keys(l).join("|")})`,"gi"),g=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;var X=c=>{let r=`${c}`.replace(R,(m,a)=>l[a]).toUpperCase(),o=g.exec(r);if(!o)return NaN;let[,s,i,u,e]=o;return(t[s]||0)+(t[i]||0)+(t[u]||0)+(t[e]||0)};var d=/^\s*\//,y=c=>{let r=NaN,o=`${c}`.replace(N,(a,n)=>` ${I[n]}`).replace("\u2044","/").trim();if(o.length===0)return NaN;let s=p.exec(o);if(!s)return X(o);let[,i,u,e]=s,m=u.replace(/[,_]/g,"");if(!m&&e&&e.startsWith(".")?r=0:r=parseInt(m),!e)return r*(i==="-"?-1:1);if(e.startsWith(".")){let a=parseFloat(e);r+=Math.round(a*1e3)/1e3}else if(d.test(e)){let a=parseInt(m),n=parseInt(e.replace("/",""));r=Math.round(a*1e3/n)/1e3}else{let a=e.split("/"),[n,x]=a.map(V=>parseInt(V));r+=Math.round(n*1e3/x)/1e3}return r*(i==="-"?-1:1)};export{y as numericQuantity,p as numericRegex,X as parseRomanNumerals,g as romanNumeralRegex,R as romanNumeralUnicodeRegex,l as romanNumeralUnicodeToAsciiMap,t as romanNumeralValues,I as vulgarFractionToAsciiMap,N as vulgarFractionsRegex};
|
|
2
|
+
//# sourceMappingURL=numeric-quantity.production.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","numberGroup2","numberGroup1","numerator","denominator","fractionArray","v"]}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "2.0.0-beta.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "numeric-quantity",
|
|
5
5
|
"author": "Jake Boone <jakeboone02@gmail.com>",
|
|
@@ -7,17 +7,21 @@
|
|
|
7
7
|
"files": [
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
|
-
"main": "./dist/
|
|
11
|
-
"module": "./dist/numeric-quantity.
|
|
12
|
-
"
|
|
13
|
-
|
|
10
|
+
"main": "./dist/cjs/index.js",
|
|
11
|
+
"module": "./dist/numeric-quantity.legacy-esm.js",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/numeric-quantity.d.ts",
|
|
16
|
+
"import": "./dist/numeric-quantity.mjs",
|
|
17
|
+
"require": "./dist/cjs/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"types": "./dist/numeric-quantity.d.ts",
|
|
14
21
|
"bugs": {
|
|
15
22
|
"url": "https://github.com/jakeboone02/numeric-quantity/issues"
|
|
16
23
|
},
|
|
17
24
|
"homepage": "https://github.com/jakeboone02/numeric-quantity",
|
|
18
|
-
"engines": {
|
|
19
|
-
"node": ">=10"
|
|
20
|
-
},
|
|
21
25
|
"repository": {
|
|
22
26
|
"type": "git",
|
|
23
27
|
"url": "https://github.com/jakeboone02/numeric-quantity.git"
|
|
@@ -30,20 +34,27 @@
|
|
|
30
34
|
],
|
|
31
35
|
"scripts": {
|
|
32
36
|
"start": "vite",
|
|
33
|
-
"build": "
|
|
34
|
-
"test": "jest
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"test": "jest",
|
|
39
|
+
"watch": "jest --watch",
|
|
35
40
|
"publish:npm": "np"
|
|
36
41
|
},
|
|
37
42
|
"devDependencies": {
|
|
38
|
-
"@babel/core": "^7.
|
|
39
|
-
"@babel/preset-env": "^7.
|
|
40
|
-
"@babel/preset-typescript": "^7.
|
|
41
|
-
"@types/jest": "^
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
43
|
+
"@babel/core": "^7.22.1",
|
|
44
|
+
"@babel/preset-env": "^7.22.4",
|
|
45
|
+
"@babel/preset-typescript": "^7.21.5",
|
|
46
|
+
"@types/jest": "^29.5.2",
|
|
47
|
+
"@types/node": "^20.2.5",
|
|
48
|
+
"gh-pages": "^5.0.0",
|
|
49
|
+
"jest": "^29.5.0",
|
|
50
|
+
"np": "^8.0.2",
|
|
51
|
+
"prettier": "^2.8.8",
|
|
52
|
+
"tsup": "^6.7.0",
|
|
53
|
+
"typescript": "^5.1.3",
|
|
54
|
+
"vite": "^4.3.9"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=16"
|
|
58
|
+
},
|
|
59
|
+
"packageManager": "yarn@3.6.0"
|
|
49
60
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
## 1.0.1 (2021-02-15)
|
|
2
|
-
|
|
3
|
-
- Added description to package.json
|
|
4
|
-
|
|
5
|
-
## 1.0.0 (2021-02-11)
|
|
6
|
-
|
|
7
|
-
- New build system ([tsdx](https://tsdx.io/))
|
|
8
|
-
|
|
9
|
-
## 0.5.1 (2019-08-24)
|
|
10
|
-
|
|
11
|
-
- Fixed README.md note about return values
|
|
12
|
-
|
|
13
|
-
## 0.5.0 (2019-08-24)
|
|
14
|
-
|
|
15
|
-
### Breaking change
|
|
16
|
-
|
|
17
|
-
- Returns `NaN` for invalid inputs instead of `-1`
|
|
18
|
-
|
|
19
|
-
### Bug fixes
|
|
20
|
-
|
|
21
|
-
- Handles negative numbers properly (fixes [#3](https://github.com/jakeboone02/numeric-quantity/issues/3))
|
|
22
|
-
|
|
23
|
-
## 0.4.2 (2019-08-23)
|
|
24
|
-
|
|
25
|
-
- Rewritten with TypeScript
|
|
26
|
-
|
|
27
|
-
## 0.3.2 (2018-09-21)
|
|
28
|
-
|
|
29
|
-
## 0.3.1 (2015-07-16)
|
|
30
|
-
|
|
31
|
-
## 0.3.0 (2015-07-16)
|
|
32
|
-
|
|
33
|
-
## 0.1.2 (2015-03-20)
|
|
34
|
-
|
|
35
|
-
## 0.1.1 (2015-03-19)
|
|
36
|
-
|
|
37
|
-
## 0.1.0 (2015-03-18)
|
package/dist/index.d.ts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";var m=(e=>(e["\xBC"]="1/4",e["\xBD"]="1/2",e["\xBE"]="3/4",e["\u2150"]="1/7",e["\u2151"]="1/9",e["\u2152"]="1/10",e["\u2153"]="1/3",e["\u2154"]="2/3",e["\u2155"]="1/5",e["\u2156"]="2/5",e["\u2157"]="3/5",e["\u2158"]="4/5",e["\u2159"]="1/6",e["\u215A"]="5/6",e["\u215B"]="1/8",e["\u215C"]="3/8",e["\u215D"]="5/8",e["\u215E"]="7/8",e))(m||{});function R(e){let t=NaN;const o=/(¼|½|¾|⅐|⅑|⅒|⅓|⅔|⅕|⅖|⅗|⅘|⅙|⅚|⅛|⅜|⅝|⅞)/,f=`${e}`.replace(o,(u,n)=>` ${m[n]}`).replace(/⁄/g,"/").trim(),d=/^(-)?\s*(\d*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?$/.exec(f);if(!d)return NaN;const[,p,r,s]=d;if(!r&&!s)return NaN;if(!r&&s&&s.search(/^\./)!==-1?t=0:t=parseInt(r),isNaN(t))return NaN;if(!s)return t*(p==="-"?-1:1);if(s.search(/^\./)!==-1){const u=parseFloat(s);t+=Math.round(u*1e3)/1e3}else if(s.search(/^\s*\//)!==-1){const u=parseInt(r),n=parseInt(s.replace("/",""));t=Math.round(u*1e3/n)/1e3}else{const u=s.split("/"),[n,b]=u.map(h=>parseInt(h));t+=Math.round(n*1e3/b)/1e3}return t*(p==="-"?-1:1)}module.exports=R;
|
|
2
|
-
//# sourceMappingURL=numeric-quantity.cjs.js.map
|