-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
156 lines (139 loc) · 5.03 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* @copyright Copyright 2016-2020 Kevin Locke <kevin@kevinlocke.name>
* @license MIT
* @module json-replace-exponentials
*/
'use strict';
/** Pattern for a JSON number in exponential notation (i.e. with mandatory
* exp production from https://tools.ietf.org/html/rfc7158#section-6 ).
*
* @private
*/
const numberExpPattern = '(-?)([0-9]+)(?:\\.([0-9]+))?[eE]([+-]?[0-9]+)';
/** Pattern for a JSON string
* https://tools.ietf.org/html/rfc7158#section-7
*
* @private
*/
const stringPattern =
'"(?:[^\\x00-\\x1F\\\\"]|\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4}))*"';
/** RegExp for JSON number in exponential notation occurring after after any
* number of quoted strings and non-quote characters (i.e. a JSON number in
* exponential notation occurring outside of a string).
*
* @private
*/
const jsonWithNumberExpRE =
// eslint-disable-next-line regexp/no-control-character
new RegExp(`((?:${stringPattern}|[^"0-9])*?)(${numberExpPattern})`, 'gy');
/** Converts the parts of a number in exponential notation to fixed-point
* notation.
*
* @private
* @param {string} signPart Sign part of number. (i.e. '-' or '')
* @param {string} intPart Integer part of number. (i.e. part before decimal)
* @param {string} fracPart Fractional part of number, if any. (i.e. part
* after decimal).
* @param {number} exponent Exponential of number. (i.e. part after "e")
* @returns {string} Number in fixed-point notation.
*/
function exponentialPartsToFixed(signPart, intPart, fracPart, exponent) {
// These are easier to express without templates
/* eslint-disable prefer-template */
if (exponent >= 0) {
// Move decimal exponent digits to the right, adding 0s as necessary
let unsigned;
if (fracPart.length <= exponent) {
unsigned = intPart
+ fracPart
+ '0'.repeat(exponent - fracPart.length);
} else {
unsigned = intPart
+ fracPart.slice(0, exponent)
+ '.'
+ fracPart.slice(exponent);
}
// Remove unnecessary leading zeros
return signPart + unsigned.replace(/^0+(?=[0-9])/, '');
}
// Move decimal -exponent digits to the left, adding 0s as necessary
exponent = -exponent;
if (intPart.length > exponent) {
return signPart
+ intPart.slice(0, exponent)
+ '.'
+ intPart.slice(exponent)
+ fracPart;
}
return signPart
+ '0.'
+ '0'.repeat(exponent - intPart.length)
+ intPart
+ fracPart;
/* eslint-enable prefer-template */
}
/** Replacer function for jsonWithNumberExpRE which replaces the number in
* exponential notation with one in fixed-point notation if the exponent
* does not exceed +/-1000.
*
* @private
* @param {string} match Substring which matched jsonWithNumberExpRE.
* @param {string} prefix Substring before the number.
* @param {string} numExp Number in exponential notation.
* @param {string} signPart Sign part of number. (i.e. '-' or '')
* @param {string} intPart Integer part of number. (i.e. part before decimal)
* @param {string|undefined} fracPart Fractional part of number, if any.
* (i.e. part after decimal).
* @param {string} expPart Exponential part of number. (i.e. part after "e")
* @returns {string} Prefix followed by numExp in fixed-point notation.
* @throws {RangeError} If expPart is larger than 1,000 or smaller than
* -1,000.
*/
function exponentialToFixedReplacer(
match,
prefix,
numExp,
signPart,
intPart,
fracPart,
expPart,
) {
// Limit exponent to mitigate issues due to large fixed-point representations
// (e.g. eating all memory for 1e99999999999)
const exp = Number(expPart);
if (exp > 1000 || exp < -1000) {
throw new RangeError(`${numExp} exponent exceeds maximum`);
}
return prefix
+ exponentialPartsToFixed(signPart, intPart, fracPart || '', exp);
}
/** Replaces numbers in exponential notation in a given JSON string.
*
* @param {string} json JSON in which to replace numbers.
* @param {(function(string):string)=} replacer Optional replacer function
* called with a number in exponential format returning a string which will
* replace the number in the return value.
* @returns {string} Input JSON with numbers in exponential format replaced
* by fixed-point format, or by replacer, if provided.
* @throws {TypeError} If json is not a string.
* @throws {TypeError} If replacer is not a function.
* @throws {RangeError} If replacer is undefined and a number in exponential
* format has an exponent which is larger than 1,000 or smaller than -1,000.
* (To mitigate risks from unexpected large size increase in output.)
*/
module.exports =
function jsonReplaceExponentials(json, replacer) {
if (typeof json !== 'string') {
throw new TypeError('json must be a string');
}
let wrapReplacer;
if (replacer === undefined) {
wrapReplacer = exponentialToFixedReplacer;
} else if (typeof replacer === 'function') {
wrapReplacer = (match, prefix, numExp) => prefix + replacer(numExp);
} else {
throw new TypeError('replacer must be a function');
}
jsonWithNumberExpRE.lastIndex = 0;
return json.replaceAll(jsonWithNumberExpRE, wrapReplacer);
};