Browse Source

Clean up code.

Tomi Cvetic 7 năm trước cách đây
mục cha
commit
f81768bf96

+ 1 - 0
package.json

@@ -5,6 +5,7 @@
   "dependencies": {
     "blob": "^0.0.4",
     "file-saver": "^1.3.3",
+    "moment": "^2.18.1",
     "msexcel-builder": "^0.0.2",
     "ods": "^1.1.7",
     "react": "^15.5.4",

+ 42 - 44
src/App.js

@@ -1,10 +1,10 @@
 import React from 'react'
 import XLSX from 'xlsx'
-import PlayerList from './player/player-list'
-import MatchList from './match/match-list'
+import player from './player'
+import match from './match'
 import Stats from './stats/stats'
 import { SheetFromArray, Workbook, saveAs } from './excel/excel'
- 
+
 class App extends React.Component {
   constructor () {
     super()
@@ -49,12 +49,12 @@ class App extends React.Component {
     const reader = new FileReader()
     reader.onload = (e) => {
       const data = e.target.result
-      const workbook = XLSX.read(data, {type: 'binary'})
+      const workbook = XLSX.read(data, {type: 'binary', cellDates: true})
       console.log(workbook)
       if (workbook.SheetNames.length !== 1) {
         throw Error(`Expected only one worksheet in the file, but found ${workbook.SheetNames.length}.`)
       }
-      const worksheet = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[workbook.SheetNames[0]], { header: 1 })
+      const worksheet = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { header: 1 })
       callback(worksheet)
     }
     reader.readAsBinaryString(file)
@@ -69,7 +69,7 @@ class App extends React.Component {
     if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
       throw Error('Wrong file structure.')
     }
-    const players = worksheet.slice(4, worksheet.length)
+    const players = worksheet.slice(4, worksheet.length).map((playerData) => new player.Player(playerData))
     this.setState({ players })
     this.generateStats()
     console.log(this.state)
@@ -84,7 +84,7 @@ class App extends React.Component {
     if (worksheet[2].length !== 8) {
       throw Error('Wrong file structure.')
     }
-    const matches = worksheet.slice(2, worksheet.length)
+    const matches = worksheet.slice(2, worksheet.length).map((matchData) => new match.Match(matchData))
     this.setState({ matches })
     this.generateStats()
     console.log(this.state)
@@ -103,22 +103,22 @@ class App extends React.Component {
     stats.players = this.state.players
 
     this.state.players.forEach((player, key) => {
-      if (!stats.playerCategories.includes(player[0])) {
-        stats.playerCategories.push(player[0])
+      if (!stats.playerCategories.includes(player.Konkurrenz)) {
+        stats.playerCategories.push(player.Konkurrenz)
       }
     })
 
     stats.matches = this.state.matches
 
     this.state.matches.forEach((match, key) => {
-      if (!!match[1] & !stats.matchDates.includes(match[1])) {
-        stats.matchDates.push(match[1])
+      if (!!match.Datum & !stats.matchDates.includes(match.DatumString)) {
+        stats.matchDates.push(match.DatumString)
       }
-      if (!!match[3] & !stats.matchCategories.includes(match[3])) {
-        stats.matchCategories.push(match[3])
+      if (!!match.Konkurrenz & !stats.matchCategories.includes(match.Konkurrenz)) {
+        stats.matchCategories.push(match.Konkurrenz)
       }
-      if (!!match[0] & !stats.matchPlaces.includes(match[0])) {
-        stats.matchPlaces.push(match[0])
+      if (!!match.Ort & !stats.matchPlaces.includes(match.Ort)) {
+        stats.matchPlaces.push(match.Ort)
       }
     })
 
@@ -151,9 +151,9 @@ class App extends React.Component {
         ['Ort', 'Zeit', 'Kategorie', 'Spieler 1', '', 'Spieler 2', '', '1. Satz', '2. Satz', '3. Satz', 'WO Grund']
       ]
       let matchListPerPlace = this.state.matches.filter((match) => {
-        return (match[1] === filter | filter === 'Alle') & (match[0] === place | place === 'Alle')
-      }).map((match) => 
-        [match[0], match[2], match[3], match[4], match[5], match[6], match[7]]
+        return (match.DatumString === filter | filter === 'Alle') & (match.Ort === place | place === 'Alle')
+      }).map((match) =>
+        [match.Ort, match.ZeitString, match.Konkurrenz, match.Spieler1, match.Spieler1Klassierung, match.Spieler2, match.Spieler2Klassierung]
       )
       console.log(matchListPerPlace)
       worksheets[place] = SheetFromArray(header.concat(matchListPerPlace))
@@ -177,33 +177,28 @@ class App extends React.Component {
 
     const payerList = {}
     this.state.matches.forEach((match) => {
-      if (!!match[4] & !payerList.hasOwnProperty(`${match[3]} ${match[4]}`)) {
-        let foundPlayer = this.state.players.find((player) => {
-          if (!!player[24]) {
-            return (`${player[5]} ${player[6]} / ${player[24]} ${player[25]}` === match[4]) & (match[3] === player[0].replace(/\s+/g, ' '))
-          } else {
-            return (`${player[5]} ${player[6]}` === match[4]) & (match[3] === player[0].replace(/\s+/g, ' '))
-          }
-        })
+      const id1 = `${match.Konkurrenz} ${match.Spieler1}`
+      if (!!match.Spieler1 & !payerList.hasOwnProperty(id1)) {
+        let foundPlayer = this.state.players.find((player) =>
+          (player.name() === match.Spieler1) & (player.Konkurrenz === match.Konkurrenz)
+        )
         if (!foundPlayer) {
           console.log(foundPlayer, match)
-          throw Error('Player 4 not found. This is an error!')
+          throw Error('Player 1 not found. This is an error!')
         }
-        payerList[`${match[3]} ${match[4]}`] = [match[0], match[1], match[2], foundPlayer[31]]
+        payerList[id1] = [match.Ort, match.DatumString, match.ZeitString, foundPlayer.bezahlt]
       }
-      if (!!match[6] & !payerList.hasOwnProperty(`${match[3]} ${match[6]}`)) {
-        let foundPlayer = this.state.players.find((player) => {
-          if (!!player[24]) {
-            return (`${player[5]} ${player[6]} / ${player[24]} ${player[25]}` === match[6]) & (match[3] === player[0].replace(/\s+/g, ' '))
-          } else {
-            return (`${player[5]} ${player[6]}` === match[6]) & (match[3] === player[0].replace(/\s+/g, ' '))
-          }
-        })
+
+      const id2 = `${match.Konkurrenz} ${match.Spieler2}`
+      if (!!match.Spieler2 & !payerList.hasOwnProperty(id2)) {
+        let foundPlayer = this.state.players.find((player) =>
+          (player.name() === match.Spieler2) & (player.Konkurrenz === match.Konkurrenz)
+        )
         if (!foundPlayer) {
           console.log(foundPlayer, match)
-          throw Error('Player 6 not found. This is an error!')
+          throw Error('Player 2 not found. This is an error!')
         }
-        payerList[`${match[3]} ${match[6]}`] = [match[0], match[1], match[2], foundPlayer[31]]
+        payerList[id2] = [match.Ort, match.DatumString, match.ZeitString, foundPlayer.bezahlt]
       }
     })
     console.log(payerList)
@@ -216,15 +211,15 @@ class App extends React.Component {
         ['bereits bez.', 'Kategorie', 'Name', 'Betrag bez.', 'Quittung abgeben']
       ]
 
-      let payListPerPlace = [] 
+      let payListPerPlace = []
       this.state.matches.filter((match) => {
-        return (match[1] === filter | filter === 'Alle') & (match[0] === place | place === 'Alle')
+        return (match.DatumString === filter | filter === 'Alle') & (match.Ort === place | place === 'Alle')
       }).forEach((match) => {
-        if (!!match[4]) {
-          payListPerPlace.push(match)
+        if (match.Spieler1) {
+          payListPerPlace.push([match.Ort])
         }
-        if (!!match[6]) {
-          payListPerPlace.push(match)
+        if (match.Spieler2) {
+          payListPerPlace.push([match.Ort])
         }
       })
       console.log(payListPerPlace)
@@ -238,6 +233,9 @@ class App extends React.Component {
   }
 
   render () {
+    const PlayerList = player.components.PlayerList
+    const MatchList = match.components.MatchList
+
     return (
       <div className='App'>
         <form>

+ 38 - 39
src/excel/excel.js

@@ -2,54 +2,53 @@ import XLSX from 'xlsx'
 import FileSaver from 'file-saver'
 import Blob from 'blob'
 
-function datenum(v, date1904) {
-  if(date1904) v+=1462;
-  var epoch = Date.parse(v);
-  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
+function datenum (v, date1904) {
+  if (date1904) v += 1462
+  var epoch = Date.parse(v)
+  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
 }
 
-export function SheetFromArray(data, opts) {
-  var ws = {};
-  var range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }};
-  for(var R = 0; R != data.length; ++R) {
-    for(var C = 0; C != data[R].length; ++C) {
-      if(range.s.r > R) range.s.r = R;
-      if(range.s.c > C) range.s.c = C;
-      if(range.e.r < R) range.e.r = R;
-      if(range.e.c < C) range.e.c = C;
-      var cell = {v: data[R][C] };
-      if(cell.v == null) continue;
-      var cell_ref = XLSX.utils.encode_cell({c:C,r:R});
-      
-      if(typeof cell.v === 'number') cell.t = 'n';
-      else if(typeof cell.v === 'boolean') cell.t = 'b';
-      else if(cell.v instanceof Date) {
-        cell.t = 'n'; cell.z = XLSX.SSF._table[14];
-        cell.v = datenum(cell.v);
-      }
-      else cell.t = 's';
-      
-      ws[cell_ref] = cell;
+export function SheetFromArray (data, opts) {
+  var ws = {}
+  var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0 }}
+  for (var R = 0; R !== data.length; ++R) {
+    for (var C = 0; C !== data[R].length; ++C) {
+      if (range.s.r > R) range.s.r = R
+      if (range.s.c > C) range.s.c = C
+      if (range.e.r < R) range.e.r = R
+      if (range.e.c < C) range.e.c = C
+      var cell = {v: data[R][C] }
+      if (cell.v == null) continue
+      var cell_ref = XLSX.utils.encode_cell({c: C, r: R})
+
+      if (typeof cell.v === 'number') cell.t = 'n'
+      else if (typeof cell.v === 'boolean') cell.t = 'b'
+      else if (cell.v instanceof Date) {
+        cell.t = 'n'; cell.z = XLSX.SSF._table[14]
+        cell.v = datenum(cell.v)
+      } else cell.t = 's'
+
+      ws[cell_ref] = cell
     }
   }
-  if(range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
-  return ws;
+  if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
+  return ws
 }
- 
-export function Workbook() {
-  if(!(this instanceof Workbook)) return new Workbook();
-  this.SheetNames = [];
-  this.Sheets = {};
+
+export function Workbook () {
+  if (!(this instanceof Workbook)) return new Workbook()
+  this.SheetNames = []
+  this.Sheets = {}
 }
 
-function s2ab(s) {
-  var buf = new ArrayBuffer(s.length);
-  var view = new Uint8Array(buf);
-  for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
-  return buf;
+function s2ab (s) {
+  var buf = new ArrayBuffer(s.length)
+  var view = new Uint8Array(buf)
+  for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF
+  return buf
 }
 
-export function saveAs(workbook, filename) {
+export function saveAs (workbook, filename) {
   const wopts = {bookType: 'xlsx', bookSST: false, type: 'binary'}
   const wbout = XLSX.write(workbook, wopts)
   FileSaver.saveAs(new Blob([s2ab(wbout)], {type: ''}), filename)

+ 4 - 0
src/match/components/index.js

@@ -0,0 +1,4 @@
+import MatchDisp from './match-disp'
+import MatchList from './match-list'
+
+export default { MatchDisp, MatchList }

+ 19 - 0
src/match/components/match-disp.js

@@ -0,0 +1,19 @@
+import React from 'react'
+
+class MatchDisp extends React.Component {
+  render () {
+    const match = this.props.match
+    return (
+      <li>
+        {match.Ort || <strong>Kein Platz zugeteilt</strong>},
+        {match.DatumString || <strong>Kein Datum zugeteilt</strong>},
+        {match.ZeitString || <strong>Keine Zeit zugeteilt</strong>},
+        {match.Konkurrenz},
+        {match.Spieler1},
+        {match.Spieler2}
+      </li>
+    )
+  }
+}
+
+export default MatchDisp

+ 4 - 4
src/match/match-list.js → src/match/components/match-list.js

@@ -1,5 +1,5 @@
 import React from 'react'
-import Match from './match'
+import MatchDisp from './match-disp'
 
 class MatchList extends React.Component {
   render () {
@@ -8,10 +8,10 @@ class MatchList extends React.Component {
       <div>
         <h2>Matches</h2>
         <ul>
-          {this.props.matches.filter((match) => 
-            (filter === "Alle" | match[1] == filter)
+          {this.props.matches.filter((match) =>
+            (filter === 'Alle' | match.DatumString === filter)
           ).map((match, key) => (
-            <Match key={key} match={match} />
+            <MatchDisp key={key} match={match} />
           ))}
         </ul>
       </div>

+ 32 - 0
src/match/index.js

@@ -0,0 +1,32 @@
+import components from './components'
+import moment from 'moment'
+
+function normalize (item, type) {
+  return item ? String(item).replace(/\s+/g, ' ').trim() : null
+}
+
+/** Class representing a player */
+class Match {
+  /**
+   * Create a player
+   * A player data item in the Swisstennis PlayerList.xlsx file has the following columns
+   * Ort | Datum | Zeit | Konkurrenz | Spieler 1 | Spieler 1 Klassierung | Spieler 2 | Spieler 2 Klassierung
+   */
+  constructor (data) {
+    this.Ort = normalize(data[0])
+    this.Datum = data[1]
+    this.DatumString = data[1] ? moment(data[1]).format('DD.MM.') : null
+    this.ZeitString = data[1] ? moment(data[1]).format('hh:mm') : null
+    this.Konkurrenz = normalize(data[3])
+    this.Spieler1 = normalize(data[4])
+    this.Spieler1Klassierung = normalize(data[5])
+    this.Spieler2 = normalize(data[6])
+    this.Spieler2Klassierung = normalize(data[7])
+  }
+
+  isDoubles () {
+    return this.Konkurrenz.match(/DM.*|[MW]D.*/)
+  }
+}
+
+export default { Match, components }

+ 0 - 25
src/match/match.js

@@ -1,25 +0,0 @@
-import React from 'react'
-
-class Match extends React.Component {
-  render () {
-    const data = this.props.match
-    const matchDate = data[1] ? data[1].split('/') : null
-    const matchTime = data[2] ? data[2].split(':') : null
-    const Ort = data[0]
-    const Datum = data[1]
-    const Zeit = data[2]
-    const Konkurrenz = data[3].replace(/\s+/, ' ')
-    const Spieler1 = data[4]
-    const Spieler1Klassierung = data[5]
-    const Spieler2 = data[6]
-    const Spieler2Klassierung = data[7]
-
-    return (
-      <li>
-        {Ort ? Ort : <strong>Kein Platz zugeteilt</strong>}, {Datum ? Datum : <strong>Kein Datum zugeteilt</strong>}, {Zeit ? Zeit : <strong>Keine Zeit zugeteilt</strong>}, {Konkurrenz}, {Spieler1}, {Spieler2}
-      </li>
-    )
-  }
-}
-
-export default Match

+ 4 - 0
src/player/components/index.js

@@ -0,0 +1,4 @@
+import PlayerDisp from './player-disp'
+import PlayerList from './player-list'
+
+export default { PlayerDisp, PlayerList }

+ 14 - 0
src/player/components/player-disp.js

@@ -0,0 +1,14 @@
+import React from 'react'
+
+class PlayerDisp extends React.Component {
+  render () {
+    const player = this.props.player
+    return (
+      <li>
+        <i>{player.Konkurrenz}</i> <b>{player.Name}</b> {player.Vorname}
+      </li>
+    )
+  }
+}
+
+export default PlayerDisp

+ 2 - 2
src/player/player-list.js → src/player/components/player-list.js

@@ -1,5 +1,5 @@
 import React from 'react'
-import Player from './player'
+import PlayerDisp from './player-disp'
 
 class PlayerList extends React.Component {
   render () {
@@ -8,7 +8,7 @@ class PlayerList extends React.Component {
         <h2>Spielerliste</h2>
         <ul>
           {this.props.players.map((player, key) => (
-            <Player key={key} player={player} />
+            <PlayerDisp key={key} player={player} />
           ))}
         </ul>
       </div>

+ 54 - 0
src/player/index.js

@@ -0,0 +1,54 @@
+import components from './components'
+
+function normalize (item, type) {
+  return item ? String(item).replace(/\s+/g, ' ').trim() : null
+}
+
+/** Class representing a player */
+class Player {
+  /**
+   * Create a player
+   * A player data item in the Swisstennis PlayerList.xlsx file has the following columns
+   * Konkurrenz | Anmeldedatum | Lizenznummer | Klub | Klub Name | Name | Vorname | Geburtsdatum | Adresse | c/o | PLZ | Ort |
+   * Land | Tel P | Tel G | Mobile | Email | Klassierung | Klass. Wert | Gesetzte | Kommentar | Einschränkungen | Kommentar |
+   * Lizenz Nr.Doppelpartner | Name Doppelpartner | Vorname Doppelpartner | Geburtsdatum Doppelpartner | Klassierung Doppelpartner |
+   * Klass. Wert Doppelpartner | bestätigt | On-line Anmeldung | bezahlt
+   */
+  constructor (data) {
+    this.Konkurrenz = normalize(data[0])
+    this.Lizenz = normalize(data[2])
+    this.Name = normalize(data[5])
+    this.Vorname = normalize(data[6])
+    this.Geburtsdatum = data[7]
+    this.Klassierung = normalize(data[17])
+    this.LizenzDP = normalize(data[23])
+    this.NameDP = normalize(data[24])
+    this.VornameDP = normalize(data[25])
+    this.GeburtsdatumDP = data[26]
+    this.KlassierungDP = normalize(data[27])
+    this.Bestaetigt = data[29] === 'TRUE'
+    this.Bezahlt = data[31] === 'TRUE'
+  }
+
+  isDoubles () {
+    return this.Konkurrenz.match(/DM.*|[MW]D.*/)
+  }
+
+  isJunior () {
+    return this.Geburtsdatum >= new Date(new Date().getFullYear())
+  }
+
+  isJuniorDP () {
+    return this.GeburtsdatumDP >= new Date(new Date().getFullYear())
+  }
+
+  name () {
+    if (this.isDoubles()) {
+      return `${this.Name} ${this.Vorname} / ${this.NameDP} ${this.VornameDP}`
+    } else {
+      return `${this.Name} ${this.Vorname}`
+    }
+  }
+}
+
+export default { Player, components }

+ 0 - 30
src/player/player.js

@@ -1,30 +0,0 @@
-import React from 'react'
-
-class Player extends React.Component {
-  render () {
-    const data = this.props.player
-    const bdate1 = data[7] ? data[7].split('/') : null
-    const bdate2 = data[26] ? data[26].split('/') : null
-    const Konkurrenz = data[0].replace(/\s+/, ' ')
-    const Lizenz = data[2]
-    const Name = data[5]
-    const Vorname = data[6]
-    const Geburtsdatum = bdate1 ? new Date(bdate1[2], bdate1[1]-1, bdate1[0]) : null
-    const Klassierung = data[17]
-    const LizenzDP = data[23]
-    const NameDP = data[24]
-    const VornameDP = data[25]
-    const GeburtsdatumDP = bdate2 ? new Date(bdate2[2], bdate2[1]-1, bdate2[0]) : null
-    const KlassierungDP = data[27]
-    const Bestaetigt = data[29] === "TRUE" ? true : false
-    const Bezahlt = data[31] === "TRUE" ? true : false
-
-    return (
-      <li>
-        <i>{Konkurrenz}</i> <b>{Name}</b> {Vorname}
-      </li>
-    )
-  }
-}
-
-export default Player

+ 16 - 19
yarn.lock

@@ -1230,12 +1230,12 @@ caniuse-api@^1.5.2:
     lodash.uniq "^4.5.0"
 
 caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
-  version "1.0.30000679"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000679.tgz#dd7be12f16577e5d6ae6db880c6d619e77dca365"
+  version "1.0.30000680"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000680.tgz#d76ebeaaeb82e3d9952bfdc5c231c4f83cd48144"
 
 caniuse-lite@^1.0.30000669, caniuse-lite@^1.0.30000670:
-  version "1.0.30000679"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000679.tgz#0fb5bb3658d4d4448f8f86a1c48df15664aa05ef"
+  version "1.0.30000680"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000680.tgz#d94d81294471617e86500f0aab90f11d22bc8934"
 
 capture-stack-trace@^1.0.0:
   version "1.0.0"
@@ -3634,12 +3634,6 @@ jest@20.0.3:
   dependencies:
     jest-cli "^20.0.3"
 
-jodid25519@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
-  dependencies:
-    jsbn "~0.1.0"
-
 js-base64@^2.1.9:
   version "2.1.9"
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
@@ -4077,6 +4071,10 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
   dependencies:
     minimist "0.0.8"
 
+moment@^2.18.1:
+  version "2.18.1"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
+
 ms@0.7.1:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -4995,10 +4993,10 @@ randomatic@^1.1.3:
     kind-of "^3.0.2"
 
 randombytes@^2.0.0, randombytes@^2.0.1:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff22efd"
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79"
   dependencies:
-    safe-buffer "^5.0.1"
+    safe-buffer "^5.1.0"
 
 range-parser@^1.0.3, range-parser@~1.2.0:
   version "1.2.0"
@@ -5267,8 +5265,8 @@ relateurl@0.2.x:
   resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
 
 remove-trailing-separator@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511"
 
 renderkid@^2.0.1:
   version "2.0.1"
@@ -5417,7 +5415,7 @@ rx@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
 
-safe-buffer@^5.0.1:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
 
@@ -5740,8 +5738,8 @@ ssf@~0.9.3:
     voc ""
 
 sshpk@^1.7.0:
-  version "1.13.0"
-  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
   dependencies:
     asn1 "~0.2.3"
     assert-plus "^1.0.0"
@@ -5750,7 +5748,6 @@ sshpk@^1.7.0:
   optionalDependencies:
     bcrypt-pbkdf "^1.0.0"
     ecc-jsbn "~0.1.1"
-    jodid25519 "^1.0.0"
     jsbn "~0.1.0"
     tweetnacl "~0.14.0"