data-structure-typed 2.2.0 → 2.2.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/CHANGELOG.md +1 -1
- package/README.md +66 -21
- package/benchmark/report.html +1 -1
- package/benchmark/report.json +145 -169
- package/dist/cjs/index.cjs +20 -20
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +20 -20
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +20 -20
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +20 -20
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +3 -1
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +1 -0
- package/dist/types/data-structures/binary-tree/bst.d.ts +1 -0
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +1 -0
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1 -0
- package/dist/types/types/data-structures/base/base.d.ts +1 -1
- package/dist/umd/data-structure-typed.js +20 -20
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +2 -2
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/base/iterable-entry-base.ts +4 -4
- package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -1
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +1 -1
- package/src/data-structures/binary-tree/avl-tree.ts +4 -2
- package/src/data-structures/binary-tree/binary-tree.ts +3 -2
- package/src/data-structures/binary-tree/bst.ts +2 -1
- package/src/data-structures/binary-tree/red-black-tree.ts +2 -1
- package/src/data-structures/binary-tree/tree-counter.ts +1 -1
- package/src/data-structures/binary-tree/tree-multi-map.ts +2 -1
- package/src/data-structures/graph/abstract-graph.ts +3 -3
- package/src/data-structures/hash/hash-map.ts +4 -4
- package/src/types/data-structures/base/base.ts +1 -1
- package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +39 -36
- package/test/performance/runner-config.json +4 -4
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +4 -4
- package/test/unit/data-structures/binary-tree/bst.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +3 -3
- package/test/unit/data-structures/graph/directed-graph.test.ts +3 -3
- package/test/unit/data-structures/hash/hash-map.test.ts +14 -14
- package/test/performance/reportor.mjs +0 -505
|
@@ -328,7 +328,7 @@ describe('HashMap', () => {
|
|
|
328
328
|
});
|
|
329
329
|
|
|
330
330
|
it('some() returns true if any element matches the condition', () => {
|
|
331
|
-
expect(hashMap.some(key => key === 'key1')).toBe(true);
|
|
331
|
+
expect(hashMap.some((_value, key) => key === 'key1')).toBe(true);
|
|
332
332
|
});
|
|
333
333
|
|
|
334
334
|
it('forEach() should execute a function for each element', () => {
|
|
@@ -338,12 +338,12 @@ describe('HashMap', () => {
|
|
|
338
338
|
});
|
|
339
339
|
|
|
340
340
|
it('map() should transform each element', () => {
|
|
341
|
-
const newHashMap = hashMap.map(
|
|
341
|
+
const newHashMap = hashMap.map(value => value.toUpperCase());
|
|
342
342
|
expect(newHashMap.get('key1')).toBe('VALUE1');
|
|
343
343
|
});
|
|
344
344
|
|
|
345
345
|
it('filter() should remove elements that do not match the condition', () => {
|
|
346
|
-
const filteredHashMap = hashMap.filter(key => key !== 'key1');
|
|
346
|
+
const filteredHashMap = hashMap.filter((_, key) => key !== 'key1');
|
|
347
347
|
expect(filteredHashMap.has('key1')).toBe(false);
|
|
348
348
|
});
|
|
349
349
|
|
|
@@ -361,28 +361,28 @@ describe('HashMap', () => {
|
|
|
361
361
|
});
|
|
362
362
|
|
|
363
363
|
it('should find', () => {
|
|
364
|
-
const found = hashMap.find(
|
|
364
|
+
const found = hashMap.find(value => value === 'value1');
|
|
365
365
|
expect(found).toEqual(['key1', 'value1']);
|
|
366
366
|
const notFound = hashMap.find(value => value === 'value6');
|
|
367
367
|
expect(notFound).toEqual(undefined);
|
|
368
368
|
});
|
|
369
369
|
|
|
370
370
|
it('should every', () => {
|
|
371
|
-
const isEvery = hashMap.every(
|
|
371
|
+
const isEvery = hashMap.every(value => value.substring(0, 5) === 'value');
|
|
372
372
|
expect(isEvery).toEqual(true);
|
|
373
|
-
const isEvery4 = hashMap.every(
|
|
373
|
+
const isEvery4 = hashMap.every(value => value.substring(0, 4) === 'value');
|
|
374
374
|
expect(isEvery4).toEqual(false);
|
|
375
375
|
});
|
|
376
376
|
|
|
377
377
|
it('should some', () => {
|
|
378
|
-
const isSome = hashMap.some(
|
|
378
|
+
const isSome = hashMap.some(value => value.substring(5, 6) === '2');
|
|
379
379
|
expect(isSome).toEqual(true);
|
|
380
|
-
const isSome4 = hashMap.some(
|
|
380
|
+
const isSome4 = hashMap.some(value => value.substring(0, 5) === 'value');
|
|
381
381
|
expect(isSome4).toEqual(true);
|
|
382
382
|
});
|
|
383
383
|
|
|
384
384
|
it('should forEach', () => {
|
|
385
|
-
hashMap.forEach((
|
|
385
|
+
hashMap.forEach((value, key, index) => expect(value.substring(5, 6)).toBe(String(index + 1)));
|
|
386
386
|
});
|
|
387
387
|
|
|
388
388
|
it('should entries', () => {
|
|
@@ -817,7 +817,7 @@ describe('LinkedHashMap', () => {
|
|
|
817
817
|
});
|
|
818
818
|
|
|
819
819
|
it('some() returns true if any element matches the condition', () => {
|
|
820
|
-
expect(hashMap.some(key => key === 'key1')).toBe(true);
|
|
820
|
+
expect(hashMap.some((_value, key) => key === 'key1')).toBe(true);
|
|
821
821
|
});
|
|
822
822
|
|
|
823
823
|
it('forEach() should execute a function for each element', () => {
|
|
@@ -827,12 +827,12 @@ describe('LinkedHashMap', () => {
|
|
|
827
827
|
});
|
|
828
828
|
|
|
829
829
|
it('map() should transform each element', () => {
|
|
830
|
-
const newHashMap = hashMap.map((
|
|
830
|
+
const newHashMap = hashMap.map((value, key) => [key, value.toUpperCase()]);
|
|
831
831
|
expect(newHashMap.get('key1')).toBe('VALUE1');
|
|
832
832
|
});
|
|
833
833
|
|
|
834
834
|
it('filter() should remove elements that do not match the condition', () => {
|
|
835
|
-
const filteredHashMap = hashMap.filter(key => key !== 'key1');
|
|
835
|
+
const filteredHashMap = hashMap.filter((_v, key) => key !== 'key1');
|
|
836
836
|
expect(filteredHashMap.has('key1')).toBe(false);
|
|
837
837
|
});
|
|
838
838
|
|
|
@@ -893,7 +893,7 @@ describe('classic uses', () => {
|
|
|
893
893
|
linkedHashMap.set(2, 'B');
|
|
894
894
|
linkedHashMap.set(3, 'C');
|
|
895
895
|
|
|
896
|
-
const filteredMap = linkedHashMap.filter(
|
|
896
|
+
const filteredMap = linkedHashMap.filter(value => value !== 'B');
|
|
897
897
|
|
|
898
898
|
const result = Array.from(filteredMap);
|
|
899
899
|
expect(result).toEqual([
|
|
@@ -907,7 +907,7 @@ describe('classic uses', () => {
|
|
|
907
907
|
linkedHashMap.set(1, 'A');
|
|
908
908
|
linkedHashMap.set(2, 'B');
|
|
909
909
|
|
|
910
|
-
const mappedMap = linkedHashMap.map((
|
|
910
|
+
const mappedMap = linkedHashMap.map((value, key) => [value, key]);
|
|
911
911
|
|
|
912
912
|
const result = Array.from(mappedMap);
|
|
913
913
|
expect(result).toEqual([
|
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import fastGlob from 'fast-glob';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const isNumber = value => {
|
|
7
|
-
return typeof value === 'number';
|
|
8
|
-
};
|
|
9
|
-
const isString = value => {
|
|
10
|
-
return typeof value === 'string';
|
|
11
|
-
};
|
|
12
|
-
const isBoolean = value => {
|
|
13
|
-
return typeof value === 'boolean';
|
|
14
|
-
};
|
|
15
|
-
const isDate = value => {
|
|
16
|
-
return value instanceof Date;
|
|
17
|
-
};
|
|
18
|
-
const isNull = value => {
|
|
19
|
-
return value === null;
|
|
20
|
-
};
|
|
21
|
-
const isUndefined = value => {
|
|
22
|
-
return typeof value === 'undefined';
|
|
23
|
-
};
|
|
24
|
-
const isFunction = value => {
|
|
25
|
-
return typeof value === 'function';
|
|
26
|
-
};
|
|
27
|
-
const isObject = value => {
|
|
28
|
-
return typeof value === 'object';
|
|
29
|
-
};
|
|
30
|
-
const isArray = value => {
|
|
31
|
-
return Array.isArray(value);
|
|
32
|
-
};
|
|
33
|
-
const isEqual = (objA, objB) => {
|
|
34
|
-
if (objA === objB) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
if (typeof objA !== 'object' || typeof objB !== 'object' || objA === null || objB === null) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
const keysA = Object.keys(objA);
|
|
41
|
-
const keysB = Object.keys(objB);
|
|
42
|
-
if (keysA.length !== keysB.length) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
for (const key of keysA) {
|
|
46
|
-
if (!keysB.includes(key)) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
if (!isEqual(objA[key], objB[key])) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return true;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
function toggleJS(options) {
|
|
57
|
-
if (options === null || options === void 0 ? void 0 : options.plainHtml) {
|
|
58
|
-
return '';
|
|
59
|
-
} else {
|
|
60
|
-
return 'onclick="json-to-html.toggleVisibility(this);return false"';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function makeLabelDiv(options, level, keyName, datatype) {
|
|
65
|
-
if (typeof keyName === 'number') {
|
|
66
|
-
return `<div class='index'><span class='json-to-html-label'>${keyName} </span></div>`;
|
|
67
|
-
} else if (typeof keyName === 'string') {
|
|
68
|
-
if (datatype === 'array') {
|
|
69
|
-
return `<div class='collapsible level${level}' ${toggleJS(options)}><span class='json-to-html-label'>${keyName}</span></div>`;
|
|
70
|
-
} else if (datatype === 'object') {
|
|
71
|
-
return `<div class='attribute collapsible level${level}' ${toggleJS(options)}><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
72
|
-
} else {
|
|
73
|
-
return `<div class='leaf level${level}'><span class='json-to-html-label'>${keyName}:</span></div>`;
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
return '';
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getContentClass(keyName) {
|
|
81
|
-
if (typeof keyName === 'string') {
|
|
82
|
-
return 'content';
|
|
83
|
-
} else {
|
|
84
|
-
return '';
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function isPlainObject(val) {
|
|
89
|
-
let lastKey;
|
|
90
|
-
let lastOwnKey;
|
|
91
|
-
for (const key in val) {
|
|
92
|
-
if (val.hasOwnProperty(key)) {
|
|
93
|
-
lastOwnKey = key;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
for (const key in val) {
|
|
97
|
-
lastKey = key;
|
|
98
|
-
}
|
|
99
|
-
return lastOwnKey === lastKey;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isLeafValue(val) {
|
|
103
|
-
return (
|
|
104
|
-
isNumber(val) ||
|
|
105
|
-
isString(val) ||
|
|
106
|
-
isBoolean(val) ||
|
|
107
|
-
isDate(val) ||
|
|
108
|
-
isNull(val) ||
|
|
109
|
-
isUndefined(val) ||
|
|
110
|
-
isNaN(val) ||
|
|
111
|
-
isFunction(val) ||
|
|
112
|
-
!isPlainObject(val)
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function isLeafObject(obj) {
|
|
117
|
-
if (!isObject(obj)) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
for (const key in obj) {
|
|
121
|
-
const val = obj[key];
|
|
122
|
-
if (!isLeafValue(val)) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function isTable(arr) {
|
|
130
|
-
if (!isArray(arr)) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
if (arr.length === 0 || !isObject(arr[0])) {
|
|
134
|
-
return false;
|
|
135
|
-
} else {
|
|
136
|
-
let nonCompliant = arr.find(row => !isLeafObject(row));
|
|
137
|
-
if (nonCompliant) {
|
|
138
|
-
return false;
|
|
139
|
-
} else {
|
|
140
|
-
const cols = Object.keys(arr[0]);
|
|
141
|
-
nonCompliant = arr.find(row => !isEqual(cols, Object.keys(row)));
|
|
142
|
-
return !nonCompliant;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function drawTable(arr) {
|
|
148
|
-
function drawRow(headers, rowObj) {
|
|
149
|
-
return '<td>' + headers.map(header => rowObj[header]).join('</td><td>') + '</td>';
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const cols = Object.keys(arr[0]);
|
|
153
|
-
const content = arr.map(rowObj => drawRow(cols, rowObj));
|
|
154
|
-
const headingHtml = '<tr><th>' + cols.join('</th><th>') + '</th></tr>';
|
|
155
|
-
const contentHtml = '<tr>' + content.join('</tr><tr>') + '</tr>';
|
|
156
|
-
return '<table style="display: table; width:100%; table-layout: fixed;">' + headingHtml + contentHtml + '</table>';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function _render(name, data, options, level, altRow) {
|
|
160
|
-
const contentClass = getContentClass(name);
|
|
161
|
-
if (isArray(data)) {
|
|
162
|
-
const title = makeLabelDiv(options, level, `${name}`, 'array');
|
|
163
|
-
let subs;
|
|
164
|
-
if (isTable(data)) {
|
|
165
|
-
subs = drawTable(data);
|
|
166
|
-
} else {
|
|
167
|
-
subs =
|
|
168
|
-
"<div class='altRows'>" +
|
|
169
|
-
data
|
|
170
|
-
.map((val, idx) => _render(idx.toString(), val, options, level + 1, idx % 2))
|
|
171
|
-
.join("</div><div class='altRows'>") +
|
|
172
|
-
'</div>';
|
|
173
|
-
}
|
|
174
|
-
return `<div class="json-to-html-collapse clearfix ${altRow}">
|
|
175
|
-
${title}
|
|
176
|
-
<div class="${contentClass}">${subs}</div>
|
|
177
|
-
</div>`;
|
|
178
|
-
} else if (isLeafValue(data)) {
|
|
179
|
-
const title = makeLabelDiv(options, level, name);
|
|
180
|
-
if (isFunction(data)) {
|
|
181
|
-
return `${title}<span class='json-to-html-value'> -function() can't _render-</span>`;
|
|
182
|
-
} else if (!isPlainObject(data)) {
|
|
183
|
-
if (isFunction(data.toString)) {
|
|
184
|
-
return `${title}<span class='json-to-html-value'> ${data.toString()}</span>`;
|
|
185
|
-
} else {
|
|
186
|
-
return `${title}<span class='json-to-html-value'> -instance object, can't render-</span>`;
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
return `${title}<span class='json-to-html-value'> ${data}</span>`;
|
|
190
|
-
}
|
|
191
|
-
} else {
|
|
192
|
-
const title = makeLabelDiv(options, level, name, 'object');
|
|
193
|
-
let count = 0;
|
|
194
|
-
const subs =
|
|
195
|
-
'<div>' +
|
|
196
|
-
Object.entries(data)
|
|
197
|
-
.map(([key, val]) => _render(key, val, options, level + 1, count++ % 2))
|
|
198
|
-
.join('</div><div>') +
|
|
199
|
-
'</div>';
|
|
200
|
-
const inner = `<div class="json-to-html-expand clearfix ${altRow}">
|
|
201
|
-
${title}
|
|
202
|
-
<div class="${contentClass}">${subs}</div>
|
|
203
|
-
</div>`;
|
|
204
|
-
return `${level === 0 ? "<div id='json-to-html'>" : ''}
|
|
205
|
-
${inner}
|
|
206
|
-
${level === 0 ? '</div>' : ''}`;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function render(name, json, options) {
|
|
211
|
-
return `${_render(name, json, options, 0, 0)}`;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
215
|
-
const __dirname = path.dirname(__filename);
|
|
216
|
-
|
|
217
|
-
function numberFix(num, decimalPlaces) {
|
|
218
|
-
if (num > 10000 || num < 0.001) {
|
|
219
|
-
const [mantissa, exponent] = num.toExponential().split('e');
|
|
220
|
-
const formattedMantissa = Number(mantissa).toFixed(decimalPlaces);
|
|
221
|
-
return `${formattedMantissa}e${exponent}`;
|
|
222
|
-
} else {
|
|
223
|
-
return num.toFixed(decimalPlaces);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const ConsoleColor = {
|
|
228
|
-
END: '\x1b[0m',
|
|
229
|
-
BOLD: '\x1b[1m',
|
|
230
|
-
DIM: '\x1b[2m',
|
|
231
|
-
ITALIC: '\x1b[3m',
|
|
232
|
-
UNDERLINE: '\x1b[4m',
|
|
233
|
-
INVERSE: '\x1b[7m',
|
|
234
|
-
STRIKETHROUGH: '\x1b[9m',
|
|
235
|
-
NO_BOLD: '\x1b[22m',
|
|
236
|
-
NO_ITALIC: '\x1b[23m',
|
|
237
|
-
NO_UNDERLINE: '\x1b[24m',
|
|
238
|
-
NO_INVERSE: '\x1b[27m',
|
|
239
|
-
NO_STRIKETHROUGH: '\x1b[29m',
|
|
240
|
-
BLACK: '\x1b[30m',
|
|
241
|
-
RED: '\x1b[31m',
|
|
242
|
-
GREEN: '\x1b[32m',
|
|
243
|
-
YELLOW: '\x1b[33m',
|
|
244
|
-
BLUE: '\x1b[34m',
|
|
245
|
-
MAGENTA: '\x1b[35m',
|
|
246
|
-
GRAY: '\x1b[90m',
|
|
247
|
-
CYAN: '\x1b[36m',
|
|
248
|
-
WHITE: '\x1b[37m',
|
|
249
|
-
BG_BLACK: '\x1b[40m',
|
|
250
|
-
BG_RED: '\x1b[41m',
|
|
251
|
-
BG_GREEN: '\x1b[42m',
|
|
252
|
-
BG_YELLOW: '\x1b[43m',
|
|
253
|
-
BG_BLUE: '\x1b[44m',
|
|
254
|
-
BG_MAGENTA: '\x1b[45m',
|
|
255
|
-
BG_CYAN: '\x1b[46m',
|
|
256
|
-
BG_WHITE: '\x1b[47m'
|
|
257
|
-
};
|
|
258
|
-
const args = process.argv.slice(2);
|
|
259
|
-
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
|
260
|
-
const isOnlyOrdered = true;
|
|
261
|
-
const runOrder = [
|
|
262
|
-
'heap',
|
|
263
|
-
'avl-tree',
|
|
264
|
-
'red-black-tree',
|
|
265
|
-
'doubly-linked-list',
|
|
266
|
-
'directed-graph',
|
|
267
|
-
'queue',
|
|
268
|
-
'deque',
|
|
269
|
-
'hash-map',
|
|
270
|
-
'trie',
|
|
271
|
-
'stack'
|
|
272
|
-
// 'singly-linked-list',
|
|
273
|
-
// 'priority-queue',
|
|
274
|
-
// 'binary-tree-overall'
|
|
275
|
-
];
|
|
276
|
-
const getRelativePath = file => {
|
|
277
|
-
return path.relative(__dirname, file);
|
|
278
|
-
};
|
|
279
|
-
const coloredLabeled = (label, file) => {
|
|
280
|
-
const relativeFilePath = getRelativePath(file);
|
|
281
|
-
const directory = path.dirname(relativeFilePath);
|
|
282
|
-
const fileName = path.basename(relativeFilePath);
|
|
283
|
-
return `${BG_YELLOW} ${label} ${END} ${GRAY}${directory}/${END}${CYAN}${fileName}${END}`;
|
|
284
|
-
};
|
|
285
|
-
const parentDirectory = path.resolve(__dirname, '../..');
|
|
286
|
-
const reportDistPath = path.join(parentDirectory, 'benchmark');
|
|
287
|
-
const testDir = path.join(__dirname, 'data-structures');
|
|
288
|
-
let allFiles = fastGlob.sync(path.join(testDir, '**', '*.test.mjs'));
|
|
289
|
-
|
|
290
|
-
let testFiles;
|
|
291
|
-
let isIndividual = false;
|
|
292
|
-
if (args.length > 0) {
|
|
293
|
-
console.log(`arguments: ${args.join(' ')}`);
|
|
294
|
-
testFiles = allFiles.filter(file => args.every(word => file.includes(word)));
|
|
295
|
-
isIndividual = true;
|
|
296
|
-
console.log(
|
|
297
|
-
`${testFiles.map(file => coloredLabeled('Found', file)).join(`
|
|
298
|
-
`)}`
|
|
299
|
-
);
|
|
300
|
-
} else {
|
|
301
|
-
isIndividual = false;
|
|
302
|
-
testFiles = allFiles;
|
|
303
|
-
}
|
|
304
|
-
const report = {};
|
|
305
|
-
let completedCount = 0;
|
|
306
|
-
const performanceTests = [];
|
|
307
|
-
for (const file of testFiles) {
|
|
308
|
-
const testName = path.basename(file, '.test.mjs');
|
|
309
|
-
const testFunction = await import(file);
|
|
310
|
-
const { suite } = testFunction;
|
|
311
|
-
if (suite)
|
|
312
|
-
performanceTests.push({
|
|
313
|
-
testName,
|
|
314
|
-
suite,
|
|
315
|
-
file
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const composeReport = () => {
|
|
320
|
-
if (!fs.existsSync(reportDistPath))
|
|
321
|
-
fs.mkdirSync(reportDistPath, {
|
|
322
|
-
recursive: true
|
|
323
|
-
});
|
|
324
|
-
const filePath = path.join(reportDistPath, 'report.json');
|
|
325
|
-
const htmlFilePath = path.join(reportDistPath, 'report.html');
|
|
326
|
-
fs.writeFileSync(filePath, JSON.stringify(report, null, 2));
|
|
327
|
-
let html = `<!DOCTYPE html>
|
|
328
|
-
<html lang="en">
|
|
329
|
-
<head>
|
|
330
|
-
<meta charset="UTF-8">
|
|
331
|
-
<title>performance of data-structure-typed</title>
|
|
332
|
-
<style>
|
|
333
|
-
*{
|
|
334
|
-
box-sizing: border-box;
|
|
335
|
-
}
|
|
336
|
-
#json-to-html {
|
|
337
|
-
padding: 0 10px 20px;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.json-to-html-label {
|
|
341
|
-
font-size: 2rem;
|
|
342
|
-
margin: 2rem 0 0 3px;
|
|
343
|
-
}
|
|
344
|
-
.content table {
|
|
345
|
-
width: 100%;
|
|
346
|
-
table-layout: fixed;
|
|
347
|
-
border-collapse: collapse;
|
|
348
|
-
margin-top: 10px;
|
|
349
|
-
font-size: 16px;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.content table th,
|
|
353
|
-
.content table td {
|
|
354
|
-
padding: 8px 12px;
|
|
355
|
-
text-align: left;
|
|
356
|
-
border: 1px solid #ddd;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.content table th {
|
|
360
|
-
background-color: #f2f2f2;
|
|
361
|
-
font-weight: bold;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
.content table tr:nth-child(odd) {
|
|
365
|
-
background-color: #ffffff;
|
|
366
|
-
}
|
|
367
|
-
</style>
|
|
368
|
-
</head>
|
|
369
|
-
<body>
|
|
370
|
-
<div id="json-to-html">`;
|
|
371
|
-
let htmlTables = '';
|
|
372
|
-
for (const r in report) {
|
|
373
|
-
if (report.hasOwnProperty(r)) {
|
|
374
|
-
htmlTables += render(report[r].testName, report[r].benchmarks, {
|
|
375
|
-
plainHtml: true,
|
|
376
|
-
'<>': 'table',
|
|
377
|
-
html: [
|
|
378
|
-
{
|
|
379
|
-
'<>': 'tr',
|
|
380
|
-
html: [
|
|
381
|
-
{
|
|
382
|
-
'<>': 'td',
|
|
383
|
-
html: '${name}'
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
'<>': 'td',
|
|
387
|
-
html: '${periodMS}'
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
'<>': 'td',
|
|
391
|
-
html: '${mean}'
|
|
392
|
-
}
|
|
393
|
-
]
|
|
394
|
-
}
|
|
395
|
-
]
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
htmlTables += `
|
|
400
|
-
|
|
401
|
-
`;
|
|
402
|
-
html += htmlTables;
|
|
403
|
-
html += `</div>
|
|
404
|
-
</body>
|
|
405
|
-
</html>`;
|
|
406
|
-
if (!isIndividual)
|
|
407
|
-
replaceMarkdownContent(
|
|
408
|
-
'[//]: # (No deletion!!! Start of Replace Section)', // Start tag
|
|
409
|
-
'[//]: # (No deletion!!! End of Replace Section)', // end identifier
|
|
410
|
-
htmlTables // New content to be inserted
|
|
411
|
-
);
|
|
412
|
-
fs.writeFileSync(htmlFilePath, html);
|
|
413
|
-
console.log(`Performance ${BOLD}${GREEN}report${END} file generated in file://${BOLD}${GREEN}${htmlFilePath}${END}`);
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
function replaceMarkdownContent(startMarker, endMarker, newText) {
|
|
417
|
-
const filePath = path.join(parentDirectory, 'README.md'); // Path to README.md file
|
|
418
|
-
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
419
|
-
if (err) {
|
|
420
|
-
console.error(`Unable to read ${filePath}:`, err);
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
// Find the start and end markers in the content
|
|
424
|
-
const startIndex = data.indexOf(startMarker);
|
|
425
|
-
const endIndex = data.indexOf(endMarker, startIndex + 1);
|
|
426
|
-
if (startIndex === -1 || endIndex === -1) {
|
|
427
|
-
console.error('Unable to find start or end marker');
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
// Replace the old content with the new text
|
|
431
|
-
const updatedMarkdown = data.slice(0, startIndex + startMarker.length) + '\n' + newText + data.slice(endIndex);
|
|
432
|
-
// Try writing the modified content back to the file
|
|
433
|
-
fs.writeFile(filePath, updatedMarkdown, 'utf8', err => {
|
|
434
|
-
if (err) {
|
|
435
|
-
console.error(`Unable to write to ${filePath}:`, err);
|
|
436
|
-
} else {
|
|
437
|
-
console.log(`The content has been successfully replaced in file://${BOLD}${GREEN}${filePath}${END}`);
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const sortedPerformanceTests = (
|
|
444
|
-
isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests]
|
|
445
|
-
).sort((a, b) => {
|
|
446
|
-
const indexA = runOrder.indexOf(a.testName);
|
|
447
|
-
const indexB = runOrder.indexOf(b.testName);
|
|
448
|
-
// If both a and b are in the runOrder, sort them according to their indices in the runOrder.
|
|
449
|
-
if (indexA !== -1 && indexB !== -1) {
|
|
450
|
-
return indexA - indexB;
|
|
451
|
-
}
|
|
452
|
-
// If there is only 'a' in the runOrder, then place 'b' in front.
|
|
453
|
-
if (indexA !== -1) {
|
|
454
|
-
return 1;
|
|
455
|
-
}
|
|
456
|
-
// If only b is in the runOrder, then a should be placed before it.
|
|
457
|
-
if (indexB !== -1) {
|
|
458
|
-
return -1;
|
|
459
|
-
}
|
|
460
|
-
// If neither a nor b are in runOrder, keep their original runOrder
|
|
461
|
-
return 0;
|
|
462
|
-
});
|
|
463
|
-
console.log(
|
|
464
|
-
`${GREEN} Matched Suites (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`
|
|
465
|
-
);
|
|
466
|
-
console.log(
|
|
467
|
-
`${GREEN} Running Suites (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
|
468
|
-
);
|
|
469
|
-
sortedPerformanceTests.forEach(item => {
|
|
470
|
-
const { suite, testName, file } = item;
|
|
471
|
-
console.log(coloredLabeled('Running', file));
|
|
472
|
-
if (suite) {
|
|
473
|
-
let runTime = 0;
|
|
474
|
-
suite
|
|
475
|
-
.on('complete', function () {
|
|
476
|
-
completedCount++;
|
|
477
|
-
report[testName] = {};
|
|
478
|
-
report[testName].benchmarks = this.map(benchmark => {
|
|
479
|
-
runTime += benchmark.times.elapsed;
|
|
480
|
-
return {
|
|
481
|
-
'test name': benchmark.name,
|
|
482
|
-
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
|
483
|
-
// 'executions per sec': numberFix(benchmark.hz, 2),
|
|
484
|
-
// 'executed times': numberFix(benchmark.count, 0),
|
|
485
|
-
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
|
486
|
-
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
|
487
|
-
};
|
|
488
|
-
});
|
|
489
|
-
report[testName].testName = testName;
|
|
490
|
-
const isDone = completedCount === sortedPerformanceTests.length;
|
|
491
|
-
runTime = Number(runTime.toFixed(2));
|
|
492
|
-
const isTimeWarn = runTime > 120;
|
|
493
|
-
console.log(
|
|
494
|
-
// `Files: ${GREEN}${testFileCount}${END} `,
|
|
495
|
-
// `Suites: ${GREEN}${performanceTests.length}${END} `,
|
|
496
|
-
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`,
|
|
497
|
-
`Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
498
|
-
);
|
|
499
|
-
if (isDone) {
|
|
500
|
-
composeReport();
|
|
501
|
-
}
|
|
502
|
-
})
|
|
503
|
-
.run({ async: false });
|
|
504
|
-
}
|
|
505
|
-
});
|