numeric-quantity 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -16
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/numeric-quantity.cjs.development.d.ts +225 -0
- package/dist/cjs/numeric-quantity.cjs.development.js +208 -18
- package/dist/cjs/numeric-quantity.cjs.development.js.map +1 -1
- package/dist/cjs/numeric-quantity.cjs.production.d.ts +225 -0
- package/dist/cjs/numeric-quantity.cjs.production.js +1 -1
- package/dist/cjs/numeric-quantity.cjs.production.js.map +1 -1
- package/dist/numeric-quantity.d.mts +225 -0
- package/dist/numeric-quantity.iife.umd.min.js +1 -1
- package/dist/numeric-quantity.iife.umd.min.js.map +1 -1
- package/dist/numeric-quantity.legacy-esm.d.ts +225 -0
- package/dist/numeric-quantity.legacy-esm.js +221 -37
- package/dist/numeric-quantity.legacy-esm.js.map +1 -1
- package/dist/numeric-quantity.mjs +206 -19
- package/dist/numeric-quantity.mjs.map +1 -1
- package/dist/numeric-quantity.production.d.mts +225 -0
- package/dist/numeric-quantity.production.mjs +1 -1
- package/dist/numeric-quantity.production.mjs.map +1 -1
- package/package.json +13 -14
- package/dist/types/constants.d.ts +0 -79
- package/dist/types/dev.d.ts +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/numericQuantity.d.ts +0 -12
- package/dist/types/parseRomanNumerals.d.ts +0 -8
- package/dist/types/types.d.ts +0 -49
- package/dist/types-esm/constants.d.mts +0 -79
- package/dist/types-esm/dev.d.mts +0 -1
- package/dist/types-esm/index.d.mts +0 -4
- package/dist/types-esm/numericQuantity.d.mts +0 -12
- package/dist/types-esm/parseRomanNumerals.d.mts +0 -8
- package/dist/types-esm/types.d.mts +0 -49
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface NumericQuantityOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Round the result to this many decimal places. Defaults to 3; must
|
|
5
|
+
* be greater than or equal to zero.
|
|
6
|
+
*
|
|
7
|
+
* @default 3
|
|
8
|
+
*/
|
|
9
|
+
round?: number | false;
|
|
10
|
+
/**
|
|
11
|
+
* Allow and ignore trailing invalid characters _à la_ `parseFloat`.
|
|
12
|
+
*
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
allowTrailingInvalid?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Attempt to parse Roman numerals if Arabic numeral parsing fails.
|
|
18
|
+
*
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
romanNumerals?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Generates a `bigint` value if the string represents
|
|
24
|
+
* a valid integer too large for the `number` type.
|
|
25
|
+
*/
|
|
26
|
+
bigIntOnOverflow?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Specifies which character ("." or ",") to treat as the decimal separator.
|
|
29
|
+
*
|
|
30
|
+
* @default "."
|
|
31
|
+
*/
|
|
32
|
+
decimalSeparator?: "," | ".";
|
|
33
|
+
/**
|
|
34
|
+
* Allow and strip currency symbols (Unicode `\p{Sc}` category) from the
|
|
35
|
+
* start and/or end of the string.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
allowCurrency?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Parse percentage strings by stripping the `%` suffix.
|
|
42
|
+
* - `'decimal'` or `true`: `"50%"` → `0.5` (divide by 100)
|
|
43
|
+
* - `'number'`: `"50%"` → `50` (strip `%`, keep value)
|
|
44
|
+
* - `false` or omitted: `"50%"` → `NaN` (default behavior)
|
|
45
|
+
*
|
|
46
|
+
* @default false
|
|
47
|
+
*/
|
|
48
|
+
percentage?: "decimal" | "number" | boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Return a verbose result object with additional parsing metadata.
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
verbose?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolves the return type of {@link numericQuantity} based on the options provided.
|
|
58
|
+
*/
|
|
59
|
+
type NumericQuantityReturnType<T extends NumericQuantityOptions | undefined = undefined> = T extends {
|
|
60
|
+
verbose: true;
|
|
61
|
+
} ? NumericQuantityVerboseResult : T extends {
|
|
62
|
+
bigIntOnOverflow: true;
|
|
63
|
+
} ? number | bigint : number;
|
|
64
|
+
/**
|
|
65
|
+
* Verbose result returned when `verbose: true` is set.
|
|
66
|
+
*/
|
|
67
|
+
interface NumericQuantityVerboseResult {
|
|
68
|
+
/** The parsed numeric value (NaN if invalid). */
|
|
69
|
+
value: number | bigint;
|
|
70
|
+
/** The original input string. */
|
|
71
|
+
input: string;
|
|
72
|
+
/** Currency symbol(s) stripped from the start, if any. */
|
|
73
|
+
currencyPrefix?: string;
|
|
74
|
+
/** Currency symbol(s) stripped from the end, if any. */
|
|
75
|
+
currencySuffix?: string;
|
|
76
|
+
/** True if a `%` suffix was stripped. */
|
|
77
|
+
percentageSuffix?: boolean;
|
|
78
|
+
/** Trailing invalid (usually non-numeric) characters detected in the input, if any. Populated even when `allowTrailingInvalid` is `false`. */
|
|
79
|
+
trailingInvalid?: string;
|
|
80
|
+
/** The leading sign character (`'-'` or `'+'`), if present. Omitted when no explicit sign was in the input. */
|
|
81
|
+
sign?: "-" | "+";
|
|
82
|
+
/** The whole-number part of a mixed fraction (e.g. `1` from `"1 2/3"`). Omitted for pure fractions, decimals, and integers. */
|
|
83
|
+
whole?: number;
|
|
84
|
+
/** The numerator of a fraction (e.g. `2` from `"1 2/3"`, or `1` from `"1/2"`). Always unsigned. */
|
|
85
|
+
numerator?: number;
|
|
86
|
+
/** The denominator of a fraction (e.g. `3` from `"1 2/3"`, or `2` from `"1/2"`). Always unsigned. */
|
|
87
|
+
denominator?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Unicode vulgar fraction code points.
|
|
91
|
+
*/
|
|
92
|
+
type VulgarFraction = "¼" | "½" | "¾" | "⅐" | "⅑" | "⅒" | "⅓" | "⅔" | "⅕" | "⅖" | "⅗" | "⅘" | "⅙" | "⅚" | "⅛" | "⅜" | "⅝" | "⅞" | "⅟";
|
|
93
|
+
/**
|
|
94
|
+
* Allowable Roman numeral characters (ASCII, uppercase only).
|
|
95
|
+
*/
|
|
96
|
+
type RomanNumeralAscii = "I" | "V" | "X" | "L" | "C" | "D" | "M";
|
|
97
|
+
/**
|
|
98
|
+
* Unicode Roman numeral code points (uppercase and lowercase,
|
|
99
|
+
* representing 1-12, 50, 100, 500, and 1000).
|
|
100
|
+
*/
|
|
101
|
+
type RomanNumeralUnicode = "Ⅰ" | "Ⅱ" | "Ⅲ" | "Ⅳ" | "Ⅴ" | "Ⅵ" | "Ⅶ" | "Ⅷ" | "Ⅸ" | "Ⅹ" | "Ⅺ" | "Ⅻ" | "Ⅼ" | "Ⅽ" | "Ⅾ" | "Ⅿ" | "ⅰ" | "ⅱ" | "ⅲ" | "ⅳ" | "ⅴ" | "ⅵ" | "ⅶ" | "ⅷ" | "ⅸ" | "ⅹ" | "ⅺ" | "ⅻ" | "ⅼ" | "ⅽ" | "ⅾ" | "ⅿ";
|
|
102
|
+
/**
|
|
103
|
+
* Union of ASCII and Unicode Roman numeral characters/code points.
|
|
104
|
+
*/
|
|
105
|
+
type RomanNumeral = RomanNumeralAscii | RomanNumeralUnicode;
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/constants.d.ts
|
|
108
|
+
/**
|
|
109
|
+
* Normalizes non-ASCII decimal digits to ASCII digits.
|
|
110
|
+
* Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,
|
|
111
|
+
* Devanagari, Bengali) to their ASCII equivalents (0-9).
|
|
112
|
+
*
|
|
113
|
+
* All current Unicode \p{Nd} blocks are included in decimalDigitBlockStarts.
|
|
114
|
+
*/
|
|
115
|
+
declare const normalizeDigits: (str: string) => string;
|
|
116
|
+
/**
|
|
117
|
+
* Map of Unicode fraction code points to their ASCII equivalents.
|
|
118
|
+
*/
|
|
119
|
+
declare const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ""}`>;
|
|
120
|
+
/**
|
|
121
|
+
* Captures the individual elements of a numeric string. Commas and underscores are allowed
|
|
122
|
+
* as separators, as long as they appear between digits and are not consecutive.
|
|
123
|
+
*
|
|
124
|
+
* Capture groups:
|
|
125
|
+
*
|
|
126
|
+
* | # | Description | Example(s) |
|
|
127
|
+
* | --- | ------------------------------------------------ | ------------------------------------------------------------------- |
|
|
128
|
+
* | `0` | entire string | `"2 1/3"` from `"2 1/3"` |
|
|
129
|
+
* | `1` | sign (`-` or `+`) | `"-"` from `"-2 1/3"` |
|
|
130
|
+
* | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
|
|
131
|
+
* | `3` | entire fraction, decimal portion, or denominator | `" 1/3"` from `"2 1/3"`; `".33"` from `"2.33"`; `"/3"` from `"1/3"` |
|
|
132
|
+
*
|
|
133
|
+
* _Capture group 2 may include comma/underscore separators._
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
*
|
|
137
|
+
* ```ts
|
|
138
|
+
* numericRegex.exec("1") // [ "1", "1", null, null ]
|
|
139
|
+
* numericRegex.exec("1.23") // [ "1.23", "1", ".23", null ]
|
|
140
|
+
* numericRegex.exec("1 2/3") // [ "1 2/3", "1", " 2/3", " 2" ]
|
|
141
|
+
* numericRegex.exec("2/3") // [ "2/3", "2", "/3", null ]
|
|
142
|
+
* numericRegex.exec("2 / 3") // [ "2 / 3", "2", "/ 3", null ]
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare const numericRegex: RegExp;
|
|
146
|
+
/**
|
|
147
|
+
* Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
|
|
148
|
+
* Capture group 7 contains the trailing invalid portion.
|
|
149
|
+
*/
|
|
150
|
+
declare const numericRegexWithTrailingInvalid: RegExp;
|
|
151
|
+
/**
|
|
152
|
+
* Captures any Unicode vulgar fractions.
|
|
153
|
+
*/
|
|
154
|
+
declare const vulgarFractionsRegex: RegExp;
|
|
155
|
+
type RomanNumeralSequenceFragment = `${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}` | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`;
|
|
156
|
+
/**
|
|
157
|
+
* Map of Roman numeral sequences to their decimal equivalents.
|
|
158
|
+
*/
|
|
159
|
+
declare const romanNumeralValues: { [k in RomanNumeralSequenceFragment]?: number };
|
|
160
|
+
/**
|
|
161
|
+
* Map of Unicode Roman numeral code points to their ASCII equivalents.
|
|
162
|
+
*/
|
|
163
|
+
declare const romanNumeralUnicodeToAsciiMap: Record<RomanNumeralUnicode, keyof typeof romanNumeralValues>;
|
|
164
|
+
/**
|
|
165
|
+
* Captures all Unicode Roman numeral code points.
|
|
166
|
+
*/
|
|
167
|
+
declare const romanNumeralUnicodeRegex: RegExp;
|
|
168
|
+
/**
|
|
169
|
+
* Captures a valid Roman numeral sequence.
|
|
170
|
+
*
|
|
171
|
+
* Capture groups:
|
|
172
|
+
*
|
|
173
|
+
* | # | Description | Example |
|
|
174
|
+
* | --- | --------------- | ------------------------ |
|
|
175
|
+
* | `0` | Entire string | "MCCXIV" from "MCCXIV" |
|
|
176
|
+
* | `1` | Thousands | "M" from "MCCXIV" |
|
|
177
|
+
* | `2` | Hundreds | "CC" from "MCCXIV" |
|
|
178
|
+
* | `3` | Tens | "X" from "MCCXIV" |
|
|
179
|
+
* | `4` | Ones | "IV" from "MCCXIV" |
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* romanNumeralRegex.exec("M") // [ "M", "M", "", "", "" ]
|
|
185
|
+
* romanNumeralRegex.exec("XII") // [ "XII", "", "", "X", "II" ]
|
|
186
|
+
* romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare const romanNumeralRegex: RegExp;
|
|
190
|
+
/**
|
|
191
|
+
* Default options for {@link numericQuantity}.
|
|
192
|
+
*/
|
|
193
|
+
declare const defaultOptions: Required<NumericQuantityOptions>;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/isNumericQuantity.d.ts
|
|
196
|
+
/**
|
|
197
|
+
* Checks if a string represents a valid numeric quantity.
|
|
198
|
+
*
|
|
199
|
+
* Returns `true` if the string can be parsed as a number, `false` otherwise.
|
|
200
|
+
* Accepts the same options as `numericQuantity`.
|
|
201
|
+
*/
|
|
202
|
+
declare const isNumericQuantity: (quantity: string | number, options?: NumericQuantityOptions) => boolean;
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/numericQuantity.d.ts
|
|
205
|
+
/**
|
|
206
|
+
* Converts a string to a number, like an enhanced version of `parseFloat`.
|
|
207
|
+
*
|
|
208
|
+
* The string can include mixed numbers, vulgar fractions, or Roman numerals.
|
|
209
|
+
*/
|
|
210
|
+
declare function numericQuantity(quantity: string | number): number;
|
|
211
|
+
declare function numericQuantity<T extends NumericQuantityOptions>(quantity: string | number, options: T): NumericQuantityReturnType<T>;
|
|
212
|
+
declare function numericQuantity(quantity: string | number, options?: NumericQuantityOptions): number;
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/parseRomanNumerals.d.ts
|
|
215
|
+
/**
|
|
216
|
+
* Converts a string of Roman numerals to a number, like `parseInt`
|
|
217
|
+
* for Roman numerals. Uses modern, strict rules (only 1 to 3999).
|
|
218
|
+
*
|
|
219
|
+
* The string can include ASCII representations of Roman numerals
|
|
220
|
+
* or Unicode Roman numeral code points (`U+2160` through `U+217F`).
|
|
221
|
+
*/
|
|
222
|
+
declare const parseRomanNumerals: (romanNumerals: string) => number;
|
|
223
|
+
//#endregion
|
|
224
|
+
export { NumericQuantityOptions, NumericQuantityReturnType, NumericQuantityVerboseResult, RomanNumeral, RomanNumeralAscii, RomanNumeralUnicode, VulgarFraction, defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
|
|
225
|
+
//# sourceMappingURL=numeric-quantity.legacy-esm.d.ts.map
|
|
@@ -1,5 +1,109 @@
|
|
|
1
1
|
//#region src/constants.ts
|
|
2
2
|
/**
|
|
3
|
+
* Unicode decimal digit block start code points.
|
|
4
|
+
* Each block contains 10 contiguous digits (0-9).
|
|
5
|
+
* This list covers all \p{Nd} (Decimal_Number) blocks through Unicode 17.0.
|
|
6
|
+
* The drift test in index.test.ts validates completeness against the JS engine.
|
|
7
|
+
*/
|
|
8
|
+
const decimalDigitBlockStarts = [
|
|
9
|
+
48,
|
|
10
|
+
1632,
|
|
11
|
+
1776,
|
|
12
|
+
1984,
|
|
13
|
+
2406,
|
|
14
|
+
2534,
|
|
15
|
+
2662,
|
|
16
|
+
2790,
|
|
17
|
+
2918,
|
|
18
|
+
3046,
|
|
19
|
+
3174,
|
|
20
|
+
3302,
|
|
21
|
+
3430,
|
|
22
|
+
3558,
|
|
23
|
+
3664,
|
|
24
|
+
3792,
|
|
25
|
+
3872,
|
|
26
|
+
4160,
|
|
27
|
+
4240,
|
|
28
|
+
6112,
|
|
29
|
+
6160,
|
|
30
|
+
6470,
|
|
31
|
+
6608,
|
|
32
|
+
6784,
|
|
33
|
+
6800,
|
|
34
|
+
6992,
|
|
35
|
+
7088,
|
|
36
|
+
7232,
|
|
37
|
+
7248,
|
|
38
|
+
42528,
|
|
39
|
+
43216,
|
|
40
|
+
43264,
|
|
41
|
+
43472,
|
|
42
|
+
43504,
|
|
43
|
+
43600,
|
|
44
|
+
44016,
|
|
45
|
+
65296,
|
|
46
|
+
66720,
|
|
47
|
+
68912,
|
|
48
|
+
68928,
|
|
49
|
+
69734,
|
|
50
|
+
69872,
|
|
51
|
+
69942,
|
|
52
|
+
70096,
|
|
53
|
+
70384,
|
|
54
|
+
70736,
|
|
55
|
+
70864,
|
|
56
|
+
71248,
|
|
57
|
+
71360,
|
|
58
|
+
71376,
|
|
59
|
+
71386,
|
|
60
|
+
71472,
|
|
61
|
+
71904,
|
|
62
|
+
72016,
|
|
63
|
+
72688,
|
|
64
|
+
72784,
|
|
65
|
+
73040,
|
|
66
|
+
73120,
|
|
67
|
+
73184,
|
|
68
|
+
73552,
|
|
69
|
+
90416,
|
|
70
|
+
92768,
|
|
71
|
+
92864,
|
|
72
|
+
93008,
|
|
73
|
+
93552,
|
|
74
|
+
118e3,
|
|
75
|
+
120782,
|
|
76
|
+
120792,
|
|
77
|
+
120802,
|
|
78
|
+
120812,
|
|
79
|
+
120822,
|
|
80
|
+
123200,
|
|
81
|
+
123632,
|
|
82
|
+
124144,
|
|
83
|
+
124401,
|
|
84
|
+
125264,
|
|
85
|
+
130032
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* Normalizes non-ASCII decimal digits to ASCII digits.
|
|
89
|
+
* Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,
|
|
90
|
+
* Devanagari, Bengali) to their ASCII equivalents (0-9).
|
|
91
|
+
*
|
|
92
|
+
* All current Unicode \p{Nd} blocks are included in decimalDigitBlockStarts.
|
|
93
|
+
*/
|
|
94
|
+
const normalizeDigits = (str) => str.replace(/* @__PURE__ */ new RegExp("\\p{Nd}", "gu"), (ch) => {
|
|
95
|
+
const cp = ch.codePointAt(0);
|
|
96
|
+
if (cp <= 57) return ch;
|
|
97
|
+
let lo = 0;
|
|
98
|
+
let hi = decimalDigitBlockStarts.length - 1;
|
|
99
|
+
while (lo < hi) {
|
|
100
|
+
const mid = lo + hi + 1 >>> 1;
|
|
101
|
+
if (decimalDigitBlockStarts[mid] <= cp) lo = mid;
|
|
102
|
+
else hi = mid - 1;
|
|
103
|
+
}
|
|
104
|
+
return String(cp - decimalDigitBlockStarts[lo]);
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
3
107
|
* Map of Unicode fraction code points to their ASCII equivalents.
|
|
4
108
|
*/
|
|
5
109
|
const vulgarFractionToAsciiMap = {
|
|
@@ -32,7 +136,7 @@ const vulgarFractionToAsciiMap = {
|
|
|
32
136
|
* | # | Description | Example(s) |
|
|
33
137
|
* | --- | ------------------------------------------------ | ------------------------------------------------------------------- |
|
|
34
138
|
* | `0` | entire string | `"2 1/3"` from `"2 1/3"` |
|
|
35
|
-
* | `1` |
|
|
139
|
+
* | `1` | sign (`-` or `+`) | `"-"` from `"-2 1/3"` |
|
|
36
140
|
* | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
|
|
37
141
|
* | `3` | entire fraction, decimal portion, or denominator | `" 1/3"` from `"2 1/3"`; `".33"` from `"2.33"`; `"/3"` from `"1/3"` |
|
|
38
142
|
*
|
|
@@ -48,15 +152,16 @@ const vulgarFractionToAsciiMap = {
|
|
|
48
152
|
* numericRegex.exec("2 / 3") // [ "2 / 3", "2", "/ 3", null ]
|
|
49
153
|
* ```
|
|
50
154
|
*/
|
|
51
|
-
const numericRegex =
|
|
155
|
+
const numericRegex = /^(?=[-+]?\s*\.\d|[-+]?\s*\d)([-+])?\s*((?:\d(?:[,_]\d|\d)*)*)(([eE][+-]?\d(?:[,_]\d|\d)*)?|\.\d(?:[,_]\d|\d)*([eE][+-]?\d(?:[,_]\d|\d)*)?|(\s+\d(?:[,_]\d|\d)*\s*)?\s*\/\s*\d(?:[,_]\d|\d)*)?$/;
|
|
52
156
|
/**
|
|
53
157
|
* Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
|
|
158
|
+
* Capture group 7 contains the trailing invalid portion.
|
|
54
159
|
*/
|
|
55
|
-
const numericRegexWithTrailingInvalid =
|
|
160
|
+
const numericRegexWithTrailingInvalid = /^(?=[-+]?\s*\.\d|[-+]?\s*\d)([-+])?\s*((?:\d(?:[,_]\d|\d)*)*)(([eE][+-]?\d(?:[,_]\d|\d)*)?|\.\d(?:[,_]\d|\d)*([eE][+-]?\d(?:[,_]\d|\d)*)?|(\s+\d(?:[,_]\d|\d)*\s*)?\s*\/\s*\d(?:[,_]\d|\d)*)?(\s*[^.\d/].*)?/;
|
|
56
161
|
/**
|
|
57
162
|
* Captures any Unicode vulgar fractions.
|
|
58
163
|
*/
|
|
59
|
-
const vulgarFractionsRegex =
|
|
164
|
+
const vulgarFractionsRegex = /([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟}])/g;
|
|
60
165
|
/**
|
|
61
166
|
* Map of Roman numeral sequences to their decimal equivalents.
|
|
62
167
|
*/
|
|
@@ -134,7 +239,7 @@ const romanNumeralUnicodeToAsciiMap = {
|
|
|
134
239
|
/**
|
|
135
240
|
* Captures all Unicode Roman numeral code points.
|
|
136
241
|
*/
|
|
137
|
-
const romanNumeralUnicodeRegex =
|
|
242
|
+
const romanNumeralUnicodeRegex = /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi;
|
|
138
243
|
/**
|
|
139
244
|
* Captures a valid Roman numeral sequence.
|
|
140
245
|
*
|
|
@@ -156,7 +261,7 @@ const romanNumeralUnicodeRegex = new RegExp("([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪ
|
|
|
156
261
|
* romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
|
|
157
262
|
* ```
|
|
158
263
|
*/
|
|
159
|
-
const romanNumeralRegex =
|
|
264
|
+
const romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;
|
|
160
265
|
/**
|
|
161
266
|
* Default options for {@link numericQuantity}.
|
|
162
267
|
*/
|
|
@@ -165,7 +270,10 @@ const defaultOptions = {
|
|
|
165
270
|
allowTrailingInvalid: false,
|
|
166
271
|
romanNumerals: false,
|
|
167
272
|
bigIntOnOverflow: false,
|
|
168
|
-
decimalSeparator: "."
|
|
273
|
+
decimalSeparator: ".",
|
|
274
|
+
allowCurrency: false,
|
|
275
|
+
percentage: false,
|
|
276
|
+
verbose: false
|
|
169
277
|
};
|
|
170
278
|
|
|
171
279
|
//#endregion
|
|
@@ -187,18 +295,18 @@ const parseRomanNumerals = (romanNumerals) => {
|
|
|
187
295
|
};
|
|
188
296
|
|
|
189
297
|
//#endregion
|
|
190
|
-
//#region
|
|
298
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/typeof.js
|
|
191
299
|
function _typeof(o) {
|
|
192
300
|
"@babel/helpers - typeof";
|
|
193
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o
|
|
194
|
-
return typeof o
|
|
195
|
-
} : function(o
|
|
196
|
-
return o
|
|
301
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
302
|
+
return typeof o;
|
|
303
|
+
} : function(o) {
|
|
304
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
197
305
|
}, _typeof(o);
|
|
198
306
|
}
|
|
199
307
|
|
|
200
308
|
//#endregion
|
|
201
|
-
//#region
|
|
309
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/toPrimitive.js
|
|
202
310
|
function toPrimitive(t, r) {
|
|
203
311
|
if ("object" != _typeof(t) || !t) return t;
|
|
204
312
|
var e = t[Symbol.toPrimitive];
|
|
@@ -211,14 +319,14 @@ function toPrimitive(t, r) {
|
|
|
211
319
|
}
|
|
212
320
|
|
|
213
321
|
//#endregion
|
|
214
|
-
//#region
|
|
322
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/toPropertyKey.js
|
|
215
323
|
function toPropertyKey(t) {
|
|
216
324
|
var i = toPrimitive(t, "string");
|
|
217
325
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
218
326
|
}
|
|
219
327
|
|
|
220
328
|
//#endregion
|
|
221
|
-
//#region
|
|
329
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/defineProperty.js
|
|
222
330
|
function _defineProperty(e, r, t) {
|
|
223
331
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
224
332
|
value: t,
|
|
@@ -229,13 +337,13 @@ function _defineProperty(e, r, t) {
|
|
|
229
337
|
}
|
|
230
338
|
|
|
231
339
|
//#endregion
|
|
232
|
-
//#region
|
|
340
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/objectSpread2.js
|
|
233
341
|
function ownKeys(e, r) {
|
|
234
342
|
var t = Object.keys(e);
|
|
235
343
|
if (Object.getOwnPropertySymbols) {
|
|
236
344
|
var o = Object.getOwnPropertySymbols(e);
|
|
237
|
-
r && (o = o.filter(function(r
|
|
238
|
-
return Object.getOwnPropertyDescriptor(e, r
|
|
345
|
+
r && (o = o.filter(function(r) {
|
|
346
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
239
347
|
})), t.push.apply(t, o);
|
|
240
348
|
}
|
|
241
349
|
return t;
|
|
@@ -243,10 +351,10 @@ function ownKeys(e, r) {
|
|
|
243
351
|
function _objectSpread2(e) {
|
|
244
352
|
for (var r = 1; r < arguments.length; r++) {
|
|
245
353
|
var t = null != arguments[r] ? arguments[r] : {};
|
|
246
|
-
r % 2 ? ownKeys(Object(t), !0).forEach(function(r
|
|
247
|
-
_defineProperty(e, r
|
|
248
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r
|
|
249
|
-
Object.defineProperty(e, r
|
|
354
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
355
|
+
_defineProperty(e, r, t[r]);
|
|
356
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
357
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
250
358
|
});
|
|
251
359
|
}
|
|
252
360
|
return e;
|
|
@@ -255,18 +363,65 @@ function _objectSpread2(e) {
|
|
|
255
363
|
//#endregion
|
|
256
364
|
//#region src/numericQuantity.ts
|
|
257
365
|
const spaceThenSlashRegex = /^\s*\//;
|
|
366
|
+
const currencyPrefixRegex = /* @__PURE__ */ new RegExp("^([-+]?)\\s*(\\p{Sc}+)\\s*", "u");
|
|
367
|
+
const currencySuffixRegex = /* @__PURE__ */ new RegExp("\\s*(\\p{Sc}+)$", "u");
|
|
368
|
+
const percentageSuffixRegex = /%$/;
|
|
258
369
|
function numericQuantity(quantity, options = defaultOptions) {
|
|
259
|
-
if (typeof quantity === "number" || typeof quantity === "bigint") return quantity;
|
|
260
|
-
let finalResult = NaN;
|
|
261
|
-
const quantityAsString = `${quantity}`.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace("⁄", "/").trim();
|
|
262
|
-
if (quantityAsString.length === 0) return NaN;
|
|
263
370
|
const opts = _objectSpread2(_objectSpread2({}, defaultOptions), options);
|
|
371
|
+
const originalInput = typeof quantity === "string" ? quantity : `${quantity}`;
|
|
372
|
+
let currencyPrefix;
|
|
373
|
+
let currencySuffix;
|
|
374
|
+
let percentageSuffix;
|
|
375
|
+
let trailingInvalid;
|
|
376
|
+
let parsedSign;
|
|
377
|
+
let parsedWhole;
|
|
378
|
+
let parsedNumerator;
|
|
379
|
+
let parsedDenominator;
|
|
380
|
+
const buildVerboseResult = (value) => {
|
|
381
|
+
const result = {
|
|
382
|
+
value,
|
|
383
|
+
input: originalInput
|
|
384
|
+
};
|
|
385
|
+
if (currencyPrefix) result.currencyPrefix = currencyPrefix;
|
|
386
|
+
if (currencySuffix) result.currencySuffix = currencySuffix;
|
|
387
|
+
if (percentageSuffix) result.percentageSuffix = percentageSuffix;
|
|
388
|
+
if (trailingInvalid) result.trailingInvalid = trailingInvalid;
|
|
389
|
+
if (parsedSign) result.sign = parsedSign;
|
|
390
|
+
if (parsedWhole !== void 0) result.whole = parsedWhole;
|
|
391
|
+
if (parsedNumerator !== void 0) result.numerator = parsedNumerator;
|
|
392
|
+
if (parsedDenominator !== void 0) result.denominator = parsedDenominator;
|
|
393
|
+
return result;
|
|
394
|
+
};
|
|
395
|
+
const returnValue = (value) => opts.verbose ? buildVerboseResult(value) : value;
|
|
396
|
+
if (typeof quantity === "number" || typeof quantity === "bigint") return returnValue(quantity);
|
|
397
|
+
let finalResult = NaN;
|
|
398
|
+
let workingString = `${quantity}`;
|
|
399
|
+
if (opts.allowCurrency) {
|
|
400
|
+
const prefixMatch = currencyPrefixRegex.exec(workingString);
|
|
401
|
+
if (prefixMatch && prefixMatch[2]) {
|
|
402
|
+
currencyPrefix = prefixMatch[2];
|
|
403
|
+
workingString = (prefixMatch[1] || "") + workingString.slice(prefixMatch[0].length);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (opts.allowCurrency) {
|
|
407
|
+
const suffixMatch = currencySuffixRegex.exec(workingString);
|
|
408
|
+
if (suffixMatch) {
|
|
409
|
+
currencySuffix = suffixMatch[1];
|
|
410
|
+
workingString = workingString.slice(0, -suffixMatch[0].length);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (opts.percentage && percentageSuffixRegex.test(workingString)) {
|
|
414
|
+
percentageSuffix = true;
|
|
415
|
+
workingString = workingString.slice(0, -1);
|
|
416
|
+
}
|
|
417
|
+
const quantityAsString = normalizeDigits(workingString.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace("⁄", "/").trim());
|
|
418
|
+
if (quantityAsString.length === 0) return returnValue(NaN);
|
|
264
419
|
let normalizedString = quantityAsString;
|
|
265
420
|
if (opts.decimalSeparator === ",") {
|
|
266
421
|
const commaCount = (quantityAsString.match(/,/g) || []).length;
|
|
267
422
|
if (commaCount === 1) normalizedString = quantityAsString.replaceAll(".", "_").replace(",", ".");
|
|
268
423
|
else if (commaCount > 1) {
|
|
269
|
-
if (!opts.allowTrailingInvalid) return NaN;
|
|
424
|
+
if (!opts.allowTrailingInvalid) return returnValue(NaN);
|
|
270
425
|
const firstCommaIndex = quantityAsString.indexOf(",");
|
|
271
426
|
const secondCommaIndex = quantityAsString.indexOf(",", firstCommaIndex + 1);
|
|
272
427
|
const beforeSecondComma = quantityAsString.substring(0, secondCommaIndex).replaceAll(".", "_").replace(",", ".");
|
|
@@ -274,20 +429,30 @@ function numericQuantity(quantity, options = defaultOptions) {
|
|
|
274
429
|
normalizedString = opts.allowTrailingInvalid ? beforeSecondComma + "&" + afterSecondComma : beforeSecondComma;
|
|
275
430
|
} else normalizedString = quantityAsString.replaceAll(".", "_");
|
|
276
431
|
}
|
|
277
|
-
const regexResult =
|
|
278
|
-
if (!regexResult) return opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN;
|
|
279
|
-
const
|
|
432
|
+
const regexResult = numericRegexWithTrailingInvalid.exec(normalizedString);
|
|
433
|
+
if (!regexResult) return returnValue(opts.romanNumerals ? parseRomanNumerals(quantityAsString) : NaN);
|
|
434
|
+
const rawTrailing = (regexResult[7] || normalizedString.slice(regexResult[0].length)).trim();
|
|
435
|
+
if (rawTrailing) {
|
|
436
|
+
trailingInvalid = rawTrailing;
|
|
437
|
+
if (!opts.allowTrailingInvalid) return returnValue(NaN);
|
|
438
|
+
}
|
|
439
|
+
const [, sign, ng1temp, ng2temp] = regexResult;
|
|
440
|
+
if (sign === "-" || sign === "+") parsedSign = sign;
|
|
280
441
|
const numberGroup1 = ng1temp.replaceAll(",", "").replaceAll("_", "");
|
|
281
442
|
const numberGroup2 = ng2temp === null || ng2temp === void 0 ? void 0 : ng2temp.replaceAll(",", "").replaceAll("_", "");
|
|
282
443
|
if (!numberGroup1 && numberGroup2 && numberGroup2.startsWith(".")) finalResult = 0;
|
|
283
444
|
else {
|
|
284
445
|
if (opts.bigIntOnOverflow) {
|
|
285
|
-
const asBigInt =
|
|
286
|
-
if (asBigInt > BigInt(Number.MAX_SAFE_INTEGER) || asBigInt < BigInt(Number.MIN_SAFE_INTEGER)) return asBigInt;
|
|
446
|
+
const asBigInt = sign === "-" ? BigInt(`-${numberGroup1}`) : BigInt(numberGroup1);
|
|
447
|
+
if (asBigInt > BigInt(Number.MAX_SAFE_INTEGER) || asBigInt < BigInt(Number.MIN_SAFE_INTEGER)) return returnValue(asBigInt);
|
|
287
448
|
}
|
|
288
449
|
finalResult = parseInt(numberGroup1);
|
|
289
450
|
}
|
|
290
|
-
if (!numberGroup2)
|
|
451
|
+
if (!numberGroup2) {
|
|
452
|
+
finalResult = sign === "-" ? finalResult * -1 : finalResult;
|
|
453
|
+
if (percentageSuffix && opts.percentage !== "number") finalResult = finalResult / 100;
|
|
454
|
+
return returnValue(finalResult);
|
|
455
|
+
}
|
|
291
456
|
const roundingFactor = opts.round === false ? NaN : parseFloat(`1e${Math.floor(Math.max(0, opts.round))}`);
|
|
292
457
|
if (numberGroup2.startsWith(".") || numberGroup2.startsWith("e") || numberGroup2.startsWith("E")) {
|
|
293
458
|
const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);
|
|
@@ -295,15 +460,34 @@ function numericQuantity(quantity, options = defaultOptions) {
|
|
|
295
460
|
} else if (spaceThenSlashRegex.test(numberGroup2)) {
|
|
296
461
|
const numerator = parseInt(numberGroup1);
|
|
297
462
|
const denominator = parseInt(numberGroup2.replace("/", ""));
|
|
463
|
+
parsedNumerator = numerator;
|
|
464
|
+
parsedDenominator = denominator;
|
|
298
465
|
finalResult = isNaN(roundingFactor) ? numerator / denominator : Math.round(numerator * roundingFactor / denominator) / roundingFactor;
|
|
299
466
|
} else {
|
|
300
|
-
const
|
|
301
|
-
|
|
467
|
+
const [numerator, denominator] = numberGroup2.split("/").map((v) => parseInt(v));
|
|
468
|
+
parsedWhole = finalResult;
|
|
469
|
+
parsedNumerator = numerator;
|
|
470
|
+
parsedDenominator = denominator;
|
|
302
471
|
finalResult += isNaN(roundingFactor) ? numerator / denominator : Math.round(numerator * roundingFactor / denominator) / roundingFactor;
|
|
303
472
|
}
|
|
304
|
-
|
|
473
|
+
finalResult = sign === "-" ? finalResult * -1 : finalResult;
|
|
474
|
+
if (percentageSuffix && opts.percentage !== "number") finalResult = isNaN(roundingFactor) ? finalResult / 100 : Math.round(finalResult / 100 * roundingFactor) / roundingFactor;
|
|
475
|
+
return returnValue(finalResult);
|
|
305
476
|
}
|
|
306
477
|
|
|
307
478
|
//#endregion
|
|
308
|
-
|
|
479
|
+
//#region src/isNumericQuantity.ts
|
|
480
|
+
/**
|
|
481
|
+
* Checks if a string represents a valid numeric quantity.
|
|
482
|
+
*
|
|
483
|
+
* Returns `true` if the string can be parsed as a number, `false` otherwise.
|
|
484
|
+
* Accepts the same options as `numericQuantity`.
|
|
485
|
+
*/
|
|
486
|
+
const isNumericQuantity = (quantity, options) => {
|
|
487
|
+
const result = numericQuantity(quantity, _objectSpread2(_objectSpread2({}, options), {}, { verbose: false }));
|
|
488
|
+
return typeof result === "bigint" || !isNaN(result);
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
//#endregion
|
|
492
|
+
export { defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, vulgarFractionToAsciiMap, vulgarFractionsRegex };
|
|
309
493
|
//# sourceMappingURL=numeric-quantity.legacy-esm.js.map
|