From 0faa8c64bfdd541883aae5f9011994af48fb952b Mon Sep 17 00:00:00 2001 From: zigsong Date: Tue, 2 Mar 2021 17:08:49 +0900 Subject: [PATCH 01/52] =?UTF-8?q?chore:=20eslint,=20prettier,=20cypress=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 21 + .prettierrc | 9 + package.json | 18 + yarn.lock | 2093 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2141 insertions(+) create mode 100644 .eslintrc.json create mode 100644 .prettierrc create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..c83e776f8 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "env": { + "browser": true + }, + "extends": ["airbnb-base", "plugin:prettier/recommended"], + "plugins": ["prettier"], + "parserOptions": { + "sourceType": "module" + }, + "ignorePatterns": ["cypress/"], + "rules": { + "no-new": "off", + "no-alert": "off", + "no-param-reassign": "off", + "no-return-assign": "off", + "import/extensions": "off", + "import/prefer-default-export": "off", + "max-depth": ["error", 1], + "max-lines-per-function": ["error", 15] + } +} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..fa7a6af4a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "useTabs": false, + "tabWidth": 2, + "printWidth": 80, + "singleQuote": true, + "arrowParens": "always", + "trailingComma": "all" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..b7c024834 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "javascript-youtube-classroom", + "version": "1.0.0", + "main": "index.js", + "repository": "https://github.com/zigsong/javascript-youtube-classroom.git", + "author": "zigsong ", + "license": "MIT", + "devDependencies": { + "cypress": "^6.6.0", + "eslint": "^7.21.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-prettier": "^3.3.1", + "prettier": "^2.2.1" + }, + "dependencies": { + "eslint-config-airbnb-base": "^14.2.1" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..7a318745a --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2093 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/highlight@^7.10.4": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.8.tgz#10b2dac78526424dfc1f47650d0e415dfd9dc481" + integrity sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@cypress/listr-verbose-renderer@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/request@^2.88.5": + version "2.88.5" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7" + integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@eslint/eslintrc@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" + integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" + integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== + dependencies: + any-observable "^0.3.0" + +"@types/node@12.12.50": + version "12.12.50" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.50.tgz#e9b2e85fafc15f2a8aa8fdd41091b983da5fd6ee" + integrity sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w== + +"@types/sinonjs__fake-timers@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + +"@types/sizzle@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== + +acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84" + integrity sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== + +arch@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +blob-util@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + +cli-cursor@^2.0.0, cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-table3@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" + integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== + dependencies: + object-assign "^4.1.0" + string-width "^4.2.0" + optionalDependencies: + colors "^1.1.2" + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +confusing-browser-globals@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" + integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cypress@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.6.0.tgz#659c64cdb06e51b6be18fdac39d8f192deb54fa0" + integrity sha512-+Xx3Zn653LJHUsCb9h1Keql2jlazbr1ROmbY6DFJMmXKLgXP4ez9cE403W93JNGRbZK0Tng3R/oP8mvd9XAPVg== + dependencies: + "@cypress/listr-verbose-renderer" "^0.4.1" + "@cypress/request" "^2.88.5" + "@cypress/xvfb" "^1.2.4" + "@types/node" "12.12.50" + "@types/sinonjs__fake-timers" "^6.0.1" + "@types/sizzle" "^2.3.2" + arch "^2.1.2" + blob-util "2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.9.3" + debug "4.3.2" + eventemitter2 "^6.4.2" + execa "^4.0.2" + executable "^4.1.1" + extract-zip "^1.7.0" + fs-extra "^9.0.1" + getos "^3.2.1" + is-ci "^2.0.0" + is-installed-globally "^0.3.2" + lazy-ass "^1.6.0" + listr "^0.14.3" + lodash "^4.17.19" + log-symbols "^4.0.0" + minimist "^1.2.5" + moment "^2.29.1" + ospath "^1.2.2" + pretty-bytes "^5.4.1" + ramda "~0.27.1" + request-progress "^3.0.0" + supports-color "^7.2.0" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + +dayjs@^1.9.3: + version "1.10.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" + integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== + +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.3.tgz#56bc8b5cc36b2cca25a13be07f3c02c2343db6b7" + integrity sha512-VMzHx/Bczjg59E6jZOQjHeN3DEoptdhejpARgflAViidlqSpjdq9zA6lKwlhRRs/lOw1gHJv2xkkSFRgvEwbQg== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.2" + is-string "^1.0.5" + object-inspect "^1.9.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-config-airbnb-base@^14.2.1: + version "14.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" + integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.2" + +eslint-config-prettier@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6" + integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw== + +eslint-plugin-prettier@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^7.21.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" + integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.20" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eventemitter2@^6.4.2: + version "6.4.4" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b" + integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw== + +execa@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + 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" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + 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@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" + integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== + dependencies: + ini "1.3.7" + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-bigints@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + +is-bigint@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + +is-boolean-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" + integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + dependencies: + call-bind "^1.0.0" + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + +is-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== + dependencies: + symbol-observable "^1.1.0" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= + +listr-update-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^2.3.0" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== + dependencies: + chalk "^2.4.1" + cli-cursor "^2.1.0" + date-fns "^1.27.2" + figures "^2.0.0" + +listr@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.5.0" + listr-verbose-renderer "^0.5.0" + p-map "^2.0.0" + rxjs "^6.3.3" + +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash@^4.17.19, lodash@^4.17.20: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +log-update@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" + integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.29" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" + integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== + dependencies: + mime-db "1.46.0" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^0.5.4: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +moment@^2.29.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.entries@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6" + integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + has "^1.0.3" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +pretty-bytes@^5.4.1: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +ramda@~0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + +readable-stream@^2.2.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rxjs@^6.3.3: + version "6.6.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" + integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.2.1: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +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" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +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" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0, supports-color@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +table@^6.0.4: + version "6.0.7" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" + integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== + dependencies: + ajv "^7.0.2" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +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= + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +unbox-primitive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" + integrity sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.0" + has-symbols "^1.0.0" + which-boxed-primitive "^1.0.1" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" + integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +which-boxed-primitive@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" From 81bffc170a137302599505bde2e7f5c900b50bc9 Mon Sep 17 00:00:00 2001 From: zigsong Date: Tue, 2 Mar 2021 17:09:38 +0900 Subject: [PATCH 02/52] =?UTF-8?q?docs:=20todo=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 142ccdcea..0a14643ff 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,31 @@ - [ ] 검색 모달에 다시 접근했을 때 가장 마지막에 검색한 키워드로 검색한 결과를 보여준다. - [ ] 최근 검색 키워드를 3개까지 화면상에 검색창 하단에 보여준다. +### ✅ TODO(FEAT) - step1 +- [x] eslint, prettier, cypress 환경설정 +- [x] TODO 작성 +- [ ] 클릭한 탭의 색을 하이라이트한다. +- [ ] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. +- [ ] 최근 검색어에 목록에 최근 검색한 검색어가 3개 뜬다. +- [ ] 가장 마지막에 검색한 검색 결과 동영상들을 보여준다. +- [ ] 검색어를 입력 받을 수 있다. +- [ ] 검색을 실행하면 youtube api를 통해 사용자가 입력한 검색어로 검색 결과를 가져온다. +- [ ] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. +- [ ] youtube api를 통해 가져온 검색 결과를 10개씩 보여준다. +- [ ] 검색 후 스크롤를 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. +- [ ] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. +- [ ] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. + - [ ] 저장한 동영상들을 `볼 영상` 목록에 추가한다. + +### 👾 TODO(TEST) - step1 +- [ ] 클릭한 탭의 색을 하이라이트한다. +- [ ] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. +- [ ] 현재 검색한 검색어가 최근 검색어 목록에 남는다. +- [ ] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. +- [ ] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. +- [ ] 검색 후 스크롤를 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. +- [ ] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. + ### 🎯🎯 step2 강의실 관리 기능 - [ ] 가장 처음에는 저장된 영상이 없음으로, 비어있다는 것을 사용자에게 알려주는 상태를 보여준다. From 66d31bb206b7dd7d28cfcaecb2912f714b1a3f5a Mon Sep 17 00:00:00 2001 From: zigsong Date: Wed, 3 Mar 2021 11:28:34 +0900 Subject: [PATCH 03/52] =?UTF-8?q?chore:=20cypress=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cypress.json | 1 + cypress/fixtures/example.json | 5 +++++ cypress/plugins/index.js | 21 +++++++++++++++++++++ cypress/support/commands.js | 25 +++++++++++++++++++++++++ cypress/support/index.js | 20 ++++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 cypress.json create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/index.js diff --git a/cypress.json b/cypress.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/cypress.json @@ -0,0 +1 @@ +{} diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 000000000..da18d9352 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 000000000..aa9918d21 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,21 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 000000000..ca4d256f3 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 000000000..d68db96df --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') From ef30ca7791357c0997b736bc461f51f1a890f34f Mon Sep 17 00:00:00 2001 From: zigsong Date: Wed, 3 Mar 2021 11:29:28 +0900 Subject: [PATCH 04/52] =?UTF-8?q?test:=20=ED=81=B4=EB=A6=AD=ED=95=9C=20?= =?UTF-8?q?=ED=83=AD=EC=9D=98=20=EC=83=89=20=EB=B3=80=EA=B2=BD=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- cypress/integration/myYouTube.spec.js | 17 +++++++++++++++++ index.html | 6 +++--- src/css/shared/modules/layout.css | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 cypress/integration/myYouTube.spec.js diff --git a/README.md b/README.md index 0a14643ff..af5769de3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ - [ ] 저장한 동영상들을 `볼 영상` 목록에 추가한다. ### 👾 TODO(TEST) - step1 -- [ ] 클릭한 탭의 색을 하이라이트한다. +- [x] 클릭한 탭의 색을 하이라이트한다. - [ ] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. - [ ] 현재 검색한 검색어가 최근 검색어 목록에 남는다. - [ ] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. diff --git a/cypress/integration/myYouTube.spec.js b/cypress/integration/myYouTube.spec.js new file mode 100644 index 000000000..74f7b5285 --- /dev/null +++ b/cypress/integration/myYouTube.spec.js @@ -0,0 +1,17 @@ +describe('racing-game', () => { + beforeEach(() => { + cy.visit('http://localhost:5500/'); + cy.window().then((win) => cy.stub(win, 'alert').as('windowAlert')); + }); + + it('클릭한 탭의 색을 하이라이트한다.', () => { + cy.get('#nav-bar > button').each((button) => { + cy.wrap(button).click(); + cy.wrap(button).should( + 'have.css', + 'background-color', + 'rgb(179, 234, 242)', + ); + }); + }); +}); diff --git a/index.html b/index.html index c5e27ed2f..566d1df93 100644 --- a/index.html +++ b/index.html @@ -12,9 +12,9 @@

👩🏻‍💻 나만의 유튜브 강의실 👨🏻‍💻

-
-
+
-
-
-

아두이노 무드등

-
- - 메이커준 - -
-

2021년 3월 2일

-
-
- -
-
-
-
diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index 22cbe9221..7dbcb1641 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -7,6 +7,7 @@ import Video from '../js/models/Video.js'; export default class YoutubeController { constructor() { this.videos = []; + this.nextPageToken; this.selectedTab = $('#saved-btn'); this.navigationView = new NavigationView($('#nav-bar')); this.searchModalView = new SearchModalView($('.modal')); @@ -22,6 +23,7 @@ export default class YoutubeController { this.changeNavTab($('#saved-btn')), ); this.searchModalView.on('submitSearch', (e) => this.searchVideo(e.detail)); + this.searchModalView.on('scrollResult', (e) => this.searchVideo(e.detail)); } changeNavTab(currentTab) { @@ -33,21 +35,25 @@ export default class YoutubeController { } } - generateVideos(items) { - if (items.length === 0) { + generateVideos(response) { + const { prevPageToken, nextPageToken, items } = response; + + if (items.length === 0 && !prevPageToken) { this.searchModalView.showNoResult(); return; } - this.videos = [ + this.nextPageToken = nextPageToken; + const newVideos = [ ...items.map((item) => new Video(item.id.videoId, item.snippet)), ]; + this.videos = [...this.videos, ...newVideos]; - this.searchModalView.renderVideoClips(this.videos); + this.searchModalView.renderVideoClips(newVideos); } searchVideo(keyword) { this.searchModalView.startSearch(); - searchRequest(keyword, this.generateVideos.bind(this)); + searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } } diff --git a/src/js/request.js b/src/js/request.js index 18a4e2f60..97c4e6c19 100644 --- a/src/js/request.js +++ b/src/js/request.js @@ -1,12 +1,16 @@ import youtubeKey from '../../youtubeAPI.js'; import { SEARCH_URL } from '../js/utils/constants.js'; -export const searchRequest = (keyword, callback) => { - fetch(`${SEARCH_URL}&key=${youtubeKey}&q=${keyword}`) +export const searchRequest = (keyword, pageToken, callback) => { + const requestURL = pageToken + ? `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}&pageToken=${pageToken}` + : `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}`; + + fetch(requestURL) .then((response) => { return response.json(); }) .then(function (res) { - callback(res.items); + callback(res); }); }; diff --git a/src/js/utils/constants.js b/src/js/utils/constants.js index 5b0a585c2..5d5c59dcc 100644 --- a/src/js/utils/constants.js +++ b/src/js/utils/constants.js @@ -1,7 +1,8 @@ export const SEARCH_URL = - 'https://www.googleapis.com/youtube/v3/search?type=video&part=snippet&maxResults=5'; + 'https://www.googleapis.com/youtube/v3/search?type=video&part=snippet&maxResults=10'; export const VALUE = { KEYWORD_COUNT: 3, CLIPS_PER_SCROLL: 10, + THROTTLE_TIME: 1000, }; diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index 734c21c08..1d86db563 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -39,6 +39,10 @@ export const $ = (function () { this.each((element) => (element.innerHTML = template)); }; + constructor.prototype.addInnerHTML = function (template) { + this.each((element) => (element.innerHTML += template)); + }; + constructor.prototype.addClass = function (className) { this.each((element) => element.classList.add(className)); }; diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index b94c5d36b..f3b4642cb 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -6,9 +6,14 @@ import View from './View.js'; export default class SearchModalView extends View { constructor($element) { super($element); + this.closeButton = $('.modal-close'); this.searchForm = $('#modal-search-form'); + this.modalVideos = $('#modal-videos'); + this.searchKeyword; + this.bindModalEvents(); + this.bindScrollEvent(); } bindModalEvents() { @@ -20,20 +25,37 @@ export default class SearchModalView extends View { this.searchForm.setEvent('submit', (e) => { e.preventDefault(); - const searchKeyword = e.target.elements.search.value; + this.searchKeyword = e.target.elements.search.value; - this.emit('submitSearch', searchKeyword); - this.setRecentChip(searchKeyword); + this.emit('submitSearch', this.searchKeyword); + this.setRecentChip(); this.updateChips(); }); } - setRecentChip(searchKeyword) { + bindScrollEvent() { + let throttle; // null + + this.modalVideos.setEvent('scroll', (e) => { + if (throttle) return; + + const { scrollTop, scrollHeight, offsetHeight } = e.target; + if (scrollTop === scrollHeight - offsetHeight) { + throttle = setTimeout(() => { + throttle = null; + }, VALUE.THROTTLE_TIME); + + this.emit('scrollResult', this.searchKeyword); + } + }); + } + + setRecentChip() { const recentKeywords = localStorage.getItem('searchKeyword') ? JSON.parse(localStorage.getItem('searchKeyword')) : []; - if (recentKeywords.includes(searchKeyword)) { + if (recentKeywords.includes(this.searchKeyword)) { return; } @@ -41,7 +63,7 @@ export default class SearchModalView extends View { recentKeywords.pop(); } - recentKeywords.unshift(searchKeyword); + recentKeywords.unshift(this.searchKeyword); localStorage.setItem('searchKeyword', JSON.stringify(recentKeywords)); } @@ -66,13 +88,14 @@ export default class SearchModalView extends View { } renderVideoClips(videos) { + $('.skeleton').hide(); const videoClips = videos.map((video) => clipMaker(video)).join(''); - $('#modal-videos').setInnerHTML(videoClips); + $('#modal-videos').addInnerHTML(videoClips); } skeletonTemplate() { return ` -
+

@@ -81,13 +104,13 @@ export default class SearchModalView extends View { } startSearch() { - $('#modal-videos').setInnerHTML( + $('#modal-videos').addInnerHTML( this.skeletonTemplate().repeat(VALUE.CLIPS_PER_SCROLL), ); } showNoResult() { - $('#modal-videos').setInnerHTML( + this.modalVideos.setInnerHTML( `
From 5f773b51dab33635b7a3d832f3aa22c6c8c40562 Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 18:31:00 +0900 Subject: [PATCH 24/52] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=EC=B0=BD?= =?UTF-8?q?=EC=9D=84=20=EC=97=B4=EB=A9=B4=20=EA=B0=80=EC=9E=A5=20=EB=A7=88?= =?UTF-8?q?=EC=A7=80=EB=A7=89=EC=97=90=20=EA=B2=80=EC=83=89=ED=95=9C=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=EB=8F=99=EC=98=81?= =?UTF-8?q?=EC=83=81=EB=93=A4=EC=9D=84=20=EB=B3=B4=EC=97=AC=EC=A3=BC?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/js/YoutubeController.js | 10 +++++----- src/js/utils/dom.js | 6 ++++++ src/js/views/SearchModalView.js | 34 ++++++++++++++++++++++++--------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7c9371550..69c21c03c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ c

- [x] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. - [x] 검색 모달 창의 x 버튼을 누르면 검색 모달 창이 닫히고, 볼 영상 목록으로 돌아간다. - [x] 최근 검색어에 목록에 최근 검색한 검색어가 3개 뜬다. -- [ ] 가장 마지막에 검색한 검색 결과 동영상들을 보여준다. +- [x] 가장 마지막에 검색한 검색 결과 동영상들을 보여준다. - [x] 검색어를 입력 받을 수 있다. - [x] 검색을 실행하면 youtube api를 통해 사용자가 입력한 검색어로 검색 결과를 가져온다. - [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index 7dbcb1641..f215a7311 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -19,11 +19,11 @@ export default class YoutubeController { bindEvents() { this.navigationView.on('clickNavTab', (e) => this.changeNavTab(e.detail)); - this.searchModalView.on('closeModal', () => - this.changeNavTab($('#saved-btn')), - ); - this.searchModalView.on('submitSearch', (e) => this.searchVideo(e.detail)); - this.searchModalView.on('scrollResult', (e) => this.searchVideo(e.detail)); + this.searchModalView + .on('openModal', (e) => this.searchVideo(e.detail)) + .on('submitSearch', (e) => this.searchVideo(e.detail)) + .on('scrollResult', (e) => this.searchVideo(e.detail)) + .on('closeModal', () => this.changeNavTab($('#saved-btn'))); } changeNavTab(currentTab) { diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index 1d86db563..3d7f75c68 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -51,6 +51,12 @@ export const $ = (function () { this.each((element) => element.classList.remove(className)); }; + constructor.prototype.getText = function (className) { + if (!this.element) return; + + return this.element.innerText; + }; + const instantiate = function (selector) { return new constructor(selector); }; diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index f3b4642cb..e9519a4a7 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -2,7 +2,7 @@ import { $ } from '../utils/dom.js'; import { VALUE } from '../utils/constants.js'; import clipMaker from '../utils/clipMaker.js'; import View from './View.js'; -// import notFoundImg from '../../images/status/not_found.png'; + export default class SearchModalView extends View { constructor($element) { super($element); @@ -12,6 +12,7 @@ export default class SearchModalView extends View { this.modalVideos = $('#modal-videos'); this.searchKeyword; + this.updateChips(); this.bindModalEvents(); this.bindScrollEvent(); } @@ -27,6 +28,7 @@ export default class SearchModalView extends View { this.searchKeyword = e.target.elements.search.value; + this.clearVideoClips(); this.emit('submitSearch', this.searchKeyword); this.setRecentChip(); this.updateChips(); @@ -34,7 +36,7 @@ export default class SearchModalView extends View { } bindScrollEvent() { - let throttle; // null + let throttle; this.modalVideos.setEvent('scroll', (e) => { if (throttle) return; @@ -51,9 +53,7 @@ export default class SearchModalView extends View { } setRecentChip() { - const recentKeywords = localStorage.getItem('searchKeyword') - ? JSON.parse(localStorage.getItem('searchKeyword')) - : []; + const recentKeywords = this.getRecentKeywords(); if (recentKeywords.includes(this.searchKeyword)) { return; @@ -69,18 +69,30 @@ export default class SearchModalView extends View { chipTemplate(recentKeywords) { return recentKeywords - .map((keyword) => `${keyword}`) + .map( + (keyword, idx) => `${keyword}`, + ) .join(''); } updateChips() { - const recentKeywords = JSON.parse(localStorage.getItem('searchKeyword')); + const recentKeywords = this.getRecentKeywords(); $('#chip-container').setInnerHTML(this.chipTemplate(recentKeywords)); } + getRecentKeywords() { + return localStorage.getItem('searchKeyword') + ? JSON.parse(localStorage.getItem('searchKeyword')) + : []; + } + openModal() { this.$element.addClass('open'); + + const latestKeyword = $('#chip-1').getText(); + + if (latestKeyword) this.emit('openModal', latestKeyword); } closeModal() { @@ -90,7 +102,11 @@ export default class SearchModalView extends View { renderVideoClips(videos) { $('.skeleton').hide(); const videoClips = videos.map((video) => clipMaker(video)).join(''); - $('#modal-videos').addInnerHTML(videoClips); + this.modalVideos.addInnerHTML(videoClips); + } + + clearVideoClips() { + this.modalVideos.setInnerHTML(''); } skeletonTemplate() { @@ -104,7 +120,7 @@ export default class SearchModalView extends View { } startSearch() { - $('#modal-videos').addInnerHTML( + this.modalVideos.addInnerHTML( this.skeletonTemplate().repeat(VALUE.CLIPS_PER_SCROLL), ); } From 3222470449066ec7c15eb92098cb41e1b512692a Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 20:35:18 +0900 Subject: [PATCH 25/52] =?UTF-8?q?refactor:=20Video=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=ED=94=84=EB=9D=BC=EC=9D=B4=EB=B9=97,=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=B6=9C=EB=A0=A5=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/models/Video.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/js/models/Video.js b/src/js/models/Video.js index 6546b87a6..32dc532d7 100644 --- a/src/js/models/Video.js +++ b/src/js/models/Video.js @@ -2,10 +2,31 @@ export default class Video { constructor(videoId, videoData) { const { title, channelId, channelTitle, publishedAt } = videoData; - this.id = videoId; - this.title = title; - this.channelId = channelId; - this.channelTitle = channelTitle; - this.publishedAt = publishedAt; + this._id = videoId; + this._title = title; + this._channelId = channelId; + this._channelTitle = channelTitle; + this._publishedAt = new Date(publishedAt); + } + + get id() { + return this._id; + } + + get title() { + return this._title; + } + + get channelId() { + return this._channelId; + } + + get channelTitle() { + return this._channelTitle; + } + + get publishedAt() { + const options = { year: 'numeric', month: 'long', day: 'numeric' }; + return this._publishedAt.toLocaleDateString('ko-KR', options); } } From acaedc1f4b2735441f71e3211e9b589deb83bbf2 Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 21:27:22 +0900 Subject: [PATCH 26/52] =?UTF-8?q?style:=20=EB=AA=A8=EB=8B=AC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=81=B4=EB=A6=BD=20=EC=A0=80=EC=9E=A5=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 4 ++-- src/css/ui/modules/youtubeCard.css | 10 +++++++++- src/js/utils/clipMaker.js | 24 ++++++++++++++++++++++-- src/js/views/SearchModalView.js | 4 +++- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index 8332eb3ca..32a6b2b34 100644 --- a/index.html +++ b/index.html @@ -73,9 +73,9 @@

🔎 유튜브 검색

-
+
최근 검색어: -
+
diff --git a/src/css/ui/modules/youtubeCard.css b/src/css/ui/modules/youtubeCard.css index 200b2a286..43cb98693 100644 --- a/src/css/ui/modules/youtubeCard.css +++ b/src/css/ui/modules/youtubeCard.css @@ -1,15 +1,17 @@ .video-wrapper { display: grid; grid-template-columns: repeat(auto-fill, 236px); + /* grid-template-rows: repeat(auto-fill, 254px); */ grid-row-gap: 0px; grid-column-gap: 4px; justify-content: center; } .clip { - height: 219px; + height: 254px; cursor: pointer; text-decoration: none; + position: relative; } .clip .preview-container { @@ -55,6 +57,12 @@ width: 100%; } +.clip .clip-save { + position: absolute; + bottom: 18px; + right: 2px; +} + .clip .content-container .meta p { font-size: 12px; display: inline-block; diff --git a/src/js/utils/clipMaker.js b/src/js/utils/clipMaker.js index 82a74b191..84c5a3f53 100644 --- a/src/js/utils/clipMaker.js +++ b/src/js/utils/clipMaker.js @@ -1,4 +1,4 @@ -export default function clipMaker(video) { +export default function clipMaker(video, type) { return `
@@ -25,7 +25,27 @@ export default function clipMaker(video) {

${video.publishedAt}

-
+
+ ${type.isModal ? saveButtonTemplate() : buttonPackTemplate()} `; } + +function saveButtonTemplate() { + return ` +
+ +
+ `; +} + +function buttonPackTemplate() { + return ` +
+ + 👍 + 💬 + 🗑️ +
+ `; +} diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index e9519a4a7..e5a022072 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -101,7 +101,9 @@ export default class SearchModalView extends View { renderVideoClips(videos) { $('.skeleton').hide(); - const videoClips = videos.map((video) => clipMaker(video)).join(''); + const videoClips = videos + .map((video) => clipMaker(video, { isModal: false })) + .join(''); this.modalVideos.addInnerHTML(videoClips); } From b10b22709ff4ed942722c1cc51a18440ef950280 Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 22:48:59 +0900 Subject: [PATCH 27/52] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EB=8F=99=EC=98=81=EC=83=81=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/js/YoutubeController.js | 13 ++++++++++++ src/js/utils/clipMaker.js | 6 +++--- src/js/utils/localStorage.js | 35 +++++++++++++++++++++++++++++++++ src/js/views/SearchModalView.js | 32 +++++++++--------------------- 5 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 src/js/utils/localStorage.js diff --git a/README.md b/README.md index 69c21c03c..a14ecea2b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ c

- [x] youtube api를 통해 가져온 검색 결과를 10개씩 보여준다. - [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. - [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. -- [ ] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. +- [x] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. - [ ] 저장한 동영상들을 `볼 영상` 목록에 추가한다. ### 👾 TODO(TEST) - step1 diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index f215a7311..54bee6e01 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -1,7 +1,13 @@ import { $ } from './utils/dom.js'; +import { + setRecentChip, + getRecentKeywords, + setSavedVideos, +} from './utils/localStorage.js'; import { searchRequest } from '../js/request.js'; import NavigationView from './views/NavigationView.js'; import SearchModalView from './views/SearchModalView.js'; +import SavedVideosView from './views/SavedVideosView.js'; import Video from '../js/models/Video.js'; export default class YoutubeController { @@ -23,6 +29,7 @@ export default class YoutubeController { .on('openModal', (e) => this.searchVideo(e.detail)) .on('submitSearch', (e) => this.searchVideo(e.detail)) .on('scrollResult', (e) => this.searchVideo(e.detail)) + .on('clickSaveButton', (e) => this.saveVideo(e.detail)) .on('closeModal', () => this.changeNavTab($('#saved-btn'))); } @@ -52,7 +59,13 @@ export default class YoutubeController { this.searchModalView.renderVideoClips(newVideos); } + saveVideo(videoId) { + setSavedVideos(videoId); + } + searchVideo(keyword) { + setRecentChip(keyword); + this.searchModalView.updateChips(getRecentKeywords()); this.searchModalView.startSearch(); searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } diff --git a/src/js/utils/clipMaker.js b/src/js/utils/clipMaker.js index 84c5a3f53..294c4b551 100644 --- a/src/js/utils/clipMaker.js +++ b/src/js/utils/clipMaker.js @@ -26,15 +26,15 @@ export default function clipMaker(video, type) { - ${type.isModal ? saveButtonTemplate() : buttonPackTemplate()} + ${type.isModal ? saveButtonTemplate(video.id) : buttonPackTemplate()} `; } -function saveButtonTemplate() { +function saveButtonTemplate(videoId) { return `

- +
`; } diff --git a/src/js/utils/localStorage.js b/src/js/utils/localStorage.js new file mode 100644 index 000000000..54b61b37b --- /dev/null +++ b/src/js/utils/localStorage.js @@ -0,0 +1,35 @@ +import { VALUE } from './constants.js'; + +export function setRecentChip(keyword) { + const recentKeywords = getRecentKeywords(); + + if (recentKeywords.includes(keyword)) { + return; + } + + if (recentKeywords.length >= VALUE.KEYWORD_COUNT) { + recentKeywords.pop(); + } + + recentKeywords.unshift(keyword); + localStorage.setItem('searchKeyword', JSON.stringify(recentKeywords)); +} + +export function getRecentKeywords() { + return localStorage.getItem('searchKeyword') + ? JSON.parse(localStorage.getItem('searchKeyword')) + : []; +} + +export function setSavedVideos(videoId) { + const savedVideos = getSavedVideos(); + + savedVideos.push(videoId); + localStorage.setItem('savedVideos', JSON.stringify(savedVideos)); +} + +export function getSavedVideos() { + return localStorage.getItem('savedVideos') + ? JSON.parse(localStorage.getItem('savedVideos')) + : []; +} diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index e5a022072..ba2d0ea05 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -1,6 +1,7 @@ import { $ } from '../utils/dom.js'; import { VALUE } from '../utils/constants.js'; import clipMaker from '../utils/clipMaker.js'; +import { getRecentKeywords } from '../utils/localStorage.js'; import View from './View.js'; export default class SearchModalView extends View { @@ -30,8 +31,6 @@ export default class SearchModalView extends View { this.clearVideoClips(); this.emit('submitSearch', this.searchKeyword); - this.setRecentChip(); - this.updateChips(); }); } @@ -52,19 +51,11 @@ export default class SearchModalView extends View { }); } - setRecentChip() { - const recentKeywords = this.getRecentKeywords(); - - if (recentKeywords.includes(this.searchKeyword)) { - return; - } - - if (recentKeywords.length >= VALUE.KEYWORD_COUNT) { - recentKeywords.pop(); - } - - recentKeywords.unshift(this.searchKeyword); - localStorage.setItem('searchKeyword', JSON.stringify(recentKeywords)); + bindSaveEvent() { + $('.clip-save-btn').setEvent('click', (e) => { + const videoId = e.target.dataset.videoId; + this.emit('clickSaveButton', videoId); + }); } chipTemplate(recentKeywords) { @@ -76,17 +67,11 @@ export default class SearchModalView extends View { } updateChips() { - const recentKeywords = this.getRecentKeywords(); + const recentKeywords = getRecentKeywords(); $('#chip-container').setInnerHTML(this.chipTemplate(recentKeywords)); } - getRecentKeywords() { - return localStorage.getItem('searchKeyword') - ? JSON.parse(localStorage.getItem('searchKeyword')) - : []; - } - openModal() { this.$element.addClass('open'); @@ -102,9 +87,10 @@ export default class SearchModalView extends View { renderVideoClips(videos) { $('.skeleton').hide(); const videoClips = videos - .map((video) => clipMaker(video, { isModal: false })) + .map((video) => clipMaker(video, { isModal: true })) .join(''); this.modalVideos.addInnerHTML(videoClips); + this.bindSaveEvent(); } clearVideoClips() { From 1ca3fb4597e27f5cc9b67b33d606a72c58a6d388 Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 22:49:25 +0900 Subject: [PATCH 28/52] =?UTF-8?q?refactor:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=ED=83=AD=20custom=20dom=20library=20event?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/views/NavigationView.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/js/views/NavigationView.js b/src/js/views/NavigationView.js index 0009d2f7a..6e1d4cc53 100644 --- a/src/js/views/NavigationView.js +++ b/src/js/views/NavigationView.js @@ -10,10 +10,8 @@ export default class NavigationView extends View { } bindTabEvents() { - this.tabButtons.each((button) => { - button.addEventListener('click', () => { - this.emit('clickNavTab', $(`#${button.id}`)); - }); + this.tabButtons.setEvent('click', (e) => { + this.emit('clickNavTab', $(`#${e.target.id}`)); }); } From 10a03670bbd213ee3f86fae5be0f5504ef9b1d66 Mon Sep 17 00:00:00 2001 From: zigsong Date: Thu, 4 Mar 2021 23:55:13 +0900 Subject: [PATCH 29/52] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=EB=8F=99=EC=98=81=EC=83=81=EB=93=A4=EC=9D=84=20=EB=B3=BC=20?= =?UTF-8?q?=EC=98=81=EC=83=81=20=EB=AA=A9=EB=A1=9D=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- index.html | 33 --------------------------------- src/js/YoutubeController.js | 30 +++++++++++++++++++++++++++--- src/js/request.js | 14 +++++++++++++- src/js/utils/constants.js | 3 +++ src/js/utils/localStorage.js | 6 +++--- src/js/views/SavedVideosView.js | 22 ++++++++++++++++++++++ 7 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 src/js/views/SavedVideosView.js diff --git a/README.md b/README.md index a14ecea2b..70d67c6c4 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ c

- [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. - [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. - [x] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. - - [ ] 저장한 동영상들을 `볼 영상` 목록에 추가한다. + - [x] 저장한 동영상들을 `볼 영상` 목록에 추가한다. + - [ ] 이미 저장된 동영상의 경우 저장 버튼이 보이지 않는다. ### 👾 TODO(TEST) - step1 - [x] 클릭한 탭의 색을 하이라이트한다. diff --git a/index.html b/index.html index 32a6b2b34..03db68b94 100644 --- a/index.html +++ b/index.html @@ -22,39 +22,6 @@

👩🏻‍💻 나만의 유튜브 강의실
-
-
- -
-
-

아두이노 무드등

-
- - 메이커준 - -
-

2021년 3월 2일

-
-
- - 👍 - 💬 - 🗑️ -
-
-
-
diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index 54bee6e01..c56497a9d 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -2,9 +2,10 @@ import { $ } from './utils/dom.js'; import { setRecentChip, getRecentKeywords, - setSavedVideos, + setSavedVideoId, + getSavedVideoIds, } from './utils/localStorage.js'; -import { searchRequest } from '../js/request.js'; +import { searchRequest, videoRequest } from '../js/request.js'; import NavigationView from './views/NavigationView.js'; import SearchModalView from './views/SearchModalView.js'; import SavedVideosView from './views/SavedVideosView.js'; @@ -17,10 +18,12 @@ export default class YoutubeController { this.selectedTab = $('#saved-btn'); this.navigationView = new NavigationView($('#nav-bar')); this.searchModalView = new SearchModalView($('.modal')); + this.savedVideosView = new SavedVideosView($('#main-videos')); } init() { this.bindEvents(); + this.loadSavedVideos(); } bindEvents() { @@ -60,7 +63,10 @@ export default class YoutubeController { } saveVideo(videoId) { - setSavedVideos(videoId); + const videoToSave = this.videos.find((video) => video.id === videoId); + + this.savedVideosView.addSavedVideoClip(videoToSave); + setSavedVideoId(videoId); } searchVideo(keyword) { @@ -69,4 +75,22 @@ export default class YoutubeController { this.searchModalView.startSearch(); searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } + + generateSavedVideos(response) { + const { items } = response; + + const savedVideos = [ + ...items.map((item) => new Video(item.id, item.snippet)), + ]; + + this.savedVideosView.renderSavedVideoClips(savedVideos); + } + + loadSavedVideos() { + const savedVideoIds = getSavedVideoIds(); + + if (savedVideoIds.length === 0) return; + + videoRequest(savedVideoIds, this.generateSavedVideos.bind(this)); + } } diff --git a/src/js/request.js b/src/js/request.js index 97c4e6c19..9042b9b73 100644 --- a/src/js/request.js +++ b/src/js/request.js @@ -1,5 +1,5 @@ import youtubeKey from '../../youtubeAPI.js'; -import { SEARCH_URL } from '../js/utils/constants.js'; +import { SEARCH_URL, VIDEO_URL } from '../js/utils/constants.js'; export const searchRequest = (keyword, pageToken, callback) => { const requestURL = pageToken @@ -14,3 +14,15 @@ export const searchRequest = (keyword, pageToken, callback) => { callback(res); }); }; + +export const videoRequest = (videoIds, callback) => { + const requestURL = `${VIDEO_URL}&key=${youtubeKey}&id=${videoIds.join(',')}`; + + fetch(requestURL) + .then((response) => { + return response.json(); + }) + .then(function (res) { + callback(res); + }); +}; diff --git a/src/js/utils/constants.js b/src/js/utils/constants.js index 5d5c59dcc..6f659b377 100644 --- a/src/js/utils/constants.js +++ b/src/js/utils/constants.js @@ -1,6 +1,9 @@ export const SEARCH_URL = 'https://www.googleapis.com/youtube/v3/search?type=video&part=snippet&maxResults=10'; +export const VIDEO_URL = + 'https://www.googleapis.com/youtube/v3/videos?part=snippet'; + export const VALUE = { KEYWORD_COUNT: 3, CLIPS_PER_SCROLL: 10, diff --git a/src/js/utils/localStorage.js b/src/js/utils/localStorage.js index 54b61b37b..b4155160b 100644 --- a/src/js/utils/localStorage.js +++ b/src/js/utils/localStorage.js @@ -21,14 +21,14 @@ export function getRecentKeywords() { : []; } -export function setSavedVideos(videoId) { - const savedVideos = getSavedVideos(); +export function setSavedVideoId(videoId) { + const savedVideos = getSavedVideoIds(); savedVideos.push(videoId); localStorage.setItem('savedVideos', JSON.stringify(savedVideos)); } -export function getSavedVideos() { +export function getSavedVideoIds() { return localStorage.getItem('savedVideos') ? JSON.parse(localStorage.getItem('savedVideos')) : []; diff --git a/src/js/views/SavedVideosView.js b/src/js/views/SavedVideosView.js new file mode 100644 index 000000000..740dc2234 --- /dev/null +++ b/src/js/views/SavedVideosView.js @@ -0,0 +1,22 @@ +import { $ } from '../utils/dom.js'; +import { getSavedVideoIds } from '../utils/localStorage.js'; +import clipMaker from '../utils/clipMaker.js'; +import View from './View.js'; + +export default class SavedVideosView extends View { + constructor($element) { + super($element); + } + + renderSavedVideoClips(videos) { + const savedVideoClips = videos + .map((video) => clipMaker(video, { isModal: false })) + .join(''); + + $('#main-videos').addInnerHTML(savedVideoClips); + } + + addSavedVideoClip(video) { + $('#main-videos').addInnerHTML(clipMaker(video, { isModal: false })); + } +} From 87dc8331ef38823f0c0934eaf6a8e991c893805c Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 00:33:20 +0900 Subject: [PATCH 30/52] =?UTF-8?q?test:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EB=8F=99=EC=98=81=EC=83=81=EC=9D=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=A9=B4=20=EB=8F=99=EC=98=81=EC=83=81?= =?UTF-8?q?=EC=9D=84=20=EB=B3=BC=20=EC=98=81=EC=83=81=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80=ED=95=98=EB=8A=94=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- cypress/integration/myYouTube.spec.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70d67c6c4..7c95aa187 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ c

- [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. - [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. - [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. -- [ ] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. +- [x] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. ### 🎯🎯 step2 강의실 관리 기능 diff --git a/cypress/integration/myYouTube.spec.js b/cypress/integration/myYouTube.spec.js index 9721305ae..85e65d012 100644 --- a/cypress/integration/myYouTube.spec.js +++ b/cypress/integration/myYouTube.spec.js @@ -53,11 +53,21 @@ describe('simba-tube', () => { cy.get('.not-found').should('be.visible'); }); - it.only('검색 후 스크롤를 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다.', () => { + it('검색 후 스크롤를 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다.', () => { cy.get('#search-btn').click(); cy.get('#modal-search-input').type('방탄소년단'); cy.get('#modal-search-button').click(); cy.get('#modal-videos').scrollTo('bottom'); cy.get('#modal-videos').find('.clip').should('have.length', 20); }); + + it.only('검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 볼 영상 목록에 보여준다.', () => { + cy.get('#search-btn').click(); + cy.get('#modal-search-input').type('방탄소년단'); + cy.get('#modal-search-button').click(); + cy.get('.clip-save-btn').eq(0).click(); + + cy.get('#saved-video-count').should('have.text', 1); + cy.get('#main-videos').find('.clip').should('have.length', 1); + }); }); From f28e78a8ddb64550ee080f642cd8860c45aa9399 Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 00:35:09 +0900 Subject: [PATCH 31/52] =?UTF-8?q?feat:=20=EB=8F=99=EC=98=81=EC=83=81=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EC=8B=9C=20=ED=95=B4=EB=8B=B9=20=EB=8F=99?= =?UTF-8?q?=EC=98=81=EC=83=81=EC=9D=98=20=EC=A0=80=EC=9E=A5=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94,=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B0=9C=EC=88=98=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- index.html | 2 +- src/js/utils/clipMaker.js | 13 ++++++++++--- src/js/utils/dom.js | 4 ++++ src/js/views/SearchModalView.js | 23 +++++++++++++++++++---- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7c95aa187..81ed340a4 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,8 @@ c

- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. - [x] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. - [x] 저장한 동영상들을 `볼 영상` 목록에 추가한다. - - [ ] 이미 저장된 동영상의 경우 저장 버튼이 보이지 않는다. + - [x] 이미 저장된 동영상의 경우 저장 버튼을 비활성화한다. + - [x] 저장한 동영상의 개수를 업데이트한다. ### 👾 TODO(TEST) - step1 - [x] 클릭한 탭의 색을 하이라이트한다. diff --git a/index.html b/index.html index 03db68b94..c5bf981ad 100644 --- a/index.html +++ b/index.html @@ -46,7 +46,7 @@

🔎 유튜브 검색

- 저장된 영상 갯수: 50개 + 저장된 영상 개수:
diff --git a/src/js/utils/clipMaker.js b/src/js/utils/clipMaker.js index 294c4b551..8c873f397 100644 --- a/src/js/utils/clipMaker.js +++ b/src/js/utils/clipMaker.js @@ -1,4 +1,6 @@ export default function clipMaker(video, type) { + const { isModal, isSaved } = type; + return `
@@ -26,15 +28,20 @@ export default function clipMaker(video, type) {
- ${type.isModal ? saveButtonTemplate(video.id) : buttonPackTemplate()} + ${isModal ? saveButtonTemplate(video.id, isSaved) : buttonPackTemplate()}
`; } -function saveButtonTemplate(videoId) { +function saveButtonTemplate(videoId, isSaved) { return `
- +
`; } diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index 3d7f75c68..f48d2c00c 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -57,6 +57,10 @@ export const $ = (function () { return this.element.innerText; }; + constructor.prototype.setText = function (text) { + return (this.element.innerText = text); + }; + const instantiate = function (selector) { return new constructor(selector); }; diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index ba2d0ea05..16891b5eb 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -1,7 +1,7 @@ import { $ } from '../utils/dom.js'; import { VALUE } from '../utils/constants.js'; import clipMaker from '../utils/clipMaker.js'; -import { getRecentKeywords } from '../utils/localStorage.js'; +import { getRecentKeywords, getSavedVideoIds } from '../utils/localStorage.js'; import View from './View.js'; export default class SearchModalView extends View { @@ -53,8 +53,11 @@ export default class SearchModalView extends View { bindSaveEvent() { $('.clip-save-btn').setEvent('click', (e) => { + e.target.setAttribute('disabled', true); const videoId = e.target.dataset.videoId; + this.emit('clickSaveButton', videoId); + this.updateSavedCount(); }); } @@ -74,20 +77,32 @@ export default class SearchModalView extends View { openModal() { this.$element.addClass('open'); + this.updateSavedCount(); const latestKeyword = $('#chip-1').getText(); - if (latestKeyword) this.emit('openModal', latestKeyword); } + updateSavedCount() { + const savedVideoIds = getSavedVideoIds(); + + $('#saved-video-count').setText(savedVideoIds.length); + } + closeModal() { this.$element.removeClass('open'); } renderVideoClips(videos) { $('.skeleton').hide(); + + const savedVideoIds = getSavedVideoIds(); const videoClips = videos - .map((video) => clipMaker(video, { isModal: true })) + .map((video) => { + const isSaved = savedVideoIds.includes(video.id); + + return clipMaker(video, { isModal: true, isSaved: isSaved }); + }) .join(''); this.modalVideos.addInnerHTML(videoClips); this.bindSaveEvent(); @@ -99,7 +114,7 @@ export default class SearchModalView extends View { skeletonTemplate() { return ` -
+

From 2ea95b8a41bee3226a04b676dd591d3e9383c35b Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 00:52:58 +0900 Subject: [PATCH 32/52] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=8B=9C=20=EC=B5=9C=EA=B7=BC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=96=B4=EB=A5=BC=20=EC=9C=A0=EC=A7=80=ED=95=98?= =?UTF-8?q?=EA=B3=A0,=20=EB=AA=A8=EB=8B=AC=EC=9D=84=20=EB=8B=AB=EC=9D=84?= =?UTF-8?q?=20=EB=95=8C=20=EA=B2=80=EC=83=89=20=EB=82=B4=EC=97=AD=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/YoutubeController.js | 2 ++ src/js/utils/dom.js | 4 ++++ src/js/views/SearchModalView.js | 11 ++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index c56497a9d..d46735692 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -37,6 +37,8 @@ export default class YoutubeController { } changeNavTab(currentTab) { + this.nextPageToken = null; + this.navigationView.toggleTabColor(this.selectedTab, currentTab); this.selectedTab = currentTab; diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index f48d2c00c..b591940c0 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -61,6 +61,10 @@ export const $ = (function () { return (this.element.innerText = text); }; + constructor.prototype.setValue = function (value) { + return (this.element.value = value); + }; + const instantiate = function (selector) { return new constructor(selector); }; diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index 16891b5eb..f281ce4b9 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -28,8 +28,6 @@ export default class SearchModalView extends View { e.preventDefault(); this.searchKeyword = e.target.elements.search.value; - - this.clearVideoClips(); this.emit('submitSearch', this.searchKeyword); }); } @@ -78,9 +76,16 @@ export default class SearchModalView extends View { openModal() { this.$element.addClass('open'); this.updateSavedCount(); + this.clearVideoClips(); const latestKeyword = $('#chip-1').getText(); - if (latestKeyword) this.emit('openModal', latestKeyword); + + if (latestKeyword) { + this.searchKeyword = latestKeyword; + + $('#modal-search-input').setValue(latestKeyword); + this.emit('openModal', latestKeyword); + } } updateSavedCount() { From 7e8d6cb56663f2a7029eedbd7ce6ce831fccba82 Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 00:56:58 +0900 Subject: [PATCH 33/52] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=EC=96=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=9E=85=EB=A0=A5=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=84=20=EC=8B=9C=20=EA=B2=BD=EA=B3=A0=EC=B0=BD=20=EB=9D=84?= =?UTF-8?q?=EC=9A=B0=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/YoutubeController.js | 7 +++++++ src/js/utils/constants.js | 4 ++++ src/js/utils/validator.js | 3 +++ 3 files changed, 14 insertions(+) create mode 100644 src/js/utils/validator.js diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index d46735692..ab896a521 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -5,6 +5,8 @@ import { setSavedVideoId, getSavedVideoIds, } from './utils/localStorage.js'; +import { isEmptySearchKeyword } from './utils/validator.js'; +import { ALERT_MESSAGES } from './utils/constants.js'; import { searchRequest, videoRequest } from '../js/request.js'; import NavigationView from './views/NavigationView.js'; import SearchModalView from './views/SearchModalView.js'; @@ -72,6 +74,11 @@ export default class YoutubeController { } searchVideo(keyword) { + if (isEmptySearchKeyword(keyword)) { + alert(ALERT_MESSAGES.EMPTY_SEARCH_KEYWORD); + return; + } + setRecentChip(keyword); this.searchModalView.updateChips(getRecentKeywords()); this.searchModalView.startSearch(); diff --git a/src/js/utils/constants.js b/src/js/utils/constants.js index 6f659b377..9d72015d7 100644 --- a/src/js/utils/constants.js +++ b/src/js/utils/constants.js @@ -9,3 +9,7 @@ export const VALUE = { CLIPS_PER_SCROLL: 10, THROTTLE_TIME: 1000, }; + +export const ALERT_MESSAGES = { + EMPTY_SEARCH_KEYWORD: '검색어를 입력해주세요', +}; diff --git a/src/js/utils/validator.js b/src/js/utils/validator.js new file mode 100644 index 000000000..39c2c8bf9 --- /dev/null +++ b/src/js/utils/validator.js @@ -0,0 +1,3 @@ +export function isEmptySearchKeyword(keyword) { + return keyword === ''; +} From 1115673f9734a3fd79290ac072b7b8d94b237d49 Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 16:34:32 +0900 Subject: [PATCH 34/52] =?UTF-8?q?feat:=20=EC=B5=9C=EA=B7=BC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=96=B4=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EA=B2=80=EC=83=89=EC=96=B4=EB=A1=9C=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/views/SearchModalView.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index f281ce4b9..05680813e 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -59,6 +59,17 @@ export default class SearchModalView extends View { }); } + bindChipsEvent() { + $('.chip').setEvent('click', (e) => { + const chipText = e.target.innerText; + $('#modal-search-input').setValue(chipText); + this.searchKeyword = chipText; + this.clearVideoClips(); + + this.emit('clickChip', chipText); + }); + } + chipTemplate(recentKeywords) { return recentKeywords .map( @@ -69,8 +80,9 @@ export default class SearchModalView extends View { updateChips() { const recentKeywords = getRecentKeywords(); - $('#chip-container').setInnerHTML(this.chipTemplate(recentKeywords)); + + this.bindChipsEvent(); } openModal() { From f1d3b39b26efa54e0617fa722583542760c80192 Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 16:37:01 +0900 Subject: [PATCH 35/52] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=8B=9C=EC=97=90=EB=A7=8C=20nextPageToke?= =?UTF-8?q?n=20=EA=B0=92=EC=9D=84=20=EC=9C=A0=EC=A7=80=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/YoutubeController.js | 53 ++++++++++++++++++++---------------- src/js/utils/dom.js | 2 +- src/js/utils/localStorage.js | 4 ++- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index ab896a521..e547ac182 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -33,14 +33,13 @@ export default class YoutubeController { this.searchModalView .on('openModal', (e) => this.searchVideo(e.detail)) .on('submitSearch', (e) => this.searchVideo(e.detail)) - .on('scrollResult', (e) => this.searchVideo(e.detail)) + .on('scrollResult', (e) => this.scrollVideo(e.detail)) .on('clickSaveButton', (e) => this.saveVideo(e.detail)) + .on('clickChip', (e) => this.searchVideo(e.detail)) .on('closeModal', () => this.changeNavTab($('#saved-btn'))); } changeNavTab(currentTab) { - this.nextPageToken = null; - this.navigationView.toggleTabColor(this.selectedTab, currentTab); this.selectedTab = currentTab; @@ -49,6 +48,24 @@ export default class YoutubeController { } } + generateSavedVideos(response) { + const { items } = response; + + const savedVideos = [ + ...items.map((item) => new Video(item.id, item.snippet)), + ]; + + this.savedVideosView.renderSavedVideoClips(savedVideos); + } + + loadSavedVideos() { + const savedVideoIds = getSavedVideoIds(); + + if (savedVideoIds.length === 0) return; + + videoRequest(savedVideoIds, this.generateSavedVideos.bind(this)); + } + generateVideos(response) { const { prevPageToken, nextPageToken, items } = response; @@ -66,14 +83,14 @@ export default class YoutubeController { this.searchModalView.renderVideoClips(newVideos); } - saveVideo(videoId) { - const videoToSave = this.videos.find((video) => video.id === videoId); - - this.savedVideosView.addSavedVideoClip(videoToSave); - setSavedVideoId(videoId); + scrollVideo(keyword) { + this.searchModalView.startSearch(); + searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } searchVideo(keyword) { + this.nextPageToken = null; + if (isEmptySearchKeyword(keyword)) { alert(ALERT_MESSAGES.EMPTY_SEARCH_KEYWORD); return; @@ -81,25 +98,15 @@ export default class YoutubeController { setRecentChip(keyword); this.searchModalView.updateChips(getRecentKeywords()); + this.searchModalView.clearVideoClips(); this.searchModalView.startSearch(); searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } - generateSavedVideos(response) { - const { items } = response; - - const savedVideos = [ - ...items.map((item) => new Video(item.id, item.snippet)), - ]; - - this.savedVideosView.renderSavedVideoClips(savedVideos); - } - - loadSavedVideos() { - const savedVideoIds = getSavedVideoIds(); - - if (savedVideoIds.length === 0) return; + saveVideo(videoId) { + const videoToSave = this.videos.find((video) => video.id === videoId); - videoRequest(savedVideoIds, this.generateSavedVideos.bind(this)); + this.savedVideosView.addSavedVideoClip(videoToSave); + setSavedVideoId(videoId); } } diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index b591940c0..debefc56a 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -40,7 +40,7 @@ export const $ = (function () { }; constructor.prototype.addInnerHTML = function (template) { - this.each((element) => (element.innerHTML += template)); + this.each((element) => element.insertAdjacentHTML('beforeend', template)); }; constructor.prototype.addClass = function (className) { diff --git a/src/js/utils/localStorage.js b/src/js/utils/localStorage.js index b4155160b..9b37756cf 100644 --- a/src/js/utils/localStorage.js +++ b/src/js/utils/localStorage.js @@ -4,7 +4,7 @@ export function setRecentChip(keyword) { const recentKeywords = getRecentKeywords(); if (recentKeywords.includes(keyword)) { - return; + recentKeywords.splice(recentKeywords.indexOf(keyword), 1); } if (recentKeywords.length >= VALUE.KEYWORD_COUNT) { @@ -24,6 +24,8 @@ export function getRecentKeywords() { export function setSavedVideoId(videoId) { const savedVideos = getSavedVideoIds(); + if (savedVideos.includes(videoId)) return; + savedVideos.push(videoId); localStorage.setItem('savedVideos', JSON.stringify(savedVideos)); } From c11d143485287a81dc54a6b5f423398cd198765f Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 16:51:22 +0900 Subject: [PATCH 36/52] =?UTF-8?q?test:=20=EC=B5=9C=EA=B7=BC=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=96=B4=EB=A5=BC=20=EB=88=84=EB=A5=B4=EB=A9=B4=20?= =?UTF-8?q?=ED=95=B4=EB=8B=B9=20=EA=B2=80=EC=83=89=EC=96=B4=EB=A1=9C=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=9D=84=20=EC=8B=A4=ED=96=89=ED=95=9C=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=EB=B3=B4=EC=97=AC=EC=A3=BC?= =?UTF-8?q?=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + cypress/integration/myYouTube.spec.js | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 81ed340a4..daa42d161 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ c

- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. - [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. - [x] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. +- [x] 최근 검색어를 누르면 해당 검색어로 검색을 실행한 결과를 보여준다. ### 🎯🎯 step2 강의실 관리 기능 diff --git a/cypress/integration/myYouTube.spec.js b/cypress/integration/myYouTube.spec.js index 85e65d012..31e0a3f3a 100644 --- a/cypress/integration/myYouTube.spec.js +++ b/cypress/integration/myYouTube.spec.js @@ -61,7 +61,7 @@ describe('simba-tube', () => { cy.get('#modal-videos').find('.clip').should('have.length', 20); }); - it.only('검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 볼 영상 목록에 보여준다.', () => { + it('검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 볼 영상 목록에 보여준다.', () => { cy.get('#search-btn').click(); cy.get('#modal-search-input').type('방탄소년단'); cy.get('#modal-search-button').click(); @@ -70,4 +70,18 @@ describe('simba-tube', () => { cy.get('#saved-video-count').should('have.text', 1); cy.get('#main-videos').find('.clip').should('have.length', 1); }); + + it.only('최근 검색어 클릭 시 해당 검색어로 검색을 한다.', () => { + cy.get('#search-btn').click(); + cy.get('#modal-search-input').type('방탄소년단'); + cy.get('#modal-search-button').click(); + + cy.get('#modal-search-input').clear().type('데이식스'); + cy.get('#modal-search-button').click(); + + cy.get('#chip-1').should('have.text', '데이식스'); + cy.get('#chip-2').click(); + cy.get('#modal-search-input').should('have.value', '방탄소년단'); + cy.get('#chip-1').should('have.text', '방탄소년단'); + }); }); From 1e45399303323490d159b5628c7cfc9dcd43d93b Mon Sep 17 00:00:00 2001 From: zigsong Date: Fri, 5 Mar 2021 17:40:21 +0900 Subject: [PATCH 37/52] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=20=ED=9B=84=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EA=B2=B0=EA=B3=BC=20=EC=B0=BD=EC=9D=98=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=EB=A5=BC=20=EC=B5=9C=EC=83=81?= =?UTF-8?q?=EB=8B=A8=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/YoutubeController.js | 2 + src/js/views/SearchModalView.js | 66 +++++++++++++++++---------------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index e547ac182..491886321 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -100,6 +100,8 @@ export default class YoutubeController { this.searchModalView.updateChips(getRecentKeywords()); this.searchModalView.clearVideoClips(); this.searchModalView.startSearch(); + this.searchModalView.scrollToTop(); + searchRequest(keyword, this.nextPageToken, this.generateVideos.bind(this)); } diff --git a/src/js/views/SearchModalView.js b/src/js/views/SearchModalView.js index 05680813e..b3628de8c 100644 --- a/src/js/views/SearchModalView.js +++ b/src/js/views/SearchModalView.js @@ -70,21 +70,6 @@ export default class SearchModalView extends View { }); } - chipTemplate(recentKeywords) { - return recentKeywords - .map( - (keyword, idx) => `${keyword}`, - ) - .join(''); - } - - updateChips() { - const recentKeywords = getRecentKeywords(); - $('#chip-container').setInnerHTML(this.chipTemplate(recentKeywords)); - - this.bindChipsEvent(); - } - openModal() { this.$element.addClass('open'); this.updateSavedCount(); @@ -106,27 +91,23 @@ export default class SearchModalView extends View { $('#saved-video-count').setText(savedVideoIds.length); } - closeModal() { - this.$element.removeClass('open'); + chipTemplate(recentKeywords) { + return recentKeywords + .map( + (keyword, idx) => `${keyword}`, + ) + .join(''); } - renderVideoClips(videos) { - $('.skeleton').hide(); - - const savedVideoIds = getSavedVideoIds(); - const videoClips = videos - .map((video) => { - const isSaved = savedVideoIds.includes(video.id); + updateChips() { + const recentKeywords = getRecentKeywords(); + $('#chip-container').setInnerHTML(this.chipTemplate(recentKeywords)); - return clipMaker(video, { isModal: true, isSaved: isSaved }); - }) - .join(''); - this.modalVideos.addInnerHTML(videoClips); - this.bindSaveEvent(); + this.bindChipsEvent(); } - clearVideoClips() { - this.modalVideos.setInnerHTML(''); + scrollToTop() { + this.modalVideos.each((container) => (container.scrollTop = 0)); } skeletonTemplate() { @@ -145,6 +126,25 @@ export default class SearchModalView extends View { ); } + renderVideoClips(videos) { + $('.skeleton').hide(); + + const savedVideoIds = getSavedVideoIds(); + const videoClips = videos + .map((video) => { + const isSaved = savedVideoIds.includes(video.id); + + return clipMaker(video, { isModal: true, isSaved: isSaved }); + }) + .join(''); + this.modalVideos.addInnerHTML(videoClips); + this.bindSaveEvent(); + } + + clearVideoClips() { + this.modalVideos.setInnerHTML(''); + } + showNoResult() { this.modalVideos.setInnerHTML( ` @@ -153,4 +153,8 @@ export default class SearchModalView extends View { `, ); } + + closeModal() { + this.$element.removeClass('open'); + } } From bb430b308e23e3c180df50f6ca1811bddcf33b17 Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 20:23:58 +0900 Subject: [PATCH 38/52] =?UTF-8?q?refator:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=97=90=EC=84=9C=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cypress/integration/myYouTube.spec.js | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/cypress/integration/myYouTube.spec.js b/cypress/integration/myYouTube.spec.js index 31e0a3f3a..63e41f640 100644 --- a/cypress/integration/myYouTube.spec.js +++ b/cypress/integration/myYouTube.spec.js @@ -4,6 +4,12 @@ describe('simba-tube', () => { cy.window().then((win) => cy.stub(win, 'alert').as('windowAlert')); }); + const searchVideo = (keyword) => { + cy.get('#search-btn').click(); + cy.get('#modal-search-input').type(keyword); + cy.get('#modal-search-button').click(); + }; + it('클릭한 탭의 색을 하이라이트한다.', () => { cy.get('#nav-bar > button').each((button) => { cy.wrap(button).click(); @@ -32,49 +38,38 @@ describe('simba-tube', () => { }); it('현재 검색한 검색어가 최근 검색어 목록에 남는다.', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('불닭'); - cy.get('#modal-search-button').click(); - cy.get('.chip').eq(0).should('have.text', '불닭'); + const keyword = '불닭'; + + searchVideo(keyword); + cy.get('.chip').eq(0).should('have.text', keyword); }); it('youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다.', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('불닭'); - cy.get('#modal-search-button').click(); + searchVideo('불닭'); cy.get('.skeleton').should('be.visible'); }); it('검색 결과가 없는 경우 결과 없음 이미지를 보여준다. ', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('sadffsdasdb'); - cy.get('#modal-search-button').click(); - + searchVideo('sadffsdasdb'); cy.get('.not-found').should('be.visible'); }); it('검색 후 스크롤를 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다.', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('방탄소년단'); - cy.get('#modal-search-button').click(); + searchVideo('방탄소년단'); cy.get('#modal-videos').scrollTo('bottom'); cy.get('#modal-videos').find('.clip').should('have.length', 20); }); it('검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 볼 영상 목록에 보여준다.', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('방탄소년단'); - cy.get('#modal-search-button').click(); + searchVideo('방탄소년단'); cy.get('.clip-save-btn').eq(0).click(); cy.get('#saved-video-count').should('have.text', 1); cy.get('#main-videos').find('.clip').should('have.length', 1); }); - it.only('최근 검색어 클릭 시 해당 검색어로 검색을 한다.', () => { - cy.get('#search-btn').click(); - cy.get('#modal-search-input').type('방탄소년단'); - cy.get('#modal-search-button').click(); + it('최근 검색어 클릭 시 해당 검색어로 검색을 한다.', () => { + searchVideo('방탄소년단'); cy.get('#modal-search-input').clear().type('데이식스'); cy.get('#modal-search-button').click(); From cfbfcd52b06c3ab2e9463fbe621ca9a6b2b6a92e Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 20:43:01 +0900 Subject: [PATCH 39/52] =?UTF-8?q?style:=20-=20EOL=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC=20-=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++ .prettierrc | 5 ++-- .vscode/settings.json | 9 +++++++ README.md | 38 ++++++++++++++++-------------- cypress/fixtures/example.json | 5 ---- cypress/plugins/index.js | 21 ----------------- cypress/support/commands.js | 25 -------------------- cypress/support/index.js | 20 ---------------- src/css/ui/modules/modal.css | 1 - src/css/ui/modules/skeleton.css | 8 +++---- src/css/ui/modules/youtubeCard.css | 1 - src/js/index.js | 1 - src/js/request.js | 12 +++++----- src/js/utils/dom.js | 2 +- 14 files changed, 48 insertions(+), 105 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 cypress/fixtures/example.json delete mode 100644 cypress/plugins/index.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js diff --git a/.gitignore b/.gitignore index e32353a81..35d4d3918 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,8 @@ dist # TernJS port file .tern-port youtubeAPI.js + +# Cypress +cypress/fixtures/ +cypress/plugins/ +cypress/support/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index fa7a6af4a..b23a40baf 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,6 @@ "printWidth": 80, "singleQuote": true, "arrowParens": "always", - "trailingComma": "all" -} \ No newline at end of file + "trailingComma": "all", + "endOfLine": "auto" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..f083aff40 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + // Set the default + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + // For ESLint + "source.fixAll.eslint": true + }, + "liveServer.settings.CustomBrowser": "chrome:PrivateMode" +} diff --git a/README.md b/README.md index daa42d161..f12527ca5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -c

+

level1 - 나만의 유튜브 강의실

@@ -34,34 +34,36 @@ c

- [ ] 최근 검색 키워드를 3개까지 화면상에 검색창 하단에 보여준다. ### ✅ TODO(FEAT) - step1 + - [x] eslint, prettier, cypress 환경설정 -- [x] TODO 작성 +- [x] TODO 작성 - [x] 클릭한 탭의 색을 하이라이트한다. - [x] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. -- [x] 검색 모달 창의 x 버튼을 누르면 검색 모달 창이 닫히고, 볼 영상 목록으로 돌아간다. -- [x] 최근 검색어에 목록에 최근 검색한 검색어가 3개 뜬다. -- [x] 가장 마지막에 검색한 검색 결과 동영상들을 보여준다. +- [x] 검색 모달 창의 x 버튼을 누르면 검색 모달 창이 닫히고, 볼 영상 목록으로 돌아간다. +- [x] 최근 검색어에 목록에 최근 검색한 검색어가 3개 뜬다. +- [x] 가장 마지막에 검색한 검색 결과 동영상들을 보여준다. - [x] 검색어를 입력 받을 수 있다. - [x] 검색을 실행하면 youtube api를 통해 사용자가 입력한 검색어로 검색 결과를 가져온다. -- [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. +- [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. - [x] youtube api를 통해 가져온 검색 결과를 10개씩 보여준다. -- [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. -- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. -- [x] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. - - [x] 저장한 동영상들을 `볼 영상` 목록에 추가한다. +- [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. +- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. +- [x] 검색 결과 동영상의 저장 버튼을 누르면 Web Storage에 저장한다. + - [x] 저장한 동영상들을 `볼 영상` 목록에 추가한다. - [x] 이미 저장된 동영상의 경우 저장 버튼을 비활성화한다. - - [x] 저장한 동영상의 개수를 업데이트한다. + - [x] 저장한 동영상의 개수를 업데이트한다. ### 👾 TODO(TEST) - step1 + - [x] 클릭한 탭의 색을 하이라이트한다. - [x] `동영상 검색` 버튼을 누르면 검색 모달 창이 열린다. -- [x] 검색 모달 창의 x 버튼을 누르면 검색 모달 창이 닫히고, 볼 영상 목록으로 돌아간다. -- [x] 현재 검색한 검색어가 최근 검색어 목록에 남는다. -- [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. -- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. -- [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. -- [x] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. -- [x] 최근 검색어를 누르면 해당 검색어로 검색을 실행한 결과를 보여준다. +- [x] 검색 모달 창의 x 버튼을 누르면 검색 모달 창이 닫히고, 볼 영상 목록으로 돌아간다. +- [x] 현재 검색한 검색어가 최근 검색어 목록에 남는다. +- [x] youtube api에서 결과를 가져오는 동안 skeleton card UI로 로딩 화면을 보여준다. +- [x] 검색 결과가 없는 경우 결과 없음 이미지를 보여준다. +- [x] 검색 후 스크롤을 끝까지 이동시킬 경우 api 추가 요청을 통해 검색 결과를 10개씩 더 보여준다. +- [x] 검색 결과 동영상의 저장 버튼을 누르면 저장한 동영상들을 `볼 영상` 목록에 보여준다. +- [x] 최근 검색어를 누르면 해당 검색어로 검색을 실행한 결과를 보여준다. ### 🎯🎯 step2 강의실 관리 기능 diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d9352..000000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index aa9918d21..000000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index ca4d256f3..000000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index d68db96df..000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/src/css/ui/modules/modal.css b/src/css/ui/modules/modal.css index 78a0904a6..773cb1b5d 100644 --- a/src/css/ui/modules/modal.css +++ b/src/css/ui/modules/modal.css @@ -21,7 +21,6 @@ transition: top 0.25s ease; width: 960px; margin: auto; - /* overflow: auto; */ background: #fff; border-radius: 5px; position: relative; diff --git a/src/css/ui/modules/skeleton.css b/src/css/ui/modules/skeleton.css index 9726e61da..433b7475c 100644 --- a/src/css/ui/modules/skeleton.css +++ b/src/css/ui/modules/skeleton.css @@ -1,7 +1,7 @@ .skeleton { - width: 236px; - } - + width: 236px; +} + .skeleton .image, .skeleton .line { background-image: linear-gradient( @@ -37,4 +37,4 @@ 100% { background-position: 320px; } -} \ No newline at end of file +} diff --git a/src/css/ui/modules/youtubeCard.css b/src/css/ui/modules/youtubeCard.css index 43cb98693..b1a83d143 100644 --- a/src/css/ui/modules/youtubeCard.css +++ b/src/css/ui/modules/youtubeCard.css @@ -1,7 +1,6 @@ .video-wrapper { display: grid; grid-template-columns: repeat(auto-fill, 236px); - /* grid-template-rows: repeat(auto-fill, 254px); */ grid-row-gap: 0px; grid-column-gap: 4px; justify-content: center; diff --git a/src/js/index.js b/src/js/index.js index a40bc1c49..9617d6869 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,4 +1,3 @@ -import { $ } from './utils/dom.js'; import YoutubeController from './YoutubeController.js'; const youtubeController = new YoutubeController(); diff --git a/src/js/request.js b/src/js/request.js index 9042b9b73..524c23752 100644 --- a/src/js/request.js +++ b/src/js/request.js @@ -1,7 +1,7 @@ import youtubeKey from '../../youtubeAPI.js'; import { SEARCH_URL, VIDEO_URL } from '../js/utils/constants.js'; -export const searchRequest = (keyword, pageToken, callback) => { +export function searchRequest(keyword, pageToken, callback) { const requestURL = pageToken ? `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}&pageToken=${pageToken}` : `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}`; @@ -10,19 +10,19 @@ export const searchRequest = (keyword, pageToken, callback) => { .then((response) => { return response.json(); }) - .then(function (res) { + .then((res) => { callback(res); }); -}; +} -export const videoRequest = (videoIds, callback) => { +export function videoRequest(videoIds, callback) { const requestURL = `${VIDEO_URL}&key=${youtubeKey}&id=${videoIds.join(',')}`; fetch(requestURL) .then((response) => { return response.json(); }) - .then(function (res) { + .then((res) => { callback(res); }); -}; +} diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index b3d6aeba6..debefc56a 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -70,4 +70,4 @@ export const $ = (function () { }; return instantiate; -})(); \ No newline at end of file +})(); From 894cd2a7265cbe4e515f11873fb6eb438610a59d Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 21:14:55 +0900 Subject: [PATCH 40/52] =?UTF-8?q?style:=20buttom=20type=EC=97=90=EC=84=9C?= =?UTF-8?q?=20submit=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index c5bf981ad..85bcf8e8b 100644 --- a/index.html +++ b/index.html @@ -13,16 +13,19 @@

👩🏻‍💻 나만의 유튜브 강의실 👨🏻‍💻

-
-
+
@@ -37,8 +40,16 @@

👩🏻‍💻 나만의 유튜브 강의실

🔎 유튜브 검색

최근 검색어: @@ -48,8 +59,7 @@

🔎 유튜브 검색

저장된 영상 개수:
- +
From 093ea91fb33c0ee1c9879292aa4ad3d8a4524fdb Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 21:15:50 +0900 Subject: [PATCH 41/52] =?UTF-8?q?refactor:=20Nav=20Tab=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EB=B3=84=EB=A1=9C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/YoutubeController.js | 28 +++++++++++++++++++++------- src/js/views/NavigationView.js | 12 ++++++++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/js/YoutubeController.js b/src/js/YoutubeController.js index 491886321..9d1de321f 100644 --- a/src/js/YoutubeController.js +++ b/src/js/YoutubeController.js @@ -29,23 +29,37 @@ export default class YoutubeController { } bindEvents() { - this.navigationView.on('clickNavTab', (e) => this.changeNavTab(e.detail)); + this.navigationView + .on('clickSaveTab', () => this.focusSavedTab()) + .on('clickWatchedTab', () => this.focusWatchedTab()) + .on('clickSearchTab', () => this.focusSearchTab()); this.searchModalView .on('openModal', (e) => this.searchVideo(e.detail)) .on('submitSearch', (e) => this.searchVideo(e.detail)) .on('scrollResult', (e) => this.scrollVideo(e.detail)) .on('clickSaveButton', (e) => this.saveVideo(e.detail)) .on('clickChip', (e) => this.searchVideo(e.detail)) - .on('closeModal', () => this.changeNavTab($('#saved-btn'))); + .on('closeModal', () => this.changeNavTab()); } - changeNavTab(currentTab) { + focusSavedTab() { + // TODO: saved tab 이동 시 기능 구현 + this.changeNavTab($('#saved-btn')); + } + + focusWatchedTab() { + // TODO: watched tab 이동 시 기능 구현 + this.changeNavTab($('#watched-btn')); + } + + focusSearchTab() { + this.searchModalView.openModal(); + this.changeNavTab($('#search-btn')); + } + + changeNavTab(currentTab = $('#saved-btn')) { this.navigationView.toggleTabColor(this.selectedTab, currentTab); this.selectedTab = currentTab; - - if (currentTab.element.id === 'search-btn') { - this.searchModalView.openModal(); - } } generateSavedVideos(response) { diff --git a/src/js/views/NavigationView.js b/src/js/views/NavigationView.js index 6e1d4cc53..4648de898 100644 --- a/src/js/views/NavigationView.js +++ b/src/js/views/NavigationView.js @@ -10,8 +10,16 @@ export default class NavigationView extends View { } bindTabEvents() { - this.tabButtons.setEvent('click', (e) => { - this.emit('clickNavTab', $(`#${e.target.id}`)); + $('#saved-btn').setEvent('click', () => { + this.emit('clickSavedTab'); + }); + + $('#watched-btn').setEvent('click', () => { + this.emit('clickWatchedTab'); + }); + + $('#search-btn').setEvent('click', () => { + this.emit('clickSearchTab'); }); } From 5a06db93bf4136292fee8cf842a744b3c5292a31 Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 21:23:33 +0900 Subject: [PATCH 42/52] =?UTF-8?q?refactor:=20api=20URL=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/request.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/js/request.js b/src/js/request.js index 524c23752..09c2617be 100644 --- a/src/js/request.js +++ b/src/js/request.js @@ -1,10 +1,14 @@ import youtubeKey from '../../youtubeAPI.js'; import { SEARCH_URL, VIDEO_URL } from '../js/utils/constants.js'; -export function searchRequest(keyword, pageToken, callback) { - const requestURL = pageToken +function generateSearchURL(keyword, pageToken) { + return pageToken ? `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}&pageToken=${pageToken}` : `${SEARCH_URL}&key=${youtubeKey}&q=${keyword}`; +} + +export function searchRequest(keyword, pageToken, callback) { + const requestURL = generateSearchURL(keyword, pageToken); fetch(requestURL) .then((response) => { @@ -15,8 +19,12 @@ export function searchRequest(keyword, pageToken, callback) { }); } +function generateVideoURL(videoIds) { + return `${VIDEO_URL}&key=${youtubeKey}&id=${videoIds.join(',')}`; +} + export function videoRequest(videoIds, callback) { - const requestURL = `${VIDEO_URL}&key=${youtubeKey}&id=${videoIds.join(',')}`; + const requestURL = generateVideoURL(videoIds); fetch(requestURL) .then((response) => { From f215adb0206e04cc05ee5c8612edcb1496ead760 Mon Sep 17 00:00:00 2001 From: 0imbean0 Date: Sat, 6 Mar 2021 21:33:56 +0900 Subject: [PATCH 43/52] =?UTF-8?q?stlye:=20=ED=83=9C=EA=B7=B8=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/utils/clipMaker.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/utils/clipMaker.js b/src/js/utils/clipMaker.js index 8c873f397..30766bfbd 100644 --- a/src/js/utils/clipMaker.js +++ b/src/js/utils/clipMaker.js @@ -19,6 +19,7 @@ export default function clipMaker(video, type) { ${video.channelTitle} @@ -36,7 +37,8 @@ export default function clipMaker(video, type) { function saveButtonTemplate(videoId, isSaved) { return `
-