numeric-quantity 2.0.0 → 2.1.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.
@@ -1,20 +1,3 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
- var __hasOwnProp = Object.prototype.hasOwnProperty;
4
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
- var __spreadValues = (a, b) => {
7
- for (var prop in b || (b = {}))
8
- if (__hasOwnProp.call(b, prop))
9
- __defNormalProp(a, prop, b[prop]);
10
- if (__getOwnPropSymbols)
11
- for (var prop of __getOwnPropSymbols(b)) {
12
- if (__propIsEnum.call(b, prop))
13
- __defNormalProp(a, prop, b[prop]);
14
- }
15
- return a;
16
- };
17
-
18
1
  // src/constants.ts
19
2
  var vulgarFractionToAsciiMap = {
20
3
  "\xBC": "1/4",
@@ -38,7 +21,9 @@ var vulgarFractionToAsciiMap = {
38
21
  "\u215F": "1/"
39
22
  };
40
23
  var numericRegex = /^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?$/;
41
- var numericRegexWithTrailingInvalid = /^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/;
24
+ var numericRegexWithTrailingInvalid = new RegExp(
25
+ numericRegex.source.replace(/\$$/, "(?:\\s*[^\\.\\d\\/].*)?")
26
+ );
42
27
  var vulgarFractionsRegex = new RegExp(
43
28
  `(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
44
29
  );
@@ -63,10 +48,10 @@ var romanNumeralValues = {
63
48
  XL: 40,
64
49
  XXX: 30,
65
50
  XX: 20,
66
- // Twelve is only here for tests; not used in practice
67
51
  XII: 12,
68
- // Eleven is only here for tests; not used in practice
52
+ // only here for tests; not used in practice
69
53
  XI: 11,
54
+ // only here for tests; not used in practice
70
55
  X: 10,
71
56
  IX: 9,
72
57
  VIII: 8,
@@ -152,12 +137,12 @@ var romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(
152
137
  var defaultOptions = {
153
138
  round: 3,
154
139
  allowTrailingInvalid: false,
155
- romanNumerals: false
140
+ romanNumerals: false,
141
+ bigIntOnOverflow: false
156
142
  };
157
143
 
158
144
  // src/parseRomanNumerals.ts
159
145
  var parseRomanNumerals = (romanNumerals) => {
160
- var _a, _b, _c, _d;
161
146
  const normalized = `${romanNumerals}`.replace(
162
147
  romanNumeralUnicodeRegex,
163
148
  (_m, rn) => romanNumeralUnicodeToAsciiMap[rn]
@@ -167,12 +152,12 @@ var parseRomanNumerals = (romanNumerals) => {
167
152
  return NaN;
168
153
  }
169
154
  const [, thousands, hundreds, tens, ones] = regexResult;
170
- return ((_a = romanNumeralValues[thousands]) != null ? _a : 0) + ((_b = romanNumeralValues[hundreds]) != null ? _b : 0) + ((_c = romanNumeralValues[tens]) != null ? _c : 0) + ((_d = romanNumeralValues[ones]) != null ? _d : 0);
155
+ return (romanNumeralValues[thousands] ?? 0) + (romanNumeralValues[hundreds] ?? 0) + (romanNumeralValues[tens] ?? 0) + (romanNumeralValues[ones] ?? 0);
171
156
  };
172
157
 
173
158
  // src/numericQuantity.ts
174
159
  var spaceThenSlashRegex = /^\s*\//;
175
- var numericQuantity = (quantity, options = defaultOptions) => {
160
+ function numericQuantity(quantity, options = defaultOptions) {
176
161
  if (typeof quantity === "number" || typeof quantity === "bigint") {
177
162
  return quantity;
178
163
  }
@@ -184,17 +169,26 @@ var numericQuantity = (quantity, options = defaultOptions) => {
184
169
  if (quantityAsString.length === 0) {
185
170
  return NaN;
186
171
  }
187
- const opts = __spreadValues(__spreadValues({}, defaultOptions), options);
172
+ const opts = {
173
+ ...defaultOptions,
174
+ ...options
175
+ };
188
176
  const regexResult = (opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex).exec(quantityAsString);
189
177
  if (!regexResult) {
190
178
  return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;
191
179
  }
192
180
  const [, dash, ng1temp, ng2temp] = regexResult;
193
181
  const numberGroup1 = ng1temp.replace(/[,_]/g, "");
194
- const numberGroup2 = ng2temp == null ? void 0 : ng2temp.replace(/[,_]/g, "");
182
+ const numberGroup2 = ng2temp?.replace(/[,_]/g, "");
195
183
  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
196
184
  finalResult = 0;
197
185
  } else {
186
+ if (opts.bigIntOnOverflow) {
187
+ const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);
188
+ if (asBigInt > BigInt(Number.MAX_SAFE_INTEGER) || asBigInt < BigInt(Number.MIN_SAFE_INTEGER)) {
189
+ return asBigInt;
190
+ }
191
+ }
198
192
  finalResult = parseInt(numberGroup1);
199
193
  }
200
194
  if (!numberGroup2) {
@@ -214,7 +208,7 @@ var numericQuantity = (quantity, options = defaultOptions) => {
214
208
  finalResult += isNaN(roundingFactor) ? numerator / denominator : Math.round(numerator * roundingFactor / denominator) / roundingFactor;
215
209
  }
216
210
  return dash ? finalResult * -1 : finalResult;
217
- };
211
+ }
218
212
  export {
219
213
  defaultOptions,
220
214
  numericQuantity,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\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)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as `numericRegex`, but allows/ignores trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\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 keyof typeof romanNumeralValues\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\nexport const defaultOptions = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n} satisfies Required<NumericQuantityOptions>;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 = (\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) => {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\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<\n VulgarFraction,\n `${number}/${number | ''}`\n> = {\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} as const;\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * | # | Description | Example(s) |\n * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |\n * | `0` | entire string | `\"2 1/3\"` from `\"2 1/3\"` |\n * | `1` | \"negative\" dash | `\"-\"` from `\"-2 1/3\"` |\n * | `2` | whole number or numerator | `\"2\"` from `\"2 1/3\"`; `\"1\"` from `\"1/3\"` |\n * | `3` | entire fraction, decimal portion, or denominator | `\" 1/3\"` from `\"2 1/3\"`; `\".33\"` from `\"2.33\"`; `\"/3\"` from `\"1/3\"` |\n *\n * _Capture group 2 may include comma/underscore separators._\n *\n * @example\n *\n * ```ts\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 * ```\n */\nexport const numericRegex: RegExp =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid: RegExp = new RegExp(\n numericRegex.source.replace(/\\$$/, '(?:\\\\s*[^\\\\.\\\\d\\\\/].*)?')\n);\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = 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\n/**\n * Map of Roman numeral sequences to their decimal equivalents.\n */\nexport const romanNumeralValues: {\n [k in RomanNumeralSequenceFragment]?: number;\n} = {\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 XII: 12, // only here for tests; not used in practice\n XI: 11, // only here for tests; not used in practice\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} as const;\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents.\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n keyof typeof romanNumeralValues\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} as const;\n\n/**\n * Captures all Unicode Roman numeral code points.\n */\nexport const romanNumeralUnicodeRegex: RegExp = 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 * | # | Description | Example |\n * | --- | --------------- | ------------------------ |\n * | `0` | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * | `1` | Thousands | \"M\" from \"MCCXIV\" |\n * | `2` | Hundreds | \"CC\" from \"MCCXIV\" |\n * | `3` | Tens | \"X\" from \"MCCXIV\" |\n * | `4` | Ones | \"IV\" from \"MCCXIV\" |\n *\n * @example\n *\n * ```ts\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n * ```\n */\nexport const romanNumeralRegex: RegExp =\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\n/**\n * Default options for {@link numericQuantity}.\n */\nexport const defaultOptions: Required<NumericQuantityOptions> = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n bigIntOnOverflow: false,\n} as const;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string): number => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions & { bigIntOnOverflow: true }\n): number | bigint;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n if (opts.bigIntOnOverflow) {\n const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);\n if (\n asBigInt > BigInt(Number.MAX_SAFE_INTEGER) ||\n asBigInt < BigInt(Number.MIN_SAFE_INTEGER)\n ) {\n return asBigInt;\n }\n }\n\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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n}\n\nexport { numericQuantity };\n"],"mappings":"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","names":[]}
