From ca732d66c2bb2ed97b9511bb022f107a2cea923b Mon Sep 17 00:00:00 2001 From: tjallingt Date: Sat, 4 Apr 2020 15:34:07 +0200 Subject: [PATCH] feat: add useYouTube hook --- example/example.js | 46 ----- example/src/index.js | 62 +++++- package.json | 6 +- src/index.js | 19 ++ src/useYouTube.js | 226 +++++++++++++++++++++ {src => tests}/Youtube.test.js | 2 +- {src => tests}/__mocks__/youtube-player.js | 0 yarn.lock | 197 +++++++++++++++--- 8 files changed, 481 insertions(+), 77 deletions(-) delete mode 100644 example/example.js create mode 100644 src/index.js create mode 100644 src/useYouTube.js rename {src => tests}/Youtube.test.js (99%) rename {src => tests}/__mocks__/youtube-player.js (100%) diff --git a/example/example.js b/example/example.js deleted file mode 100644 index 1598aee..0000000 --- a/example/example.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useState } from 'react'; -import ReactDOM from 'react-dom'; -import YouTube from '../src/YouTube'; - -const videoIdA = 'XxVg_s8xAms'; -const videoIdB = '-DX3vJiqxm4'; - -function Example() { - const [videoId, setVideoId] = useState(videoIdA); - const [player, setPlayer] = useState(null); - - const onReady = (event) => { - // eslint-disable-next-line - console.log(`YouTube Player object for videoId: "${videoId}" has been saved to state.`); - setPlayer(event.target); - }; - - const onPlayVideo = () => { - player.playVideo(); - }; - - const onPauseVideo = () => { - player.pauseVideo(); - }; - - const onChangeVideo = () => { - setVideoId(videoId === videoIdA ? videoIdB : videoIdA); - }; - - return ( -
- - - - -
- ); -} - -ReactDOM.render(, document.getElementById('root')); diff --git a/example/src/index.js b/example/src/index.js index d77f849..8e4e34b 100644 --- a/example/src/index.js +++ b/example/src/index.js @@ -1,11 +1,61 @@ -import React, { useState } from 'react'; +import React, { Fragment, useState } from 'react'; import ReactDOM from 'react-dom'; -import YouTube from 'react-youtube'; +import YouTube, { useYouTube } from 'react-youtube'; import './styles.css'; const VIDEOS = ['XxVg_s8xAms', '-DX3vJiqxm4']; +function YouTubeHookExample() { + const [videoIndex, setVideoIndex] = useState(0); + const [width, setWidth] = useState(600); + const [hidden, setHidden] = useState(false); + const [autoplay, setAutoplay] = useState(false); + + const { targetRef, player } = useYouTube({ + videoId: VIDEOS[videoIndex], + autoplay, + width, + height: width * (9 / 16), + }); + + return ( +
+
+ + + + + +
+ + {hidden ? 'mysterious' :
} +
+ ); +} + function YouTubeComponentExample() { const [player, setPlayer] = useState(0); const [videoIndex, setVideoIndex] = useState(0); @@ -57,4 +107,10 @@ function YouTubeComponentExample() { ); } -ReactDOM.render(, document.getElementById('app')); +ReactDOM.render( + + + + , + document.getElementById('app'), +); diff --git a/package.json b/package.json index d394bb5..fd7c509 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "7.9.0", "description": "React.js powered YouTube player component", "main": "dist/index.js", - "module": "dist/index.esm.js", + "module": "dist/index.mjs", "types": "index.d.ts", "files": [ "dist", @@ -78,8 +78,8 @@ "scripts": { "test": "jest", "test:ci": "jest --ci --runInBand", - "compile:cjs": "babel src/YouTube.js --out-file dist/index.js", - "compile:es": "cross-env BABEL_ENV=es babel src/YouTube.js --out-file dist/index.esm.js", + "compile:cjs": "babel src --out-dir dist", + "compile:es": "cross-env BABEL_ENV=es babel src --out-dir dist --out-file-extension .mjs", "compile": "npm-run-all --parallel compile:*", "prepublishOnly": "npm run compile", "lint": "eslint src example", diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..aac453b --- /dev/null +++ b/src/index.js @@ -0,0 +1,19 @@ +import YouTube from './YouTube.js'; +import useYouTube from './useYouTube.js'; + +export default YouTube; +export { useYouTube }; + +/** + * Expose PlayerState constants for convenience. These constants can also be + * accessed through the global YT object after the YouTube IFrame API is instantiated. + * https://developers.google.com/youtube/iframe_api_reference#onStateChange + */ +export const PlayerState = { + UNSTARTED: -1, + ENDED: 0, + PLAYING: 1, + PAUSED: 2, + BUFFERING: 3, + CUED: 5, +}; diff --git a/src/useYouTube.js b/src/useYouTube.js new file mode 100644 index 0000000..0d54913 --- /dev/null +++ b/src/useYouTube.js @@ -0,0 +1,226 @@ +import { useState, useEffect, useRef } from 'react'; + +function loadScript(url) { + return new Promise((resolve, reject) => { + const script = Object.assign(document.createElement('script'), { + type: 'text/javascript', + charset: 'utf8', + src: url, + async: true, + onerror() { + reject(new Error(`Failed to load: ${url}`)); + }, + onload() { + resolve(); + }, + }); + + document.head.appendChild(script); + }); +} + +function loadYouTubeIframeApi() { + return new Promise((resolve, reject) => { + const previous = window.onYouTubeIframeAPIReady; + window.onYouTubeIframeAPIReady = () => { + if (previous) previous(); + resolve(); + }; + + const protocol = window.location.protocol === 'http:' ? 'http:' : 'https:'; + loadScript(`${protocol}//www.youtube.com/iframe_api`).catch(reject); + }); +} + +function getYouTubeApi() { + if (window.YT && window.YT.Player && window.YT.Player instanceof Function) { + return window.YT; + } + return null; +} + +/* + +Available options that you can pass to the YouTube Iframe API are + +- videoId updated by Player#cueVideoById + +- width updated by Player#setSize +- height updated by Player#setSize + +- playerVars + - autoplay Player#loadVideoById/Playlist + - cc_lang_pref [static] + - cc_load_policy [static] + - color [static] + - controls [static] + - disablekb [static] + - enablejsapi [static] + - end Player#cue/load* should set endSeconds + - fs [static] + - hl [static] + - iv_load_policy [static] + - list [static] + - listType [static] + - loop updated by Player#setLoop + - modestbranding [static] + - origin [static] + - playlist updated by Player#cue/loadPlaylist + - playsinline [static] + - rel [static] + - start Player#cue/load* should set startSeconds + - widget_referrer [static] + +- events [note] + - onReady + - onStateChange + - onPlaybackQualityChange + - onPlaybackRateChange + - onError + - onApiChange + +[note] youtube-player fixes the very strange Player#addEventListener behaviour +but does this by overwriting the events property so we can't set these immediately. + +*/ + +/* + +type Config = { + videoId: string, + + autoplay: boolean, + startSeconds: number, + endSeconds: number, + + width: number, + height: number, + + onReady: (event: any) => void, + onStateChange: (event: any) => void, + onPlaybackQualityChange: (event: any) => void, + onPlaybackRateChange: (event: any) => void, + onError: (event: any) => void, + onApiChange: (event: any) => void, +}; + +*/ + +export default function useYouTube(config, playerVars) { + const [YouTubeApi, setYouTubeApi] = useState(getYouTubeApi); + + const [target, setTarget] = useState(null); + const [player, setPlayer] = useState(null); + const configRef = useRef(config); + + useEffect(() => { + configRef.current = config; + }, [config]); + + useEffect(() => { + if (target === null) return undefined; + + const element = target.appendChild(document.createElement('div')); + + // TODO: use suspense + if (YouTubeApi === null) { + loadYouTubeIframeApi() + .then(() => setYouTubeApi(getYouTubeApi)) + .catch((error) => { + console.error(error); + // TODO: throw so it can be handled by an error boundary + }); + return undefined; + } + + // NOTE: The YouTube player replaces `element`. + // trying to access it after this point results in unexpected behaviour + const instance = new YouTubeApi.Player(element, { + videoId: configRef.current.videoId, + width: configRef.current.width, + height: configRef.current.height, + playerVars, + events: { + onReady(event) { + setPlayer(instance); + + if (typeof configRef.current.onReady === 'function') { + configRef.current.onReady(event); + } + }, + onStateChange(event) { + if (typeof configRef.current.onStateChange === 'function') { + configRef.current.onStateChange(event); + } + }, + onPlaybackQualityChange(event) { + if (typeof configRef.current.onPlaybackQualityChange === 'function') { + configRef.current.onPlaybackQualityChange(event); + } + }, + onPlaybackRateChange(event) { + if (typeof configRef.current.onPlaybackRateChange === 'function') { + configRef.current.onPlaybackRateChange(event); + } + }, + onError(event) { + if (typeof configRef.current.onError === 'function') { + configRef.current.onError(event); + } + }, + onApiChange(event) { + if (typeof configRef.current.onApiChange === 'function') { + configRef.current.onApiChange(event); + } + }, + }, + }); + + return () => { + instance.getIframe().remove(); + // TODO: figure out why calling instance.destroy() causes cross origin errors + setPlayer(null); + }; + }, [YouTubeApi, target, playerVars]); + + // videoId, autoplay, startSeconds, endSeconds + useEffect(() => { + if (player === null) return; + + if (!config.videoId) { + player.stopVideo(); + return; + } + + if (configRef.current.autoplay) { + player.loadVideoById({ + videoId: config.videoId, + startSeconds: configRef.current.startSeconds, + endSeconds: configRef.current.endSeconds, + }); + return; + } + + player.cueVideoById({ + videoId: config.videoId, + startSeconds: configRef.current.startSeconds, + endSeconds: configRef.current.endSeconds, + }); + }, [player, config.videoId]); + + // width, height + useEffect(() => { + if (player === null) return; + + if (config.width !== undefined && config.height !== undefined) { + // calling setSize with width and height set to undefined + // makes the player smaller than the default + player.setSize(config.width, config.height); + } + }, [player, config.width, config.height]); + + return { + player, + targetRef: setTarget, + }; +} diff --git a/src/Youtube.test.js b/tests/Youtube.test.js similarity index 99% rename from src/Youtube.test.js rename to tests/Youtube.test.js index 2b7969b..d51de8c 100644 --- a/src/Youtube.test.js +++ b/tests/Youtube.test.js @@ -1,7 +1,7 @@ import '@testing-library/jest-dom'; import React from 'react'; import { render, queryByAttribute } from '@testing-library/react'; -import YouTube from './YouTube'; +import YouTube from '../src/YouTube'; import Player, { playerMock } from './__mocks__/youtube-player'; diff --git a/src/__mocks__/youtube-player.js b/tests/__mocks__/youtube-player.js similarity index 100% rename from src/__mocks__/youtube-player.js rename to tests/__mocks__/youtube-player.js diff --git a/yarn.lock b/yarn.lock index be61878..6812c68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -66,6 +66,16 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/generator@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" + integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== + dependencies: + "@babel/types" "^7.9.5" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" @@ -156,6 +166,15 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.9.5" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -257,6 +276,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== +"@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + "@babel/helper-wrap-function@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" @@ -862,7 +886,7 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== @@ -877,7 +901,22 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": +"@babel/traverse@^7.7.0": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2" + integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.5" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.0" + "@babel/types" "^7.9.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== @@ -886,6 +925,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.7.0", "@babel/types@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" + integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1572,6 +1620,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -1600,6 +1653,11 @@ jest-diff "^25.1.0" pretty-format "^25.1.0" +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + "@types/node@>= 8": version "12.12.31" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.31.tgz#d6b4f9645fee17f11319b508fb1001797425da51" @@ -1695,6 +1753,49 @@ resolved "https://registry.yarnpkg.com/@types/youtube/-/youtube-0.0.38.tgz#04aa5efa61f4d52e9cf682575a9dfed58f31e8c1" integrity sha512-rS0nLlnnZIvOfBNT4957rkKRMHjHxutoVhSEzowocYgNiTMVBmN+Hbkf/L3TeGMMtts9uKZ9gw7O2JLHREVnIw== +"@typescript-eslint/eslint-plugin@2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.28.0.tgz#4431bc6d3af41903e5255770703d4e55a0ccbdec" + integrity sha512-w0Ugcq2iatloEabQP56BRWJowliXUP5Wv6f9fKzjJmDW81hOTBxRoJ4LoEOxRpz9gcY51Libytd2ba3yLmSOfg== + dependencies: + "@typescript-eslint/experimental-utils" "2.28.0" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.28.0.tgz#1fd0961cd8ef6522687b4c562647da6e71f8833d" + integrity sha512-4SL9OWjvFbHumM/Zh/ZeEjUFxrYKtdCi7At4GyKTbQlrj1HcphIDXlje4Uu4cY+qzszR5NdVin4CCm6AXCjd6w== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.28.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.28.0.tgz#bb761286efd2b0714761cab9d0ee5847cf080385" + integrity sha512-RqPybRDquui9d+K86lL7iPqH6Dfp9461oyqvlXMNtap+PyqYbkY5dB7LawQjDzot99fqzvS0ZLZdfe+1Bt3Jgw== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.28.0" + "@typescript-eslint/typescript-estree" "2.28.0" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/typescript-estree@2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.28.0.tgz#d34949099ff81092c36dc275b6a1ea580729ba00" + integrity sha512-HDr8MP9wfwkiuqzRVkuM3BeDrOC4cKbO5a6BymZBHUt5y/2pL0BXD6I/C/ceq2IZoHWhcASk+5/zo+dwgu9V8Q== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^6.3.0" + tsutils "^3.17.1" + JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2207,6 +2308,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -2352,7 +2458,7 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" -browserify-aes@^1.0.0: +browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -2373,6 +2479,16 @@ browserify-cipher@^1.0.0: browserify-des "^1.0.0" evp_bytestokey "^1.0.0" +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + browserify-rsa@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" @@ -3441,7 +3557,7 @@ csso@^4.0.2: dependencies: css-tree "1.0.0-alpha.39" -cssom@^0.3.4, cssom@~0.3.6: +cssom@0.3.x, cssom@^0.3.4, cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== @@ -3684,6 +3800,14 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -4072,24 +4196,6 @@ escodegen@~1.9.0: optionalDependencies: source-map "~0.6.1" -eslint-config-airbnb-base@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4" - integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw== - dependencies: - confusing-browser-globals "^1.0.9" - object.assign "^4.1.0" - object.entries "^1.1.1" - -eslint-config-airbnb@18.1.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.1.0.tgz#724d7e93dadd2169492ff5363c5aaa779e01257d" - integrity sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw== - dependencies: - eslint-config-airbnb-base "^14.1.0" - object.assign "^4.1.0" - object.entries "^1.1.1" - eslint-config-prettier@6.10.1: version "6.10.1" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz#129ef9ec575d5ddc0e269667bf09defcd898642a" @@ -4097,6 +4203,13 @@ eslint-config-prettier@6.10.1: dependencies: get-stdin "^6.0.0" +eslint-config-react-app@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df" + integrity sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ== + dependencies: + confusing-browser-globals "^1.0.9" + eslint-import-resolver-node@^0.3.2: version "0.3.3" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" @@ -4113,6 +4226,13 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" +eslint-plugin-flowtype@4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.7.0.tgz#903a6ea3eb5cbf4c7ba7fa73cc43fc39ab7e4a70" + integrity sha512-M+hxhSCk5QBEValO5/UqrS4UunT+MgplIJK5wA1sCtXjzBcZkpTGRwxmLHhGpbHcrmQecgt6ZL/KDdXWqGB7VA== + dependencies: + lodash "^4.17.15" + eslint-plugin-import@2.20.2: version "2.20.2" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" @@ -4191,6 +4311,13 @@ eslint-utils@^1.4.3: dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" @@ -5373,6 +5500,11 @@ icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" @@ -9595,6 +9727,11 @@ regexpp@^2.0.1: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpp@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + regexpu-core@^4.6.0, regexpu-core@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" @@ -10378,6 +10515,11 @@ split@^1.0.0: 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" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -11019,11 +11161,18 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -11036,7 +11185,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl@~0.14.0: +tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=