Don't sort the list on every render (#58)

This commit is contained in:
Maciej Hirsz
2018-09-27 17:01:51 +02:00
committed by GitHub
parent 87facf1552
commit 62d2847bb0
13 changed files with 416 additions and 67 deletions
+2 -1
View File
@@ -16,6 +16,7 @@
"build:backend": "scripts/build-backend.sh",
"check:backend": "tsc -p packages/backend --noEmit",
"build:common": "tsc -p packages/common",
"check:common": "tsc -p packages/common --noEmit"
"check:common": "tsc -p packages/common --noEmit",
"test:common": "scripts/test-common.sh"
}
}
+5 -1
View File
@@ -10,6 +10,10 @@
"node": ">=9.5"
},
"scripts": {
"test": "echo \"Tests only available from root wrapper\""
"test": "node ./test | tap-spec"
},
"devDependencies": {
"tap-spec": "^5.0.0",
"tape": "^4.9.1"
}
}
+78
View File
@@ -112,3 +112,81 @@ export class NumStats<T extends number> {
return this.index < this.history ? this.stack.slice(0, this.index) : this.stack;
}
}
/**
* Insert an item into a sorted array using binary search.
*
* @type {T} item type
* @param {T} item to be inserted
* @param {Array<T>} array to be modified
* @param {(a, b) => number} compare function
*
* @return {number} insertion index
*/
export function sortedInsert<T>(item: T, into: Array<T>, compare: (a: T, b: T) => number): number {
if (into.length === 0) {
into.push(item);
return 0;
}
let min = 0;
let max = into.length - 1;
while (min !== max) {
const guess = (min + max) / 2 | 0;
if (compare(item, into[guess]) < 0) {
max = Math.max(min, guess - 1);
} else {
min = Math.min(max, guess + 1);
}
}
let insert = compare(item, into[min]) <= 0 ? min : min + 1;
into.splice(insert, 0, item);
return insert;
}
/**
* Find an index of an element within a sorted array. This should be substantially
* faster than `indexOf` for large arrays.
*
* @type {T} item type
* @param {T} item to find
* @param {Array<T>} array to look through
* @param {(a, b) => number} compare function
*
* @return {number} index of the element, `-1` if not found
*/
export function sortedIndexOf<T>(item:T, within: Array<T>, compare: (a: T, b: T) => number): number {
if (within.length === 0) {
return -1;
}
let min = 0;
let max = within.length - 1;
while (min !== max) {
const guess = (min + max) / 2 | 0;
const other = within[guess];
if (item === other) {
return guess;
}
if (compare(item, other) < 0) {
max = Math.max(min, guess - 1);
} else {
min = Math.min(max, guess + 1);
}
}
if (item === within[min]) {
return min;
}
return -1;
}
+94
View File
@@ -0,0 +1,94 @@
const test = require('tape');
const common = require('../build/');
test('sortedInsert', (assert) => {
const { sortedInsert } = common;
const cmp = (a, b) => a - b;
let mod = sortedInsert(3, [1,2,4,5], cmp);
function assertInsert(item, into, equals) {
sortedInsert(item, into, cmp);
assert.same(into, equals, `Inserts ${item}`);
}
assertInsert(1, [2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(2, [1,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(3, [1,2,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(4, [1,2,3,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(5, [1,2,3,4,6,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(6, [1,2,3,4,5,7,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(7, [1,2,3,4,5,6,8,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(8, [1,2,3,4,5,6,7,9], [1,2,3,4,5,6,7,8,9]);
assertInsert(9, [1,2,3,4,5,6,7,8], [1,2,3,4,5,6,7,8,9]);
assert.end();
});
test('sortedInsert fuzz', (assert) => {
const { sortedInsert } = common;
const cmp = (a, b) => a - b;
const scramble = () => Math.random() - 0.5;
const sorted = [1,2,3,4,5,6,7,8,9];
for (let i = 0; i < 50; i++) {
const scrambled = sorted.sort(scramble);
const resorted = [];
for (const item of scrambled) {
sortedInsert(item, resorted, cmp);
}
assert.same(resorted, [1,2,3,4,5,6,7,8,9], `resort ${scrambled}`);
}
assert.end();
});
test('sortedInsert indexes', (assert) => {
const { sortedInsert } = common;
const cmp = (a, b) => a - b;
const into = [];
assert.equals(sortedInsert(5, into, cmp), 0, 'Insert 5');
assert.same(into, [5], 'Elements check out');
assert.equals(sortedInsert(1, into, cmp), 0, 'Insert 1');
assert.same(into, [1,5], 'Elements check out');
assert.equals(sortedInsert(9, into, cmp), 2, 'Insert 9');
assert.same(into, [1,5,9], 'Elements check out');
assert.equals(sortedInsert(3, into, cmp), 1, 'Insert 3');
assert.same(into, [1,3,5,9], 'Elements check out');
assert.equals(sortedInsert(7, into, cmp), 3, 'Insert 7');
assert.same(into, [1,3,5,7,9], 'Elements check out');
assert.equals(sortedInsert(4, into, cmp), 2, 'Insert 4');
assert.same(into, [1,3,4,5,7,9], 'Elements check out');
assert.equals(sortedInsert(6, into, cmp), 4, 'Insert 6');
assert.same(into, [1,3,4,5,6,7,9], 'Elements check out');
assert.equals(sortedInsert(2, into, cmp), 1, 'Insert 2');
assert.same(into, [1,2,3,4,5,6,7,9], 'Elements check out');
assert.equals(sortedInsert(8, into, cmp), 7, 'Insert 8');
assert.same(into, [1,2,3,4,5,6,7,8,9], 'Elements check out');
assert.end();
});
test('sortedIndexOf', (assert) => {
const { sortedIndexOf } = common;
const cmp = (a, b) => a - b;
assert.equals(sortedIndexOf(1, [1,2,3,4,5,6,7,8,9], cmp), 0, 'Found 1');
assert.equals(sortedIndexOf(2, [1,2,3,4,5,6,7,8,9], cmp), 1, 'Found 2');
assert.equals(sortedIndexOf(3, [1,2,3,4,5,6,7,8,9], cmp), 2, 'Found 3');
assert.equals(sortedIndexOf(4, [1,2,3,4,5,6,7,8,9], cmp), 3, 'Found 4');
assert.equals(sortedIndexOf(5, [1,2,3,4,5,6,7,8,9], cmp), 4, 'Found 5');
assert.equals(sortedIndexOf(6, [1,2,3,4,5,6,7,8,9], cmp), 5, 'Found 6');
assert.equals(sortedIndexOf(7, [1,2,3,4,5,6,7,8,9], cmp), 6, 'Found 7');
assert.equals(sortedIndexOf(8, [1,2,3,4,5,6,7,8,9], cmp), 7, 'Found 8');
assert.equals(sortedIndexOf(9, [1,2,3,4,5,6,7,8,9], cmp), 8, 'Found 9');
assert.equals(sortedIndexOf(0, [1,2,3,4,5,6,7,8,9], cmp), -1, 'No 0');
assert.equals(sortedIndexOf(10, [1,2,3,4,5,6,7,8,9], cmp), -1, 'No 10');
assert.equals(sortedIndexOf(5.5, [1,2,3,4,5,6,7,8,9], cmp), -1, 'No 5.5');
assert.end();
});
+4 -3
View File
@@ -3,7 +3,7 @@ import { Types } from '@dotstats/common';
import { Chains, Chain, Ago, OfflineIndicator } from './components';
import { Connection } from './Connection';
import { PersistentObject, PersistentSet } from './persist';
import { State } from './state';
import { State, compareNodes } from './state';
import './App.css';
@@ -36,13 +36,13 @@ export default class App extends React.Component<{}, State> {
);
this.pins = new PersistentSet<Types.NodeName>('pinned_names', (pins) => {
const { nodes } = this.state;
const { nodes, sortedNodes } = this.state;
for (const node of nodes.values()) {
node.pinned = pins.has(node.nodeDetails[0]);
}
this.setState({ nodes, pins });
this.setState({ nodes, pins, sortedNodes: sortedNodes.sort(compareNodes) });
});
this.state = {
@@ -54,6 +54,7 @@ export default class App extends React.Component<{}, State> {
subscribed: null,
chains: new Map(),
nodes: new Map(),
sortedNodes: [],
settings: this.settings.raw(),
pins: this.pins.get(),
};
+30 -9
View File
@@ -1,5 +1,6 @@
import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from '@dotstats/common';
import { State, Update } from './state';
import { sortedInsert, sortedIndexOf } from '@dotstats/common';
import { State, Update, compareNodes } from './state';
import { PersistentSet } from './persist';
const { Actions } = FeedMessage;
@@ -140,9 +141,8 @@ export class Connection {
private handleMessages = (event: MessageEvent) => {
const data = event.data as FeedMessage.Data;
const nodes = this.state.nodes;
const chains = this.state.chains;
const changes = { nodes, chains };
const { nodes, chains } = this.state;
let { sortedNodes } = this.state;
messages: for (const message of FeedMessage.deserialize(data)) {
switch (message.action) {
@@ -176,14 +176,32 @@ export class Connection {
const node = { pinned, id, nodeDetails, nodeStats, blockDetails, location };
nodes.set(id, node);
sortedInsert(node, sortedNodes, compareNodes);
if (nodes.size !== sortedNodes.length) {
console.error('Node count in sorted array is wrong!');
}
break;
}
case Actions.RemovedNode: {
nodes.delete(message.payload);
const id = message.payload;
const node = nodes.get(id);
break;
if (node) {
nodes.delete(id);
const index = sortedIndexOf(node, sortedNodes, compareNodes);
sortedNodes.splice(index, 1);
if (nodes.size !== sortedNodes.length) {
console.error('Node count in sorted array is wrong!');
}
break;
}
continue messages;
}
case Actions.LocatedNode: {
@@ -191,7 +209,7 @@ export class Connection {
const node = nodes.get(id);
if (!node) {
return;
continue messages;
}
node.location = [latitude, longitude, city];
@@ -204,10 +222,11 @@ export class Connection {
const node = nodes.get(id);
if (!node) {
return;
continue messages;
}
node.blockDetails = blockDetails;
sortedNodes = sortedNodes.sort(compareNodes);
break;
}
@@ -245,6 +264,7 @@ export class Connection {
if (this.state.subscribed === message.payload) {
nodes.clear();
sortedNodes = [];
this.state = this.update({ subscribed: null, nodes, chains });
@@ -263,6 +283,7 @@ export class Connection {
case Actions.UnsubscribedFrom: {
if (this.state.subscribed === message.payload) {
nodes.clear();
sortedNodes = [];
this.state = this.update({ subscribed: null, nodes });
}
@@ -281,7 +302,7 @@ export class Connection {
}
}
this.state = this.update(changes);
this.state = this.update({ nodes, chains, sortedNodes });
this.autoSubscribe();
}
@@ -8,34 +8,14 @@
position: relative;
}
.Chain-tabs {
position: absolute;
right: 9px;
bottom: 0;
height: 36px;
right: 5px;
bottom: 10px;
width: 200px;
text-align: right;
}
.Chain-tab-unit {
display: inline-block;
}
.Chain-tab-unit .Icon {
margin-right: 1px;
font-size: 24px;
padding: 6px;
background: #ccc;
color: #555;
cursor: pointer;
}
.Chain-tab-unit-on .Icon {
background: #222;
color: #fff;
}
.Chain-content-container {
position: absolute;
left: 0;
@@ -43,23 +43,6 @@ export namespace Chain {
}
}
function sortNodes(a: AppState.Node, b: AppState.Node): number {
if (a.pinned === b.pinned) {
if (a.blockDetails[0] === b.blockDetails[0]) {
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
// Ascending sort by propagation time
return aPropagation - bPropagation;
}
} else {
return Number(b.pinned) - Number(a.pinned);
}
// Descending sort by block number
return b.blockDetails[0] - a.blockDetails[0];
}
export class Chain extends React.Component<Chain.Props, Chain.State> {
constructor(props: Chain.Props) {
super(props);
@@ -157,9 +140,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
<Node.Row.Header settings={settings} />
<tbody>
{
nodes
.sort(sortNodes)
.map((node) => <Node.Row key={node.id} node={node} settings={settings} pins={pins} />)
nodes.map((node) => <Node.Row key={node.id} node={node} settings={settings} pins={pins} />)
}
</tbody>
</table>
@@ -220,7 +201,7 @@ export class Chain extends React.Component<Chain.Props, Chain.State> {
}
private nodes(): AppState.Node[] {
return Array.from(this.props.appState.nodes.values());
return this.props.appState.sortedNodes;
}
private pixelPosition(lat: Types.Latitude, lon: Types.Longitude): Node.Location.Position {
@@ -0,0 +1,24 @@
.Chain-Tab {
display: inline-block;
}
.Chain-Tab .Icon {
margin-right: 5px;
font-size: 24px;
padding: 6px;
color: #555;
cursor: pointer;
padding: 10px;
border-radius: 40px;
transition: background-color 0.15s linear;
}
.Chain-Tab:hover .Icon {
background: #ccc;
}
.Chain-Tab-on .Icon, .Chain-Tab-on:hover .Icon {
background: #d64ca8;
color: #fff;
}
@@ -2,6 +2,8 @@ import * as React from 'react';
import { Chain } from './';
import { Icon } from '../';
import './Tab.css';
export namespace Tab {
export interface Props {
label: string;
@@ -17,7 +19,7 @@ export class Tab extends React.Component<Tab.Props, {}> {
public render() {
const { label, icon, display, current } = this.props;
const highlight = display === current;
const className = highlight ? 'Chain-tab-unit-on Chain-tab-unit' : 'Chain-tab-unit';
const className = highlight ? 'Chain-Tab-on Chain-Tab' : 'Chain-Tab';
return (
<div className={className} onClick={this.onClick}>
+18
View File
@@ -35,8 +35,26 @@ export interface State {
subscribed: Maybe<Types.ChainLabel>;
chains: Map<Types.ChainLabel, Types.NodeCount>;
nodes: Map<Types.NodeId, State.Node>;
sortedNodes: State.Node[];
settings: Readonly<State.Settings>;
pins: Readonly<Set<Types.NodeName>>;
}
export type Update = <K extends keyof State>(changes: Pick<State, K> | null) => Readonly<State>;
export function compareNodes(a: State.Node, b: State.Node): number {
if (a.pinned === b.pinned) {
if (a.blockDetails[0] === b.blockDetails[0]) {
const aPropagation = a.blockDetails[4] == null ? Infinity : a.blockDetails[4] as number;
const bPropagation = b.blockDetails[4] == null ? Infinity : b.blockDetails[4] as number;
// Ascending sort by propagation time
return aPropagation - bPropagation;
}
} else {
return +b.pinned - +a.pinned;
}
// Descending sort by block number
return b.blockDetails[0] - a.blockDetails[0];
}
+1
View File
@@ -0,0 +1 @@
tsc -p packages/common && node packages/common/test | tap-spec
+153 -9
View File
@@ -1289,6 +1289,10 @@ buffer-indexof@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
buffer-shims@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
@@ -1421,7 +1425,7 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
chalk@1.1.3, chalk@^1.1.3:
chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -2007,7 +2011,7 @@ decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
deep-equal@^1.0.1:
deep-equal@^1.0.1, deep-equal@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@@ -2055,7 +2059,7 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
defined@^1.0.0:
defined@^1.0.0, defined@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@@ -2321,7 +2325,7 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.5.1, es-abstract@^1.7.0:
es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
dependencies:
@@ -2691,6 +2695,13 @@ fbjs@^0.8.16, fbjs@^0.8.9:
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
figures@^1.4.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
dependencies:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -2791,6 +2802,12 @@ follow-redirects@^1.0.0:
dependencies:
debug "^3.1.0"
for-each@~0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
dependencies:
is-callable "^1.1.3"
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -2903,7 +2920,7 @@ fsevents@^1.0.0, fsevents@^1.1.3, fsevents@^1.2.2, fsevents@^1.2.3:
nan "^2.9.2"
node-pre-gyp "^0.10.0"
function-bind@^1.1.1:
function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -2979,6 +2996,17 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@~7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
@@ -3132,7 +3160,7 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
has@^1.0.1:
has@^1.0.1, has@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
dependencies:
@@ -3549,7 +3577,7 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
is-finite@^1.0.0:
is-finite@^1.0.0, is-finite@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
dependencies:
@@ -4595,7 +4623,7 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -4891,6 +4919,10 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
object-inspect@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
object-keys@^1.0.8:
version "1.0.12"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
@@ -5103,6 +5135,10 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
parse-ms@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d"
parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@@ -5215,6 +5251,10 @@ pkg-dir@^2.0.0:
dependencies:
find-up "^2.1.0"
plur@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156"
pn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
@@ -5553,10 +5593,22 @@ pretty-format@^22.4.0, pretty-format@^22.4.3:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
pretty-ms@^2.1.0:
version "2.1.0"
resolved "http://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz#4257c256df3fb0b451d6affaab021884126981dc"
dependencies:
is-finite "^1.0.1"
parse-ms "^1.0.0"
plur "^1.0.0"
private@^0.1.6, private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -5724,6 +5776,10 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
re-emitter@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.3.tgz#fa9e319ffdeeeb35b27296ef0f3d374dac2f52a7"
react-dev-utils@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.1.tgz#1f396e161fe44b595db1b186a40067289bf06613"
@@ -5889,6 +5945,18 @@ readable-stream@1.0:
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@2.2.9:
version "2.2.9"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
dependencies:
buffer-shims "~1.0.0"
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "~1.0.0"
process-nextick-args "~1.0.6"
string_decoder "~1.0.0"
util-deprecate "~1.0.1"
readdirp@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
@@ -6123,6 +6191,12 @@ resolve@^1.1.7, resolve@^1.3.2:
dependencies:
path-parse "^1.0.5"
resolve@~1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3"
dependencies:
path-parse "^1.0.5"
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -6130,6 +6204,12 @@ restore-cursor@^2.0.0:
onetime "^2.0.0"
signal-exit "^3.0.2"
resumer@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759"
dependencies:
through "~2.3.4"
ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
@@ -6527,6 +6607,12 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
split@1.0.0:
version "1.0.0"
resolved "http://registry.npmjs.org/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae"
dependencies:
through "2"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -6636,6 +6722,14 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string.prototype.trim@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
dependencies:
define-properties "^1.1.2"
es-abstract "^1.5.0"
function-bind "^1.0.2"
string_decoder@^1.0.0, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@@ -6646,6 +6740,12 @@ string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
string_decoder@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
safe-buffer "~5.1.0"
strip-ansi@3.0.1, strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -6763,10 +6863,50 @@ symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
tap-out@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tap-out/-/tap-out-2.1.0.tgz#c093079a915036de8b835bfa3297f14458b15358"
dependencies:
re-emitter "1.1.3"
readable-stream "2.2.9"
split "1.0.0"
trim "0.0.1"
tap-spec@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/tap-spec/-/tap-spec-5.0.0.tgz#7329e4e66e8aa68da2a164215abbb903a7c5d352"
dependencies:
chalk "^1.0.0"
duplexer "^0.1.1"
figures "^1.4.0"
lodash "^4.17.10"
pretty-ms "^2.1.0"
repeat-string "^1.5.2"
tap-out "^2.1.0"
through2 "^2.0.0"
tapable@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
tape@^4.9.1:
version "4.9.1"
resolved "https://registry.yarnpkg.com/tape/-/tape-4.9.1.tgz#1173d7337e040c76fbf42ec86fcabedc9b3805c9"
dependencies:
deep-equal "~1.0.1"
defined "~1.0.0"
for-each "~0.3.3"
function-bind "~1.1.1"
glob "~7.1.2"
has "~1.0.3"
inherits "~2.0.3"
minimist "~1.2.0"
object-inspect "~1.6.0"
resolve "~1.7.1"
resumer "~0.0.0"
string.prototype.trim "~1.1.2"
through "~2.3.8"
tar@^4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd"
@@ -6810,7 +6950,7 @@ through2@^2.0.0:
readable-stream "^2.1.5"
xtend "~4.0.1"
through@^2.3.6:
through@2, through@^2.3.6, through@~2.3.4, through@~2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -6903,6 +7043,10 @@ trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
trim@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
ts-jest@22.0.1:
version "22.0.1"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-22.0.1.tgz#48942936a466c2e76e259b02e2f1356f1839afc3"