@@ -1,2 +1,2 @@
1
- var F=Object.defineProperty;var V=Object.getOwnPropertySymbols;var T=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable;var X=(r,a,e)=>a in r?F(r,a,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[a]=e,N=(r,a)=>{for(var e in a||(a={}))T.call(a,e)&&X(r,e,a[e]);if(V)for(var e of V(a))D.call(a,e)&&X(r,e,a[e]);return r};var R={"\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/"},y=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?$/,M=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/,C=new RegExp(`(${Object.keys(R).join("|")})`),l={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},g={"\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"},$=new RegExp(`(${Object.keys(g).join("|")})`,"gi"),A=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,f={round:3,allowTrailingInvalid:!1,romanNumerals:!1};var h=r=>{var I,m,u,o;let a=`${r}`.replace($,(n,t)=>g[t]).toUpperCase(),e=A.exec(a);if(!e)return NaN;let[,c,i,d,p]=e;return((I=l[c])!=null?I:0)+((m=l[i])!=null?m:0)+((u=l[d])!=null?u:0)+((o=l[p])!=null?o:0)};var L=/^\s*\//,Q=(r,a=f)=>{if(typeof r=="number"||typeof r=="bigint")return r;let e=NaN,c=`${r}`.replace(C,(t,s)=>` ${R[s]}`).replace("\u2044","/").trim();if(c.length===0)return NaN;let i=N(N({},f),a),d=(i.allowTrailingInvalid?M:y).exec(c);if(!d)return i.romanNumerals?h(c):NaN;let[,p,I,m]=d,u=I.replace(/[,_]/g,""),o=m==null?void 0:m.replace(/[,_]/g,"");if(!u&&o&&o.startsWith(".")?e=0:e=parseInt(u),!o)return p?e*-1:e;let n=i.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,i.round))}`);if(o.startsWith(".")||o.startsWith("e")||o.startsWith("E")){let t=parseFloat(`${e}${o}`);e=isNaN(n)?t:Math.round(t*n)/n}else if(L.test(o)){let t=parseInt(u),s=parseInt(o.replace("/",""));e=isNaN(n)?t/s:Math.round(t*n/s)/n}else{let t=o.split("/"),[s,x]=t.map(_=>parseInt(_));e+=isNaN(n)?s/x:Math.round(s*n/x)/n}return p?e*-1:e};export{f as defaultOptions,Q as numericQuantity,y as numericRegex,M as numericRegexWithTrailingInvalid,h as parseRomanNumerals,A as romanNumeralRegex,$ as romanNumeralUnicodeRegex,g as romanNumeralUnicodeToAsciiMap,l as romanNumeralValues,R as vulgarFractionToAsciiMap,C as vulgarFractionsRegex};
1
+ var N={"\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/"},g=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?$/,y=new RegExp(g.source.replace(/\$$/,"(?:\\s*[^\\.\\d\\/].*)?")),V=new RegExp(`(${Object.keys(N).join("|")})`),c={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},d={"\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"},X=new RegExp(`(${Object.keys(d).join("|")})`,"gi"),b=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,R={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1};var M=i=>{let p=`${i}`.replace(X,(f,I)=>d[I]).toUpperCase(),e=b.exec(p);if(!e)return NaN;let[,s,a,l,u]=e;return(c[s]??0)+(c[a]??0)+(c[l]??0)+(c[u]??0)};var A=/^\s*\//;function _(i,p=R){if(typeof i=="number"||typeof i=="bigint")return i;let e=NaN,s=`${i}`.replace(V,(n,o)=>` ${N[o]}`).replace("\u2044","/").trim();if(s.length===0)return NaN;let a={...R,...p},l=(a.allowTrailingInvalid?y:g).exec(s);if(!l)return a.romanNumerals?M(s):NaN;let[,u,f,I]=l,m=f.replace(/[,_]/g,""),r=I?.replace(/[,_]/g,"");if(!m&&r&&r.startsWith("."))e=0;else{if(a.bigIntOnOverflow){let n=BigInt(u?`-${m}`:m);if(n>BigInt(Number.MAX_SAFE_INTEGER)||n<BigInt(Number.MIN_SAFE_INTEGER))return n}e=parseInt(m)}if(!r)return u?e*-1:e;let t=a.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,a.round))}`);if(r.startsWith(".")||r.startsWith("e")||r.startsWith("E")){let n=parseFloat(`${e}${r}`);e=isNaN(t)?n:Math.round(n*t)/t}else if(A.test(r)){let n=parseInt(m),o=parseInt(r.replace("/",""));e=isNaN(t)?n/o:Math.round(n*t/o)/t}else{let n=r.split("/"),[o,x]=n.map($=>parseInt($));e+=isNaN(t)?o/x:Math.round(o*t/x)/t}return u?e*-1:e}export{R as defaultOptions,_ as numericQuantity,g as numericRegex,y as numericRegexWithTrailingInvalid,M as parseRomanNumerals,b as romanNumeralRegex,X as romanNumeralUnicodeRegex,d as romanNumeralUnicodeToAsciiMap,c as romanNumeralValues,N as vulgarFractionToAsciiMap,V as vulgarFractionsRegex};
2
2
  //# sourceMappingURL=numeric-quantity.production.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\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)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as `numericRegex`, but allows/ignores trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\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 keyof typeof romanNumeralValues\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\nexport const defaultOptions = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n} satisfies Required<NumericQuantityOptions>;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 = (\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) => {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","numericRegexWithTrailingInvalid","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","defaultOptions","parseRomanNumerals","romanNumerals","_a","_b","_c","_d","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","options","defaultOptions","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","opts","__spreadValues","regexResult","numericRegexWithTrailingInvalid","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","roundingFactor","decimalValue","numerator","denominator","fractionArray","v"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\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<\n VulgarFraction,\n `${number}/${number | ''}`\n> = {\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} as const;\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * | # | Description | Example(s) |\n * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |\n * | `0` | entire string | `\"2 1/3\"` from `\"2 1/3\"` |\n * | `1` | \"negative\" dash | `\"-\"` from `\"-2 1/3\"` |\n * | `2` | whole number or numerator | `\"2\"` from `\"2 1/3\"`; `\"1\"` from `\"1/3\"` |\n * | `3` | entire fraction, decimal portion, or denominator | `\" 1/3\"` from `\"2 1/3\"`; `\".33\"` from `\"2.33\"`; `\"/3\"` from `\"1/3\"` |\n *\n * _Capture group 2 may include comma/underscore separators._\n *\n * @example\n *\n * ```ts\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 * ```\n */\nexport const numericRegex: RegExp =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid: RegExp = new RegExp(\n numericRegex.source.replace(/\\$$/, '(?:\\\\s*[^\\\\.\\\\d\\\\/].*)?')\n);\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = 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\n/**\n * Map of Roman numeral sequences to their decimal equivalents.\n */\nexport const romanNumeralValues: {\n [k in RomanNumeralSequenceFragment]?: number;\n} = {\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 XII: 12, // only here for tests; not used in practice\n XI: 11, // only here for tests; not used in practice\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} as const;\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents.\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n keyof typeof romanNumeralValues\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} as const;\n\n/**\n * Captures all Unicode Roman numeral code points.\n */\nexport const romanNumeralUnicodeRegex: RegExp = 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 * | # | Description | Example |\n * | --- | --------------- | ------------------------ |\n * | `0` | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * | `1` | Thousands | \"M\" from \"MCCXIV\" |\n * | `2` | Hundreds | \"CC\" from \"MCCXIV\" |\n * | `3` | Tens | \"X\" from \"MCCXIV\" |\n * | `4` | Ones | \"IV\" from \"MCCXIV\" |\n *\n * @example\n *\n * ```ts\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n * ```\n */\nexport const romanNumeralRegex: RegExp =\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\n/**\n * Default options for {@link numericQuantity}.\n */\nexport const defaultOptions: Required<NumericQuantityOptions> = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n bigIntOnOverflow: false,\n} as const;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string): number => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions & { bigIntOnOverflow: true }\n): number | bigint;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n if (opts.bigIntOnOverflow) {\n const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);\n if (\n asBigInt > BigInt(Number.MAX_SAFE_INTEGER) ||\n asBigInt < BigInt(Number.MIN_SAFE_INTEGER)\n ) {\n return asBigInt;\n }\n }\n\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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n}\n\nexport { numericQuantity };\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","numericRegexWithTrailingInvalid","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","defaultOptions","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","options","defaultOptions","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","opts","regexResult","numericRegexWithTrailingInvalid","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","asBigInt","roundingFactor","decimalValue","numerator","denominator","fractionArray","v"]}
@@ -1,2 +1,2 @@
1
- "use strict";var NumericQuantity=(()=>{var N=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames,h=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,v=Object.prototype.propertyIsEnumerable;var _=(a,r,e)=>r in a?N(a,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[r]=e,x=(a,r)=>{for(var e in r||(r={}))F.call(r,e)&&_(a,e,r[e]);if(h)for(var e of h(r))v.call(r,e)&&_(a,e,r[e]);return a};var O=(a,r)=>{for(var e in r)N(a,e,{get:r[e],enumerable:!0})},b=(a,r,e,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of L(r))!F.call(a,n)&&n!==e&&N(a,n,{get:()=>r[n],enumerable:!(i=D(r,n))||i.enumerable});return a};var U=a=>b(N({},"__esModule",{value:!0}),a);var Q={};O(Q,{defaultOptions:()=>f,numericQuantity:()=>E,numericRegex:()=>V,numericRegexWithTrailingInvalid:()=>X,parseRomanNumerals:()=>$,romanNumeralRegex:()=>C,romanNumeralUnicodeRegex:()=>M,romanNumeralUnicodeToAsciiMap:()=>g,romanNumeralValues:()=>l,vulgarFractionToAsciiMap:()=>R,vulgarFractionsRegex:()=>y});var R={"\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/"},V=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?$/,X=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/,y=new RegExp(`(${Object.keys(R).join("|")})`),l={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},g={"\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"},M=new RegExp(`(${Object.keys(g).join("|")})`,"gi"),C=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,f={round:3,allowTrailingInvalid:!1,romanNumerals:!1};var $=a=>{var I,u,c,o;let r=`${a}`.replace(M,(t,s)=>g[s]).toUpperCase(),e=C.exec(r);if(!e)return NaN;let[,i,n,d,p]=e;return((I=l[i])!=null?I:0)+((u=l[n])!=null?u:0)+((c=l[d])!=null?c:0)+((o=l[p])!=null?o:0)};var k=/^\s*\//,E=(a,r=f)=>{if(typeof a=="number"||typeof a=="bigint")return a;let e=NaN,i=`${a}`.replace(y,(s,m)=>` ${R[m]}`).replace("\u2044","/").trim();if(i.length===0)return NaN;let n=x(x({},f),r),d=(n.allowTrailingInvalid?X:V).exec(i);if(!d)return n.romanNumerals?$(i):NaN;let[,p,I,u]=d,c=I.replace(/[,_]/g,""),o=u==null?void 0:u.replace(/[,_]/g,"");if(!c&&o&&o.startsWith(".")?e=0:e=parseInt(c),!o)return p?e*-1:e;let t=n.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,n.round))}`);if(o.startsWith(".")||o.startsWith("e")||o.startsWith("E")){let s=parseFloat(`${e}${o}`);e=isNaN(t)?s:Math.round(s*t)/t}else if(k.test(o)){let s=parseInt(c),m=parseInt(o.replace("/",""));e=isNaN(t)?s/m:Math.round(s*t/m)/t}else{let s=o.split("/"),[m,A]=s.map(T=>parseInt(T));e+=isNaN(t)?m/A:Math.round(m*t/A)/t}return p?e*-1:e};return U(Q);})();
1
+ "use strict";var NumericQuantity=(()=>{var f=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var O=(n,r)=>{for(var e in r)f(n,e,{get:r[e],enumerable:!0})},F=(n,r,e,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let t of h(r))!E.call(n,t)&&t!==e&&f(n,t,{get:()=>r[t],enumerable:!(s=C(r,t))||s.enumerable});return n};var _=n=>F(f({},"__esModule",{value:!0}),n);var Q={};O(Q,{defaultOptions:()=>d,numericQuantity:()=>v,numericRegex:()=>N,numericRegexWithTrailingInvalid:()=>x,parseRomanNumerals:()=>b,romanNumeralRegex:()=>X,romanNumeralUnicodeRegex:()=>V,romanNumeralUnicodeToAsciiMap:()=>g,romanNumeralValues:()=>m,vulgarFractionToAsciiMap:()=>I,vulgarFractionsRegex:()=>y});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/"},N=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(([eE][+-]?\d(?:[\d,_]*\d)?)?|\.\d(?:[\d,_]*\d)?([eE][+-]?\d(?:[\d,_]*\d)?)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?$/,x=new RegExp(N.source.replace(/\$$/,"(?:\\s*[^\\.\\d\\/].*)?")),y=new RegExp(`(${Object.keys(I).join("|")})`),m={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},g={"\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"},V=new RegExp(`(${Object.keys(g).join("|")})`,"gi"),X=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i,d={round:3,allowTrailingInvalid:!1,romanNumerals:!1,bigIntOnOverflow:!1};var b=n=>{let r=`${n}`.replace(V,(M,R)=>g[R]).toUpperCase(),e=X.exec(r);if(!e)return NaN;let[,s,t,p,c]=e;return(m[s]??0)+(m[t]??0)+(m[p]??0)+(m[c]??0)};var T=/^\s*\//;function v(n,r=d){if(typeof n=="number"||typeof n=="bigint")return n;let e=NaN,s=`${n}`.replace(y,(a,u)=>` ${I[u]}`).replace("\u2044","/").trim();if(s.length===0)return NaN;let t={...d,...r},p=(t.allowTrailingInvalid?x:N).exec(s);if(!p)return t.romanNumerals?b(s):NaN;let[,c,M,R]=p,l=M.replace(/[,_]/g,""),o=R?.replace(/[,_]/g,"");if(!l&&o&&o.startsWith("."))e=0;else{if(t.bigIntOnOverflow){let a=BigInt(c?`-${l}`:l);if(a>BigInt(Number.MAX_SAFE_INTEGER)||a<BigInt(Number.MIN_SAFE_INTEGER))return a}e=parseInt(l)}if(!o)return c?e*-1:e;let i=t.round===!1?NaN:parseFloat(`1e${Math.floor(Math.max(0,t.round))}`);if(o.startsWith(".")||o.startsWith("e")||o.startsWith("E")){let a=parseFloat(`${e}${o}`);e=isNaN(i)?a:Math.round(a*i)/i}else if(T.test(o)){let a=parseInt(l),u=parseInt(o.replace("/",""));e=isNaN(i)?a/u:Math.round(a*i/u)/i}else{let a=o.split("/"),[u,$]=a.map(A=>parseInt(A));e+=isNaN(i)?u/$:Math.round(u*i/$)/i}return c?e*-1:e}return _(Q);})();
2
2
  //# sourceMappingURL=numeric-quantity.umd.min.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["export * from './constants';\nexport * from './numericQuantity';\nexport * from './parseRomanNumerals';\nexport * from './types';\n","import type {\n NumericQuantityOptions,\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)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as `numericRegex`, but allows/ignores trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\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 keyof typeof romanNumeralValues\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\nexport const defaultOptions = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n} satisfies Required<NumericQuantityOptions>;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 = (\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) => {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":["src_exports","__export","defaultOptions","numericQuantity","numericRegex","numericRegexWithTrailingInvalid","parseRomanNumerals","romanNumeralRegex","romanNumeralUnicodeRegex","romanNumeralUnicodeToAsciiMap","romanNumeralValues","vulgarFractionToAsciiMap","vulgarFractionsRegex","vulgarFractionToAsciiMap","numericRegex","numericRegexWithTrailingInvalid","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","defaultOptions","parseRomanNumerals","romanNumerals","_a","_b","_c","_d","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","options","defaultOptions","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","opts","__spreadValues","regexResult","numericRegexWithTrailingInvalid","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","roundingFactor","decimalValue","numerator","denominator","fractionArray","v"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["export * from './constants';\nexport * from './numericQuantity';\nexport * from './parseRomanNumerals';\nexport * from './types';\n","import type {\n NumericQuantityOptions,\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<\n VulgarFraction,\n `${number}/${number | ''}`\n> = {\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} as const;\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * | # | Description | Example(s) |\n * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |\n * | `0` | entire string | `\"2 1/3\"` from `\"2 1/3\"` |\n * | `1` | \"negative\" dash | `\"-\"` from `\"-2 1/3\"` |\n * | `2` | whole number or numerator | `\"2\"` from `\"2 1/3\"`; `\"1\"` from `\"1/3\"` |\n * | `3` | entire fraction, decimal portion, or denominator | `\" 1/3\"` from `\"2 1/3\"`; `\".33\"` from `\"2.33\"`; `\"/3\"` from `\"1/3\"` |\n *\n * _Capture group 2 may include comma/underscore separators._\n *\n * @example\n *\n * ```ts\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 * ```\n */\nexport const numericRegex: RegExp =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|\\.\\d(?:[\\d,_]*\\d)?([eE][+-]?\\d(?:[\\d,_]*\\d)?)?|(\\s+\\d(?:[\\d,_]*\\d)?\\s*)?\\s*\\/\\s*\\d(?:[\\d,_]*\\d)?)?$/;\n/**\n * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.\n */\nexport const numericRegexWithTrailingInvalid: RegExp = new RegExp(\n numericRegex.source.replace(/\\$$/, '(?:\\\\s*[^\\\\.\\\\d\\\\/].*)?')\n);\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = 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\n/**\n * Map of Roman numeral sequences to their decimal equivalents.\n */\nexport const romanNumeralValues: {\n [k in RomanNumeralSequenceFragment]?: number;\n} = {\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 XII: 12, // only here for tests; not used in practice\n XI: 11, // only here for tests; not used in practice\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} as const;\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents.\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n keyof typeof romanNumeralValues\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} as const;\n\n/**\n * Captures all Unicode Roman numeral code points.\n */\nexport const romanNumeralUnicodeRegex: RegExp = 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 * | # | Description | Example |\n * | --- | --------------- | ------------------------ |\n * | `0` | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * | `1` | Thousands | \"M\" from \"MCCXIV\" |\n * | `2` | Hundreds | \"CC\" from \"MCCXIV\" |\n * | `3` | Tens | \"X\" from \"MCCXIV\" |\n * | `4` | Ones | \"IV\" from \"MCCXIV\" |\n *\n * @example\n *\n * ```ts\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n * ```\n */\nexport const romanNumeralRegex: RegExp =\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\n/**\n * Default options for {@link numericQuantity}.\n */\nexport const defaultOptions: Required<NumericQuantityOptions> = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n bigIntOnOverflow: false,\n} as const;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string): number => {\n const normalized = `${romanNumerals}`\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\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 defaultOptions,\n numericRegex,\n numericRegexWithTrailingInvalid,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type { NumericQuantityOptions } from './types';\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 */\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(quantity: string | number): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions & { bigIntOnOverflow: true }\n): number | bigint;\nfunction numericQuantity(\n quantity: string | number,\n options?: NumericQuantityOptions\n): number;\nfunction numericQuantity(\n quantity: string | number,\n options: NumericQuantityOptions = defaultOptions\n) {\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return quantity;\n }\n\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 opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n const regexResult = (\n opts.allowTrailingInvalid ? numericRegexWithTrailingInvalid : numericRegex\n ).exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;\n }\n\n const [, dash, ng1temp, ng2temp] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n const numberGroup2 = ng2temp?.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n if (opts.bigIntOnOverflow) {\n const asBigInt = dash ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);\n if (\n asBigInt > BigInt(Number.MAX_SAFE_INTEGER) ||\n asBigInt < BigInt(Number.MIN_SAFE_INTEGER)\n ) {\n return asBigInt;\n }\n }\n\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 dash ? finalResult * -1 : finalResult;\n }\n\n const roundingFactor =\n opts.round === false\n ? NaN\n : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : Math.round(decimalValue * roundingFactor) / roundingFactor;\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 = isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\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 += isNaN(roundingFactor)\n ? numerator / denominator\n : Math.round((numerator * roundingFactor) / denominator) / roundingFactor;\n }\n\n return dash ? finalResult * -1 : finalResult;\n}\n\nexport { numericQuantity };\n"],"mappings":"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","names":["src_exports","__export","defaultOptions","numericQuantity","numericRegex","numericRegexWithTrailingInvalid","parseRomanNumerals","romanNumeralRegex","romanNumeralUnicodeRegex","romanNumeralUnicodeToAsciiMap","romanNumeralValues","vulgarFractionToAsciiMap","vulgarFractionsRegex","vulgarFractionToAsciiMap","numericRegex","numericRegexWithTrailingInvalid","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","defaultOptions","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","options","defaultOptions","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","opts","regexResult","numericRegexWithTrailingInvalid","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","asBigInt","roundingFactor","decimalValue","numerator","denominator","fractionArray","v"]}
@@ -0,0 +1,78 @@
1
+ import type { NumericQuantityOptions, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction } from "./types";
2
+ /**
3
+ * Map of Unicode fraction code points to their ASCII equivalents.
4
+ */
5
+ export declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
6
+ /**
7
+ * Captures the individual elements of a numeric string.
8
+ *
9
+ * Capture groups:
10
+ *
11
+ * | # | Description | Example(s) |
12
+ * | --- | ------------------------------------------------ | ------------------------------------------------------------------- |
13
+ * | `0` | entire string | `"2 1/3"` from `"2 1/3"` |
14
+ * | `1` | "negative" dash | `"-"` from `"-2 1/3"` |
15
+ * | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
16
+ * | `3` | entire fraction, decimal portion, or denominator | `" 1/3"` from `"2 1/3"`; `".33"` from `"2.33"`; `"/3"` from `"1/3"` |
17
+ *
18
+ * _Capture group 2 may include comma/underscore separators._
19
+ *
20
+ * @example
21
+ *
22
+ * ```ts
23
+ * numericRegex.exec("1") // [ "1", "1", null, null ]
24
+ * numericRegex.exec("1.23") // [ "1.23", "1", ".23", null ]
25
+ * numericRegex.exec("1 2/3") // [ "1 2/3", "1", " 2/3", " 2" ]
26
+ * numericRegex.exec("2/3") // [ "2/3", "2", "/3", null ]
27
+ * numericRegex.exec("2 / 3") // [ "2 / 3", "2", "/ 3", null ]
28
+ * ```
29
+ */
30
+ export declare const numericRegex: RegExp;
31
+ /**
32
+ * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
33
+ */
34
+ export declare const numericRegexWithTrailingInvalid: RegExp;
35
+ /**
36
+ * Captures any Unicode vulgar fractions.
37
+ */
38
+ export declare const vulgarFractionsRegex: RegExp;
39
+ type RomanNumeralSequenceFragment = `${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;
40
+ /**
41
+ * Map of Roman numeral sequences to their decimal equivalents.
42
+ */
43
+ export declare const romanNumeralValues: { [k in RomanNumeralSequenceFragment]? : number };
44
+ /**
45
+ * Map of Unicode Roman numeral code points to their ASCII equivalents.
46
+ */
47
+ export declare const romanNumeralUnicodeToAsciiMap: Record<RomanNumeralUnicode, keyof typeof romanNumeralValues>;
48
+ /**
49
+ * Captures all Unicode Roman numeral code points.
50
+ */
51
+ export declare const romanNumeralUnicodeRegex: RegExp;
52
+ /**
53
+ * Captures a valid Roman numeral sequence.
54
+ *
55
+ * Capture groups:
56
+ *
57
+ * | # | Description | Example |
58
+ * | --- | --------------- | ------------------------ |
59
+ * | `0` | Entire string | "MCCXIV" from "MCCXIV" |
60
+ * | `1` | Thousands | "M" from "MCCXIV" |
61
+ * | `2` | Hundreds | "CC" from "MCCXIV" |
62
+ * | `3` | Tens | "X" from "MCCXIV" |
63
+ * | `4` | Ones | "IV" from "MCCXIV" |
64
+ *
65
+ * @example
66
+ *
67
+ * ```ts
68
+ * romanNumeralRegex.exec("M") // [ "M", "M", "", "", "" ]
69
+ * romanNumeralRegex.exec("XII") // [ "XII", "", "", "X", "II" ]
70
+ * romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
71
+ * ```
72
+ */
73
+ export declare const romanNumeralRegex: RegExp;
74
+ /**
75
+ * Default options for {@link numericQuantity}.
76
+ */
77
+ export declare const defaultOptions: Required<NumericQuantityOptions>;
78
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export * from "./constants";
2
+ export * from "./numericQuantity";
3
+ export * from "./parseRomanNumerals";
4
+ export * from "./types";
@@ -0,0 +1,13 @@
1
+ import type { NumericQuantityOptions } from "./types";
2
+ /**
3
+ * Converts a string to a number, like an enhanced version of `parseFloat`.
4
+ *
5
+ * The string can include mixed numbers, vulgar fractions, or Roman numerals.
6
+ */
7
+ declare function numericQuantity(quantity: string | number): number;
8
+ declare function numericQuantity(quantity: string | number): number;
9
+ declare function numericQuantity(quantity: string | number, options: NumericQuantityOptions & {
10
+ bigIntOnOverflow: true
11
+ }): number | bigint;
12
+ declare function numericQuantity(quantity: string | number, options?: NumericQuantityOptions): number;
13
+ export { numericQuantity };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Converts a string of Roman numerals to a number, like `parseInt`
3
+ * for Roman numerals. Uses modern, strict rules (only 1 to 3999).
4
+ *
5
+ * The string can include ASCII representations of Roman numerals
6
+ * or Unicode Roman numeral code points (`U+2160` through `U+217F`).
7
+ */
8
+ export declare const parseRomanNumerals: (romanNumerals: string) => number;
@@ -0,0 +1,43 @@
1
+ export interface NumericQuantityOptions {
2
+ /**
3
+ * Round the result to this many decimal places. Defaults to 3; must
4
+ * be greater than or equal to zero.
5
+ *
6
+ * @default 3
7
+ */
8
+ round?: number | false;
9
+ /**
10
+ * Allow and ignore trailing invalid characters _à la_ `parseFloat`.
11
+ *
12
+ * @default false
13
+ */
14
+ allowTrailingInvalid?: boolean;
15
+ /**
16
+ * Attempt to parse Roman numerals if Arabic numeral parsing fails.
17
+ *
18
+ * @default false
19
+ */
20
+ romanNumerals?: boolean;
21
+ /**
22
+ * Generates a `bigint` value if the string represents
23
+ * a valid integer too large for the `number` type.
24
+ */
25
+ bigIntOnOverflow?: boolean;
26
+ }
27
+ /**
28
+ * Unicode vulgar fraction code points.
29
+ */
30
+ export type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
31
+ /**
32
+ * Allowable Roman numeral characters (ASCII, uppercase only).
33
+ */
34
+ export type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
35
+ /**
36
+ * Unicode Roman numeral code points (uppercase and lowercase,
37
+ * representing 1-12, 50, 100, 500, and 1000).
38
+ */
39
+ export type RomanNumeralUnicode = "Ⅰ" | "Ⅱ" | "Ⅲ" | "Ⅳ" | "Ⅴ" | "Ⅵ" | "Ⅶ" | "Ⅷ" | "Ⅸ" | "Ⅹ" | "Ⅺ" | "Ⅻ" | "Ⅼ" | "Ⅽ" | "Ⅾ" | "Ⅿ" | "ⅰ" | "ⅱ" | "ⅲ" | "ⅳ" | "ⅴ" | "ⅵ" | "ⅶ" | "ⅷ" | "ⅸ" | "ⅹ" | "ⅺ" | "ⅻ" | "ⅼ" | "ⅽ" | "ⅾ" | "ⅿ";
40
+ /**
41
+ * Union of ASCII and Unicode Roman numeral characters/code points.
42
+ */
43
+ export type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;