csv-sort 6.0.2 → 6.0.6
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/CHANGELOG.md +0 -6
- package/LICENSE +1 -1
- package/dist/csv-sort.esm.js +408 -241
- package/dist/csv-sort.umd.js +8 -8
- package/package.json +37 -41
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,6 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## 6.0.1 (2021-09-13)
|
|
7
|
-
|
|
8
|
-
### Bug Fixes
|
|
9
|
-
|
|
10
|
-
- bump TS and separate ESLint plugins away from this monorepo ([2e07d42](https://github.com/codsen/codsen/commit/2e07d424222b6ffedf5fb45c83ad453627ec2904))
|
|
11
|
-
|
|
12
6
|
## 6.0.0 (2021-09-09)
|
|
13
7
|
|
|
14
8
|
### Features
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2010
|
|
3
|
+
Copyright (c) 2010-2021 Roy Revelt and other contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
6
|
a copy of this software and associated documentation files (the
|
package/dist/csv-sort.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name csv-sort
|
|
3
3
|
* @fileoverview Sorts double-entry bookkeeping CSV coming from internet banking
|
|
4
|
-
* @version 6.0.
|
|
4
|
+
* @version 6.0.6
|
|
5
5
|
* @author Roy Revelt, Codsen Ltd
|
|
6
6
|
* @license MIT
|
|
7
7
|
* {@link https://codsen.com/os/csv-sort/}
|
|
@@ -12,268 +12,435 @@ import { splitEasy } from 'csv-split-easy';
|
|
|
12
12
|
import currency from 'currency.js';
|
|
13
13
|
|
|
14
14
|
function isNumeric(str) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if (!str.trim()) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return Number(str) === Number(str);
|
|
19
19
|
}
|
|
20
|
-
const currencySigns = [
|
|
20
|
+
const currencySigns = [
|
|
21
|
+
"د.إ",
|
|
22
|
+
"؋",
|
|
23
|
+
"L",
|
|
24
|
+
"֏",
|
|
25
|
+
"ƒ",
|
|
26
|
+
"Kz",
|
|
27
|
+
"$",
|
|
28
|
+
"ƒ",
|
|
29
|
+
"₼",
|
|
30
|
+
"KM",
|
|
31
|
+
"৳",
|
|
32
|
+
"лв",
|
|
33
|
+
".د.ب",
|
|
34
|
+
"FBu",
|
|
35
|
+
"$b",
|
|
36
|
+
"R$",
|
|
37
|
+
"฿",
|
|
38
|
+
"Nu.",
|
|
39
|
+
"P",
|
|
40
|
+
"p.",
|
|
41
|
+
"BZ$",
|
|
42
|
+
"FC",
|
|
43
|
+
"CHF",
|
|
44
|
+
"¥",
|
|
45
|
+
"₡",
|
|
46
|
+
"₱",
|
|
47
|
+
"Kč",
|
|
48
|
+
"Fdj",
|
|
49
|
+
"kr",
|
|
50
|
+
"RD$",
|
|
51
|
+
"دج",
|
|
52
|
+
"kr",
|
|
53
|
+
"Nfk",
|
|
54
|
+
"Br",
|
|
55
|
+
"Ξ",
|
|
56
|
+
"€",
|
|
57
|
+
"₾",
|
|
58
|
+
"₵",
|
|
59
|
+
"GH₵",
|
|
60
|
+
"D",
|
|
61
|
+
"FG",
|
|
62
|
+
"Q",
|
|
63
|
+
"L",
|
|
64
|
+
"kn",
|
|
65
|
+
"G",
|
|
66
|
+
"Ft",
|
|
67
|
+
"Rp",
|
|
68
|
+
"₪",
|
|
69
|
+
"₹",
|
|
70
|
+
"ع.د",
|
|
71
|
+
"﷼",
|
|
72
|
+
"kr",
|
|
73
|
+
"J$",
|
|
74
|
+
"JD",
|
|
75
|
+
"¥",
|
|
76
|
+
"KSh",
|
|
77
|
+
"лв",
|
|
78
|
+
"៛",
|
|
79
|
+
"CF",
|
|
80
|
+
"₩",
|
|
81
|
+
"₩",
|
|
82
|
+
"KD",
|
|
83
|
+
"лв",
|
|
84
|
+
"₭",
|
|
85
|
+
"₨",
|
|
86
|
+
"M",
|
|
87
|
+
"Ł",
|
|
88
|
+
"Lt",
|
|
89
|
+
"Ls",
|
|
90
|
+
"LD",
|
|
91
|
+
"MAD",
|
|
92
|
+
"lei",
|
|
93
|
+
"Ar",
|
|
94
|
+
"ден",
|
|
95
|
+
"K",
|
|
96
|
+
"₮",
|
|
97
|
+
"MOP$",
|
|
98
|
+
"UM",
|
|
99
|
+
"₨",
|
|
100
|
+
"Rf",
|
|
101
|
+
"MK",
|
|
102
|
+
"RM",
|
|
103
|
+
"MT",
|
|
104
|
+
"₦",
|
|
105
|
+
"C$",
|
|
106
|
+
"kr",
|
|
107
|
+
"₨",
|
|
108
|
+
"﷼",
|
|
109
|
+
"B/.",
|
|
110
|
+
"S/.",
|
|
111
|
+
"K",
|
|
112
|
+
"₱",
|
|
113
|
+
"₨",
|
|
114
|
+
"zł",
|
|
115
|
+
"Gs",
|
|
116
|
+
"﷼",
|
|
117
|
+
"¥",
|
|
118
|
+
"lei",
|
|
119
|
+
"Дин.",
|
|
120
|
+
"₽",
|
|
121
|
+
"R₣",
|
|
122
|
+
"﷼",
|
|
123
|
+
"₨",
|
|
124
|
+
"ج.س.",
|
|
125
|
+
"kr",
|
|
126
|
+
"£",
|
|
127
|
+
"Le",
|
|
128
|
+
"S",
|
|
129
|
+
"Db",
|
|
130
|
+
"E",
|
|
131
|
+
"฿",
|
|
132
|
+
"SM",
|
|
133
|
+
"T",
|
|
134
|
+
"د.ت",
|
|
135
|
+
"T$",
|
|
136
|
+
"₤",
|
|
137
|
+
"₺",
|
|
138
|
+
"TT$",
|
|
139
|
+
"NT$",
|
|
140
|
+
"TSh",
|
|
141
|
+
"₴",
|
|
142
|
+
"USh",
|
|
143
|
+
"$U",
|
|
144
|
+
"лв",
|
|
145
|
+
"Bs",
|
|
146
|
+
"₫",
|
|
147
|
+
"VT",
|
|
148
|
+
"WS$",
|
|
149
|
+
"FCFA",
|
|
150
|
+
"Ƀ",
|
|
151
|
+
"CFA",
|
|
152
|
+
"₣",
|
|
153
|
+
"﷼",
|
|
154
|
+
"R",
|
|
155
|
+
"Z$",
|
|
156
|
+
];
|
|
21
157
|
function findType(something) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
158
|
+
/* istanbul ignore next */
|
|
159
|
+
if (typeof something !== "string") {
|
|
160
|
+
throw new Error(`csv-sort/util/findType(): input must be string! Currently it's: ${typeof something}`);
|
|
161
|
+
}
|
|
162
|
+
if (isNumeric(something)) {
|
|
163
|
+
return "numeric";
|
|
164
|
+
}
|
|
165
|
+
/* istanbul ignore next */
|
|
166
|
+
if (currencySigns.some((singleSign) =>
|
|
167
|
+
isNumeric(something.replace(singleSign, "").replace(/[,.]/g, "")))) {
|
|
168
|
+
return "numeric";
|
|
169
|
+
}
|
|
170
|
+
if (!something.trim()) {
|
|
171
|
+
return "empty";
|
|
172
|
+
}
|
|
173
|
+
return "text";
|
|
38
174
|
}
|
|
39
175
|
|
|
40
176
|
function sort(input) {
|
|
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
|
-
|
|
177
|
+
let msgContent = null;
|
|
178
|
+
let msgType = null;
|
|
179
|
+
if (typeof input !== "string") {
|
|
180
|
+
throw new TypeError(`csv-sort/csvSort(): [THROW_ID_01] The input is of a wrong type! We accept either string of array of arrays. We got instead: ${typeof input}, equal to:\n${JSON.stringify(input, null, 4)}`);
|
|
181
|
+
}
|
|
182
|
+
else if (!input.trim()) {
|
|
183
|
+
return { res: [[""]], msgContent, msgType };
|
|
184
|
+
}
|
|
185
|
+
let content = splitEasy(input);
|
|
186
|
+
let schema = [];
|
|
187
|
+
let stateHeaderRowPresent = false;
|
|
188
|
+
let stateDataColumnRowLengthIsConsistent = true;
|
|
189
|
+
const stateColumnsContainingSameValueEverywhere = [];
|
|
190
|
+
let indexAtWhichEmptyCellsStart = null;
|
|
191
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
192
|
+
if (!schema.length) {
|
|
193
|
+
/* istanbul ignore next */
|
|
194
|
+
if (content[i].length !== 1 || content[i][0] !== "") {
|
|
195
|
+
for (let y = 0, len = content[i].length; y < len; y++) {
|
|
196
|
+
schema.push(findType(content[i][y].trim()));
|
|
197
|
+
if (indexAtWhichEmptyCellsStart === null &&
|
|
198
|
+
findType(content[i][y].trim()) === "empty") {
|
|
199
|
+
indexAtWhichEmptyCellsStart = y;
|
|
200
|
+
}
|
|
201
|
+
if (indexAtWhichEmptyCellsStart !== null &&
|
|
202
|
+
findType(content[i][y].trim()) !== "empty") {
|
|
203
|
+
indexAtWhichEmptyCellsStart = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
70
207
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
208
|
+
else {
|
|
209
|
+
if (i === 0) {
|
|
210
|
+
stateHeaderRowPresent = content[i].every((el) => findType(el) === "text" || findType(el) === "empty");
|
|
211
|
+
}
|
|
212
|
+
/* istanbul ignore else */
|
|
213
|
+
if (!stateHeaderRowPresent && schema.length !== content[i].length) {
|
|
214
|
+
stateDataColumnRowLengthIsConsistent = false;
|
|
215
|
+
}
|
|
216
|
+
let perRowIndexAtWhichEmptyCellsStart = null;
|
|
217
|
+
for (let y = 0, len = content[i].length; y < len; y++) {
|
|
218
|
+
/* istanbul ignore else */
|
|
219
|
+
if (perRowIndexAtWhichEmptyCellsStart === null &&
|
|
220
|
+
findType(content[i][y].trim()) === "empty") {
|
|
221
|
+
perRowIndexAtWhichEmptyCellsStart = y;
|
|
222
|
+
}
|
|
223
|
+
/* istanbul ignore else */
|
|
224
|
+
if (perRowIndexAtWhichEmptyCellsStart !== null &&
|
|
225
|
+
findType(content[i][y].trim()) !== "empty") {
|
|
226
|
+
perRowIndexAtWhichEmptyCellsStart = null;
|
|
227
|
+
}
|
|
228
|
+
/* istanbul ignore else */
|
|
229
|
+
if (findType(content[i][y].trim()) !== schema[y] &&
|
|
230
|
+
!stateHeaderRowPresent) {
|
|
231
|
+
const toAdd = findType(content[i][y].trim());
|
|
232
|
+
/* istanbul ignore else */
|
|
233
|
+
if (Array.isArray(schema[y])) {
|
|
234
|
+
if (!schema[y].includes(toAdd)) {
|
|
235
|
+
schema[y].push(findType(content[i][y].trim()));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else if (schema[y] !== toAdd) {
|
|
239
|
+
const temp = schema[y];
|
|
240
|
+
schema[y] = [];
|
|
241
|
+
schema[y].push(temp);
|
|
242
|
+
schema[y].push(toAdd);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/* istanbul ignore next */
|
|
247
|
+
if (indexAtWhichEmptyCellsStart !== null &&
|
|
248
|
+
perRowIndexAtWhichEmptyCellsStart !== null &&
|
|
249
|
+
perRowIndexAtWhichEmptyCellsStart > indexAtWhichEmptyCellsStart &&
|
|
250
|
+
(!stateHeaderRowPresent || (stateHeaderRowPresent && i !== 0))) {
|
|
251
|
+
indexAtWhichEmptyCellsStart = perRowIndexAtWhichEmptyCellsStart;
|
|
252
|
+
}
|
|
85
253
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
254
|
+
}
|
|
255
|
+
/* istanbul ignore else */
|
|
256
|
+
if (!indexAtWhichEmptyCellsStart) {
|
|
257
|
+
indexAtWhichEmptyCellsStart = schema.length;
|
|
258
|
+
}
|
|
259
|
+
let nonEmptyColsStartAt = 0;
|
|
260
|
+
for (let i = 0, len = schema.length; i < len; i++) {
|
|
261
|
+
if (schema[i] === "empty") {
|
|
262
|
+
nonEmptyColsStartAt = i;
|
|
89
263
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const toAdd = findType(content[i][y].trim());
|
|
93
|
-
/* istanbul ignore else */
|
|
94
|
-
if (Array.isArray(schema[y])) {
|
|
95
|
-
if (!schema[y].includes(toAdd)) {
|
|
96
|
-
schema[y].push(findType(content[i][y].trim()));
|
|
97
|
-
}
|
|
98
|
-
} else if (schema[y] !== toAdd) {
|
|
99
|
-
const temp = schema[y];
|
|
100
|
-
schema[y] = [];
|
|
101
|
-
schema[y].push(temp);
|
|
102
|
-
schema[y].push(toAdd);
|
|
103
|
-
}
|
|
264
|
+
else {
|
|
265
|
+
break;
|
|
104
266
|
}
|
|
105
|
-
}
|
|
106
|
-
/* istanbul ignore next */
|
|
107
|
-
if (indexAtWhichEmptyCellsStart !== null && perRowIndexAtWhichEmptyCellsStart !== null && perRowIndexAtWhichEmptyCellsStart > indexAtWhichEmptyCellsStart && (!stateHeaderRowPresent || stateHeaderRowPresent && i !== 0)) {
|
|
108
|
-
indexAtWhichEmptyCellsStart = perRowIndexAtWhichEmptyCellsStart;
|
|
109
|
-
}
|
|
110
267
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
let nonEmptyColsStartAt = 0;
|
|
117
|
-
for (let i = 0, len = schema.length; i < len; i++) {
|
|
118
|
-
if (schema[i] === "empty") {
|
|
119
|
-
nonEmptyColsStartAt = i;
|
|
120
|
-
} else {
|
|
121
|
-
break;
|
|
268
|
+
/* istanbul ignore else */
|
|
269
|
+
if (nonEmptyColsStartAt !== 0) {
|
|
270
|
+
content = content.map((arr) => arr.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart));
|
|
271
|
+
schema = schema.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart);
|
|
122
272
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
numericSchemaColumns.push(i);
|
|
273
|
+
const numericSchemaColumns = [];
|
|
274
|
+
let balanceColumnIndex;
|
|
275
|
+
schema.forEach((colType, i) => {
|
|
276
|
+
if (colType === "numeric") {
|
|
277
|
+
numericSchemaColumns.push(i);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const traverseUpToThisIndexAtTheTop = stateHeaderRowPresent ? 1 : 0;
|
|
281
|
+
if (numericSchemaColumns.length === 1) {
|
|
282
|
+
balanceColumnIndex = numericSchemaColumns[0];
|
|
134
283
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
284
|
+
else if (numericSchemaColumns.length === 0) {
|
|
285
|
+
throw new Error('csv-sort/csvSort(): [THROW_ID_03] Your CSV file does not contain numeric-only columns and computer was not able to detect the "Balance" column!');
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
let potentialBalanceColumnIndexesList = Array.from(numericSchemaColumns);
|
|
289
|
+
const deleteFromPotentialBalanceColumnIndexesList = [];
|
|
290
|
+
for (let i = 0, len = potentialBalanceColumnIndexesList.length; i < len; i++) {
|
|
291
|
+
const suspectedBalanceColumnsIndexNumber = potentialBalanceColumnIndexesList[i];
|
|
292
|
+
let previousValue;
|
|
293
|
+
let lookForTwoEqualAndConsecutive = true;
|
|
294
|
+
let firstValue;
|
|
295
|
+
let lookForAllTheSame = true;
|
|
296
|
+
for (let rowNum = traverseUpToThisIndexAtTheTop, len2 = content.length; rowNum < len2; rowNum++) {
|
|
297
|
+
/* istanbul ignore else */
|
|
298
|
+
if (lookForTwoEqualAndConsecutive) {
|
|
299
|
+
if (previousValue == null) {
|
|
300
|
+
previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
|
|
301
|
+
}
|
|
302
|
+
else if (previousValue ===
|
|
303
|
+
content[rowNum][suspectedBalanceColumnsIndexNumber]) {
|
|
304
|
+
deleteFromPotentialBalanceColumnIndexesList.push(suspectedBalanceColumnsIndexNumber);
|
|
305
|
+
lookForTwoEqualAndConsecutive = false;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/* istanbul ignore else */
|
|
312
|
+
if (lookForAllTheSame) {
|
|
313
|
+
if (firstValue == null) {
|
|
314
|
+
firstValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
|
|
315
|
+
}
|
|
316
|
+
else if (content[rowNum][suspectedBalanceColumnsIndexNumber] !== firstValue) {
|
|
317
|
+
lookForAllTheSame = false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (!lookForTwoEqualAndConsecutive) {
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/* istanbul ignore else */
|
|
325
|
+
if (lookForAllTheSame) {
|
|
326
|
+
stateColumnsContainingSameValueEverywhere.push(suspectedBalanceColumnsIndexNumber);
|
|
327
|
+
}
|
|
161
328
|
}
|
|
329
|
+
potentialBalanceColumnIndexesList = pull(potentialBalanceColumnIndexesList, ...deleteFromPotentialBalanceColumnIndexesList);
|
|
162
330
|
/* istanbul ignore else */
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
firstValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
|
|
166
|
-
} else if (content[rowNum][suspectedBalanceColumnsIndexNumber] !== firstValue) {
|
|
167
|
-
lookForAllTheSame = false;
|
|
168
|
-
}
|
|
331
|
+
if (potentialBalanceColumnIndexesList.length === 1) {
|
|
332
|
+
balanceColumnIndex = potentialBalanceColumnIndexesList[0];
|
|
169
333
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
334
|
+
else if (potentialBalanceColumnIndexesList.length === 0) {
|
|
335
|
+
throw new Error('csv-sort/csvSort(): [THROW_ID_04] The computer can\'t find the "Balance" column! It saw some numeric-only columns, but they all seem to have certain rows with the same values as rows right below/above them!');
|
|
172
336
|
}
|
|
173
|
-
|
|
174
|
-
/* istanbul ignore else */
|
|
175
|
-
if (lookForAllTheSame) {
|
|
176
|
-
stateColumnsContainingSameValueEverywhere.push(suspectedBalanceColumnsIndexNumber);
|
|
177
|
-
}
|
|
337
|
+
else ;
|
|
178
338
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (potentialBalanceColumnIndexesList.length === 1) {
|
|
182
|
-
balanceColumnIndex = potentialBalanceColumnIndexesList[0];
|
|
183
|
-
} else if (potentialBalanceColumnIndexesList.length === 0) {
|
|
184
|
-
throw new Error('csv-sort/csvSort(): [THROW_ID_04] The computer can\'t find the "Balance" column! It saw some numeric-only columns, but they all seem to have certain rows with the same values as rows right below/above them!');
|
|
185
|
-
} else ;
|
|
186
|
-
}
|
|
187
|
-
if (!balanceColumnIndex) {
|
|
188
|
-
throw new Error("csv-sort/csvSort(): [THROW_ID_05] Sadly computer couldn't find its way in this CSV and had to stop working on it.");
|
|
189
|
-
}
|
|
190
|
-
const potentialCreditDebitColumns = pull(Array.from(schema.reduce((result, el, index) => {
|
|
191
|
-
if (typeof el === "string" && el === "numeric" || Array.isArray(el) && el.includes("numeric")) {
|
|
192
|
-
result.push(index);
|
|
339
|
+
if (!balanceColumnIndex) {
|
|
340
|
+
throw new Error("csv-sort/csvSort(): [THROW_ID_05] Sadly computer couldn't find its way in this CSV and had to stop working on it.");
|
|
193
341
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const usedUpRows = [];
|
|
199
|
-
const bottom = stateHeaderRowPresent ? 1 : 0;
|
|
200
|
-
for (let y = content.length - 2; y >= bottom; y--) {
|
|
201
|
-
for (let suspectedRowsIndex = content.length - 2; suspectedRowsIndex >= bottom; suspectedRowsIndex--) {
|
|
202
|
-
if (!usedUpRows.includes(suspectedRowsIndex)) {
|
|
203
|
-
let thisRowIsDone = false;
|
|
204
|
-
for (let suspectedColIndex = 0, len = potentialCreditDebitColumns.length; suspectedColIndex < len; suspectedColIndex++) {
|
|
205
|
-
let diffVal = null;
|
|
206
|
-
if (content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
|
|
207
|
-
diffVal = currency(content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]]);
|
|
208
|
-
}
|
|
209
|
-
let totalVal = null;
|
|
210
|
-
/* istanbul ignore else */
|
|
211
|
-
if (content[suspectedRowsIndex][balanceColumnIndex] !== "") {
|
|
212
|
-
totalVal = currency(content[suspectedRowsIndex][balanceColumnIndex]);
|
|
213
|
-
}
|
|
214
|
-
let topmostResContentBalance = null;
|
|
215
|
-
/* istanbul ignore else */
|
|
216
|
-
if (resContent[0][balanceColumnIndex] !== "") {
|
|
217
|
-
topmostResContentBalance = currency(resContent[0][balanceColumnIndex]).format();
|
|
218
|
-
}
|
|
219
|
-
let currentRowsDiffVal = null;
|
|
220
|
-
/* istanbul ignore else */
|
|
221
|
-
if (resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
|
|
222
|
-
currentRowsDiffVal = currency(resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]]).format();
|
|
223
|
-
}
|
|
224
|
-
let lastResContentRowsBalance = null;
|
|
225
|
-
/* istanbul ignore else */
|
|
226
|
-
if (resContent[resContent.length - 1][balanceColumnIndex] !== "") {
|
|
227
|
-
lastResContentRowsBalance = currency(resContent[resContent.length - 1][balanceColumnIndex]);
|
|
228
|
-
}
|
|
229
|
-
/* istanbul ignore else */
|
|
230
|
-
if (diffVal && totalVal.add(diffVal).format() === topmostResContentBalance) {
|
|
231
|
-
resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
232
|
-
usedUpRows.push(suspectedRowsIndex);
|
|
233
|
-
thisRowIsDone = true;
|
|
234
|
-
break;
|
|
235
|
-
} else if (diffVal && totalVal.subtract(diffVal).format() === topmostResContentBalance) {
|
|
236
|
-
resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
237
|
-
usedUpRows.push(suspectedRowsIndex);
|
|
238
|
-
thisRowIsDone = true;
|
|
239
|
-
break;
|
|
240
|
-
} else if (currentRowsDiffVal && lastResContentRowsBalance.add(currentRowsDiffVal).format() === totalVal.format()) {
|
|
241
|
-
resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
242
|
-
usedUpRows.push(suspectedRowsIndex);
|
|
243
|
-
thisRowIsDone = true;
|
|
244
|
-
break;
|
|
245
|
-
} else if (currentRowsDiffVal && lastResContentRowsBalance.subtract(currentRowsDiffVal).format() === totalVal.format()) {
|
|
246
|
-
resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
247
|
-
usedUpRows.push(suspectedRowsIndex);
|
|
248
|
-
thisRowIsDone = true;
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
342
|
+
const potentialCreditDebitColumns = pull(Array.from(schema.reduce((result, el, index) => {
|
|
343
|
+
if ((typeof el === "string" && el === "numeric") ||
|
|
344
|
+
(Array.isArray(el) && el.includes("numeric"))) {
|
|
345
|
+
result.push(index);
|
|
251
346
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
347
|
+
return result;
|
|
348
|
+
}, [])), balanceColumnIndex, ...stateColumnsContainingSameValueEverywhere);
|
|
349
|
+
const resContent = [];
|
|
350
|
+
resContent.push(content[content.length - 1].slice(0, indexAtWhichEmptyCellsStart));
|
|
351
|
+
const usedUpRows = [];
|
|
352
|
+
const bottom = stateHeaderRowPresent ? 1 : 0;
|
|
353
|
+
for (let y = content.length - 2; y >= bottom; y--) {
|
|
354
|
+
for (let suspectedRowsIndex = content.length - 2; suspectedRowsIndex >= bottom; suspectedRowsIndex--) {
|
|
355
|
+
if (!usedUpRows.includes(suspectedRowsIndex)) {
|
|
356
|
+
let thisRowIsDone = false;
|
|
357
|
+
for (let suspectedColIndex = 0, len = potentialCreditDebitColumns.length; suspectedColIndex < len; suspectedColIndex++) {
|
|
358
|
+
let diffVal = null;
|
|
359
|
+
if (content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
|
|
360
|
+
diffVal = currency(content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]]);
|
|
361
|
+
}
|
|
362
|
+
let totalVal = null;
|
|
363
|
+
/* istanbul ignore else */
|
|
364
|
+
if (content[suspectedRowsIndex][balanceColumnIndex] !== "") {
|
|
365
|
+
totalVal = currency(content[suspectedRowsIndex][balanceColumnIndex]);
|
|
366
|
+
}
|
|
367
|
+
let topmostResContentBalance = null;
|
|
368
|
+
/* istanbul ignore else */
|
|
369
|
+
if (resContent[0][balanceColumnIndex] !== "") {
|
|
370
|
+
topmostResContentBalance = currency(resContent[0][balanceColumnIndex]).format();
|
|
371
|
+
}
|
|
372
|
+
let currentRowsDiffVal = null;
|
|
373
|
+
/* istanbul ignore else */
|
|
374
|
+
if (resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
|
|
375
|
+
currentRowsDiffVal = currency(resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]]).format();
|
|
376
|
+
}
|
|
377
|
+
let lastResContentRowsBalance = null;
|
|
378
|
+
/* istanbul ignore else */
|
|
379
|
+
if (resContent[resContent.length - 1][balanceColumnIndex] !== "") {
|
|
380
|
+
lastResContentRowsBalance = currency(resContent[resContent.length - 1][balanceColumnIndex]);
|
|
381
|
+
}
|
|
382
|
+
/* istanbul ignore else */
|
|
383
|
+
if (diffVal &&
|
|
384
|
+
totalVal.add(diffVal).format() ===
|
|
385
|
+
topmostResContentBalance) {
|
|
386
|
+
resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
387
|
+
usedUpRows.push(suspectedRowsIndex);
|
|
388
|
+
thisRowIsDone = true;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
else if (diffVal &&
|
|
392
|
+
totalVal.subtract(diffVal).format() ===
|
|
393
|
+
topmostResContentBalance) {
|
|
394
|
+
resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
395
|
+
usedUpRows.push(suspectedRowsIndex);
|
|
396
|
+
thisRowIsDone = true;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
else if (currentRowsDiffVal &&
|
|
400
|
+
lastResContentRowsBalance
|
|
401
|
+
.add(currentRowsDiffVal)
|
|
402
|
+
.format() === totalVal.format()) {
|
|
403
|
+
resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
404
|
+
usedUpRows.push(suspectedRowsIndex);
|
|
405
|
+
thisRowIsDone = true;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
else if (currentRowsDiffVal &&
|
|
409
|
+
lastResContentRowsBalance
|
|
410
|
+
.subtract(currentRowsDiffVal)
|
|
411
|
+
.format() === totalVal.format()) {
|
|
412
|
+
resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
|
|
413
|
+
usedUpRows.push(suspectedRowsIndex);
|
|
414
|
+
thisRowIsDone = true;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/* istanbul ignore else */
|
|
419
|
+
if (thisRowIsDone) {
|
|
420
|
+
thisRowIsDone = false;
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/* istanbul ignore else */
|
|
427
|
+
if (stateHeaderRowPresent) {
|
|
428
|
+
if (stateDataColumnRowLengthIsConsistent &&
|
|
429
|
+
content[0].length > schema.length) {
|
|
430
|
+
content[0].length = schema.length;
|
|
256
431
|
}
|
|
257
|
-
|
|
432
|
+
resContent.unshift(content[0].slice(0, indexAtWhichEmptyCellsStart));
|
|
258
433
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
content[0].length = schema.length;
|
|
434
|
+
/* istanbul ignore else */
|
|
435
|
+
if (content.length - (stateHeaderRowPresent ? 2 : 1) !== usedUpRows.length) {
|
|
436
|
+
msgContent = "Not all rows were recognised!";
|
|
437
|
+
msgType = "alert";
|
|
264
438
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
msgType = "alert";
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
res: resContent,
|
|
274
|
-
msgContent,
|
|
275
|
-
msgType
|
|
276
|
-
};
|
|
439
|
+
return {
|
|
440
|
+
res: resContent,
|
|
441
|
+
msgContent,
|
|
442
|
+
msgType,
|
|
443
|
+
};
|
|
277
444
|
}
|
|
278
445
|
|
|
279
446
|
export { sort };
|
package/dist/csv-sort.umd.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name csv-sort
|
|
3
3
|
* @fileoverview Sorts double-entry bookkeeping CSV coming from internet banking
|
|
4
|
-
* @version 6.0.
|
|
4
|
+
* @version 6.0.6
|
|
5
5
|
* @author Roy Revelt, Codsen Ltd
|
|
6
6
|
* @license MIT
|
|
7
7
|
* {@link https://codsen.com/os/csv-sort/}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
/**
|
|
12
12
|
* @name ranges-sort
|
|
13
13
|
* @fileoverview Sort string index ranges
|
|
14
|
-
* @version 5.0.
|
|
14
|
+
* @version 5.0.6
|
|
15
15
|
* @author Roy Revelt, Codsen Ltd
|
|
16
16
|
* @license MIT
|
|
17
17
|
* {@link https://codsen.com/os/ranges-sort/}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
/**
|
|
20
20
|
* @name ranges-merge
|
|
21
21
|
* @fileoverview Merge and sort string index ranges
|
|
22
|
-
* @version 8.0.
|
|
22
|
+
* @version 8.0.6
|
|
23
23
|
* @author Roy Revelt, Codsen Ltd
|
|
24
24
|
* @license MIT
|
|
25
25
|
* {@link https://codsen.com/os/ranges-merge/}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
/**
|
|
28
28
|
* @name ranges-apply
|
|
29
29
|
* @fileoverview Take an array of string index ranges, delete/replace the string according to them
|
|
30
|
-
* @version 6.0.
|
|
30
|
+
* @version 6.0.6
|
|
31
31
|
* @author Roy Revelt, Codsen Ltd
|
|
32
32
|
* @license MIT
|
|
33
33
|
* {@link https://codsen.com/os/ranges-apply/}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
/**
|
|
36
36
|
* @name string-collapse-leading-whitespace
|
|
37
37
|
* @fileoverview Collapse the leading and trailing whitespace of a string
|
|
38
|
-
* @version 6.0.
|
|
38
|
+
* @version 6.0.6
|
|
39
39
|
* @author Roy Revelt, Codsen Ltd
|
|
40
40
|
* @license MIT
|
|
41
41
|
* {@link https://codsen.com/os/string-collapse-leading-whitespace/}
|
|
@@ -44,7 +44,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
|
|
|
44
44
|
/**
|
|
45
45
|
* @name ranges-push
|
|
46
46
|
* @fileoverview Gather string index ranges
|
|
47
|
-
* @version 6.0.
|
|
47
|
+
* @version 6.0.6
|
|
48
48
|
* @author Roy Revelt, Codsen Ltd
|
|
49
49
|
* @license MIT
|
|
50
50
|
* {@link https://codsen.com/os/ranges-push/}
|
|
@@ -52,7 +52,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
|
|
|
52
52
|
/**
|
|
53
53
|
* @name string-remove-thousand-separators
|
|
54
54
|
* @fileoverview Detects and removes thousand separators (dot/comma/quote/space) from string-type digits
|
|
55
|
-
* @version 6.0.
|
|
55
|
+
* @version 6.0.6
|
|
56
56
|
* @author Roy Revelt, Codsen Ltd
|
|
57
57
|
* @license MIT
|
|
58
58
|
* {@link https://codsen.com/os/string-remove-thousand-separators/}
|
|
@@ -60,7 +60,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
|
|
|
60
60
|
/**
|
|
61
61
|
* @name csv-split-easy
|
|
62
62
|
* @fileoverview Splits the CSV string into array of arrays, each representing a row of columns
|
|
63
|
-
* @version 6.0.
|
|
63
|
+
* @version 6.0.6
|
|
64
64
|
* @author Roy Revelt, Codsen Ltd
|
|
65
65
|
* @license MIT
|
|
66
66
|
* {@link https://codsen.com/os/csv-split-easy/}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "csv-sort",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.6",
|
|
4
4
|
"description": "Sorts double-entry bookkeeping CSV coming from internet banking",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accountancy",
|
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
"types": "types/index.d.ts",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rollup -c",
|
|
42
|
-
"esbuild": "node '../../scripts/esbuild.js'",
|
|
43
|
-
"
|
|
44
|
-
"ci_test": "npm run build && npm run format && tap --no-only --reporter=silent
|
|
42
|
+
"build:esbuild": "node '../../scripts/esbuild.js'",
|
|
43
|
+
"build:esbuild:dev": "cross-env MODE=dev node '../../scripts/esbuild.js'",
|
|
44
|
+
"ci_test": "npm run build && npm run format && tap --no-only --reporter=silent",
|
|
45
|
+
"clean_types": "../../scripts/cleanTypes.js",
|
|
45
46
|
"dev": "rollup -c --dev",
|
|
46
47
|
"devunittest": "npm run dev && tap --only -R 'base'",
|
|
47
48
|
"format": "npm run lect && npm run prettier && npm run lint",
|
|
@@ -51,20 +52,15 @@
|
|
|
51
52
|
"prettier": "../../node_modules/prettier/bin-prettier.js '*.{js,css,scss,vue,md,ts}' --write --loglevel silent",
|
|
52
53
|
"republish": "npm publish || :",
|
|
53
54
|
"tap": "tap",
|
|
54
|
-
"tsc": "tsc",
|
|
55
55
|
"pretest": "npm run build",
|
|
56
|
-
"test": "npm run
|
|
56
|
+
"test": "npm run test:ci && npm run perf",
|
|
57
|
+
"test:ci": "npm run unittest && npm run test:examples && npm run format",
|
|
57
58
|
"test:examples": "../../scripts/test-examples.js && npm run lect && npm run prettier",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"clean_types": "../../scripts/cleanTypes.js"
|
|
59
|
+
"tsc": "tsc",
|
|
60
|
+
"unittest": "tap --no-only --reporter=terse && tsc -p tsconfig.json --noEmit"
|
|
61
61
|
},
|
|
62
62
|
"tap": {
|
|
63
63
|
"check-coverage": false,
|
|
64
|
-
"coverage-report": [
|
|
65
|
-
"json-summary",
|
|
66
|
-
"text"
|
|
67
|
-
],
|
|
68
64
|
"node-arg": [
|
|
69
65
|
"--no-warnings",
|
|
70
66
|
"--experimental-loader",
|
|
@@ -87,51 +83,51 @@
|
|
|
87
83
|
}
|
|
88
84
|
},
|
|
89
85
|
"dependencies": {
|
|
90
|
-
"@babel/runtime": "^7.
|
|
91
|
-
"csv-split-easy": "^6.0.
|
|
86
|
+
"@babel/runtime": "^7.16.3",
|
|
87
|
+
"csv-split-easy": "^6.0.6",
|
|
92
88
|
"currency.js": "^2.0.4",
|
|
93
89
|
"lodash.pull": "^4.1.0"
|
|
94
90
|
},
|
|
95
91
|
"devDependencies": {
|
|
96
|
-
"@babel/cli": "^7.
|
|
97
|
-
"@babel/core": "^7.
|
|
98
|
-
"@babel/node": "^7.
|
|
99
|
-
"@babel/plugin-external-helpers": "^7.
|
|
100
|
-
"@babel/plugin-proposal-class-properties": "^7.
|
|
101
|
-
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.
|
|
102
|
-
"@babel/plugin-proposal-object-rest-spread": "^7.
|
|
103
|
-
"@babel/plugin-proposal-optional-chaining": "^7.
|
|
104
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
105
|
-
"@babel/preset-env": "^7.
|
|
106
|
-
"@babel/preset-typescript": "^7.
|
|
107
|
-
"@babel/register": "^7.
|
|
92
|
+
"@babel/cli": "^7.16.0",
|
|
93
|
+
"@babel/core": "^7.16.0",
|
|
94
|
+
"@babel/node": "^7.16.0",
|
|
95
|
+
"@babel/plugin-external-helpers": "^7.16.0",
|
|
96
|
+
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
|
97
|
+
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0",
|
|
98
|
+
"@babel/plugin-proposal-object-rest-spread": "^7.16.0",
|
|
99
|
+
"@babel/plugin-proposal-optional-chaining": "^7.16.0",
|
|
100
|
+
"@babel/plugin-transform-runtime": "^7.16.4",
|
|
101
|
+
"@babel/preset-env": "^7.16.4",
|
|
102
|
+
"@babel/preset-typescript": "^7.16.0",
|
|
103
|
+
"@babel/register": "^7.16.0",
|
|
108
104
|
"@istanbuljs/esm-loader-hook": "^0.1.2",
|
|
109
105
|
"@rollup/plugin-babel": "^5.3.0",
|
|
110
|
-
"@rollup/plugin-commonjs": "^
|
|
106
|
+
"@rollup/plugin-commonjs": "^21.0.1",
|
|
111
107
|
"@rollup/plugin-json": "^4.1.0",
|
|
112
|
-
"@rollup/plugin-node-resolve": "^13.0.
|
|
108
|
+
"@rollup/plugin-node-resolve": "^13.0.6",
|
|
113
109
|
"@rollup/plugin-strip": "^2.1.0",
|
|
114
|
-
"@rollup/plugin-typescript": "^8.
|
|
110
|
+
"@rollup/plugin-typescript": "^8.3.0",
|
|
115
111
|
"@types/lodash.pull": "^4.1.6",
|
|
116
|
-
"@types/node": "^16.9
|
|
112
|
+
"@types/node": "^16.11.9",
|
|
117
113
|
"@types/tap": "^15.0.5",
|
|
118
|
-
"@typescript-eslint/eslint-plugin": "^4.
|
|
119
|
-
"@typescript-eslint/parser": "^4.
|
|
120
|
-
"core-js": "^3.
|
|
114
|
+
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
115
|
+
"@typescript-eslint/parser": "^5.4.0",
|
|
116
|
+
"core-js": "^3.19.1",
|
|
121
117
|
"cross-env": "^7.0.3",
|
|
122
|
-
"eslint": "^
|
|
123
|
-
"lect": "^0.18.
|
|
124
|
-
"rollup": "^2.
|
|
118
|
+
"eslint": "^8.3.0",
|
|
119
|
+
"lect": "^0.18.6",
|
|
120
|
+
"rollup": "^2.60.0",
|
|
125
121
|
"rollup-plugin-ascii": "^0.0.3",
|
|
126
122
|
"rollup-plugin-banner": "^0.2.1",
|
|
127
123
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
128
|
-
"rollup-plugin-dts": "^4.0.
|
|
124
|
+
"rollup-plugin-dts": "^4.0.1",
|
|
129
125
|
"rollup-plugin-terser": "^7.0.2",
|
|
130
|
-
"tap": "^15.
|
|
126
|
+
"tap": "^15.1.2",
|
|
131
127
|
"tslib": "^2.3.1",
|
|
132
|
-
"typescript": "^4.
|
|
128
|
+
"typescript": "^4.5.2"
|
|
133
129
|
},
|
|
134
130
|
"engines": {
|
|
135
|
-
"node": ">=
|
|
131
|
+
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
136
132
|
}
|
|
137
133
|
}
|