numeric-quantity 2.0.0-beta.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,8 @@ console.log(numericQuantity('2 2/3')); // 2.667
27
27
 
28
28
  ### CDN
29
29
 
30
+ As an ES module:
31
+
30
32
  ```html
31
33
  <script type="module">
32
34
  import { numericQuantity } from 'https://cdn.jsdelivr.net/npm/numeric-quantity/+esm';
@@ -35,6 +37,15 @@ console.log(numericQuantity('2 2/3')); // 2.667
35
37
  </script>
36
38
  ```
37
39
 
40
+ As UMD (all exports are properties of the global object `NumericQuantity`):
41
+
42
+ ```html
43
+ <script src="https://unpkg.com/numeric-quantity"></script>
44
+ <script>
45
+ console.log(NumericQuantity.numericQuantity('xii')); // 12
46
+ </script>
47
+ ```
48
+
38
49
  ## Other exports
39
50
 
40
51
  | Name | Type | Description |
@@ -54,7 +54,7 @@ var vulgarFractionToAsciiMap = {
54
54
  "\u215E": "7/8",
55
55
  "\u215F": "1/"
56
56
  };
57
- var numericRegex = /^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/;
57
+ var numericRegex = /^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/;
58
58
  var vulgarFractionsRegex = new RegExp(
59
59
  `(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
60
60
  );
@@ -183,6 +183,9 @@ var parseRomanNumerals = (romanNumerals) => {
183
183
  // src/numericQuantity.ts
184
184
  var spaceThenSlashRegex = /^\s*\//;
185
185
  var numericQuantity = (quantity) => {
186
+ if (typeof quantity === "number" || typeof quantity === "bigint") {
187
+ return quantity;
188
+ }
186
189
  let finalResult = NaN;
187
190
  const quantityAsString = `${quantity}`.replace(
188
191
  vulgarFractionsRegex,
@@ -195,15 +198,16 @@ var numericQuantity = (quantity) => {
195
198
  if (!regexResult) {
196
199
  return parseRomanNumerals(quantityAsString);
197
200
  }
198
- const [, dash, ng1temp, numberGroup2] = regexResult;
201
+ const [, dash, ng1temp, ng2temp] = regexResult;
199
202
  const numberGroup1 = ng1temp.replace(/[,_]/g, "");
203
+ const numberGroup2 = ng2temp == null ? void 0 : ng2temp.replace(/[,_]/g, "");
200
204
  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
201
205
  finalResult = 0;
202
206
  } else {
203
207
  finalResult = parseInt(numberGroup1);
204
208
  }
205
209
  if (!numberGroup2) {
206
- return finalResult * (dash === "-" ? -1 : 1);
210
+ return dash ? finalResult * -1 : finalResult;
207
211
  }
208
212
  if (numberGroup2.startsWith(".")) {
209
213
  const numerator = parseFloat(numberGroup2);
@@ -217,7 +221,7 @@ var numericQuantity = (quantity) => {
217
221
  const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
218
222
  finalResult += Math.round(numerator * 1e3 / denominator) / 1e3;
219
223
  }
220
- return finalResult * (dash === "-" ? -1 : 1);
224
+ return dash ? finalResult * -1 : finalResult;
221
225
  };
222
226
  // Annotate the CommonJS export names for ESM import in node:
223
227
  0 && (module.exports = {
@@ -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 RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":[]}
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 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)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":[]}
@@ -1,2 +1,2 @@
1
- "use strict";var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var A=(r,e)=>{for(var a in e)p(r,a,{get:e[a],enumerable:!0})},$=(r,e,a,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of C(e))!M.call(r,o)&&o!==a&&p(r,o,{get:()=>e[o],enumerable:!(s=f(e,o))||s.enumerable});return r};var y=r=>$(p({},"__esModule",{value:!0}),r);var F={};A(F,{numericQuantity:()=>D,numericRegex:()=>N,parseRomanNumerals:()=>x,romanNumeralRegex:()=>X,romanNumeralUnicodeRegex:()=>g,romanNumeralUnicodeToAsciiMap:()=>I,romanNumeralValues:()=>i,vulgarFractionToAsciiMap:()=>u,vulgarFractionsRegex:()=>R});module.exports=y(F);var u={"\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+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/,R=new RegExp(`(${Object.keys(u).join("|")})`),i={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},I={"\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"},g=new RegExp(`(${Object.keys(I).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;var x=r=>{let e=`${r}`.replace(g,(c,t)=>I[t]).toUpperCase(),a=X.exec(e);if(!a)return NaN;let[,s,o,l,n]=a;return(i[s]||0)+(i[o]||0)+(i[l]||0)+(i[n]||0)};var h=/^\s*\//,D=r=>{let e=NaN,a=`${r}`.replace(R,(t,m)=>` ${u[m]}`).replace("\u2044","/").trim();if(a.length===0)return NaN;let s=N.exec(a);if(!s)return x(a);let[,o,l,n]=s,c=l.replace(/[,_]/g,"");if(!c&&n&&n.startsWith(".")?e=0:e=parseInt(c),!n)return e*(o==="-"?-1:1);if(n.startsWith(".")){let t=parseFloat(n);e+=Math.round(t*1e3)/1e3}else if(h.test(n)){let t=parseInt(c),m=parseInt(n.replace("/",""));e=Math.round(t*1e3/m)/1e3}else{let t=n.split("/"),[m,V]=t.map(d=>parseInt(d));e+=Math.round(m*1e3/V)/1e3}return e*(o==="-"?-1:1)};0&&(module.exports={numericQuantity,numericRegex,parseRomanNumerals,romanNumeralRegex,romanNumeralUnicodeRegex,romanNumeralUnicodeToAsciiMap,romanNumeralValues,vulgarFractionToAsciiMap,vulgarFractionsRegex});
1
+ "use strict";var N=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var y=(r,e)=>{for(var o in e)N(r,o,{get:e[o],enumerable:!0})},$=(r,e,o,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of M(e))!A.call(r,a)&&a!==o&&N(r,a,{get:()=>e[a],enumerable:!(t=C(e,a))||t.enumerable});return r};var h=r=>$(N({},"__esModule",{value:!0}),r);var L={};y(L,{numericQuantity:()=>F,numericRegex:()=>R,parseRomanNumerals:()=>V,romanNumeralRegex:()=>f,romanNumeralUnicodeRegex:()=>g,romanNumeralUnicodeToAsciiMap:()=>I,romanNumeralValues:()=>m,vulgarFractionToAsciiMap:()=>l,vulgarFractionsRegex:()=>d});module.exports=h(L);var l={"\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/"},R=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/,d=new RegExp(`(${Object.keys(l).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},I={"\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"},g=new RegExp(`(${Object.keys(I).join("|")})`,"gi"),f=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;var V=r=>{let e=`${r}`.replace(g,(u,n)=>I[n]).toUpperCase(),o=f.exec(e);if(!o)return NaN;let[,t,a,p,i]=o;return(m[t]||0)+(m[a]||0)+(m[p]||0)+(m[i]||0)};var D=/^\s*\//,F=r=>{if(typeof r=="number"||typeof r=="bigint")return r;let e=NaN,o=`${r}`.replace(d,(s,c)=>` ${l[c]}`).replace("\u2044","/").trim();if(o.length===0)return NaN;let t=R.exec(o);if(!t)return V(o);let[,a,p,i]=t,u=p.replace(/[,_]/g,""),n=i==null?void 0:i.replace(/[,_]/g,"");if(!u&&n&&n.startsWith(".")?e=0:e=parseInt(u),!n)return a?e*-1:e;if(n.startsWith(".")){let s=parseFloat(n);e+=Math.round(s*1e3)/1e3}else if(D.test(n)){let s=parseInt(u),c=parseInt(n.replace("/",""));e=Math.round(s*1e3/c)/1e3}else{let s=n.split("/"),[c,X]=s.map(x=>parseInt(x));e+=Math.round(c*1e3/X)/1e3}return a?e*-1:e};0&&(module.exports={numericQuantity,numericRegex,parseRomanNumerals,romanNumeralRegex,romanNumeralUnicodeRegex,romanNumeralUnicodeToAsciiMap,romanNumeralValues,vulgarFractionToAsciiMap,vulgarFractionsRegex});
2
2
  //# sourceMappingURL=numeric-quantity.cjs.production.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 RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":["src_exports","__export","numericQuantity","numericRegex","parseRomanNumerals","romanNumeralRegex","romanNumeralUnicodeRegex","romanNumeralUnicodeToAsciiMap","romanNumeralValues","vulgarFractionToAsciiMap","vulgarFractionsRegex","__toCommonJS","vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","numberGroup2","numberGroup1","numerator","denominator","fractionArray","v"]}
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 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)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":["src_exports","__export","numericQuantity","numericRegex","parseRomanNumerals","romanNumeralRegex","romanNumeralUnicodeRegex","romanNumeralUnicodeToAsciiMap","romanNumeralValues","vulgarFractionToAsciiMap","vulgarFractionsRegex","__toCommonJS","vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","numerator","denominator","fractionArray","v"]}
@@ -1,6 +1,19 @@
1
+ /**
2
+ * Unicode vulgar fraction code points
3
+ */
1
4
  type VulgarFraction = '¼' | '½' | '¾' | '⅐' | '⅑' | '⅒' | '⅓' | '⅔' | '⅕' | '⅖' | '⅗' | '⅘' | '⅙' | '⅚' | '⅛' | '⅜' | '⅝' | '⅞' | '⅟';
5
+ /**
6
+ * Allowable Roman numeral characters (ASCII, uppercase only)
7
+ */
2
8
  type RomanNumeralAscii = 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M';
9
+ /**
10
+ * Unicode Roman numeral code points (uppercase and lowercase,
11
+ * representing 1-12, 50, 100, 500, and 1000)
12
+ */
3
13
  type RomanNumeralUnicode = 'Ⅰ' | 'Ⅱ' | 'Ⅲ' | 'Ⅳ' | 'Ⅴ' | 'Ⅵ' | 'Ⅶ' | 'Ⅷ' | 'Ⅸ' | 'Ⅹ' | 'Ⅺ' | 'Ⅻ' | 'Ⅼ' | 'Ⅽ' | 'Ⅾ' | 'Ⅿ' | 'ⅰ' | 'ⅱ' | 'ⅲ' | 'ⅳ' | 'ⅴ' | 'ⅵ' | 'ⅶ' | 'ⅷ' | 'ⅸ' | 'ⅹ' | 'ⅺ' | 'ⅻ' | 'ⅼ' | 'ⅽ' | 'ⅾ' | 'ⅿ';
14
+ /**
15
+ * Union of ASCII and Unicode Roman numeral characters/code points
16
+ */
4
17
  type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
5
18
 
6
19
  /**
@@ -81,7 +94,7 @@ declare const romanNumeralValues: {
81
94
  /**
82
95
  * Map of Unicode Roman numeral code points to their ASCII equivalents
83
96
  */
84
- declare const romanNumeralUnicodeToAsciiMap: Record<RomanNumeralUnicode, string>;
97
+ declare const romanNumeralUnicodeToAsciiMap: Record<RomanNumeralUnicode, keyof typeof romanNumeralValues>;
85
98
  /**
86
99
  * Captures all Unicode Roman numeral code points
87
100
  */
@@ -117,7 +130,7 @@ declare const romanNumeralRegex: RegExp;
117
130
  *
118
131
  * The string can include mixed numbers, vulgar fractions, or Roman numerals.
119
132
  */
120
- declare const numericQuantity: (quantity: string) => number;
133
+ declare const numericQuantity: (quantity: string | number) => number;
121
134
 
122
135
  declare const parseRomanNumerals: (romanNumerals: string) => number;
123
136
 
@@ -20,7 +20,7 @@ var vulgarFractionToAsciiMap = {
20
20
  "\u215E": "7/8",
21
21
  "\u215F": "1/"
22
22
  };
23
- var numericRegex = /^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/;
23
+ var numericRegex = /^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/;
24
24
  var vulgarFractionsRegex = new RegExp(
25
25
  `(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
26
26
  );
@@ -149,6 +149,9 @@ var parseRomanNumerals = (romanNumerals) => {
149
149
  // src/numericQuantity.ts
150
150
  var spaceThenSlashRegex = /^\s*\//;
151
151
  var numericQuantity = (quantity) => {
152
+ if (typeof quantity === "number" || typeof quantity === "bigint") {
153
+ return quantity;
154
+ }
152
155
  let finalResult = NaN;
153
156
  const quantityAsString = `${quantity}`.replace(
154
157
  vulgarFractionsRegex,
@@ -161,15 +164,16 @@ var numericQuantity = (quantity) => {
161
164
  if (!regexResult) {
162
165
  return parseRomanNumerals(quantityAsString);
163
166
  }
164
- const [, dash, ng1temp, numberGroup2] = regexResult;
167
+ const [, dash, ng1temp, ng2temp] = regexResult;
165
168
  const numberGroup1 = ng1temp.replace(/[,_]/g, "");
169
+ const numberGroup2 = ng2temp == null ? void 0 : ng2temp.replace(/[,_]/g, "");
166
170
  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
167
171
  finalResult = 0;
168
172
  } else {
169
173
  finalResult = parseInt(numberGroup1);
170
174
  }
171
175
  if (!numberGroup2) {
172
- return finalResult * (dash === "-" ? -1 : 1);
176
+ return dash ? finalResult * -1 : finalResult;
173
177
  }
174
178
  if (numberGroup2.startsWith(".")) {
175
179
  const numerator = parseFloat(numberGroup2);
@@ -183,7 +187,7 @@ var numericQuantity = (quantity) => {
183
187
  const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
184
188
  finalResult += Math.round(numerator * 1e3 / denominator) / 1e3;
185
189
  }
186
- return finalResult * (dash === "-" ? -1 : 1);
190
+ return dash ? finalResult * -1 : finalResult;
187
191
  };
188
192
  export {
189
193
  numericQuantity,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":[]}
@@ -20,7 +20,7 @@ var vulgarFractionToAsciiMap = {
20
20
  "\u215E": "7/8",
21
21
  "\u215F": "1/"
22
22
  };
23
- var numericRegex = /^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/;
23
+ var numericRegex = /^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/;
24
24
  var vulgarFractionsRegex = new RegExp(
25
25
  `(${Object.keys(vulgarFractionToAsciiMap).join("|")})`
26
26
  );
@@ -149,6 +149,9 @@ var parseRomanNumerals = (romanNumerals) => {
149
149
  // src/numericQuantity.ts
150
150
  var spaceThenSlashRegex = /^\s*\//;
151
151
  var numericQuantity = (quantity) => {
152
+ if (typeof quantity === "number" || typeof quantity === "bigint") {
153
+ return quantity;
154
+ }
152
155
  let finalResult = NaN;
153
156
  const quantityAsString = `${quantity}`.replace(
154
157
  vulgarFractionsRegex,
@@ -161,15 +164,16 @@ var numericQuantity = (quantity) => {
161
164
  if (!regexResult) {
162
165
  return parseRomanNumerals(quantityAsString);
163
166
  }
164
- const [, dash, ng1temp, numberGroup2] = regexResult;
167
+ const [, dash, ng1temp, ng2temp] = regexResult;
165
168
  const numberGroup1 = ng1temp.replace(/[,_]/g, "");
169
+ const numberGroup2 = ng2temp == null ? void 0 : ng2temp.replace(/[,_]/g, "");
166
170
  if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) {
167
171
  finalResult = 0;
168
172
  } else {
169
173
  finalResult = parseInt(numberGroup1);
170
174
  }
171
175
  if (!numberGroup2) {
172
- return finalResult * (dash === "-" ? -1 : 1);
176
+ return dash ? finalResult * -1 : finalResult;
173
177
  }
174
178
  if (numberGroup2.startsWith(".")) {
175
179
  const numerator = parseFloat(numberGroup2);
@@ -183,7 +187,7 @@ var numericQuantity = (quantity) => {
183
187
  const [numerator, denominator] = fractionArray.map((v) => parseInt(v));
184
188
  finalResult += Math.round(numerator * 1e3 / denominator) / 1e3;
185
189
  }
186
- return finalResult * (dash === "-" ? -1 : 1);
190
+ return dash ? finalResult * -1 : finalResult;
187
191
  };
188
192
  export {
189
193
  numericQuantity,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":[]}
@@ -1,2 +1,2 @@
1
- var I={"\xBC":"1/4","\xBD":"1/2","\xBE":"3/4","\u2150":"1/7","\u2151":"1/9","\u2152":"1/10","\u2153":"1/3","\u2154":"2/3","\u2155":"1/5","\u2156":"2/5","\u2157":"3/5","\u2158":"4/5","\u2159":"1/6","\u215A":"5/6","\u215B":"1/8","\u215C":"3/8","\u215D":"5/8","\u215E":"7/8","\u215F":"1/"},p=/^(?=-?\s*\.\d|-?\s*\d+)(-)?\s*((?:\d+[\d,_]*)*)(\.\d+|(\s+\d*\s*)?\s*\/\s*\d+)?(?:\s*[^\.\d\/].*)?/,N=new RegExp(`(${Object.keys(I).join("|")})`),t={MMM:3e3,MM:2e3,M:1e3,CM:900,DCCC:800,DCC:700,DC:600,D:500,CD:400,CCC:300,CC:200,C:100,XC:90,LXXX:80,LXX:70,LX:60,L:50,XL:40,XXX:30,XX:20,XII:12,XI:11,X:10,IX:9,VIII:8,VII:7,VI:6,V:5,IV:4,III:3,II:2,I:1},l={"\u2160":"I","\u2161":"II","\u2162":"III","\u2163":"IV","\u2164":"V","\u2165":"VI","\u2166":"VII","\u2167":"VIII","\u2168":"IX","\u2169":"X","\u216A":"XI","\u216B":"XII","\u216C":"L","\u216D":"C","\u216E":"D","\u216F":"M","\u2170":"I","\u2171":"II","\u2172":"III","\u2173":"IV","\u2174":"V","\u2175":"VI","\u2176":"VII","\u2177":"VIII","\u2178":"IX","\u2179":"X","\u217A":"XI","\u217B":"XII","\u217C":"L","\u217D":"C","\u217E":"D","\u217F":"M"},R=new RegExp(`(${Object.keys(l).join("|")})`,"gi"),g=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;var X=c=>{let r=`${c}`.replace(R,(m,a)=>l[a]).toUpperCase(),o=g.exec(r);if(!o)return NaN;let[,s,i,u,e]=o;return(t[s]||0)+(t[i]||0)+(t[u]||0)+(t[e]||0)};var d=/^\s*\//,y=c=>{let r=NaN,o=`${c}`.replace(N,(a,n)=>` ${I[n]}`).replace("\u2044","/").trim();if(o.length===0)return NaN;let s=p.exec(o);if(!s)return X(o);let[,i,u,e]=s,m=u.replace(/[,_]/g,"");if(!m&&e&&e.startsWith(".")?r=0:r=parseInt(m),!e)return r*(i==="-"?-1:1);if(e.startsWith(".")){let a=parseFloat(e);r+=Math.round(a*1e3)/1e3}else if(d.test(e)){let a=parseInt(m),n=parseInt(e.replace("/",""));r=Math.round(a*1e3/n)/1e3}else{let a=e.split("/"),[n,x]=a.map(V=>parseInt(V));r+=Math.round(n*1e3/x)/1e3}return r*(i==="-"?-1:1)};export{y as numericQuantity,p as numericRegex,X as parseRomanNumerals,g as romanNumeralRegex,R as romanNumeralUnicodeRegex,l as romanNumeralUnicodeToAsciiMap,t as romanNumeralValues,I as vulgarFractionToAsciiMap,N as vulgarFractionsRegex};
1
+ var I={"\xBC":"1/4","\xBD":"1/2","\xBE":"3/4","\u2150":"1/7","\u2151":"1/9","\u2152":"1/10","\u2153":"1/3","\u2154":"2/3","\u2155":"1/5","\u2156":"2/5","\u2157":"3/5","\u2158":"4/5","\u2159":"1/6","\u215A":"5/6","\u215B":"1/8","\u215C":"3/8","\u215D":"5/8","\u215E":"7/8","\u215F":"1/"},N=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/,R=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},p={"\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"},d=new RegExp(`(${Object.keys(p).join("|")})`,"gi"),g=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;var f=o=>{let e=`${o}`.replace(d,(u,r)=>p[r]).toUpperCase(),a=g.exec(e);if(!a)return NaN;let[,i,c,l,t]=a;return(m[i]||0)+(m[c]||0)+(m[l]||0)+(m[t]||0)};var x=/^\s*\//,h=o=>{if(typeof o=="number"||typeof o=="bigint")return o;let e=NaN,a=`${o}`.replace(R,(n,s)=>` ${I[s]}`).replace("\u2044","/").trim();if(a.length===0)return NaN;let i=N.exec(a);if(!i)return f(a);let[,c,l,t]=i,u=l.replace(/[,_]/g,""),r=t==null?void 0:t.replace(/[,_]/g,"");if(!u&&r&&r.startsWith(".")?e=0:e=parseInt(u),!r)return c?e*-1:e;if(r.startsWith(".")){let n=parseFloat(r);e+=Math.round(n*1e3)/1e3}else if(x.test(r)){let n=parseInt(u),s=parseInt(r.replace("/",""));e=Math.round(n*1e3/s)/1e3}else{let n=r.split("/"),[s,V]=n.map(X=>parseInt(X));e+=Math.round(s*1e3/V)/1e3}return c?e*-1:e};export{h as numericQuantity,N as numericRegex,f as parseRomanNumerals,g as romanNumeralRegex,d as romanNumeralUnicodeRegex,p as romanNumeralUnicodeToAsciiMap,m as romanNumeralValues,I as vulgarFractionToAsciiMap,R 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 RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d+)(-)?\\s*((?:\\d+[\\d,_]*)*)(\\.\\d+|(\\s+\\d*\\s*)?\\s*\\/\\s*\\d+)?(?:\\s*[^\\.\\d\\/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions\n */\nexport const vulgarFractionsRegex = new RegExp(\n `(${Object.keys(vulgarFractionToAsciiMap).join('|')})`\n);\n// #endregion\n\n// #region Roman numerals\ntype RomanNumeralSequenceFragment =\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;\n\nexport const romanNumeralValues = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // Twelve is only here for tests; not used in practice\n XII: 12,\n // Eleven is only here for tests; not used in practice\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} satisfies { [k in RomanNumeralSequenceFragment]?: number };\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n string\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n};\n\n/**\n * Captures all Unicode Roman numeral code points\n */\nexport const romanNumeralUnicodeRegex = new RegExp(\n `(${Object.keys(romanNumeralUnicodeToAsciiMap).join('|')})`,\n 'gi'\n);\n\n/**\n * Captures a valid Roman numeral sequence\n *\n * Capture groups:\n *\n * +=====+=================+==========================+\n * | # | Description | Example |\n * +=====+=================+==========================+\n * | 0 | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 1 | Thousands | \"M\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 2 | Hundreds | \"CC\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 3 | Tens | \"X\" from \"MCCXIV\" |\n * +-----+-----------------+--------------------------+\n * | 4 | Ones | \"IV\" from \"MCCXIV\" |\n * +=====+=================+==========================+\n *\n * @example\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n */\nexport const romanNumeralRegex =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n// #endregion\n","import {\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralRegex,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RNV = keyof typeof romanNumeralValues;\n\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string) => {\n let finalResult = NaN;\n\n // Coerce to string in case qty is a number\n const quantityAsString = `${quantity}`\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) =>\n ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim();\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return NaN;\n }\n\n const regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, numberGroup2] = regexResult;\n const numberGroup1 = ng1temp.replace(/[,_]/g, '');\n\n // Numerify capture group 1\n if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith('.')) {\n finalResult = 0;\n } else {\n finalResult = parseInt(numberGroup1);\n }\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n return finalResult * (dash === '-' ? -1 : 1);\n }\n\n if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return finalResult * (dash === '-' ? -1 : 1);\n};\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","numberGroup2","numberGroup1","numerator","denominator","fractionArray","v"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts"],"sourcesContent":["import type {\n RomanNumeralAscii,\n RomanNumeralUnicode,\n VulgarFraction,\n} from './types';\n\n// #region Arabic numerals\n/**\n * Map of Unicode fraction code points to their ASCII equivalents\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, string> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n};\n\n/**\n * Captures the individual elements of a numeric string.\n *\n * Capture groups:\n *\n * +=====+====================+========================+\n * | # | Description | Example |\n * +=====+====================+========================+\n * | 0 | entire string | \"2 1/3\" from \"2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 1 | \"negative\" dash | \"-\" from \"-2 1/3\" |\n * +-----+--------------------+------------------------+\n * | 2 | the whole number | \"2\" from \"2 1/3\" |\n * | | - OR - | |\n * | | the numerator | \"1\" from \"1/3\" |\n * | + + |\n * | (This may include comma/underscore separators) |\n * +-----+--------------------+------------------------+\n * | 3 | entire fraction | \"1/3\" from \"2 1/3\" |\n * | | - OR - | |\n * | | decimal portion | \".33\" from \"2.33\" |\n * | | - OR - | |\n * | | denominator | \"/3\" from \"1/3\" |\n * +=====+====================+========================+\n *\n * @example\n * numericRegex.exec(\"1\") // [ \"1\", \"1\", null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", \"1\", \".23\", null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", \"1\", \" 2/3\", \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", \"2\", \"/3\", null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", \"2\", \"/ 3\", null ]\n */\nexport const numericRegex =\n /^(?=-?\\s*\\.\\d|-?\\s*\\d)(-)?\\s*((?:\\d(?:[\\d,_]*\\d)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":["vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","numerator","denominator","fractionArray","v"]}
@@ -0,0 +1,2 @@
1
+ "use strict";var NumericQuantity=(()=>{var N=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var y=(r,e)=>{for(var o in e)N(r,o,{get:e[o],enumerable:!0})},$=(r,e,o,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of M(e))!A.call(r,a)&&a!==o&&N(r,a,{get:()=>e[a],enumerable:!(t=C(e,a))||t.enumerable});return r};var h=r=>$(N({},"__esModule",{value:!0}),r);var L={};y(L,{numericQuantity:()=>F,numericRegex:()=>R,parseRomanNumerals:()=>V,romanNumeralRegex:()=>f,romanNumeralUnicodeRegex:()=>g,romanNumeralUnicodeToAsciiMap:()=>I,romanNumeralValues:()=>m,vulgarFractionToAsciiMap:()=>l,vulgarFractionsRegex:()=>d});var l={"\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/"},R=/^(?=-?\s*\.\d|-?\s*\d)(-)?\s*((?:\d(?:[\d,_]*\d)?)*)(\.\d(?:[\d,_]*\d)?|(\s+\d(?:[\d,_]*\d)?\s*)?\s*\/\s*\d(?:[\d,_]*\d)?)?(?:\s*[^\.\d\/].*)?/,d=new RegExp(`(${Object.keys(l).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},I={"\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"},g=new RegExp(`(${Object.keys(I).join("|")})`,"gi"),f=/^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;var V=r=>{let e=`${r}`.replace(g,(u,n)=>I[n]).toUpperCase(),o=f.exec(e);if(!o)return NaN;let[,t,a,p,i]=o;return(m[t]||0)+(m[a]||0)+(m[p]||0)+(m[i]||0)};var D=/^\s*\//,F=r=>{if(typeof r=="number"||typeof r=="bigint")return r;let e=NaN,o=`${r}`.replace(d,(s,c)=>` ${l[c]}`).replace("\u2044","/").trim();if(o.length===0)return NaN;let t=R.exec(o);if(!t)return V(o);let[,a,p,i]=t,u=p.replace(/[,_]/g,""),n=i==null?void 0:i.replace(/[,_]/g,"");if(!u&&n&&n.startsWith(".")?e=0:e=parseInt(u),!n)return a?e*-1:e;if(n.startsWith(".")){let s=parseFloat(n);e+=Math.round(s*1e3)/1e3}else if(D.test(n)){let s=parseInt(u),c=parseInt(n.replace("/",""));e=Math.round(s*1e3/c)/1e3}else{let s=n.split("/"),[c,X]=s.map(x=>parseInt(x));e+=Math.round(c*1e3/X)/1e3}return a?e*-1:e};return h(L);})();
2
+ //# sourceMappingURL=numeric-quantity.umd.min.js.map
@@ -0,0 +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 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)?)*)(\\.\\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","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\nexport const parseRomanNumerals = (romanNumerals: string) => {\n const normalized = `${romanNumerals}`\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) =>\n romanNumeralUnicodeToAsciiMap[rn]\n )\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RNV] || 0) +\n (romanNumeralValues[hundreds as RNV] || 0) +\n (romanNumeralValues[tens as RNV] || 0) +\n (romanNumeralValues[ones as RNV] || 0)\n );\n};\n","import {\n numericRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\n\nconst spaceThenSlashRegex = /^\\s*\\//;\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n */\nexport const numericQuantity = (quantity: string | number) => {\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 regexResult = numericRegex.exec(quantityAsString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n return parseRomanNumerals(quantityAsString);\n }\n\n const [, dash, ng1temp, 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 if (numberGroup2.startsWith('.')) {\n // If first char is \".\" it's a decimal so just trim to 3 decimal places\n const numerator = parseFloat(numberGroup2);\n finalResult += Math.round(numerator * 1000) / 1000;\n } else if (spaceThenSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n finalResult = Math.round((numerator * 1000) / denominator) / 1000;\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n finalResult += Math.round((numerator * 1000) / denominator) / 1000;\n }\n\n return dash ? finalResult * -1 : finalResult;\n};\n"],"mappings":"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","names":["src_exports","__export","numericQuantity","numericRegex","parseRomanNumerals","romanNumeralRegex","romanNumeralUnicodeRegex","romanNumeralUnicodeToAsciiMap","romanNumeralValues","vulgarFractionToAsciiMap","vulgarFractionsRegex","vulgarFractionToAsciiMap","numericRegex","vulgarFractionsRegex","romanNumeralValues","romanNumeralUnicodeToAsciiMap","romanNumeralUnicodeRegex","romanNumeralRegex","parseRomanNumerals","romanNumerals","normalized","romanNumeralUnicodeRegex","_m","rn","romanNumeralUnicodeToAsciiMap","regexResult","romanNumeralRegex","thousands","hundreds","tens","ones","romanNumeralValues","spaceThenSlashRegex","numericQuantity","quantity","finalResult","quantityAsString","vulgarFractionsRegex","_m","vf","vulgarFractionToAsciiMap","regexResult","numericRegex","parseRomanNumerals","dash","ng1temp","ng2temp","numberGroup1","numberGroup2","numerator","denominator","fractionArray","v"]}
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.0.0-beta.0",
2
+ "version": "2.0.0-beta.1",
3
3
  "license": "MIT",
4
4
  "name": "numeric-quantity",
5
5
  "author": "Jake Boone <jakeboone02@gmail.com>",
@@ -18,6 +18,7 @@
18
18
  }
19
19
  },
20
20
  "types": "./dist/numeric-quantity.d.ts",
21
+ "unpkg": "./dist/numeric-quantity.umd.min.js",
21
22
  "bugs": {
22
23
  "url": "https://github.com/jakeboone02/numeric-quantity/issues"
23
24
  },
@@ -37,18 +38,20 @@
37
38
  "build": "tsup",
38
39
  "test": "jest",
39
40
  "watch": "jest --watch",
40
- "publish:npm": "np"
41
+ "publish:npm": "np",
42
+ "pretty-print": "prettier --write *.{mjs,js,ts,json} src/*.*"
41
43
  },
42
44
  "devDependencies": {
43
- "@babel/core": "^7.22.1",
44
- "@babel/preset-env": "^7.22.4",
45
- "@babel/preset-typescript": "^7.21.5",
45
+ "@babel/core": "^7.22.5",
46
+ "@babel/preset-env": "^7.22.5",
47
+ "@babel/preset-typescript": "^7.22.5",
46
48
  "@types/jest": "^29.5.2",
47
- "@types/node": "^20.2.5",
49
+ "@types/node": "^20.3.0",
48
50
  "gh-pages": "^5.0.0",
49
51
  "jest": "^29.5.0",
50
- "np": "^8.0.2",
52
+ "np": "^8.0.3",
51
53
  "prettier": "^2.8.8",
54
+ "prettier-plugin-organize-imports": "^3.2.2",
52
55
  "tsup": "^6.7.0",
53
56
  "typescript": "^5.1.3",
54
57
  "vite": "^4.3.9"